ExtensionCompilerPass.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <?php
  2. /*
  3. * This file is part of the Sonata project.
  4. *
  5. * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sonata\AdminBundle\DependencyInjection\Compiler;
  11. use Symfony\Component\DependencyInjection\Definition;
  12. use Symfony\Component\DependencyInjection\ContainerBuilder;
  13. use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  14. use Symfony\Component\DependencyInjection\Reference;
  15. /**
  16. * Class ExtensionCompilerPass
  17. *
  18. * @package Sonata\AdminBundle\DependencyInjection\Compiler
  19. * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
  20. */
  21. class ExtensionCompilerPass implements CompilerPassInterface
  22. {
  23. /**
  24. * {@inheritDoc}
  25. */
  26. public function process(ContainerBuilder $container)
  27. {
  28. $universalExtensions = array();
  29. foreach ($container->findTaggedServiceIds('sonata.admin.extension') as $id => $tags) {
  30. foreach ($tags as $attributes) {
  31. $target = false;
  32. if (isset($attributes['target'])) {
  33. $target = $attributes['target'];
  34. }
  35. if (isset($attributes['global']) && $attributes['global']) {
  36. $universalExtensions[] = $id;
  37. }
  38. if (!$target || !$container->hasDefinition($target)) {
  39. continue;
  40. }
  41. $container
  42. ->getDefinition($target)
  43. ->addMethodCall('addExtension', array(new Reference($id)))
  44. ;
  45. }
  46. }
  47. $extensionConfig = $container->getParameter('sonata.admin.extension.map');
  48. $extensionMap = $this->flattenExtensionConfiguration($extensionConfig);
  49. foreach ($container->findTaggedServiceIds('sonata.admin') as $id => $attributes) {
  50. $admin = $container->getDefinition($id);
  51. foreach ($universalExtensions as $extension) {
  52. $admin->addMethodCall('addExtension', array(new Reference($extension)));
  53. }
  54. $extensions = $this->getExtensionsForAdmin($id, $admin, $container, $extensionMap);
  55. foreach ($extensions as $extension) {
  56. if (!$container->has($extension)) {
  57. throw new \InvalidArgumentException(sprintf('Unable to find extension service for id %s', $extension));
  58. }
  59. $admin->addMethodCall('addExtension', array(new Reference($extension)));
  60. }
  61. }
  62. }
  63. /**
  64. * @param string $id
  65. * @param Definition $admin
  66. * @param ContainerBuilder $container
  67. * @param array $extensionMap
  68. *
  69. * @return array
  70. */
  71. protected function getExtensionsForAdmin($id, Definition $admin, ContainerBuilder $container, array $extensionMap)
  72. {
  73. $extensions = array();
  74. $classReflection = $subjectReflection = null;
  75. $excludes = $extensionMap['excludes'];
  76. unset($extensionMap['excludes']);
  77. foreach ($extensionMap as $type => $subjects) {
  78. foreach ($subjects as $subject => $extensionList) {
  79. if ('admins' == $type) {
  80. if ($id == $subject) {
  81. $extensions = array_merge($extensions, $extensionList);
  82. }
  83. } else {
  84. $class = $this->getManagedClass($admin, $container);
  85. if (!class_exists($class)) {
  86. continue;
  87. }
  88. $classReflection = new \ReflectionClass($class);
  89. $subjectReflection = new \ReflectionClass($subject);
  90. }
  91. if ('instanceof' == $type) {
  92. if ($subjectReflection->getName() == $classReflection->getName() || $classReflection->isSubclassOf($subject)) {
  93. $extensions = array_merge($extensions, $extensionList);
  94. }
  95. }
  96. if ('implements' == $type) {
  97. if ($classReflection->implementsInterface($subject)) {
  98. $extensions = array_merge($extensions, $extensionList);
  99. }
  100. }
  101. if ('extends' == $type) {
  102. if ($classReflection->isSubclassOf($subject)) {
  103. $extensions = array_merge($extensions, $extensionList);
  104. }
  105. }
  106. if ('uses' == $type) {
  107. if ($this->hasTrait($classReflection, $subject)) {
  108. $extensions = array_merge($extensions, $extensionList);
  109. }
  110. }
  111. }
  112. }
  113. if (isset($excludes[$id])) {
  114. $extensions = array_diff($extensions, $excludes[$id]);
  115. }
  116. return $extensions;
  117. }
  118. /**
  119. * Resolves the class argument of the admin to an actual class (in case of %parameter%)
  120. *
  121. * @param Definition $admin
  122. * @param ContainerBuilder $container
  123. *
  124. * @return string
  125. */
  126. protected function getManagedClass(Definition $admin, ContainerBuilder $container)
  127. {
  128. return $container->getParameterBag()->resolveValue($admin->getArgument(1));
  129. }
  130. /**
  131. * @param array $config
  132. *
  133. * @return array
  134. */
  135. protected function flattenExtensionConfiguration(array $config)
  136. {
  137. $extensionMap = array(
  138. 'excludes' => array(),
  139. 'admins' => array(),
  140. 'implements' => array(),
  141. 'extends' => array(),
  142. 'instanceof' => array(),
  143. 'uses' => array(),
  144. );
  145. foreach ($config as $extension => $options) {
  146. foreach ($options as $key => $value) {
  147. foreach ($value as $source) {
  148. if (!isset($extensionMap[$key][$source])) {
  149. $extensionMap[$key][$source] = array();
  150. }
  151. array_push($extensionMap[$key][$source], $extension);
  152. }
  153. }
  154. }
  155. return $extensionMap;
  156. }
  157. protected function hasTrait(\ReflectionClass $class, $traitName)
  158. {
  159. if (in_array($traitName, $class->getTraitNames())) {
  160. return true;
  161. }
  162. if (!$parentClass = $class->getParentClass()) {
  163. return false;
  164. }
  165. return $this->hasTrait($parentClass, $traitName);
  166. }
  167. }