/
githubmirror
/
symfony
Обзор
Документация
Войти
/
githubmirror
/
symfony
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
8.2
src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php
218 строк
9 KB
Nicolas Grekas
[DependencyInjection] Various fixes and hardenings
18 май 2026, 19:42
18 май 2026, 19:42
742d852
Код
Авторство
О чём код?
<?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\DependencyInjection\Loader; use Symfony\Component\DependencyInjection\Attribute\When; use Symfony\Component\DependencyInjection\Attribute\WhenNot; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Loader\Configurator\App; use Symfony\Component\DependencyInjection\Loader\Configurator\AppReference; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; /** * PhpFileLoader loads service definitions from a PHP file. * * The PHP file is required and the $container variable can be * used within the file to change the container. * * @author Fabien Potencier <fabien@symfony.com> */ class PhpFileLoader extends FileLoader { use ContentLoaderTrait; protected bool $autoRegisterAliasesForSinglyImplementedInterfaces = false; public function load(mixed $resource, ?string $type = null): mixed { // the container and loader variables are exposed to the included file below $container = $this->container; $loader = $this; $path = $this->locator->locate($resource); $this->setCurrentDir(\dirname($path)); $this->container->fileExists($path); // Force load ContainerConfigurator to make env(), param() etc available. class_exists(ContainerConfigurator::class); // Expose AppReference::config() as App::config() if (!class_exists(App::class)) { class_alias(AppReference::class, App::class); } // the closure forbids access to the private scope in the included file $load = \Closure::bind(static function ($path, $env) use ($container, $loader, $resource, $type) { return include $path; }, null, null); $instanceof = $this->instanceof; $this->instanceof = []; try { if (1 === $result = $load($path, $this->env)) { $result = null; } if (\is_object($result) && \is_callable($result)) { $this->callConfigurator($result, new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource, $this->env), $path); } elseif (\is_array($result)) { ++$this->importing; try { $this->loadContent([ 'imports' => ContainerConfigurator::processValue($result['imports'] ?? []), 'parameters' => ContainerConfigurator::processValue($result['parameters'] ?? []), 'services' => ContainerConfigurator::processValue($result['services'] ?? [], true), ], $path); foreach ($result as $namespace => $config) { if (!\is_array($config)) { throw new InvalidArgumentException(\sprintf('The "%s" key should contain an array in "%s".', $namespace, $path)); } if (\in_array($namespace, ['imports', 'parameters', 'services'], true)) { continue; } if (str_starts_with($namespace, 'when@')) { $knownEnvs = $this->container->hasParameter('.container.known_envs') ? array_flip($this->container->getParameter('.container.known_envs')) : []; $this->container->setParameter('.container.known_envs', array_keys($knownEnvs + [substr($namespace, 5) => true])); continue; } $this->loadExtensionConfig($namespace, ContainerConfigurator::processValue($config)); } // per-env configuration if ($this->env && isset($result[$when = 'when@'.$this->env])) { if (!\is_array($result[$when])) { throw new InvalidArgumentException(\sprintf('The "%s" key should contain an array in "%s".', $when, $path)); } $this->loadContent([ 'imports' => ContainerConfigurator::processValue($result[$when]['imports'] ?? []), 'parameters' => ContainerConfigurator::processValue($result[$when]['parameters'] ?? []), 'services' => ContainerConfigurator::processValue($result[$when]['services'] ?? [], true), ], $path); foreach ($result[$when] as $namespace => $config) { if (!\is_array($config)) { throw new InvalidArgumentException(\sprintf('The "%s" key should contain an array in "%s".', $namespace, $path)); } if (!\in_array($namespace, ['imports', 'parameters', 'services'], true) && !str_starts_with($namespace, 'when@')) { $this->loadExtensionConfig($namespace, ContainerConfigurator::processValue($config)); } } } } finally { --$this->importing; } } $this->loadExtensionConfigs(); } finally { $this->instanceof = $instanceof; $this->registerAliasesForSinglyImplementedInterfaces(); } return null; } public function supports(mixed $resource, ?string $type = null): bool { if (!\is_string($resource)) { return false; } if (null === $type && 'php' === pathinfo($resource, \PATHINFO_EXTENSION)) { return true; } return 'php' === $type; } /** * Resolve the parameters to the $callback and execute it. * * @param-immediately-invoked-callable $callback */ private function callConfigurator(callable $callback, ContainerConfigurator $containerConfigurator, string $path): void { $callback = $callback(...); $arguments = []; $r = new \ReflectionFunction($callback); $excluded = true; $whenAttributes = $r->getAttributes(When::class, \ReflectionAttribute::IS_INSTANCEOF); $notWhenAttributes = $r->getAttributes(WhenNot::class, \ReflectionAttribute::IS_INSTANCEOF); if ($whenAttributes && $notWhenAttributes) { throw new LogicException('Using both #[When] and #[WhenNot] attributes on the same target is not allowed.'); } if (!$whenAttributes && !$notWhenAttributes) { $excluded = false; } foreach ($whenAttributes as $attribute) { if ($this->env === $attribute->newInstance()->env) { $excluded = false; break; } } foreach ($notWhenAttributes as $attribute) { if ($excluded = $this->env === $attribute->newInstance()->env) { break; } } if ($excluded) { return; } foreach ($r->getParameters() as $parameter) { $reflectionType = $parameter->getType(); if (!$reflectionType instanceof \ReflectionNamedType) { throw new \InvalidArgumentException(\sprintf('Could not resolve argument "$%s" for "%s". You must typehint it (for example with "%s" or "%s").', $parameter->getName(), $path, ContainerConfigurator::class, ContainerBuilder::class)); } $type = $reflectionType->getName(); switch ($type) { case ContainerConfigurator::class: $arguments[] = $containerConfigurator; break; case ContainerBuilder::class: $arguments[] = $this->container; break; case FileLoader::class: case self::class: $arguments[] = $this; break; case 'string': if (null !== $this->env && 'env' === $parameter->getName()) { $arguments[] = $this->env; break; } // no break default: throw new \InvalidArgumentException(\sprintf('Could not resolve argument "%s" for "%s".', $type.' $'.$parameter->getName(), $path)); } } ++$this->importing; try { $callback(...$arguments); } finally { --$this->importing; } } }