Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\HttpKernel\DependencyInjection; Chris@0: Chris@0: use Composer\Autoload\ClassLoader; Chris@0: use Symfony\Component\Debug\DebugClassLoader; Chris@0: use Symfony\Component\DependencyInjection\ContainerBuilder; Chris@0: use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; Chris@0: use Symfony\Component\HttpKernel\Kernel; Chris@0: Chris@0: /** Chris@0: * Sets the classes to compile in the cache for the container. Chris@0: * Chris@0: * @author Fabien Potencier Chris@0: */ Chris@0: class AddClassesToCachePass implements CompilerPassInterface Chris@0: { Chris@0: private $kernel; Chris@0: Chris@0: public function __construct(Kernel $kernel) Chris@0: { Chris@0: $this->kernel = $kernel; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function process(ContainerBuilder $container) Chris@0: { Chris@0: $classes = array(); Chris@0: $annotatedClasses = array(); Chris@0: foreach ($container->getExtensions() as $extension) { Chris@0: if ($extension instanceof Extension) { Chris@0: $classes = array_merge($classes, $extension->getClassesToCompile()); Chris@0: $annotatedClasses = array_merge($annotatedClasses, $extension->getAnnotatedClassesToCompile()); Chris@0: } Chris@0: } Chris@0: Chris@0: $classes = $container->getParameterBag()->resolveValue($classes); Chris@0: $annotatedClasses = $container->getParameterBag()->resolveValue($annotatedClasses); Chris@0: $existingClasses = $this->getClassesInComposerClassMaps(); Chris@0: Chris@0: $this->kernel->setClassCache($this->expandClasses($classes, $existingClasses)); Chris@0: $this->kernel->setAnnotatedClassCache($this->expandClasses($annotatedClasses, $existingClasses)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Expands the given class patterns using a list of existing classes. Chris@0: * Chris@0: * @param array $patterns The class patterns to expand Chris@0: * @param array $classes The existing classes to match against the patterns Chris@0: * Chris@0: * @return array A list of classes derivated from the patterns Chris@0: */ Chris@0: private function expandClasses(array $patterns, array $classes) Chris@0: { Chris@0: $expanded = array(); Chris@0: Chris@0: // Explicit classes declared in the patterns are returned directly Chris@0: foreach ($patterns as $key => $pattern) { Chris@0: if (substr($pattern, -1) !== '\\' && false === strpos($pattern, '*')) { Chris@0: unset($patterns[$key]); Chris@0: $expanded[] = ltrim($pattern, '\\'); Chris@0: } Chris@0: } Chris@0: Chris@0: // Match patterns with the classes list Chris@0: $regexps = $this->patternsToRegexps($patterns); Chris@0: Chris@0: foreach ($classes as $class) { Chris@0: $class = ltrim($class, '\\'); Chris@0: Chris@0: if ($this->matchAnyRegexps($class, $regexps)) { Chris@0: $expanded[] = $class; Chris@0: } Chris@0: } Chris@0: Chris@0: return array_unique($expanded); Chris@0: } Chris@0: Chris@0: private function getClassesInComposerClassMaps() Chris@0: { Chris@0: $classes = array(); Chris@0: Chris@0: foreach (spl_autoload_functions() as $function) { Chris@0: if (!is_array($function)) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: if ($function[0] instanceof DebugClassLoader) { Chris@0: $function = $function[0]->getClassLoader(); Chris@0: } Chris@0: Chris@0: if (is_array($function) && $function[0] instanceof ClassLoader) { Chris@0: $classes += array_filter($function[0]->getClassMap()); Chris@0: } Chris@0: } Chris@0: Chris@0: return array_keys($classes); Chris@0: } Chris@0: Chris@0: private function patternsToRegexps($patterns) Chris@0: { Chris@0: $regexps = array(); Chris@0: Chris@0: foreach ($patterns as $pattern) { Chris@0: // Escape user input Chris@0: $regex = preg_quote(ltrim($pattern, '\\')); Chris@0: Chris@0: // Wildcards * and ** Chris@0: $regex = strtr($regex, array('\\*\\*' => '.*?', '\\*' => '[^\\\\]*?')); Chris@0: Chris@0: // If this class does not end by a slash, anchor the end Chris@0: if (substr($regex, -1) !== '\\') { Chris@0: $regex .= '$'; Chris@0: } Chris@0: Chris@0: $regexps[] = '{^\\\\'.$regex.'}'; Chris@0: } Chris@0: Chris@0: return $regexps; Chris@0: } Chris@0: Chris@0: private function matchAnyRegexps($class, $regexps) Chris@0: { Chris@0: $blacklisted = false !== strpos($class, 'Test'); Chris@0: Chris@0: foreach ($regexps as $regex) { Chris@0: if ($blacklisted && false === strpos($regex, 'Test')) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: if (preg_match($regex, '\\'.$class)) { Chris@0: return true; Chris@0: } Chris@0: } Chris@0: Chris@0: return false; Chris@0: } Chris@0: }