ExtensionCompilerPass.php 5.8 KB

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