ExtensionCompilerPass.php 6.5 KB

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