No Description

AddClassesToCachePass.php 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  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 Symfony\Component\HttpKernel\DependencyInjection;
  11. use Composer\Autoload\ClassLoader;
  12. use Symfony\Component\Debug\DebugClassLoader;
  13. use Symfony\Component\DependencyInjection\ContainerBuilder;
  14. use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  15. use Symfony\Component\HttpKernel\Kernel;
  16. /**
  17. * Sets the classes to compile in the cache for the container.
  18. *
  19. * @author Fabien Potencier <fabien@symfony.com>
  20. */
  21. class AddClassesToCachePass implements CompilerPassInterface
  22. {
  23. private $kernel;
  24. public function __construct(Kernel $kernel)
  25. {
  26. $this->kernel = $kernel;
  27. }
  28. /**
  29. * {@inheritdoc}
  30. */
  31. public function process(ContainerBuilder $container)
  32. {
  33. $classes = array();
  34. $annotatedClasses = array();
  35. foreach ($container->getExtensions() as $extension) {
  36. if ($extension instanceof Extension) {
  37. $classes = array_merge($classes, $extension->getClassesToCompile());
  38. $annotatedClasses = array_merge($annotatedClasses, $extension->getAnnotatedClassesToCompile());
  39. }
  40. }
  41. $classes = $container->getParameterBag()->resolveValue($classes);
  42. $annotatedClasses = $container->getParameterBag()->resolveValue($annotatedClasses);
  43. $existingClasses = $this->getClassesInComposerClassMaps();
  44. $this->kernel->setClassCache($this->expandClasses($classes, $existingClasses));
  45. $this->kernel->setAnnotatedClassCache($this->expandClasses($annotatedClasses, $existingClasses));
  46. }
  47. /**
  48. * Expands the given class patterns using a list of existing classes.
  49. *
  50. * @param array $patterns The class patterns to expand
  51. * @param array $classes The existing classes to match against the patterns
  52. *
  53. * @return array A list of classes derivated from the patterns
  54. */
  55. private function expandClasses(array $patterns, array $classes)
  56. {
  57. $expanded = array();
  58. // Explicit classes declared in the patterns are returned directly
  59. foreach ($patterns as $key => $pattern) {
  60. if (substr($pattern, -1) !== '\\' && false === strpos($pattern, '*')) {
  61. unset($patterns[$key]);
  62. $expanded[] = ltrim($pattern, '\\');
  63. }
  64. }
  65. // Match patterns with the classes list
  66. $regexps = $this->patternsToRegexps($patterns);
  67. foreach ($classes as $class) {
  68. $class = ltrim($class, '\\');
  69. if ($this->matchAnyRegexps($class, $regexps)) {
  70. $expanded[] = $class;
  71. }
  72. }
  73. return array_unique($expanded);
  74. }
  75. private function getClassesInComposerClassMaps()
  76. {
  77. $classes = array();
  78. foreach (spl_autoload_functions() as $function) {
  79. if (!is_array($function)) {
  80. continue;
  81. }
  82. if ($function[0] instanceof DebugClassLoader) {
  83. $function = $function[0]->getClassLoader();
  84. }
  85. if (is_array($function) && $function[0] instanceof ClassLoader) {
  86. $classes += array_filter($function[0]->getClassMap());
  87. }
  88. }
  89. return array_keys($classes);
  90. }
  91. private function patternsToRegexps($patterns)
  92. {
  93. $regexps = array();
  94. foreach ($patterns as $pattern) {
  95. // Escape user input
  96. $regex = preg_quote(ltrim($pattern, '\\'));
  97. // Wildcards * and **
  98. $regex = strtr($regex, array('\\*\\*' => '.*?', '\\*' => '[^\\\\]*?'));
  99. // If this class does not end by a slash, anchor the end
  100. if (substr($regex, -1) !== '\\') {
  101. $regex .= '$';
  102. }
  103. $regexps[] = '{^\\\\'.$regex.'}';
  104. }
  105. return $regexps;
  106. }
  107. private function matchAnyRegexps($class, $regexps)
  108. {
  109. $blacklisted = false !== strpos($class, 'Test');
  110. foreach ($regexps as $regex) {
  111. if ($blacklisted && false === strpos($regex, 'Test')) {
  112. continue;
  113. }
  114. if (preg_match($regex, '\\'.$class)) {
  115. return true;
  116. }
  117. }
  118. return false;
  119. }
  120. }