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