vendor/symfony/dependency-injection/Loader/FileLoader.php line 55

Open in your IDE?
  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\DependencyInjection\Loader;
  11. use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
  12. use Symfony\Component\Config\Exception\LoaderLoadException;
  13. use Symfony\Component\Config\FileLocatorInterface;
  14. use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader;
  15. use Symfony\Component\Config\Loader\Loader;
  16. use Symfony\Component\Config\Resource\GlobResource;
  17. use Symfony\Component\DependencyInjection\Attribute\When;
  18. use Symfony\Component\DependencyInjection\ChildDefinition;
  19. use Symfony\Component\DependencyInjection\Compiler\RegisterAutoconfigureAttributesPass;
  20. use Symfony\Component\DependencyInjection\ContainerBuilder;
  21. use Symfony\Component\DependencyInjection\Definition;
  22. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  23. /**
  24.  * FileLoader is the abstract class used by all built-in loaders that are file based.
  25.  *
  26.  * @author Fabien Potencier <fabien@symfony.com>
  27.  */
  28. abstract class FileLoader extends BaseFileLoader
  29. {
  30.     public const ANONYMOUS_ID_REGEXP '/^\.\d+_[^~]*+~[._a-zA-Z\d]{7}$/';
  31.     protected $container;
  32.     protected $isLoadingInstanceof false;
  33.     protected $instanceof = [];
  34.     protected $interfaces = [];
  35.     protected $singlyImplemented = [];
  36.     protected $autoRegisterAliasesForSinglyImplementedInterfaces true;
  37.     public function __construct(ContainerBuilder $containerFileLocatorInterface $locatorstring $env null)
  38.     {
  39.         $this->container $container;
  40.         parent::__construct($locator$env);
  41.     }
  42.     /**
  43.      * {@inheritdoc}
  44.      *
  45.      * @param bool|string $ignoreErrors Whether errors should be ignored; pass "not_found" to ignore only when the loaded resource is not found
  46.      */
  47.     public function import($resourcestring $type null$ignoreErrors falsestring $sourceResource null$exclude null)
  48.     {
  49.         $args \func_get_args();
  50.         if ($ignoreNotFound 'not_found' === $ignoreErrors) {
  51.             $args[2] = false;
  52.         } elseif (!\is_bool($ignoreErrors)) {
  53.             throw new \TypeError(sprintf('Invalid argument $ignoreErrors provided to "%s::import()": boolean or "not_found" expected, "%s" given.', static::class, get_debug_type($ignoreErrors)));
  54.         }
  55.         try {
  56.             return parent::import(...$args);
  57.         } catch (LoaderLoadException $e) {
  58.             if (!$ignoreNotFound || !($prev $e->getPrevious()) instanceof FileLocatorFileNotFoundException) {
  59.                 throw $e;
  60.             }
  61.             foreach ($prev->getTrace() as $frame) {
  62.                 if ('import' === ($frame['function'] ?? null) && is_a($frame['class'] ?? ''Loader::class, true)) {
  63.                     break;
  64.                 }
  65.             }
  66.             if (__FILE__ !== $frame['file']) {
  67.                 throw $e;
  68.             }
  69.         }
  70.         return null;
  71.     }
  72.     /**
  73.      * Registers a set of classes as services using PSR-4 for discovery.
  74.      *
  75.      * @param Definition           $prototype A definition to use as template
  76.      * @param string               $namespace The namespace prefix of classes in the scanned directory
  77.      * @param string               $resource  The directory to look for classes, glob-patterns allowed
  78.      * @param string|string[]|null $exclude   A globbed path of files to exclude or an array of globbed paths of files to exclude
  79.      */
  80.     public function registerClasses(Definition $prototypestring $namespacestring $resource$exclude null)
  81.     {
  82.         if (!str_ends_with($namespace'\\')) {
  83.             throw new InvalidArgumentException(sprintf('Namespace prefix must end with a "\\": "%s".'$namespace));
  84.         }
  85.         if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+\\\\)++$/'$namespace)) {
  86.             throw new InvalidArgumentException(sprintf('Namespace is not a valid PSR-4 prefix: "%s".'$namespace));
  87.         }
  88.         $autoconfigureAttributes = new RegisterAutoconfigureAttributesPass();
  89.         $autoconfigureAttributes $autoconfigureAttributes->accept($prototype) ? $autoconfigureAttributes null;
  90.         $classes $this->findClasses($namespace$resource, (array) $exclude$autoconfigureAttributes);
  91.         // prepare for deep cloning
  92.         $serializedPrototype serialize($prototype);
  93.         foreach ($classes as $class => $errorMessage) {
  94.             if (null === $errorMessage && $autoconfigureAttributes && $this->env) {
  95.                 $r $this->container->getReflectionClass($class);
  96.                 $attribute null;
  97.                 foreach ($r->getAttributes(When::class) as $attribute) {
  98.                     if ($this->env === $attribute->newInstance()->env) {
  99.                         $attribute null;
  100.                         break;
  101.                     }
  102.                 }
  103.                 if (null !== $attribute) {
  104.                     continue;
  105.                 }
  106.             }
  107.             if (interface_exists($classfalse)) {
  108.                 $this->interfaces[] = $class;
  109.             } else {
  110.                 $this->setDefinition($class$definition unserialize($serializedPrototype));
  111.                 if (null !== $errorMessage) {
  112.                     $definition->addError($errorMessage);
  113.                     continue;
  114.                 }
  115.                 foreach (class_implements($classfalse) as $interface) {
  116.                     $this->singlyImplemented[$interface] = ($this->singlyImplemented[$interface] ?? $class) !== $class false $class;
  117.                 }
  118.             }
  119.         }
  120.         if ($this->autoRegisterAliasesForSinglyImplementedInterfaces) {
  121.             $this->registerAliasesForSinglyImplementedInterfaces();
  122.         }
  123.     }
  124.     public function registerAliasesForSinglyImplementedInterfaces()
  125.     {
  126.         foreach ($this->interfaces as $interface) {
  127.             if (!empty($this->singlyImplemented[$interface]) && !$this->container->has($interface)) {
  128.                 $this->container->setAlias($interface$this->singlyImplemented[$interface]);
  129.             }
  130.         }
  131.         $this->interfaces $this->singlyImplemented = [];
  132.     }
  133.     /**
  134.      * Registers a definition in the container with its instanceof-conditionals.
  135.      */
  136.     protected function setDefinition(string $idDefinition $definition)
  137.     {
  138.         $this->container->removeBindings($id);
  139.         if ($this->isLoadingInstanceof) {
  140.             if (!$definition instanceof ChildDefinition) {
  141.                 throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.'$idget_debug_type($definition)));
  142.             }
  143.             $this->instanceof[$id] = $definition;
  144.         } else {
  145.             $this->container->setDefinition($id$definition->setInstanceofConditionals($this->instanceof));
  146.         }
  147.     }
  148.     private function findClasses(string $namespacestring $pattern, array $excludePatterns, ?RegisterAutoconfigureAttributesPass $autoconfigureAttributes): array
  149.     {
  150.         $parameterBag $this->container->getParameterBag();
  151.         $excludePaths = [];
  152.         $excludePrefix null;
  153.         $excludePatterns $parameterBag->unescapeValue($parameterBag->resolveValue($excludePatterns));
  154.         foreach ($excludePatterns as $excludePattern) {
  155.             foreach ($this->glob($excludePatterntrue$resourcetruetrue) as $path => $info) {
  156.                 if (null === $excludePrefix) {
  157.                     $excludePrefix $resource->getPrefix();
  158.                 }
  159.                 // normalize Windows slashes and remove trailing slashes
  160.                 $excludePaths[rtrim(str_replace('\\''/'$path), '/')] = true;
  161.             }
  162.         }
  163.         $pattern $parameterBag->unescapeValue($parameterBag->resolveValue($pattern));
  164.         $classes = [];
  165.         $extRegexp '/\\.php$/';
  166.         $prefixLen null;
  167.         foreach ($this->glob($patterntrue$resourcefalsefalse$excludePaths) as $path => $info) {
  168.             if (null === $prefixLen) {
  169.                 $prefixLen \strlen($resource->getPrefix());
  170.                 if ($excludePrefix && !str_starts_with($excludePrefix$resource->getPrefix())) {
  171.                     throw new InvalidArgumentException(sprintf('Invalid "exclude" pattern when importing classes for "%s": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s).'$namespace$excludePattern$pattern));
  172.                 }
  173.             }
  174.             if (isset($excludePaths[str_replace('\\''/'$path)])) {
  175.                 continue;
  176.             }
  177.             if (!preg_match($extRegexp$path$m) || !$info->isReadable()) {
  178.                 continue;
  179.             }
  180.             $class $namespace.ltrim(str_replace('/''\\'substr($path$prefixLen, -\strlen($m[0]))), '\\');
  181.             if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/'$class)) {
  182.                 continue;
  183.             }
  184.             try {
  185.                 $r $this->container->getReflectionClass($class);
  186.             } catch (\ReflectionException $e) {
  187.                 $classes[$class] = $e->getMessage();
  188.                 continue;
  189.             }
  190.             // check to make sure the expected class exists
  191.             if (!$r) {
  192.                 throw new InvalidArgumentException(sprintf('Expected to find class "%s" in file "%s" while importing services from resource "%s", but it was not found! Check the namespace prefix used with the resource.'$class$path$pattern));
  193.             }
  194.             if ($r->isInstantiable() || $r->isInterface()) {
  195.                 $classes[$class] = null;
  196.             }
  197.             if ($autoconfigureAttributes && !$r->isInstantiable()) {
  198.                 $autoconfigureAttributes->processClass($this->container$r);
  199.             }
  200.         }
  201.         // track only for new & removed files
  202.         if ($resource instanceof GlobResource) {
  203.             $this->container->addResource($resource);
  204.         } else {
  205.             foreach ($resource as $path) {
  206.                 $this->container->fileExists($pathfalse);
  207.             }
  208.         }
  209.         return $classes;
  210.     }
  211. }