vendor/symfony/dependency-injection/Loader/PhpFileLoader.php line 151

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\Builder\ConfigBuilderGenerator;
  12. use Symfony\Component\Config\Builder\ConfigBuilderGeneratorInterface;
  13. use Symfony\Component\Config\Builder\ConfigBuilderInterface;
  14. use Symfony\Component\Config\FileLocatorInterface;
  15. use Symfony\Component\DependencyInjection\Attribute\When;
  16. use Symfony\Component\DependencyInjection\Container;
  17. use Symfony\Component\DependencyInjection\ContainerBuilder;
  18. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  19. use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
  20. use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
  21. use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
  22. /**
  23.  * PhpFileLoader loads service definitions from a PHP file.
  24.  *
  25.  * The PHP file is required and the $container variable can be
  26.  * used within the file to change the container.
  27.  *
  28.  * @author Fabien Potencier <fabien@symfony.com>
  29.  */
  30. class PhpFileLoader extends FileLoader
  31. {
  32.     protected $autoRegisterAliasesForSinglyImplementedInterfaces false;
  33.     private $generator;
  34.     public function __construct(ContainerBuilder $containerFileLocatorInterface $locator, ?string $env null, ?ConfigBuilderGeneratorInterface $generator null)
  35.     {
  36.         parent::__construct($container$locator$env);
  37.         $this->generator $generator;
  38.     }
  39.     /**
  40.      * {@inheritdoc}
  41.      */
  42.     public function load($resource, ?string $type null)
  43.     {
  44.         // the container and loader variables are exposed to the included file below
  45.         $container $this->container;
  46.         $loader $this;
  47.         $path $this->locator->locate($resource);
  48.         $this->setCurrentDir(\dirname($path));
  49.         $this->container->fileExists($path);
  50.         // the closure forbids access to the private scope in the included file
  51.         $load \Closure::bind(function ($path$env) use ($container$loader$resource$type) {
  52.             return include $path;
  53.         }, $thisProtectedPhpFileLoader::class);
  54.         try {
  55.             $callback $load($path$this->env);
  56.             if (\is_object($callback) && \is_callable($callback)) {
  57.                 $this->executeCallback($callback, new ContainerConfigurator($this->container$this$this->instanceof$path$resource$this->env), $path);
  58.             }
  59.         } finally {
  60.             $this->instanceof = [];
  61.             $this->registerAliasesForSinglyImplementedInterfaces();
  62.         }
  63.         return null;
  64.     }
  65.     /**
  66.      * {@inheritdoc}
  67.      */
  68.     public function supports($resource, ?string $type null)
  69.     {
  70.         if (!\is_string($resource)) {
  71.             return false;
  72.         }
  73.         if (null === $type && 'php' === pathinfo($resource\PATHINFO_EXTENSION)) {
  74.             return true;
  75.         }
  76.         return 'php' === $type;
  77.     }
  78.     /**
  79.      * Resolve the parameters to the $callback and execute it.
  80.      */
  81.     private function executeCallback(callable $callbackContainerConfigurator $containerConfiguratorstring $path)
  82.     {
  83.         if (!$callback instanceof \Closure) {
  84.             $callback \Closure::fromCallable($callback);
  85.         }
  86.         $arguments = [];
  87.         $configBuilders = [];
  88.         $r = new \ReflectionFunction($callback);
  89.         if (\PHP_VERSION_ID >= 80000) {
  90.             $attribute null;
  91.             foreach ($r->getAttributes(When::class) as $attribute) {
  92.                 if ($this->env === $attribute->newInstance()->env) {
  93.                     $attribute null;
  94.                     break;
  95.                 }
  96.             }
  97.             if (null !== $attribute) {
  98.                 return;
  99.             }
  100.         }
  101.         foreach ($r->getParameters() as $parameter) {
  102.             $reflectionType $parameter->getType();
  103.             if (!$reflectionType instanceof \ReflectionNamedType) {
  104.                 throw new \InvalidArgumentException(sprintf('Could not resolve argument "$%s" for "%s". You must typehint it (for example with "%s" or "%s").'$parameter->getName(), $pathContainerConfigurator::class, ContainerBuilder::class));
  105.             }
  106.             $type $reflectionType->getName();
  107.             switch ($type) {
  108.                 case ContainerConfigurator::class:
  109.                     $arguments[] = $containerConfigurator;
  110.                     break;
  111.                 case ContainerBuilder::class:
  112.                     $arguments[] = $this->container;
  113.                     break;
  114.                 case FileLoader::class:
  115.                 case self::class:
  116.                     $arguments[] = $this;
  117.                     break;
  118.                 default:
  119.                     try {
  120.                         $configBuilder $this->configBuilder($type);
  121.                     } catch (InvalidArgumentException|\LogicException $e) {
  122.                         throw new \InvalidArgumentException(sprintf('Could not resolve argument "%s" for "%s".'$type.' $'.$parameter->getName(), $path), 0$e);
  123.                     }
  124.                     $configBuilders[] = $configBuilder;
  125.                     $arguments[] = $configBuilder;
  126.             }
  127.         }
  128.         // Force load ContainerConfigurator to make env(), param() etc available.
  129.         class_exists(ContainerConfigurator::class);
  130.         $callback(...$arguments);
  131.         /** @var ConfigBuilderInterface $configBuilder */
  132.         foreach ($configBuilders as $configBuilder) {
  133.             $containerConfigurator->extension($configBuilder->getExtensionAlias(), $configBuilder->toArray());
  134.         }
  135.     }
  136.     /**
  137.      * @param string $namespace FQCN string for a class implementing ConfigBuilderInterface
  138.      */
  139.     private function configBuilder(string $namespace): ConfigBuilderInterface
  140.     {
  141.         if (!class_exists(ConfigBuilderGenerator::class)) {
  142.             throw new \LogicException('You cannot use the config builder as the Config component is not installed. Try running "composer require symfony/config".');
  143.         }
  144.         if (null === $this->generator) {
  145.             throw new \LogicException('You cannot use the ConfigBuilders without providing a class implementing ConfigBuilderGeneratorInterface.');
  146.         }
  147.         // If class exists and implements ConfigBuilderInterface
  148.         if (class_exists($namespace) && is_subclass_of($namespaceConfigBuilderInterface::class)) {
  149.             return new $namespace();
  150.         }
  151.         // If it does not start with Symfony\Config\ we dont know how to handle this
  152.         if ('Symfony\\Config\\' !== substr($namespace015)) {
  153.             throw new InvalidArgumentException(sprintf('Could not find or generate class "%s".'$namespace));
  154.         }
  155.         // Try to get the extension alias
  156.         $alias Container::underscore(substr($namespace15, -6));
  157.         if (false !== strpos($alias'\\')) {
  158.             throw new InvalidArgumentException('You can only use "root" ConfigBuilders from "Symfony\\Config\\" namespace. Nested classes like "Symfony\\Config\\Framework\\CacheConfig" cannot be used.');
  159.         }
  160.         if (!$this->container->hasExtension($alias)) {
  161.             $extensions array_filter(array_map(function (ExtensionInterface $ext) { return $ext->getAlias(); }, $this->container->getExtensions()));
  162.             throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s". Looked for namespace "%s", found "%s".'$namespace$alias$extensions implode('", "'$extensions) : 'none'));
  163.         }
  164.         $extension $this->container->getExtension($alias);
  165.         if (!$extension instanceof ConfigurationExtensionInterface) {
  166.             throw new \LogicException(sprintf('You cannot use the config builder for "%s" because the extension does not implement "%s".'$namespaceConfigurationExtensionInterface::class));
  167.         }
  168.         $configuration $extension->getConfiguration([], $this->container);
  169.         $loader $this->generator->build($configuration);
  170.         return $loader();
  171.     }
  172. }
  173. /**
  174.  * @internal
  175.  */
  176. final class ProtectedPhpFileLoader extends PhpFileLoader
  177. {
  178. }