ヤミRoot VoidGate
User / IP
:
216.73.216.84
Host / Server
:
146.88.233.70 / dev.loger.cm
System
:
Linux hybrid1120.fr.ns.planethoster.net 3.10.0-957.21.2.el7.x86_64 #1 SMP Wed Jun 5 14:26:44 UTC 2019 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
logercm
/
dev.loger.cm
/
fixtures
/
assert
/
Viewing: dependency-injection.tar
Argument/AbstractArgument.php 0000644 00000001635 15120140333 0012302 0 ustar 00 <?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\Argument; /** * Represents an abstract service argument, which have to be set by a compiler pass or a DI extension. */ final class AbstractArgument { private $text; private $context; public function __construct(string $text = '') { $this->text = trim($text, '. '); } public function setContext(string $context): void { $this->context = $context.' is abstract'.('' === $this->text ? '' : ': '); } public function getText(): string { return $this->text; } public function getTextWithContext(): string { return $this->context.$this->text.'.'; } } Argument/ArgumentInterface.php 0000644 00000001072 15120140333 0012432 0 ustar 00 <?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\Argument; /** * Represents a complex argument containing nested values. * * @author Titouan Galopin <galopintitouan@gmail.com> */ interface ArgumentInterface { /** * @return array */ public function getValues(); public function setValues(array $values); } Argument/BoundArgument.php 0000644 00000002750 15120140333 0011605 0 ustar 00 <?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\Argument; /** * @author Guilhem Niot <guilhem.niot@gmail.com> */ final class BoundArgument implements ArgumentInterface { public const SERVICE_BINDING = 0; public const DEFAULTS_BINDING = 1; public const INSTANCEOF_BINDING = 2; private static $sequence = 0; private $value; private $identifier; private $used; private $type; private $file; public function __construct($value, bool $trackUsage = true, int $type = 0, string $file = null) { $this->value = $value; if ($trackUsage) { $this->identifier = ++self::$sequence; } else { $this->used = true; } $this->type = $type; $this->file = $file; } /** * {@inheritdoc} */ public function getValues(): array { return [$this->value, $this->identifier, $this->used, $this->type, $this->file]; } /** * {@inheritdoc} */ public function setValues(array $values) { if (5 === \count($values)) { [$this->value, $this->identifier, $this->used, $this->type, $this->file] = $values; } else { [$this->value, $this->identifier, $this->used] = $values; } } } Argument/IteratorArgument.php 0000644 00000001002 15120140333 0012314 0 ustar 00 <?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\Argument; /** * Represents a collection of values to lazily iterate over. * * @author Titouan Galopin <galopintitouan@gmail.com> */ class IteratorArgument implements ArgumentInterface { use ReferenceSetArgumentTrait; } Argument/ReferenceSetArgumentTrait.php 0000644 00000002435 15120140333 0014114 0 ustar 00 <?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\Argument; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; /** * @author Titouan Galopin <galopintitouan@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ trait ReferenceSetArgumentTrait { private $values; /** * @param Reference[] $values */ public function __construct(array $values) { $this->setValues($values); } /** * @return Reference[] */ public function getValues() { return $this->values; } /** * @param Reference[] $values The service references to put in the set */ public function setValues(array $values) { foreach ($values as $k => $v) { if (null !== $v && !$v instanceof Reference) { throw new InvalidArgumentException(sprintf('A "%s" must hold only Reference instances, "%s" given.', __CLASS__, get_debug_type($v))); } } $this->values = $values; } } Argument/RewindableGenerator.php 0000644 00000001624 15120140333 0012755 0 ustar 00 <?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\Argument; /** * @internal */ class RewindableGenerator implements \IteratorAggregate, \Countable { private $generator; private $count; /** * @param int|callable $count */ public function __construct(callable $generator, $count) { $this->generator = $generator; $this->count = $count; } public function getIterator(): \Traversable { $g = $this->generator; return $g(); } public function count(): int { if (\is_callable($count = $this->count)) { $this->count = $count(); } return $this->count; } } Argument/ServiceClosureArgument.php 0000644 00000002253 15120140333 0013471 0 ustar 00 <?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\Argument; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; /** * Represents a service wrapped in a memoizing closure. * * @author Nicolas Grekas <p@tchwork.com> */ class ServiceClosureArgument implements ArgumentInterface { private $values; public function __construct(Reference $reference) { $this->values = [$reference]; } /** * {@inheritdoc} */ public function getValues() { return $this->values; } /** * {@inheritdoc} */ public function setValues(array $values) { if ([0] !== array_keys($values) || !($values[0] instanceof Reference || null === $values[0])) { throw new InvalidArgumentException('A ServiceClosureArgument must hold one and only one Reference.'); } $this->values = $values; } } Argument/ServiceLocator.php 0000644 00000002407 15120140333 0011756 0 ustar 00 <?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\Argument; use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator; /** * @author Nicolas Grekas <p@tchwork.com> * * @internal */ class ServiceLocator extends BaseServiceLocator { private $factory; private $serviceMap; private $serviceTypes; public function __construct(\Closure $factory, array $serviceMap, array $serviceTypes = null) { $this->factory = $factory; $this->serviceMap = $serviceMap; $this->serviceTypes = $serviceTypes; parent::__construct($serviceMap); } /** * {@inheritdoc} * * @return mixed */ public function get(string $id) { return isset($this->serviceMap[$id]) ? ($this->factory)(...$this->serviceMap[$id]) : parent::get($id); } /** * {@inheritdoc} */ public function getProvidedServices(): array { return $this->serviceTypes ?? $this->serviceTypes = array_map(function () { return '?'; }, $this->serviceMap); } } Argument/ServiceLocatorArgument.php 0000644 00000002050 15120140333 0013453 0 ustar 00 <?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\Argument; use Symfony\Component\DependencyInjection\Reference; /** * Represents a closure acting as a service locator. * * @author Nicolas Grekas <p@tchwork.com> */ class ServiceLocatorArgument implements ArgumentInterface { use ReferenceSetArgumentTrait; private $taggedIteratorArgument; /** * @param Reference[]|TaggedIteratorArgument $values */ public function __construct($values = []) { if ($values instanceof TaggedIteratorArgument) { $this->taggedIteratorArgument = $values; $this->values = []; } else { $this->setValues($values); } } public function getTaggedIteratorArgument(): ?TaggedIteratorArgument { return $this->taggedIteratorArgument; } } Argument/TaggedIteratorArgument.php 0000644 00000005324 15120140333 0013443 0 ustar 00 <?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\Argument; /** * Represents a collection of services found by tag name to lazily iterate over. * * @author Roland Franssen <franssen.roland@gmail.com> */ class TaggedIteratorArgument extends IteratorArgument { private $tag; private $indexAttribute; private $defaultIndexMethod; private $defaultPriorityMethod; private $needsIndexes = false; /** * @param string $tag The name of the tag identifying the target services * @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection * @param string|null $defaultIndexMethod The static method that should be called to get each service's key when their tag doesn't define the previous attribute * @param bool $needsIndexes Whether indexes are required and should be generated when computing the map * @param string|null $defaultPriorityMethod The static method that should be called to get each service's priority when their tag doesn't define the "priority" attribute */ public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null) { parent::__construct([]); if (null === $indexAttribute && $needsIndexes) { $indexAttribute = preg_match('/[^.]++$/', $tag, $m) ? $m[0] : $tag; } $this->tag = $tag; $this->indexAttribute = $indexAttribute; $this->defaultIndexMethod = $defaultIndexMethod ?: ($indexAttribute ? 'getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute))).'Name' : null); $this->needsIndexes = $needsIndexes; $this->defaultPriorityMethod = $defaultPriorityMethod ?: ($indexAttribute ? 'getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute))).'Priority' : null); } public function getTag() { return $this->tag; } public function getIndexAttribute(): ?string { return $this->indexAttribute; } public function getDefaultIndexMethod(): ?string { return $this->defaultIndexMethod; } public function needsIndexes(): bool { return $this->needsIndexes; } public function getDefaultPriorityMethod(): ?string { return $this->defaultPriorityMethod; } } Attribute/AsTaggedItem.php 0000644 00000001216 15120140333 0011506 0 ustar 00 <?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\Attribute; /** * An attribute to tell under which index and priority a service class should be found in tagged iterators/locators. * * @author Nicolas Grekas <p@tchwork.com> */ #[\Attribute(\Attribute::TARGET_CLASS)] class AsTaggedItem { public function __construct( public ?string $index = null, public ?int $priority = null, ) { } } Attribute/Autoconfigure.php 0000644 00000001631 15120140333 0012023 0 ustar 00 <?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\Attribute; /** * An attribute to tell how a base type should be autoconfigured. * * @author Nicolas Grekas <p@tchwork.com> */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] class Autoconfigure { public function __construct( public ?array $tags = null, public ?array $calls = null, public ?array $bind = null, public bool|string|null $lazy = null, public ?bool $public = null, public ?bool $shared = null, public ?bool $autowire = null, public ?array $properties = null, public array|string|null $configurator = null, ) { } } Attribute/AutoconfigureTag.php 0000644 00000001336 15120140333 0012461 0 ustar 00 <?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\Attribute; /** * An attribute to tell how a base type should be tagged. * * @author Nicolas Grekas <p@tchwork.com> */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] class AutoconfigureTag extends Autoconfigure { public function __construct(string $name = null, array $attributes = []) { parent::__construct( tags: [ [$name ?? 0 => $attributes], ] ); } } Attribute/TaggedIterator.php 0000644 00000001122 15120140333 0012111 0 ustar 00 <?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\Attribute; #[\Attribute(\Attribute::TARGET_PARAMETER)] class TaggedIterator { public function __construct( public string $tag, public ?string $indexAttribute = null, public ?string $defaultIndexMethod = null, public ?string $defaultPriorityMethod = null, ) { } } Attribute/TaggedLocator.php 0000644 00000001121 15120140333 0011722 0 ustar 00 <?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\Attribute; #[\Attribute(\Attribute::TARGET_PARAMETER)] class TaggedLocator { public function __construct( public string $tag, public ?string $indexAttribute = null, public ?string $defaultIndexMethod = null, public ?string $defaultPriorityMethod = null, ) { } } Attribute/Target.php 0000644 00000003134 15120140333 0010437 0 ustar 00 <?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\Attribute; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * An attribute to tell how a dependency is used and hint named autowiring aliases. * * @author Nicolas Grekas <p@tchwork.com> */ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class Target { /** * @var string */ public $name; public function __construct(string $name) { $this->name = lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $name)))); } public static function parseName(\ReflectionParameter $parameter): string { if (80000 > \PHP_VERSION_ID || !$target = $parameter->getAttributes(self::class)[0] ?? null) { return $parameter->name; } $name = $target->newInstance()->name; if (!preg_match('/^[a-zA-Z_\x7f-\xff]/', $name)) { if (($function = $parameter->getDeclaringFunction()) instanceof \ReflectionMethod) { $function = $function->class.'::'.$function->name; } else { $function = $function->name; } throw new InvalidArgumentException(sprintf('Invalid #[Target] name "%s" on parameter "$%s" of "%s()": the first character must be a letter.', $name, $parameter->name, $function)); } return $name; } } Attribute/When.php 0000644 00000001225 15120140333 0010111 0 ustar 00 <?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\Attribute; /** * An attribute to tell under which environment this class should be registered as a service. * * @author Nicolas Grekas <p@tchwork.com> */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_FUNCTION | \Attribute::IS_REPEATABLE)] class When { public function __construct( public string $env, ) { } } Compiler/Compiler.php 0000644 00000005244 15120140333 0010576 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\EnvParameterException; /** * This class is used to remove circular dependencies between individual passes. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class Compiler { private $passConfig; private $log = []; private $serviceReferenceGraph; public function __construct() { $this->passConfig = new PassConfig(); $this->serviceReferenceGraph = new ServiceReferenceGraph(); } /** * @return PassConfig */ public function getPassConfig() { return $this->passConfig; } /** * @return ServiceReferenceGraph */ public function getServiceReferenceGraph() { return $this->serviceReferenceGraph; } public function addPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { $this->passConfig->addPass($pass, $type, $priority); } /** * @final */ public function log(CompilerPassInterface $pass, string $message) { if (str_contains($message, "\n")) { $message = str_replace("\n", "\n".\get_class($pass).': ', trim($message)); } $this->log[] = \get_class($pass).': '.$message; } /** * @return array */ public function getLog() { return $this->log; } /** * Run the Compiler and process all Passes. */ public function compile(ContainerBuilder $container) { try { foreach ($this->passConfig->getPasses() as $pass) { $pass->process($container); } } catch (\Exception $e) { $usedEnvs = []; $prev = $e; do { $msg = $prev->getMessage(); if ($msg !== $resolvedMsg = $container->resolveEnvPlaceholders($msg, null, $usedEnvs)) { $r = new \ReflectionProperty($prev, 'message'); $r->setAccessible(true); $r->setValue($prev, $resolvedMsg); } } while ($prev = $prev->getPrevious()); if ($usedEnvs) { $e = new EnvParameterException($usedEnvs, $e); } throw $e; } finally { $this->getServiceReferenceGraph()->clear(); } } } Compiler/CompilerPassInterface.php 0000644 00000001234 15120140333 0013241 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Interface that must be implemented by compilation passes. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface CompilerPassInterface { /** * You can modify the container here before it is dumped to PHP code. */ public function process(ContainerBuilder $container); } Compiler/DecoratorServicePass.php 0000644 00000012525 15120140333 0013116 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Reference; /** * Overwrites a service but keeps the overridden one. * * @author Christophe Coevoet <stof@notk.org> * @author Fabien Potencier <fabien@symfony.com> * @author Diego Saint Esteben <diego@saintesteben.me> */ class DecoratorServicePass extends AbstractRecursivePass { private $innerId = '.inner'; public function __construct(?string $innerId = '.inner') { if (0 < \func_num_args()) { trigger_deprecation('symfony/dependency-injection', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); } $this->innerId = $innerId; } public function process(ContainerBuilder $container) { $definitions = new \SplPriorityQueue(); $order = \PHP_INT_MAX; foreach ($container->getDefinitions() as $id => $definition) { if (!$decorated = $definition->getDecoratedService()) { continue; } $definitions->insert([$id, $definition], [$decorated[2], --$order]); } $decoratingDefinitions = []; $tagsToKeep = $container->hasParameter('container.behavior_describing_tags') ? $container->getParameter('container.behavior_describing_tags') : ['proxy', 'container.do_not_inline', 'container.service_locator', 'container.service_subscriber', 'container.service_subscriber.locator']; foreach ($definitions as [$id, $definition]) { $decoratedService = $definition->getDecoratedService(); [$inner, $renamedId] = $decoratedService; $invalidBehavior = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; $definition->setDecoratedService(null); if (!$renamedId) { $renamedId = $id.'.inner'; } $this->currentId = $renamedId; $this->processValue($definition); $definition->innerServiceId = $renamedId; $definition->decorationOnInvalid = $invalidBehavior; // we create a new alias/service for the service we are replacing // to be able to reference it in the new one if ($container->hasAlias($inner)) { $alias = $container->getAlias($inner); $public = $alias->isPublic(); $container->setAlias($renamedId, new Alias((string) $alias, false)); $decoratedDefinition = $container->findDefinition($alias); } elseif ($container->hasDefinition($inner)) { $decoratedDefinition = $container->getDefinition($inner); $public = $decoratedDefinition->isPublic(); $decoratedDefinition->setPublic(false); $container->setDefinition($renamedId, $decoratedDefinition); $decoratingDefinitions[$inner] = $decoratedDefinition; } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) { $container->removeDefinition($id); continue; } elseif (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) { $public = $definition->isPublic(); $decoratedDefinition = null; } else { throw new ServiceNotFoundException($inner, $id); } if ($decoratedDefinition && $decoratedDefinition->isSynthetic()) { throw new InvalidArgumentException(sprintf('A synthetic service cannot be decorated: service "%s" cannot decorate "%s".', $id, $inner)); } if (isset($decoratingDefinitions[$inner])) { $decoratingDefinition = $decoratingDefinitions[$inner]; $decoratingTags = $decoratingDefinition->getTags(); $resetTags = []; // Behavior-describing tags must not be transferred out to decorators foreach ($tagsToKeep as $containerTag) { if (isset($decoratingTags[$containerTag])) { $resetTags[$containerTag] = $decoratingTags[$containerTag]; unset($decoratingTags[$containerTag]); } } $definition->setTags(array_merge($decoratingTags, $definition->getTags())); $decoratingDefinition->setTags($resetTags); $decoratingDefinitions[$inner] = $definition; } $container->setAlias($inner, $id)->setPublic($public); } } protected function processValue($value, bool $isRoot = false) { if ($value instanceof Reference && $this->innerId === (string) $value) { return new Reference($this->currentId, $value->getInvalidBehavior()); } return parent::processValue($value, $isRoot); } } Compiler/DefinitionErrorExceptionPass.php 0000644 00000003516 15120140333 0014634 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; /** * Throws an exception for any Definitions that have errors and still exist. * * @author Ryan Weaver <ryan@knpuniversity.com> */ class DefinitionErrorExceptionPass extends AbstractRecursivePass { /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if (!$value instanceof Definition || !$value->hasErrors()) { return parent::processValue($value, $isRoot); } if ($isRoot && !$value->isPublic()) { $graph = $this->container->getCompiler()->getServiceReferenceGraph(); $runtimeException = false; foreach ($graph->getNode($this->currentId)->getInEdges() as $edge) { if (!$edge->getValue() instanceof Reference || ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE !== $edge->getValue()->getInvalidBehavior()) { $runtimeException = false; break; } $runtimeException = true; } if ($runtimeException) { return parent::processValue($value, $isRoot); } } // only show the first error so the user can focus on it $errors = $value->getErrors(); $message = reset($errors); throw new RuntimeException($message); } } Compiler/ExtensionCompilerPass.php 0000644 00000001574 15120140333 0013324 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * A pass to automatically process extensions if they implement * CompilerPassInterface. * * @author Wouter J <wouter@wouterj.nl> */ class ExtensionCompilerPass implements CompilerPassInterface { /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { foreach ($container->getExtensions() as $extension) { if (!$extension instanceof CompilerPassInterface) { continue; } $extension->process($container); } } } Compiler/InlineServiceDefinitionsPass.php 0000644 00000016723 15120140333 0014612 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Reference; /** * Inline service definitions where this is possible. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class InlineServiceDefinitionsPass extends AbstractRecursivePass { private $analyzingPass; private $cloningIds = []; private $connectedIds = []; private $notInlinedIds = []; private $inlinedIds = []; private $notInlinableIds = []; private $graph; public function __construct(AnalyzeServiceReferencesPass $analyzingPass = null) { $this->analyzingPass = $analyzingPass; } public function process(ContainerBuilder $container) { $this->container = $container; if ($this->analyzingPass) { $analyzedContainer = new ContainerBuilder(); $analyzedContainer->setAliases($container->getAliases()); $analyzedContainer->setDefinitions($container->getDefinitions()); foreach ($container->getExpressionLanguageProviders() as $provider) { $analyzedContainer->addExpressionLanguageProvider($provider); } } else { $analyzedContainer = $container; } try { $remainingInlinedIds = []; $this->connectedIds = $this->notInlinedIds = $container->getDefinitions(); do { if ($this->analyzingPass) { $analyzedContainer->setDefinitions(array_intersect_key($analyzedContainer->getDefinitions(), $this->connectedIds)); $this->analyzingPass->process($analyzedContainer); } $this->graph = $analyzedContainer->getCompiler()->getServiceReferenceGraph(); $notInlinedIds = $this->notInlinedIds; $this->connectedIds = $this->notInlinedIds = $this->inlinedIds = []; foreach ($analyzedContainer->getDefinitions() as $id => $definition) { if (!$this->graph->hasNode($id)) { continue; } foreach ($this->graph->getNode($id)->getOutEdges() as $edge) { if (isset($notInlinedIds[$edge->getSourceNode()->getId()])) { $this->currentId = $id; $this->processValue($definition, true); break; } } } foreach ($this->inlinedIds as $id => $isPublicOrNotShared) { if ($isPublicOrNotShared) { $remainingInlinedIds[$id] = $id; } else { $container->removeDefinition($id); $analyzedContainer->removeDefinition($id); } } } while ($this->inlinedIds && $this->analyzingPass); foreach ($remainingInlinedIds as $id) { if (isset($this->notInlinableIds[$id])) { continue; } $definition = $container->getDefinition($id); if (!$definition->isShared() && !$definition->isPublic()) { $container->removeDefinition($id); } } } finally { $this->container = null; $this->connectedIds = $this->notInlinedIds = $this->inlinedIds = []; $this->notInlinableIds = []; $this->graph = null; } } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if ($value instanceof ArgumentInterface) { // References found in ArgumentInterface::getValues() are not inlineable return $value; } if ($value instanceof Definition && $this->cloningIds) { if ($value->isShared()) { return $value; } $value = clone $value; } if (!$value instanceof Reference) { return parent::processValue($value, $isRoot); } elseif (!$this->container->hasDefinition($id = (string) $value)) { return $value; } $definition = $this->container->getDefinition($id); if (!$this->isInlineableDefinition($id, $definition)) { $this->notInlinableIds[$id] = true; return $value; } $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); $this->inlinedIds[$id] = $definition->isPublic() || !$definition->isShared(); $this->notInlinedIds[$this->currentId] = true; if ($definition->isShared()) { return $definition; } if (isset($this->cloningIds[$id])) { $ids = array_keys($this->cloningIds); $ids[] = $id; throw new ServiceCircularReferenceException($id, \array_slice($ids, array_search($id, $ids))); } $this->cloningIds[$id] = true; try { return $this->processValue($definition); } finally { unset($this->cloningIds[$id]); } } /** * Checks if the definition is inlineable. */ private function isInlineableDefinition(string $id, Definition $definition): bool { if ($definition->hasErrors() || $definition->isDeprecated() || $definition->isLazy() || $definition->isSynthetic() || $definition->hasTag('container.do_not_inline')) { return false; } if (!$definition->isShared()) { if (!$this->graph->hasNode($id)) { return true; } foreach ($this->graph->getNode($id)->getInEdges() as $edge) { $srcId = $edge->getSourceNode()->getId(); $this->connectedIds[$srcId] = true; if ($edge->isWeak() || $edge->isLazy()) { return !$this->connectedIds[$id] = true; } } return true; } if ($definition->isPublic()) { return false; } if (!$this->graph->hasNode($id)) { return true; } if ($this->currentId == $id) { return false; } $this->connectedIds[$id] = true; $srcIds = []; $srcCount = 0; foreach ($this->graph->getNode($id)->getInEdges() as $edge) { $srcId = $edge->getSourceNode()->getId(); $this->connectedIds[$srcId] = true; if ($edge->isWeak() || $edge->isLazy()) { return false; } $srcIds[$srcId] = true; ++$srcCount; } if (1 !== \count($srcIds)) { $this->notInlinedIds[$id] = true; return false; } if ($srcCount > 1 && \is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) { return false; } return $this->container->getDefinition($srcId)->isShared(); } } Compiler/MergeExtensionConfigurationPass.php 0000644 00000020246 15120140333 0015336 0 ustar 00 <?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\Compiler; use Symfony\Component\Config\Definition\BaseNode; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; /** * Merges extension configs into the container builder. * * @author Fabien Potencier <fabien@symfony.com> */ class MergeExtensionConfigurationPass implements CompilerPassInterface { /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { $parameters = $container->getParameterBag()->all(); $definitions = $container->getDefinitions(); $aliases = $container->getAliases(); $exprLangProviders = $container->getExpressionLanguageProviders(); $configAvailable = class_exists(BaseNode::class); foreach ($container->getExtensions() as $extension) { if ($extension instanceof PrependExtensionInterface) { $extension->prepend($container); } } foreach ($container->getExtensions() as $name => $extension) { if (!$config = $container->getExtensionConfig($name)) { // this extension was not called continue; } $resolvingBag = $container->getParameterBag(); if ($resolvingBag instanceof EnvPlaceholderParameterBag && $extension instanceof Extension) { // create a dedicated bag so that we can track env vars per-extension $resolvingBag = new MergeExtensionConfigurationParameterBag($resolvingBag); if ($configAvailable) { BaseNode::setPlaceholderUniquePrefix($resolvingBag->getEnvPlaceholderUniquePrefix()); } } $config = $resolvingBag->resolveValue($config); try { $tmpContainer = new MergeExtensionConfigurationContainerBuilder($extension, $resolvingBag); $tmpContainer->setResourceTracking($container->isTrackingResources()); $tmpContainer->addObjectResource($extension); if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) { $tmpContainer->addObjectResource($configuration); } foreach ($exprLangProviders as $provider) { $tmpContainer->addExpressionLanguageProvider($provider); } $extension->load($config, $tmpContainer); } catch (\Exception $e) { if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { $container->getParameterBag()->mergeEnvPlaceholders($resolvingBag); } throw $e; } if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { // don't keep track of env vars that are *overridden* when configs are merged $resolvingBag->freezeAfterProcessing($extension, $tmpContainer); } $container->merge($tmpContainer); $container->getParameterBag()->add($parameters); } $container->addDefinitions($definitions); $container->addAliases($aliases); } } /** * @internal */ class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag { private $processedEnvPlaceholders; public function __construct(parent $parameterBag) { parent::__construct($parameterBag->all()); $this->mergeEnvPlaceholders($parameterBag); } public function freezeAfterProcessing(Extension $extension, ContainerBuilder $container) { if (!$config = $extension->getProcessedConfigs()) { // Extension::processConfiguration() wasn't called, we cannot know how configs were merged return; } $this->processedEnvPlaceholders = []; // serialize config and container to catch env vars nested in object graphs $config = serialize($config).serialize($container->getDefinitions()).serialize($container->getAliases()).serialize($container->getParameterBag()->all()); foreach (parent::getEnvPlaceholders() as $env => $placeholders) { foreach ($placeholders as $placeholder) { if (false !== stripos($config, $placeholder)) { $this->processedEnvPlaceholders[$env] = $placeholders; break; } } } } /** * {@inheritdoc} */ public function getEnvPlaceholders(): array { return $this->processedEnvPlaceholders ?? parent::getEnvPlaceholders(); } public function getUnusedEnvPlaceholders(): array { return null === $this->processedEnvPlaceholders ? [] : array_diff_key(parent::getEnvPlaceholders(), $this->processedEnvPlaceholders); } } /** * A container builder preventing using methods that wouldn't have any effect from extensions. * * @internal */ class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder { private $extensionClass; public function __construct(ExtensionInterface $extension, ParameterBagInterface $parameterBag = null) { parent::__construct($parameterBag); $this->extensionClass = \get_class($extension); } /** * {@inheritdoc} */ public function addCompilerPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0): self { throw new LogicException(sprintf('You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.', get_debug_type($pass), $this->extensionClass)); } /** * {@inheritdoc} */ public function registerExtension(ExtensionInterface $extension) { throw new LogicException(sprintf('You cannot register extension "%s" from "%s". Extensions must be registered before the container is compiled.', get_debug_type($extension), $this->extensionClass)); } /** * {@inheritdoc} */ public function compile(bool $resolveEnvPlaceholders = false) { throw new LogicException(sprintf('Cannot compile the container in extension "%s".', $this->extensionClass)); } /** * {@inheritdoc} */ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null) { if (true !== $format || !\is_string($value)) { return parent::resolveEnvPlaceholders($value, $format, $usedEnvs); } $bag = $this->getParameterBag(); $value = $bag->resolveValue($value); if (!$bag instanceof EnvPlaceholderParameterBag) { return parent::resolveEnvPlaceholders($value, $format, $usedEnvs); } foreach ($bag->getEnvPlaceholders() as $env => $placeholders) { if (!str_contains($env, ':')) { continue; } foreach ($placeholders as $placeholder) { if (false !== stripos($value, $placeholder)) { throw new RuntimeException(sprintf('Using a cast in "env(%s)" is incompatible with resolution at compile time in "%s". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead.', $env, $this->extensionClass)); } } } return parent::resolveEnvPlaceholders($value, $format, $usedEnvs); } } Compiler/PassConfig.php 0000644 00000016573 15120140333 0011067 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * Compiler Pass Configuration. * * This class has a default configuration embedded. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class PassConfig { public const TYPE_AFTER_REMOVING = 'afterRemoving'; public const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization'; public const TYPE_BEFORE_REMOVING = 'beforeRemoving'; public const TYPE_OPTIMIZE = 'optimization'; public const TYPE_REMOVE = 'removing'; private $mergePass; private $afterRemovingPasses = []; private $beforeOptimizationPasses = []; private $beforeRemovingPasses = []; private $optimizationPasses; private $removingPasses; public function __construct() { $this->mergePass = new MergeExtensionConfigurationPass(); $this->beforeOptimizationPasses = [ 100 => [ new ResolveClassPass(), new RegisterAutoconfigureAttributesPass(), new AttributeAutoconfigurationPass(), new ResolveInstanceofConditionalsPass(), new RegisterEnvVarProcessorsPass(), ], -1000 => [new ExtensionCompilerPass()], ]; $this->optimizationPasses = [[ $autoAliasServicePass = new AutoAliasServicePass(), new ValidateEnvPlaceholdersPass(), new ResolveDecoratorStackPass(), new ResolveChildDefinitionsPass(), new RegisterServiceSubscribersPass(), new ResolveParameterPlaceHoldersPass(false, false), new ResolveFactoryClassPass(), new ResolveNamedArgumentsPass(), new AutowireRequiredMethodsPass(), new AutowireRequiredPropertiesPass(), new ResolveBindingsPass(), new ServiceLocatorTagPass(), new DecoratorServicePass(), new CheckDefinitionValidityPass(), new AutowirePass(false), new ServiceLocatorTagPass(), new ResolveTaggedIteratorArgumentPass(), new ResolveServiceSubscribersPass(), new ResolveReferencesToAliasesPass(), new ResolveInvalidReferencesPass(), new AnalyzeServiceReferencesPass(true), new CheckCircularReferencesPass(), new CheckReferenceValidityPass(), new CheckArgumentsValidityPass(false), ]]; $this->removingPasses = [[ new RemovePrivateAliasesPass(), (new ReplaceAliasByActualDefinitionPass())->setAutoAliasServicePass($autoAliasServicePass), new RemoveAbstractDefinitionsPass(), new RemoveUnusedDefinitionsPass(), new AnalyzeServiceReferencesPass(), new CheckExceptionOnInvalidReferenceBehaviorPass(), new InlineServiceDefinitionsPass(new AnalyzeServiceReferencesPass()), new AnalyzeServiceReferencesPass(), new DefinitionErrorExceptionPass(), ]]; $this->afterRemovingPasses = [[ new ResolveHotPathPass(), new ResolveNoPreloadPass(), new AliasDeprecatedPublicServicesPass(), ]]; } /** * Returns all passes in order to be processed. * * @return CompilerPassInterface[] */ public function getPasses() { return array_merge( [$this->mergePass], $this->getBeforeOptimizationPasses(), $this->getOptimizationPasses(), $this->getBeforeRemovingPasses(), $this->getRemovingPasses(), $this->getAfterRemovingPasses() ); } /** * Adds a pass. * * @throws InvalidArgumentException when a pass type doesn't exist */ public function addPass(CompilerPassInterface $pass, string $type = self::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { $property = $type.'Passes'; if (!isset($this->$property)) { throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); } $passes = &$this->$property; if (!isset($passes[$priority])) { $passes[$priority] = []; } $passes[$priority][] = $pass; } /** * Gets all passes for the AfterRemoving pass. * * @return CompilerPassInterface[] */ public function getAfterRemovingPasses() { return $this->sortPasses($this->afterRemovingPasses); } /** * Gets all passes for the BeforeOptimization pass. * * @return CompilerPassInterface[] */ public function getBeforeOptimizationPasses() { return $this->sortPasses($this->beforeOptimizationPasses); } /** * Gets all passes for the BeforeRemoving pass. * * @return CompilerPassInterface[] */ public function getBeforeRemovingPasses() { return $this->sortPasses($this->beforeRemovingPasses); } /** * Gets all passes for the Optimization pass. * * @return CompilerPassInterface[] */ public function getOptimizationPasses() { return $this->sortPasses($this->optimizationPasses); } /** * Gets all passes for the Removing pass. * * @return CompilerPassInterface[] */ public function getRemovingPasses() { return $this->sortPasses($this->removingPasses); } /** * Gets the Merge pass. * * @return CompilerPassInterface */ public function getMergePass() { return $this->mergePass; } public function setMergePass(CompilerPassInterface $pass) { $this->mergePass = $pass; } /** * Sets the AfterRemoving passes. * * @param CompilerPassInterface[] $passes */ public function setAfterRemovingPasses(array $passes) { $this->afterRemovingPasses = [$passes]; } /** * Sets the BeforeOptimization passes. * * @param CompilerPassInterface[] $passes */ public function setBeforeOptimizationPasses(array $passes) { $this->beforeOptimizationPasses = [$passes]; } /** * Sets the BeforeRemoving passes. * * @param CompilerPassInterface[] $passes */ public function setBeforeRemovingPasses(array $passes) { $this->beforeRemovingPasses = [$passes]; } /** * Sets the Optimization passes. * * @param CompilerPassInterface[] $passes */ public function setOptimizationPasses(array $passes) { $this->optimizationPasses = [$passes]; } /** * Sets the Removing passes. * * @param CompilerPassInterface[] $passes */ public function setRemovingPasses(array $passes) { $this->removingPasses = [$passes]; } /** * Sort passes by priority. * * @param array $passes CompilerPassInterface instances with their priority as key * * @return CompilerPassInterface[] */ private function sortPasses(array $passes): array { if (0 === \count($passes)) { return []; } krsort($passes); // Flatten the array return array_merge(...$passes); } } Compiler/PriorityTaggedServiceTrait.php 0000644 00000015233 15120140333 0014305 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\TypedReference; /** * Trait that allows a generic method to find and sort service by priority option in the tag. * * @author Iltar van der Berg <kjarli@gmail.com> */ trait PriorityTaggedServiceTrait { /** * Finds all services with the given tag name and order them by their priority. * * The order of additions must be respected for services having the same priority, * and knowing that the \SplPriorityQueue class does not respect the FIFO method, * we should not use that class. * * @see https://bugs.php.net/53710 * @see https://bugs.php.net/60926 * * @param string|TaggedIteratorArgument $tagName * * @return Reference[] */ private function findAndSortTaggedServices($tagName, ContainerBuilder $container): array { $indexAttribute = $defaultIndexMethod = $needsIndexes = $defaultPriorityMethod = null; if ($tagName instanceof TaggedIteratorArgument) { $indexAttribute = $tagName->getIndexAttribute(); $defaultIndexMethod = $tagName->getDefaultIndexMethod(); $needsIndexes = $tagName->needsIndexes(); $defaultPriorityMethod = $tagName->getDefaultPriorityMethod() ?? 'getDefaultPriority'; $tagName = $tagName->getTag(); } $i = 0; $services = []; foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) { $defaultPriority = null; $defaultIndex = null; $definition = $container->getDefinition($serviceId); $class = $definition->getClass(); $class = $container->getParameterBag()->resolveValue($class) ?: null; $checkTaggedItem = !$definition->hasTag(80000 <= \PHP_VERSION_ID && $definition->isAutoconfigured() ? 'container.ignore_attributes' : $tagName); foreach ($attributes as $attribute) { $index = $priority = null; if (isset($attribute['priority'])) { $priority = $attribute['priority']; } elseif (null === $defaultPriority && $defaultPriorityMethod && $class) { $defaultPriority = PriorityTaggedServiceUtil::getDefault($container, $serviceId, $class, $defaultPriorityMethod, $tagName, 'priority', $checkTaggedItem); } $priority = $priority ?? $defaultPriority ?? $defaultPriority = 0; if (null === $indexAttribute && !$defaultIndexMethod && !$needsIndexes) { $services[] = [$priority, ++$i, null, $serviceId, null]; continue 2; } if (null !== $indexAttribute && isset($attribute[$indexAttribute])) { $index = $attribute[$indexAttribute]; } elseif (null === $defaultIndex && $defaultPriorityMethod && $class) { $defaultIndex = PriorityTaggedServiceUtil::getDefault($container, $serviceId, $class, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute, $checkTaggedItem); } $index = $index ?? $defaultIndex ?? $defaultIndex = $serviceId; $services[] = [$priority, ++$i, $index, $serviceId, $class]; } } uasort($services, static function ($a, $b) { return $b[0] <=> $a[0] ?: $a[1] <=> $b[1]; }); $refs = []; foreach ($services as [, , $index, $serviceId, $class]) { if (!$class) { $reference = new Reference($serviceId); } elseif ($index === $serviceId) { $reference = new TypedReference($serviceId, $class); } else { $reference = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $index); } if (null === $index) { $refs[] = $reference; } else { $refs[$index] = $reference; } } return $refs; } } /** * @internal */ class PriorityTaggedServiceUtil { /** * @return string|int|null */ public static function getDefault(ContainerBuilder $container, string $serviceId, string $class, string $defaultMethod, string $tagName, ?string $indexAttribute, bool $checkTaggedItem) { if (!($r = $container->getReflectionClass($class)) || (!$checkTaggedItem && !$r->hasMethod($defaultMethod))) { return null; } if ($checkTaggedItem && !$r->hasMethod($defaultMethod)) { foreach ($r->getAttributes(AsTaggedItem::class) as $attribute) { return 'priority' === $indexAttribute ? $attribute->newInstance()->priority : $attribute->newInstance()->index; } return null; } if (null !== $indexAttribute) { $service = $class !== $serviceId ? sprintf('service "%s"', $serviceId) : 'on the corresponding service'; $message = [sprintf('Either method "%s::%s()" should ', $class, $defaultMethod), sprintf(' or tag "%s" on %s is missing attribute "%s".', $tagName, $service, $indexAttribute)]; } else { $message = [sprintf('Method "%s::%s()" should ', $class, $defaultMethod), '.']; } if (!($rm = $r->getMethod($defaultMethod))->isStatic()) { throw new InvalidArgumentException(implode('be static', $message)); } if (!$rm->isPublic()) { throw new InvalidArgumentException(implode('be public', $message)); } $default = $rm->invoke(null); if ('priority' === $indexAttribute) { if (!\is_int($default)) { throw new InvalidArgumentException(implode(sprintf('return int (got "%s")', get_debug_type($default)), $message)); } return $default; } if (\is_int($default)) { $default = (string) $default; } if (!\is_string($default)) { throw new InvalidArgumentException(implode(sprintf('return string|int (got "%s")', get_debug_type($default)), $message)); } return $default; } } Compiler/RegisterAutoconfigureAttributesPass.php 0000644 00000006460 15120140333 0016242 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Attribute\Autoconfigure; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; /** * Reads #[Autoconfigure] attributes on definitions that are autoconfigured * and don't have the "container.ignore_attributes" tag. * * @author Nicolas Grekas <p@tchwork.com> */ final class RegisterAutoconfigureAttributesPass implements CompilerPassInterface { private static $registerForAutoconfiguration; /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { if (80000 > \PHP_VERSION_ID) { return; } foreach ($container->getDefinitions() as $id => $definition) { if ($this->accept($definition) && $class = $container->getReflectionClass($definition->getClass(), false)) { $this->processClass($container, $class); } } } public function accept(Definition $definition): bool { return 80000 <= \PHP_VERSION_ID && $definition->isAutoconfigured() && !$definition->hasTag('container.ignore_attributes'); } public function processClass(ContainerBuilder $container, \ReflectionClass $class) { foreach ($class->getAttributes(Autoconfigure::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) { self::registerForAutoconfiguration($container, $class, $attribute); } } private static function registerForAutoconfiguration(ContainerBuilder $container, \ReflectionClass $class, \ReflectionAttribute $attribute) { if (self::$registerForAutoconfiguration) { return (self::$registerForAutoconfiguration)($container, $class, $attribute); } $parseDefinitions = new \ReflectionMethod(YamlFileLoader::class, 'parseDefinitions'); $parseDefinitions->setAccessible(true); $yamlLoader = $parseDefinitions->getDeclaringClass()->newInstanceWithoutConstructor(); self::$registerForAutoconfiguration = static function (ContainerBuilder $container, \ReflectionClass $class, \ReflectionAttribute $attribute) use ($parseDefinitions, $yamlLoader) { $attribute = (array) $attribute->newInstance(); foreach ($attribute['tags'] ?? [] as $i => $tag) { if (\is_array($tag) && [0] === array_keys($tag)) { $attribute['tags'][$i] = [$class->name => $tag[0]]; } } $parseDefinitions->invoke( $yamlLoader, [ 'services' => [ '_instanceof' => [ $class->name => [$container->registerForAutoconfiguration($class->name)] + $attribute, ], ], ], $class->getFileName(), false ); }; return (self::$registerForAutoconfiguration)($container, $class, $attribute); } } Compiler/RegisterEnvVarProcessorsPass.php 0000644 00000005674 15120140333 0014653 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\EnvVarProcessor; use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\Reference; /** * Creates the container.env_var_processors_locator service. * * @author Nicolas Grekas <p@tchwork.com> */ class RegisterEnvVarProcessorsPass implements CompilerPassInterface { private const ALLOWED_TYPES = ['array', 'bool', 'float', 'int', 'string']; public function process(ContainerBuilder $container) { $bag = $container->getParameterBag(); $types = []; $processors = []; foreach ($container->findTaggedServiceIds('container.env_var_processor') as $id => $tags) { if (!$r = $container->getReflectionClass($class = $container->getDefinition($id)->getClass())) { throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); } elseif (!$r->isSubclassOf(EnvVarProcessorInterface::class)) { throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class)); } foreach ($class::getProvidedTypes() as $prefix => $type) { $processors[$prefix] = new Reference($id); $types[$prefix] = self::validateProvidedTypes($type, $class); } } if ($bag instanceof EnvPlaceholderParameterBag) { foreach (EnvVarProcessor::getProvidedTypes() as $prefix => $type) { if (!isset($types[$prefix])) { $types[$prefix] = self::validateProvidedTypes($type, EnvVarProcessor::class); } } $bag->setProvidedTypes($types); } if ($processors) { $container->setAlias('container.env_var_processors_locator', (string) ServiceLocatorTagPass::register($container, $processors)) ->setPublic(true) ; } } private static function validateProvidedTypes(string $types, string $class): array { $types = explode('|', $types); foreach ($types as $type) { if (!\in_array($type, self::ALLOWED_TYPES)) { throw new InvalidArgumentException(sprintf('Invalid type "%s" returned by "%s::getProvidedTypes()", expected one of "%s".', $type, $class, implode('", "', self::ALLOWED_TYPES))); } } return $types; } } Compiler/RegisterReverseContainerPass.php 0000644 00000004566 15120140333 0014644 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; /** * @author Nicolas Grekas <p@tchwork.com> */ class RegisterReverseContainerPass implements CompilerPassInterface { private $beforeRemoving; private $serviceId; private $tagName; public function __construct(bool $beforeRemoving, string $serviceId = 'reverse_container', string $tagName = 'container.reversible') { if (1 < \func_num_args()) { trigger_deprecation('symfony/dependency-injection', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); } $this->beforeRemoving = $beforeRemoving; $this->serviceId = $serviceId; $this->tagName = $tagName; } public function process(ContainerBuilder $container) { if (!$container->hasDefinition($this->serviceId)) { return; } $refType = $this->beforeRemoving ? ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; $services = []; foreach ($container->findTaggedServiceIds($this->tagName) as $id => $tags) { $services[$id] = new Reference($id, $refType); } if ($this->beforeRemoving) { // prevent inlining of the reverse container $services[$this->serviceId] = new Reference($this->serviceId, $refType); } $locator = $container->getDefinition($this->serviceId)->getArgument(1); if ($locator instanceof Reference) { $locator = $container->getDefinition((string) $locator); } if ($locator instanceof Definition) { foreach ($services as $id => $ref) { $services[$id] = new ServiceClosureArgument($ref); } $locator->replaceArgument(0, $services); } else { $locator->setValues($services); } } } Compiler/RegisterServiceSubscribersPass.php 0000644 00000014773 15120140333 0015176 0 ustar 00 <?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\Compiler; use Psr\Container\ContainerInterface as PsrContainerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Contracts\Service\ServiceProviderInterface; use Symfony\Contracts\Service\ServiceSubscriberInterface; /** * Compiler pass to register tagged services that require a service locator. * * @author Nicolas Grekas <p@tchwork.com> */ class RegisterServiceSubscribersPass extends AbstractRecursivePass { protected function processValue($value, bool $isRoot = false) { if (!$value instanceof Definition || $value->isAbstract() || $value->isSynthetic() || !$value->hasTag('container.service_subscriber')) { return parent::processValue($value, $isRoot); } $serviceMap = []; $autowire = $value->isAutowired(); foreach ($value->getTag('container.service_subscriber') as $attributes) { if (!$attributes) { $autowire = true; continue; } ksort($attributes); if ([] !== array_diff(array_keys($attributes), ['id', 'key'])) { throw new InvalidArgumentException(sprintf('The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "%s" given for service "%s".', implode('", "', array_keys($attributes)), $this->currentId)); } if (!\array_key_exists('id', $attributes)) { throw new InvalidArgumentException(sprintf('Missing "id" attribute on "container.service_subscriber" tag with key="%s" for service "%s".', $attributes['key'], $this->currentId)); } if (!\array_key_exists('key', $attributes)) { $attributes['key'] = $attributes['id']; } if (isset($serviceMap[$attributes['key']])) { continue; } $serviceMap[$attributes['key']] = new Reference($attributes['id']); } $class = $value->getClass(); if (!$r = $this->container->getReflectionClass($class)) { throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $this->currentId)); } if (!$r->isSubclassOf(ServiceSubscriberInterface::class)) { throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $this->currentId, ServiceSubscriberInterface::class)); } $class = $r->name; $replaceDeprecatedSession = $this->container->has('.session.deprecated') && $r->isSubclassOf(AbstractController::class); $subscriberMap = []; foreach ($class::getSubscribedServices() as $key => $type) { if (!\is_string($type) || !preg_match('/(?(DEFINE)(?<cn>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))(?(DEFINE)(?<fqcn>(?&cn)(?:\\\\(?&cn))*+))^\??(?&fqcn)(?:(?:\|(?&fqcn))*+|(?:&(?&fqcn))*+)$/', $type)) { throw new InvalidArgumentException(sprintf('"%s::getSubscribedServices()" must return valid PHP types for service "%s" key "%s", "%s" returned.', $class, $this->currentId, $key, \is_string($type) ? $type : get_debug_type($type))); } if ($optionalBehavior = '?' === $type[0]) { $type = substr($type, 1); $optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; } if (\is_int($name = $key)) { $key = $type; $name = null; } if (!isset($serviceMap[$key])) { if (!$autowire) { throw new InvalidArgumentException(sprintf('Service "%s" misses a "container.service_subscriber" tag with "key"/"id" attributes corresponding to entry "%s" as returned by "%s::getSubscribedServices()".', $this->currentId, $key, $class)); } if ($replaceDeprecatedSession && SessionInterface::class === $type) { // This prevents triggering the deprecation when building the container // Should be removed in Symfony 6.0 $type = '.session.deprecated'; } $serviceMap[$key] = new Reference($type); } if ($name) { if (false !== $i = strpos($name, '::get')) { $name = lcfirst(substr($name, 5 + $i)); } elseif (str_contains($name, '::')) { $name = null; } } if (null !== $name && !$this->container->has($name) && !$this->container->has($type.' $'.$name)) { $camelCaseName = lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $name)))); $name = $this->container->has($type.' $'.$camelCaseName) ? $camelCaseName : $name; } $subscriberMap[$key] = new TypedReference((string) $serviceMap[$key], $type, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $name); unset($serviceMap[$key]); } if ($serviceMap = array_keys($serviceMap)) { $message = sprintf(1 < \count($serviceMap) ? 'keys "%s" do' : 'key "%s" does', str_replace('%', '%%', implode('", "', $serviceMap))); throw new InvalidArgumentException(sprintf('Service %s not exist in the map returned by "%s::getSubscribedServices()" for service "%s".', $message, $class, $this->currentId)); } $locatorRef = ServiceLocatorTagPass::register($this->container, $subscriberMap, $this->currentId); $value->addTag('container.service_subscriber.locator', ['id' => (string) $locatorRef]); $value->setBindings([ PsrContainerInterface::class => new BoundArgument($locatorRef, false), ServiceProviderInterface::class => new BoundArgument($locatorRef, false), ] + $value->getBindings()); return parent::processValue($value); } } Compiler/RemoveAbstractDefinitionsPass.php 0000644 00000001614 15120140333 0014765 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Removes abstract Definitions. */ class RemoveAbstractDefinitionsPass implements CompilerPassInterface { /** * Removes abstract definitions from the ContainerBuilder. */ public function process(ContainerBuilder $container) { foreach ($container->getDefinitions() as $id => $definition) { if ($definition->isAbstract()) { $container->removeDefinition($id); $container->log($this, sprintf('Removed service "%s"; reason: abstract.', $id)); } } } } Compiler/RemovePrivateAliasesPass.php 0000644 00000002132 15120140333 0013736 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Remove private aliases from the container. They were only used to establish * dependencies between services, and these dependencies have been resolved in * one of the previous passes. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class RemovePrivateAliasesPass implements CompilerPassInterface { /** * Removes private aliases from the ContainerBuilder. */ public function process(ContainerBuilder $container) { foreach ($container->getAliases() as $id => $alias) { if ($alias->isPublic()) { continue; } $container->removeAlias($id); $container->log($this, sprintf('Removed service "%s"; reason: private alias.', $id)); } } } Compiler/RemoveUnusedDefinitionsPass.php 0000644 00000005454 15120140333 0014473 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** * Removes unused service definitions from the container. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ class RemoveUnusedDefinitionsPass extends AbstractRecursivePass { private $connectedIds = []; /** * Processes the ContainerBuilder to remove unused definitions. */ public function process(ContainerBuilder $container) { try { $this->enableExpressionProcessing(); $this->container = $container; $connectedIds = []; $aliases = $container->getAliases(); foreach ($aliases as $id => $alias) { if ($alias->isPublic()) { $this->connectedIds[] = (string) $aliases[$id]; } } foreach ($container->getDefinitions() as $id => $definition) { if ($definition->isPublic()) { $connectedIds[$id] = true; $this->processValue($definition); } } while ($this->connectedIds) { $ids = $this->connectedIds; $this->connectedIds = []; foreach ($ids as $id) { if (!isset($connectedIds[$id]) && $container->hasDefinition($id)) { $connectedIds[$id] = true; $this->processValue($container->getDefinition($id)); } } } foreach ($container->getDefinitions() as $id => $definition) { if (!isset($connectedIds[$id])) { $container->removeDefinition($id); $container->resolveEnvPlaceholders(!$definition->hasErrors() ? serialize($definition) : $definition); $container->log($this, sprintf('Removed service "%s"; reason: unused.', $id)); } } } finally { $this->container = null; $this->connectedIds = []; } } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if (!$value instanceof Reference) { return parent::processValue($value, $isRoot); } if (ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior()) { $this->connectedIds[] = (string) $value; } return $value; } } Compiler/ReplaceAliasByActualDefinitionPass.php 0000644 00000010613 15120140333 0015632 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Reference; /** * Replaces aliases with actual service definitions, effectively removing these * aliases. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ReplaceAliasByActualDefinitionPass extends AbstractRecursivePass { private $replacements; private $autoAliasServicePass; /** * @internal to be removed in Symfony 6.0 * * @return $this */ public function setAutoAliasServicePass(AutoAliasServicePass $autoAliasServicePass): self { $this->autoAliasServicePass = $autoAliasServicePass; return $this; } /** * Process the Container to replace aliases with service definitions. * * @throws InvalidArgumentException if the service definition does not exist */ public function process(ContainerBuilder $container) { // First collect all alias targets that need to be replaced $seenAliasTargets = []; $replacements = []; $privateAliases = $this->autoAliasServicePass ? $this->autoAliasServicePass->getPrivateAliases() : []; foreach ($privateAliases as $target) { $target->setDeprecated('symfony/dependency-injection', '5.4', 'Accessing the "%alias_id%" service directly from the container is deprecated, use dependency injection instead.'); } foreach ($container->getAliases() as $definitionId => $target) { $targetId = (string) $target; // Special case: leave this target alone if ('service_container' === $targetId) { continue; } // Check if target needs to be replaced if (isset($replacements[$targetId])) { $container->setAlias($definitionId, $replacements[$targetId])->setPublic($target->isPublic()); if ($target->isDeprecated()) { $container->getAlias($definitionId)->setDeprecated(...array_values($target->getDeprecation('%alias_id%'))); } } // No need to process the same target twice if (isset($seenAliasTargets[$targetId])) { continue; } // Process new target $seenAliasTargets[$targetId] = true; try { $definition = $container->getDefinition($targetId); } catch (ServiceNotFoundException $e) { if ('' !== $e->getId() && '@' === $e->getId()[0]) { throw new ServiceNotFoundException($e->getId(), $e->getSourceId(), null, [substr($e->getId(), 1)]); } throw $e; } if ($definition->isPublic()) { continue; } // Remove private definition and schedule for replacement $definition->setPublic($target->isPublic()); $container->setDefinition($definitionId, $definition); $container->removeDefinition($targetId); $replacements[$targetId] = $definitionId; if ($target->isPublic() && $target->isDeprecated()) { $definition->addTag('container.private', $target->getDeprecation('%service_id%')); } } $this->replacements = $replacements; parent::process($container); $this->replacements = []; } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if ($value instanceof Reference && isset($this->replacements[$referenceId = (string) $value])) { // Perform the replacement $newId = $this->replacements[$referenceId]; $value = new Reference($newId, $value->getInvalidBehavior()); $this->container->log($this, sprintf('Changed reference of service "%s" previously pointing to "%s" to "%s".', $this->currentId, $referenceId, $newId)); } return parent::processValue($value, $isRoot); } } Compiler/ResolveBindingsPass.php 0000644 00000023072 15120140333 0012747 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\Attribute\Target; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\TypedReference; /** * @author Guilhem Niot <guilhem.niot@gmail.com> */ class ResolveBindingsPass extends AbstractRecursivePass { private $usedBindings = []; private $unusedBindings = []; private $errorMessages = []; /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { $this->usedBindings = $container->getRemovedBindingIds(); try { parent::process($container); foreach ($this->unusedBindings as [$key, $serviceId, $bindingType, $file]) { $argumentType = $argumentName = $message = null; if (str_contains($key, ' ')) { [$argumentType, $argumentName] = explode(' ', $key, 2); } elseif ('$' === $key[0]) { $argumentName = $key; } else { $argumentType = $key; } if ($argumentType) { $message .= sprintf('of type "%s" ', $argumentType); } if ($argumentName) { $message .= sprintf('named "%s" ', $argumentName); } if (BoundArgument::DEFAULTS_BINDING === $bindingType) { $message .= 'under "_defaults"'; } elseif (BoundArgument::INSTANCEOF_BINDING === $bindingType) { $message .= 'under "_instanceof"'; } else { $message .= sprintf('for service "%s"', $serviceId); } if ($file) { $message .= sprintf(' in file "%s"', $file); } $message = sprintf('A binding is configured for an argument %s, but no corresponding argument has been found. It may be unused and should be removed, or it may have a typo.', $message); if ($this->errorMessages) { $message .= sprintf("\nCould be related to%s:", 1 < \count($this->errorMessages) ? ' one of' : ''); } foreach ($this->errorMessages as $m) { $message .= "\n - ".$m; } throw new InvalidArgumentException($message); } } finally { $this->usedBindings = []; $this->unusedBindings = []; $this->errorMessages = []; } } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if ($value instanceof TypedReference && $value->getType() === (string) $value) { // Already checked $bindings = $this->container->getDefinition($this->currentId)->getBindings(); $name = $value->getName(); if (isset($name, $bindings[$name = $value.' $'.$name])) { return $this->getBindingValue($bindings[$name]); } if (isset($bindings[$value->getType()])) { return $this->getBindingValue($bindings[$value->getType()]); } return parent::processValue($value, $isRoot); } if (!$value instanceof Definition || !$bindings = $value->getBindings()) { return parent::processValue($value, $isRoot); } $bindingNames = []; foreach ($bindings as $key => $binding) { [$bindingValue, $bindingId, $used, $bindingType, $file] = $binding->getValues(); if ($used) { $this->usedBindings[$bindingId] = true; unset($this->unusedBindings[$bindingId]); } elseif (!isset($this->usedBindings[$bindingId])) { $this->unusedBindings[$bindingId] = [$key, $this->currentId, $bindingType, $file]; } if (preg_match('/^(?:(?:array|bool|float|int|string|iterable|([^ $]++)) )\$/', $key, $m)) { $bindingNames[substr($key, \strlen($m[0]))] = $binding; } if (!isset($m[1])) { continue; } if (is_subclass_of($m[1], \UnitEnum::class)) { $bindingNames[substr($key, \strlen($m[0]))] = $binding; continue; } if (null !== $bindingValue && !$bindingValue instanceof Reference && !$bindingValue instanceof Definition && !$bindingValue instanceof TaggedIteratorArgument && !$bindingValue instanceof ServiceLocatorArgument) { throw new InvalidArgumentException(sprintf('Invalid value for binding key "%s" for service "%s": expected "%s", "%s", "%s", "%s" or null, "%s" given.', $key, $this->currentId, Reference::class, Definition::class, TaggedIteratorArgument::class, ServiceLocatorArgument::class, get_debug_type($bindingValue))); } } if ($value->isAbstract()) { return parent::processValue($value, $isRoot); } $calls = $value->getMethodCalls(); try { if ($constructor = $this->getConstructor($value, false)) { $calls[] = [$constructor, $value->getArguments()]; } } catch (RuntimeException $e) { $this->errorMessages[] = $e->getMessage(); $this->container->getDefinition($this->currentId)->addError($e->getMessage()); return parent::processValue($value, $isRoot); } foreach ($calls as $i => $call) { [$method, $arguments] = $call; if ($method instanceof \ReflectionFunctionAbstract) { $reflectionMethod = $method; } else { try { $reflectionMethod = $this->getReflectionMethod($value, $method); } catch (RuntimeException $e) { if ($value->getFactory()) { continue; } throw $e; } } $names = []; foreach ($reflectionMethod->getParameters() as $key => $parameter) { $names[$key] = $parameter->name; if (\array_key_exists($key, $arguments) && '' !== $arguments[$key]) { continue; } if (\array_key_exists($parameter->name, $arguments) && '' !== $arguments[$parameter->name]) { continue; } $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter); $name = Target::parseName($parameter); if ($typeHint && \array_key_exists($k = ltrim($typeHint, '\\').' $'.$name, $bindings)) { $arguments[$key] = $this->getBindingValue($bindings[$k]); continue; } if (\array_key_exists('$'.$name, $bindings)) { $arguments[$key] = $this->getBindingValue($bindings['$'.$name]); continue; } if ($typeHint && '\\' === $typeHint[0] && isset($bindings[$typeHint = substr($typeHint, 1)])) { $arguments[$key] = $this->getBindingValue($bindings[$typeHint]); continue; } if (isset($bindingNames[$name]) || isset($bindingNames[$parameter->name])) { $bindingKey = array_search($binding, $bindings, true); $argumentType = substr($bindingKey, 0, strpos($bindingKey, ' ')); $this->errorMessages[] = sprintf('Did you forget to add the type "%s" to argument "$%s" of method "%s::%s()"?', $argumentType, $parameter->name, $reflectionMethod->class, $reflectionMethod->name); } } foreach ($names as $key => $name) { if (\array_key_exists($name, $arguments) && (0 === $key || \array_key_exists($key - 1, $arguments))) { $arguments[$key] = $arguments[$name]; unset($arguments[$name]); } } if ($arguments !== $call[1]) { ksort($arguments, \SORT_NATURAL); $calls[$i][1] = $arguments; } } if ($constructor) { [, $arguments] = array_pop($calls); if ($arguments !== $value->getArguments()) { $value->setArguments($arguments); } } if ($calls !== $value->getMethodCalls()) { $value->setMethodCalls($calls); } return parent::processValue($value, $isRoot); } /** * @return mixed */ private function getBindingValue(BoundArgument $binding) { [$bindingValue, $bindingId] = $binding->getValues(); $this->usedBindings[$bindingId] = true; unset($this->unusedBindings[$bindingId]); return $bindingValue; } } Compiler/ResolveChildDefinitionsPass.php 0000644 00000017026 15120140333 0014433 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\ExceptionInterface; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; /** * This replaces all ChildDefinition instances with their equivalent fully * merged Definition instance. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ class ResolveChildDefinitionsPass extends AbstractRecursivePass { private $currentPath; protected function processValue($value, bool $isRoot = false) { if (!$value instanceof Definition) { return parent::processValue($value, $isRoot); } if ($isRoot) { // yes, we are specifically fetching the definition from the // container to ensure we are not operating on stale data $value = $this->container->getDefinition($this->currentId); } if ($value instanceof ChildDefinition) { $this->currentPath = []; $value = $this->resolveDefinition($value); if ($isRoot) { $this->container->setDefinition($this->currentId, $value); } } return parent::processValue($value, $isRoot); } /** * Resolves the definition. * * @throws RuntimeException When the definition is invalid */ private function resolveDefinition(ChildDefinition $definition): Definition { try { return $this->doResolveDefinition($definition); } catch (ServiceCircularReferenceException $e) { throw $e; } catch (ExceptionInterface $e) { $r = new \ReflectionProperty($e, 'message'); $r->setAccessible(true); $r->setValue($e, sprintf('Service "%s": %s', $this->currentId, $e->getMessage())); throw $e; } } private function doResolveDefinition(ChildDefinition $definition): Definition { if (!$this->container->has($parent = $definition->getParent())) { throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent)); } $searchKey = array_search($parent, $this->currentPath); $this->currentPath[] = $parent; if (false !== $searchKey) { throw new ServiceCircularReferenceException($parent, \array_slice($this->currentPath, $searchKey)); } $parentDef = $this->container->findDefinition($parent); if ($parentDef instanceof ChildDefinition) { $id = $this->currentId; $this->currentId = $parent; $parentDef = $this->resolveDefinition($parentDef); $this->container->setDefinition($parent, $parentDef); $this->currentId = $id; } $this->container->log($this, sprintf('Resolving inheritance for "%s" (parent: %s).', $this->currentId, $parent)); $def = new Definition(); // merge in parent definition // purposely ignored attributes: abstract, shared, tags, autoconfigured $def->setClass($parentDef->getClass()); $def->setArguments($parentDef->getArguments()); $def->setMethodCalls($parentDef->getMethodCalls()); $def->setProperties($parentDef->getProperties()); if ($parentDef->isDeprecated()) { $deprecation = $parentDef->getDeprecation('%service_id%'); $def->setDeprecated($deprecation['package'], $deprecation['version'], $deprecation['message']); } $def->setFactory($parentDef->getFactory()); $def->setConfigurator($parentDef->getConfigurator()); $def->setFile($parentDef->getFile()); $def->setPublic($parentDef->isPublic()); $def->setLazy($parentDef->isLazy()); $def->setAutowired($parentDef->isAutowired()); $def->setChanges($parentDef->getChanges()); $def->setBindings($definition->getBindings() + $parentDef->getBindings()); $def->setSynthetic($definition->isSynthetic()); // overwrite with values specified in the decorator $changes = $definition->getChanges(); if (isset($changes['class'])) { $def->setClass($definition->getClass()); } if (isset($changes['factory'])) { $def->setFactory($definition->getFactory()); } if (isset($changes['configurator'])) { $def->setConfigurator($definition->getConfigurator()); } if (isset($changes['file'])) { $def->setFile($definition->getFile()); } if (isset($changes['public'])) { $def->setPublic($definition->isPublic()); } else { $def->setPublic($parentDef->isPublic()); } if (isset($changes['lazy'])) { $def->setLazy($definition->isLazy()); } if (isset($changes['deprecated'])) { if ($definition->isDeprecated()) { $deprecation = $definition->getDeprecation('%service_id%'); $def->setDeprecated($deprecation['package'], $deprecation['version'], $deprecation['message']); } else { $def->setDeprecated(false); } } if (isset($changes['autowired'])) { $def->setAutowired($definition->isAutowired()); } if (isset($changes['shared'])) { $def->setShared($definition->isShared()); } if (isset($changes['decorated_service'])) { $decoratedService = $definition->getDecoratedService(); if (null === $decoratedService) { $def->setDecoratedService($decoratedService); } else { $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2], $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); } } // merge arguments foreach ($definition->getArguments() as $k => $v) { if (is_numeric($k)) { $def->addArgument($v); } elseif (str_starts_with($k, 'index_')) { $def->replaceArgument((int) substr($k, \strlen('index_')), $v); } else { $def->setArgument($k, $v); } } // merge properties foreach ($definition->getProperties() as $k => $v) { $def->setProperty($k, $v); } // append method calls if ($calls = $definition->getMethodCalls()) { $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls)); } $def->addError($parentDef); $def->addError($definition); // these attributes are always taken from the child $def->setAbstract($definition->isAbstract()); $def->setTags($definition->getTags()); // autoconfigure is never taken from parent (on purpose) // and it's not legal on an instanceof $def->setAutoconfigured($definition->isAutoconfigured()); if (!$def->hasTag('proxy')) { foreach ($parentDef->getTag('proxy') as $v) { $def->addTag('proxy', $v); } } return $def; } } Compiler/ResolveClassPass.php 0000644 00000003031 15120140333 0012250 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * @author Nicolas Grekas <p@tchwork.com> */ class ResolveClassPass implements CompilerPassInterface { /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { foreach ($container->getDefinitions() as $id => $definition) { if ($definition->isSynthetic() || null !== $definition->getClass()) { continue; } 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]*+)++$/', $id)) { if ($definition instanceof ChildDefinition && !class_exists($id)) { throw new InvalidArgumentException(sprintf('Service definition "%s" has a parent but no class, and its name looks like an FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id)); } $definition->setClass($id); } } } } Compiler/ResolveDecoratorStackPass.php 0000644 00000011002 15120140333 0014110 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Reference; /** * @author Nicolas Grekas <p@tchwork.com> */ class ResolveDecoratorStackPass implements CompilerPassInterface { private $tag; public function __construct(string $tag = 'container.stack') { if (0 < \func_num_args()) { trigger_deprecation('symfony/dependency-injection', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); } $this->tag = $tag; } public function process(ContainerBuilder $container) { $stacks = []; foreach ($container->findTaggedServiceIds($this->tag) as $id => $tags) { $definition = $container->getDefinition($id); if (!$definition instanceof ChildDefinition) { throw new InvalidArgumentException(sprintf('Invalid service "%s": only definitions with a "parent" can have the "%s" tag.', $id, $this->tag)); } if (!$stack = $definition->getArguments()) { throw new InvalidArgumentException(sprintf('Invalid service "%s": the stack of decorators is empty.', $id)); } $stacks[$id] = $stack; } if (!$stacks) { return; } $resolvedDefinitions = []; foreach ($container->getDefinitions() as $id => $definition) { if (!isset($stacks[$id])) { $resolvedDefinitions[$id] = $definition; continue; } foreach (array_reverse($this->resolveStack($stacks, [$id]), true) as $k => $v) { $resolvedDefinitions[$k] = $v; } $alias = $container->setAlias($id, $k); if ($definition->getChanges()['public'] ?? false) { $alias->setPublic($definition->isPublic()); } if ($definition->isDeprecated()) { $alias->setDeprecated(...array_values($definition->getDeprecation('%alias_id%'))); } } $container->setDefinitions($resolvedDefinitions); } private function resolveStack(array $stacks, array $path): array { $definitions = []; $id = end($path); $prefix = '.'.$id.'.'; if (!isset($stacks[$id])) { return [$id => new ChildDefinition($id)]; } if (key($path) !== $searchKey = array_search($id, $path)) { throw new ServiceCircularReferenceException($id, \array_slice($path, $searchKey)); } foreach ($stacks[$id] as $k => $definition) { if ($definition instanceof ChildDefinition && isset($stacks[$definition->getParent()])) { $path[] = $definition->getParent(); $definition = unserialize(serialize($definition)); // deep clone } elseif ($definition instanceof Definition) { $definitions[$decoratedId = $prefix.$k] = $definition; continue; } elseif ($definition instanceof Reference || $definition instanceof Alias) { $path[] = (string) $definition; } else { throw new InvalidArgumentException(sprintf('Invalid service "%s": unexpected value of type "%s" found in the stack of decorators.', $id, get_debug_type($definition))); } $p = $prefix.$k; foreach ($this->resolveStack($stacks, $path) as $k => $v) { $definitions[$decoratedId = $p.$k] = $definition instanceof ChildDefinition ? $definition->setParent($k) : new ChildDefinition($k); $definition = null; } array_pop($path); } if (1 === \count($path)) { foreach ($definitions as $k => $definition) { $definition->setPublic(false)->setTags([])->setDecoratedService($decoratedId); } $definition->setDecoratedService(null); } return $definitions; } } Compiler/ResolveEnvPlaceholdersPass.php 0000644 00000002515 15120140333 0014267 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Definition; /** * Replaces env var placeholders by their current values. */ class ResolveEnvPlaceholdersPass extends AbstractRecursivePass { protected function processValue($value, bool $isRoot = false) { if (\is_string($value)) { return $this->container->resolveEnvPlaceholders($value, true); } if ($value instanceof Definition) { $changes = $value->getChanges(); if (isset($changes['class'])) { $value->setClass($this->container->resolveEnvPlaceholders($value->getClass(), true)); } if (isset($changes['file'])) { $value->setFile($this->container->resolveEnvPlaceholders($value->getFile(), true)); } } $value = parent::processValue($value, $isRoot); if ($value && \is_array($value) && !$isRoot) { $value = array_combine($this->container->resolveEnvPlaceholders(array_keys($value), true), $value); } return $value; } } Compiler/ResolveFactoryClassPass.php 0000644 00000002301 15120140333 0013577 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ class ResolveFactoryClassPass extends AbstractRecursivePass { /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if ($value instanceof Definition && \is_array($factory = $value->getFactory()) && null === $factory[0]) { if (null === $class = $value->getClass()) { throw new RuntimeException(sprintf('The "%s" service is defined to be created by a factory, but is missing the factory class. Did you forget to define the factory or service class?', $this->currentId)); } $factory[0] = $class; $value->setFactory($factory); } return parent::processValue($value, $isRoot); } } Compiler/ResolveHotPathPass.php 0000644 00000004771 15120140333 0012566 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; /** * Propagate "container.hot_path" tags to referenced services. * * @author Nicolas Grekas <p@tchwork.com> */ class ResolveHotPathPass extends AbstractRecursivePass { private $tagName; private $resolvedIds = []; public function __construct(string $tagName = 'container.hot_path') { if (0 < \func_num_args()) { trigger_deprecation('symfony/dependency-injection', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); } $this->tagName = $tagName; } /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { try { parent::process($container); $container->getDefinition('service_container')->clearTag($this->tagName); } finally { $this->resolvedIds = []; } } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if ($value instanceof ArgumentInterface) { return $value; } if ($value instanceof Definition && $isRoot) { if ($value->isDeprecated()) { return $value->clearTag($this->tagName); } $this->resolvedIds[$this->currentId] = true; if (!$value->hasTag($this->tagName)) { return $value; } } if ($value instanceof Reference && ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior() && $this->container->hasDefinition($id = (string) $value)) { $definition = $this->container->getDefinition($id); if ($definition->isDeprecated() || $definition->hasTag($this->tagName)) { return $value; } $definition->addTag($this->tagName); if (isset($this->resolvedIds[$id])) { parent::processValue($definition, false); } return $value; } return parent::processValue($value, $isRoot); } } Compiler/ResolveInstanceofConditionalsPass.php 0000644 00000016003 15120140333 0015646 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * Applies instanceof conditionals to definitions. * * @author Nicolas Grekas <p@tchwork.com> */ class ResolveInstanceofConditionalsPass implements CompilerPassInterface { /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { foreach ($container->getAutoconfiguredInstanceof() as $interface => $definition) { if ($definition->getArguments()) { throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines arguments but these are not supported and should be removed.', $interface)); } } $tagsToKeep = []; if ($container->hasParameter('container.behavior_describing_tags')) { $tagsToKeep = $container->getParameter('container.behavior_describing_tags'); } foreach ($container->getDefinitions() as $id => $definition) { $container->setDefinition($id, $this->processDefinition($container, $id, $definition, $tagsToKeep)); } if ($container->hasParameter('container.behavior_describing_tags')) { $container->getParameterBag()->remove('container.behavior_describing_tags'); } } private function processDefinition(ContainerBuilder $container, string $id, Definition $definition, array $tagsToKeep): Definition { $instanceofConditionals = $definition->getInstanceofConditionals(); $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : []; if (!$instanceofConditionals && !$autoconfiguredInstanceof) { return $definition; } if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) { return $definition; } $conditionals = $this->mergeConditionals($autoconfiguredInstanceof, $instanceofConditionals, $container); $definition->setInstanceofConditionals([]); $shared = null; $instanceofTags = []; $instanceofCalls = []; $instanceofBindings = []; $reflectionClass = null; $parent = $definition instanceof ChildDefinition ? $definition->getParent() : null; foreach ($conditionals as $interface => $instanceofDefs) { if ($interface !== $class && !($reflectionClass ?? $reflectionClass = $container->getReflectionClass($class, false) ?: false)) { continue; } if ($interface !== $class && !is_subclass_of($class, $interface)) { continue; } foreach ($instanceofDefs as $key => $instanceofDef) { /** @var ChildDefinition $instanceofDef */ $instanceofDef = clone $instanceofDef; $instanceofDef->setAbstract(true)->setParent($parent ?: '.abstract.instanceof.'.$id); $parent = '.instanceof.'.$interface.'.'.$key.'.'.$id; $container->setDefinition($parent, $instanceofDef); $instanceofTags[] = $instanceofDef->getTags(); $instanceofBindings = $instanceofDef->getBindings() + $instanceofBindings; foreach ($instanceofDef->getMethodCalls() as $methodCall) { $instanceofCalls[] = $methodCall; } $instanceofDef->setTags([]); $instanceofDef->setMethodCalls([]); $instanceofDef->setBindings([]); if (isset($instanceofDef->getChanges()['shared'])) { $shared = $instanceofDef->isShared(); } } } if ($parent) { $bindings = $definition->getBindings(); $abstract = $container->setDefinition('.abstract.instanceof.'.$id, $definition); $definition->setBindings([]); $definition = serialize($definition); if (Definition::class === \get_class($abstract)) { // cast Definition to ChildDefinition $definition = substr_replace($definition, '53', 2, 2); $definition = substr_replace($definition, 'Child', 44, 0); } /** @var ChildDefinition $definition */ $definition = unserialize($definition); $definition->setParent($parent); if (null !== $shared && !isset($definition->getChanges()['shared'])) { $definition->setShared($shared); } // Don't add tags to service decorators $i = \count($instanceofTags); while (0 <= --$i) { foreach ($instanceofTags[$i] as $k => $v) { if (null === $definition->getDecoratedService() || \in_array($k, $tagsToKeep, true)) { foreach ($v as $v) { if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) { continue; } $definition->addTag($k, $v); } } } } $definition->setMethodCalls(array_merge($instanceofCalls, $definition->getMethodCalls())); $definition->setBindings($bindings + $instanceofBindings); // reset fields with "merge" behavior $abstract ->setBindings([]) ->setArguments([]) ->setMethodCalls([]) ->setDecoratedService(null) ->setTags([]) ->setAbstract(true); } return $definition; } private function mergeConditionals(array $autoconfiguredInstanceof, array $instanceofConditionals, ContainerBuilder $container): array { // make each value an array of ChildDefinition $conditionals = array_map(function ($childDef) { return [$childDef]; }, $autoconfiguredInstanceof); foreach ($instanceofConditionals as $interface => $instanceofDef) { // make sure the interface/class exists (but don't validate automaticInstanceofConditionals) if (!$container->getReflectionClass($interface)) { throw new RuntimeException(sprintf('"%s" is set as an "instanceof" conditional, but it does not exist.', $interface)); } if (!isset($autoconfiguredInstanceof[$interface])) { $conditionals[$interface] = []; } $conditionals[$interface][] = $instanceofDef; } return $conditionals; } } Compiler/ResolveInvalidReferencesPass.php 0000644 00000012324 15120140333 0014600 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\TypedReference; /** * Emulates the invalid behavior if the reference is not found within the * container. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ResolveInvalidReferencesPass implements CompilerPassInterface { private $container; private $signalingException; private $currentId; /** * Process the ContainerBuilder to resolve invalid references. */ public function process(ContainerBuilder $container) { $this->container = $container; $this->signalingException = new RuntimeException('Invalid reference.'); try { foreach ($container->getDefinitions() as $this->currentId => $definition) { $this->processValue($definition); } } finally { $this->container = $this->signalingException = null; } } /** * Processes arguments to determine invalid references. * * @return mixed * * @throws RuntimeException When an invalid reference is found */ private function processValue($value, int $rootLevel = 0, int $level = 0) { if ($value instanceof ServiceClosureArgument) { $value->setValues($this->processValue($value->getValues(), 1, 1)); } elseif ($value instanceof ArgumentInterface) { $value->setValues($this->processValue($value->getValues(), $rootLevel, 1 + $level)); } elseif ($value instanceof Definition) { if ($value->isSynthetic() || $value->isAbstract()) { return $value; } $value->setArguments($this->processValue($value->getArguments(), 0)); $value->setProperties($this->processValue($value->getProperties(), 1)); $value->setMethodCalls($this->processValue($value->getMethodCalls(), 2)); } elseif (\is_array($value)) { $i = 0; foreach ($value as $k => $v) { try { if (false !== $i && $k !== $i++) { $i = false; } if ($v !== $processedValue = $this->processValue($v, $rootLevel, 1 + $level)) { $value[$k] = $processedValue; } } catch (RuntimeException $e) { if ($rootLevel < $level || ($rootLevel && !$level)) { unset($value[$k]); } elseif ($rootLevel) { throw $e; } else { $value[$k] = null; } } } // Ensure numerically indexed arguments have sequential numeric keys. if (false !== $i) { $value = array_values($value); } } elseif ($value instanceof Reference) { if ($this->container->has($id = (string) $value)) { return $value; } $currentDefinition = $this->container->getDefinition($this->currentId); // resolve decorated service behavior depending on decorator service if ($currentDefinition->innerServiceId === $id && ContainerInterface::NULL_ON_INVALID_REFERENCE === $currentDefinition->decorationOnInvalid) { return null; } $invalidBehavior = $value->getInvalidBehavior(); if (ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior && $value instanceof TypedReference && !$this->container->has($id)) { $e = new ServiceNotFoundException($id, $this->currentId); // since the error message varies by $id and $this->currentId, so should the id of the dummy errored definition $this->container->register($id = sprintf('.errored.%s.%s', $this->currentId, $id), $value->getType()) ->addError($e->getMessage()); return new TypedReference($id, $value->getType(), $value->getInvalidBehavior()); } // resolve invalid behavior if (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) { $value = null; } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) { if (0 < $level || $rootLevel) { throw $this->signalingException; } $value = null; } } return $value; } } Compiler/ResolveNamedArgumentsPass.php 0000644 00000013672 15120140333 0014131 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; use Symfony\Component\DependencyInjection\Reference; /** * Resolves named arguments to their corresponding numeric index. * * @author Kévin Dunglas <dunglas@gmail.com> */ class ResolveNamedArgumentsPass extends AbstractRecursivePass { /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if ($value instanceof AbstractArgument && $value->getText().'.' === $value->getTextWithContext()) { $value->setContext(sprintf('A value found in service "%s"', $this->currentId)); } if (!$value instanceof Definition) { return parent::processValue($value, $isRoot); } $calls = $value->getMethodCalls(); $calls[] = ['__construct', $value->getArguments()]; foreach ($calls as $i => $call) { [$method, $arguments] = $call; $parameters = null; $resolvedKeys = []; $resolvedArguments = []; foreach ($arguments as $key => $argument) { if ($argument instanceof AbstractArgument && $argument->getText().'.' === $argument->getTextWithContext()) { $argument->setContext(sprintf('Argument '.(\is_int($key) ? 1 + $key : '"%3$s"').' of '.('__construct' === $method ? 'service "%s"' : 'method call "%s::%s()"'), $this->currentId, $method, $key)); } if (\is_int($key)) { $resolvedKeys[$key] = $key; $resolvedArguments[$key] = $argument; continue; } if (null === $parameters) { $r = $this->getReflectionMethod($value, $method); $class = $r instanceof \ReflectionMethod ? $r->class : $this->currentId; $method = $r->getName(); $parameters = $r->getParameters(); } if (isset($key[0]) && '$' !== $key[0] && !class_exists($key) && !interface_exists($key, false)) { throw new InvalidArgumentException(sprintf('Invalid service "%s": did you forget to add the "$" prefix to argument "%s"?', $this->currentId, $key)); } if (isset($key[0]) && '$' === $key[0]) { foreach ($parameters as $j => $p) { if ($key === '$'.$p->name) { if ($p->isVariadic() && \is_array($argument)) { foreach ($argument as $variadicArgument) { $resolvedKeys[$j] = $j; $resolvedArguments[$j++] = $variadicArgument; } } else { $resolvedKeys[$j] = $p->name; $resolvedArguments[$j] = $argument; } continue 2; } } throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); } if (null !== $argument && !$argument instanceof Reference && !$argument instanceof Definition) { throw new InvalidArgumentException(sprintf('Invalid service "%s": the value of argument "%s" of method "%s()" must be null, an instance of "%s" or an instance of "%s", "%s" given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, get_debug_type($argument))); } $typeFound = false; foreach ($parameters as $j => $p) { if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::getTypeHint($r, $p, true) === $key) { $resolvedKeys[$j] = $p->name; $resolvedArguments[$j] = $argument; $typeFound = true; } } if (!$typeFound) { throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); } } if ($resolvedArguments !== $call[1]) { ksort($resolvedArguments); if (!$value->isAutowired() && !array_is_list($resolvedArguments)) { ksort($resolvedKeys); $resolvedArguments = array_combine($resolvedKeys, $resolvedArguments); } $calls[$i][1] = $resolvedArguments; } } [, $arguments] = array_pop($calls); if ($arguments !== $value->getArguments()) { $value->setArguments($arguments); } if ($calls !== $value->getMethodCalls()) { $value->setMethodCalls($calls); } foreach ($value->getProperties() as $key => $argument) { if ($argument instanceof AbstractArgument && $argument->getText().'.' === $argument->getTextWithContext()) { $argument->setContext(sprintf('Property "%s" of service "%s"', $key, $this->currentId)); } } return parent::processValue($value, $isRoot); } } Compiler/ResolveNoPreloadPass.php 0000644 00000006444 15120140333 0013101 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; /** * Propagate the "container.no_preload" tag. * * @author Nicolas Grekas <p@tchwork.com> */ class ResolveNoPreloadPass extends AbstractRecursivePass { private const DO_PRELOAD_TAG = '.container.do_preload'; private $tagName; private $resolvedIds = []; public function __construct(string $tagName = 'container.no_preload') { if (0 < \func_num_args()) { trigger_deprecation('symfony/dependency-injection', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); } $this->tagName = $tagName; } /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { $this->container = $container; try { foreach ($container->getDefinitions() as $id => $definition) { if ($definition->isPublic() && !$definition->isPrivate() && !isset($this->resolvedIds[$id])) { $this->resolvedIds[$id] = true; $this->processValue($definition, true); } } foreach ($container->getAliases() as $alias) { if ($alias->isPublic() && !$alias->isPrivate() && !isset($this->resolvedIds[$id = (string) $alias]) && $container->hasDefinition($id)) { $this->resolvedIds[$id] = true; $this->processValue($container->getDefinition($id), true); } } } finally { $this->resolvedIds = []; $this->container = null; } foreach ($container->getDefinitions() as $definition) { if ($definition->hasTag(self::DO_PRELOAD_TAG)) { $definition->clearTag(self::DO_PRELOAD_TAG); } elseif (!$definition->isDeprecated() && !$definition->hasErrors()) { $definition->addTag($this->tagName); } } } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if ($value instanceof Reference && ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior() && $this->container->hasDefinition($id = (string) $value)) { $definition = $this->container->getDefinition($id); if (!isset($this->resolvedIds[$id]) && (!$definition->isPublic() || $definition->isPrivate())) { $this->resolvedIds[$id] = true; $this->processValue($definition, true); } return $value; } if (!$value instanceof Definition) { return parent::processValue($value, $isRoot); } if ($value->hasTag($this->tagName) || $value->isDeprecated() || $value->hasErrors()) { return $value; } if ($isRoot) { $value->addTag(self::DO_PRELOAD_TAG); } return parent::processValue($value, $isRoot); } } Compiler/ResolveParameterPlaceHoldersPass.php 0000644 00000006152 15120140333 0015420 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; /** * Resolves all parameter placeholders "%somevalue%" to their real values. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ResolveParameterPlaceHoldersPass extends AbstractRecursivePass { private $bag; private $resolveArrays; private $throwOnResolveException; public function __construct($resolveArrays = true, $throwOnResolveException = true) { $this->resolveArrays = $resolveArrays; $this->throwOnResolveException = $throwOnResolveException; } /** * {@inheritdoc} * * @throws ParameterNotFoundException */ public function process(ContainerBuilder $container) { $this->bag = $container->getParameterBag(); try { parent::process($container); $aliases = []; foreach ($container->getAliases() as $name => $target) { $this->currentId = $name; $aliases[$this->bag->resolveValue($name)] = $target; } $container->setAliases($aliases); } catch (ParameterNotFoundException $e) { $e->setSourceId($this->currentId); throw $e; } $this->bag->resolve(); $this->bag = null; } protected function processValue($value, bool $isRoot = false) { if (\is_string($value)) { try { $v = $this->bag->resolveValue($value); } catch (ParameterNotFoundException $e) { if ($this->throwOnResolveException) { throw $e; } $v = null; $this->container->getDefinition($this->currentId)->addError($e->getMessage()); } return $this->resolveArrays || !$v || !\is_array($v) ? $v : $value; } if ($value instanceof Definition) { $value->setBindings($this->processValue($value->getBindings())); $changes = $value->getChanges(); if (isset($changes['class'])) { $value->setClass($this->bag->resolveValue($value->getClass())); } if (isset($changes['file'])) { $value->setFile($this->bag->resolveValue($value->getFile())); } $tags = $value->getTags(); if (isset($tags['proxy'])) { $tags['proxy'] = $this->bag->resolveValue($tags['proxy']); $value->setTags($tags); } } $value = parent::processValue($value, $isRoot); if ($value && \is_array($value)) { $value = array_combine($this->bag->resolveValue(array_keys($value)), $value); } return $value; } } Compiler/ResolvePrivatesPass.php 0000644 00000002232 15120140333 0013002 0 ustar 00 <?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\Compiler; trigger_deprecation('symfony/dependency-injection', '5.2', 'The "%s" class is deprecated.', ResolvePrivatesPass::class); use Symfony\Component\DependencyInjection\ContainerBuilder; /** * @author Nicolas Grekas <p@tchwork.com> * * @deprecated since Symfony 5.2 */ class ResolvePrivatesPass implements CompilerPassInterface { /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { foreach ($container->getDefinitions() as $id => $definition) { if ($definition->isPrivate()) { $definition->setPublic(false); $definition->setPrivate(true); } } foreach ($container->getAliases() as $id => $alias) { if ($alias->isPrivate()) { $alias->setPublic(false); $alias->setPrivate(true); } } } } Compiler/ResolveReferencesToAliasesPass.php 0000644 00000005306 15120140333 0015100 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Reference; /** * Replaces all references to aliases with references to the actual service. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ResolveReferencesToAliasesPass extends AbstractRecursivePass { /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { parent::process($container); foreach ($container->getAliases() as $id => $alias) { $aliasId = (string) $alias; $this->currentId = $id; if ($aliasId !== $defId = $this->getDefinitionId($aliasId, $container)) { $container->setAlias($id, $defId)->setPublic($alias->isPublic()); } } } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if (!$value instanceof Reference) { return parent::processValue($value, $isRoot); } $defId = $this->getDefinitionId($id = (string) $value, $this->container); return $defId !== $id ? new Reference($defId, $value->getInvalidBehavior()) : $value; } private function getDefinitionId(string $id, ContainerBuilder $container): string { if (!$container->hasAlias($id)) { return $id; } $alias = $container->getAlias($id); if ($alias->isDeprecated()) { $referencingDefinition = $container->hasDefinition($this->currentId) ? $container->getDefinition($this->currentId) : $container->getAlias($this->currentId); if (!$referencingDefinition->isDeprecated()) { $deprecation = $alias->getDeprecation($id); trigger_deprecation($deprecation['package'], $deprecation['version'], rtrim($deprecation['message'], '. ').'. It is being referenced by the "%s" '.($container->hasDefinition($this->currentId) ? 'service.' : 'alias.'), $this->currentId); } } $seen = []; do { if (isset($seen[$id])) { throw new ServiceCircularReferenceException($id, array_merge(array_keys($seen), [$id])); } $seen[$id] = true; $id = (string) $container->getAlias($id); } while ($container->hasAlias($id)); return $id; } } Compiler/ResolveServiceSubscribersPass.php 0000644 00000003145 15120140333 0015020 0 ustar 00 <?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\Compiler; use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Contracts\Service\ServiceProviderInterface; /** * Compiler pass to inject their service locator to service subscribers. * * @author Nicolas Grekas <p@tchwork.com> */ class ResolveServiceSubscribersPass extends AbstractRecursivePass { private $serviceLocator; protected function processValue($value, bool $isRoot = false) { if ($value instanceof Reference && $this->serviceLocator && \in_array((string) $value, [ContainerInterface::class, ServiceProviderInterface::class], true)) { return new Reference($this->serviceLocator); } if (!$value instanceof Definition) { return parent::processValue($value, $isRoot); } $serviceLocator = $this->serviceLocator; $this->serviceLocator = null; if ($value->hasTag('container.service_subscriber.locator')) { $this->serviceLocator = $value->getTag('container.service_subscriber.locator')[0]['id']; $value->clearTag('container.service_subscriber.locator'); } try { return parent::processValue($value); } finally { $this->serviceLocator = $serviceLocator; } } } Compiler/ResolveTaggedIteratorArgumentPass.php 0000644 00000001670 15120140333 0015622 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; /** * Resolves all TaggedIteratorArgument arguments. * * @author Roland Franssen <franssen.roland@gmail.com> */ class ResolveTaggedIteratorArgumentPass extends AbstractRecursivePass { use PriorityTaggedServiceTrait; /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if (!$value instanceof TaggedIteratorArgument) { return parent::processValue($value, $isRoot); } $value->setValues($this->findAndSortTaggedServices($value, $this->container)); return $value; } } Compiler/ServiceLocatorTagPass.php 0000644 00000012436 15120140333 0013234 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; /** * Applies the "container.service_locator" tag by wrapping references into ServiceClosureArgument instances. * * @author Nicolas Grekas <p@tchwork.com> */ final class ServiceLocatorTagPass extends AbstractRecursivePass { use PriorityTaggedServiceTrait; protected function processValue($value, bool $isRoot = false) { if ($value instanceof ServiceLocatorArgument) { if ($value->getTaggedIteratorArgument()) { $value->setValues($this->findAndSortTaggedServices($value->getTaggedIteratorArgument(), $this->container)); } return self::register($this->container, $value->getValues()); } if ($value instanceof Definition) { $value->setBindings(parent::processValue($value->getBindings())); } if (!$value instanceof Definition || !$value->hasTag('container.service_locator')) { return parent::processValue($value, $isRoot); } if (!$value->getClass()) { $value->setClass(ServiceLocator::class); } $services = $value->getArguments()[0] ?? null; if ($services instanceof TaggedIteratorArgument) { $services = $this->findAndSortTaggedServices($services, $this->container); } if (!\is_array($services)) { throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set.', $this->currentId)); } $i = 0; foreach ($services as $k => $v) { if ($v instanceof ServiceClosureArgument) { continue; } if (!$v instanceof Reference) { throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set, "%s" found for key "%s".', $this->currentId, get_debug_type($v), $k)); } if ($i === $k) { unset($services[$k]); $k = (string) $v; ++$i; } elseif (\is_int($k)) { $i = null; } $services[$k] = new ServiceClosureArgument($v); } ksort($services); $value->setArgument(0, $services); $id = '.service_locator.'.ContainerBuilder::hash($value); if ($isRoot) { if ($id !== $this->currentId) { $this->container->setAlias($id, new Alias($this->currentId, false)); } return $value; } $this->container->setDefinition($id, $value->setPublic(false)); return new Reference($id); } /** * @param Reference[] $refMap */ public static function register(ContainerBuilder $container, array $refMap, string $callerId = null): Reference { foreach ($refMap as $id => $ref) { if (!$ref instanceof Reference) { throw new InvalidArgumentException(sprintf('Invalid service locator definition: only services can be referenced, "%s" found for key "%s". Inject parameter values using constructors instead.', get_debug_type($ref), $id)); } $refMap[$id] = new ServiceClosureArgument($ref); } $locator = (new Definition(ServiceLocator::class)) ->addArgument($refMap) ->addTag('container.service_locator'); if (null !== $callerId && $container->hasDefinition($callerId)) { $locator->setBindings($container->getDefinition($callerId)->getBindings()); } if (!$container->hasDefinition($id = '.service_locator.'.ContainerBuilder::hash($locator))) { $container->setDefinition($id, $locator); } if (null !== $callerId) { $locatorId = $id; // Locators are shared when they hold the exact same list of factories; // to have them specialized per consumer service, we use a cloning factory // to derivate customized instances from the prototype one. $container->register($id .= '.'.$callerId, ServiceLocator::class) ->setFactory([new Reference($locatorId), 'withContext']) ->addTag('container.service_locator_context', ['id' => $callerId]) ->addArgument($callerId) ->addArgument(new Reference('service_container')); } return new Reference($id); } } Compiler/ServiceReferenceGraph.php 0000644 00000005124 15120140333 0013222 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; /** * This is a directed graph of your services. * * This information can be used by your compiler passes instead of collecting * it themselves which improves performance quite a lot. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @final */ class ServiceReferenceGraph { /** * @var ServiceReferenceGraphNode[] */ private $nodes = []; public function hasNode(string $id): bool { return isset($this->nodes[$id]); } /** * Gets a node by identifier. * * @throws InvalidArgumentException if no node matches the supplied identifier */ public function getNode(string $id): ServiceReferenceGraphNode { if (!isset($this->nodes[$id])) { throw new InvalidArgumentException(sprintf('There is no node with id "%s".', $id)); } return $this->nodes[$id]; } /** * Returns all nodes. * * @return ServiceReferenceGraphNode[] */ public function getNodes(): array { return $this->nodes; } /** * Clears all nodes. */ public function clear() { foreach ($this->nodes as $node) { $node->clear(); } $this->nodes = []; } /** * Connects 2 nodes together in the Graph. */ public function connect(?string $sourceId, $sourceValue, ?string $destId, $destValue = null, Reference $reference = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false) { if (null === $sourceId || null === $destId) { return; } $sourceNode = $this->createNode($sourceId, $sourceValue); $destNode = $this->createNode($destId, $destValue); $edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak, $byConstructor); $sourceNode->addOutEdge($edge); $destNode->addInEdge($edge); } private function createNode(string $id, $value): ServiceReferenceGraphNode { if (isset($this->nodes[$id]) && $this->nodes[$id]->getValue() === $value) { return $this->nodes[$id]; } return $this->nodes[$id] = new ServiceReferenceGraphNode($id, $value); } } Compiler/ServiceReferenceGraphEdge.php 0000644 00000004210 15120140333 0014002 0 ustar 00 <?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\Compiler; /** * Represents an edge in your service graph. * * Value is typically a reference. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ServiceReferenceGraphEdge { private $sourceNode; private $destNode; private $value; private $lazy; private $weak; private $byConstructor; public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false) { $this->sourceNode = $sourceNode; $this->destNode = $destNode; $this->value = $value; $this->lazy = $lazy; $this->weak = $weak; $this->byConstructor = $byConstructor; } /** * Returns the value of the edge. * * @return mixed */ public function getValue() { return $this->value; } /** * Returns the source node. * * @return ServiceReferenceGraphNode */ public function getSourceNode() { return $this->sourceNode; } /** * Returns the destination node. * * @return ServiceReferenceGraphNode */ public function getDestNode() { return $this->destNode; } /** * Returns true if the edge is lazy, meaning it's a dependency not requiring direct instantiation. * * @return bool */ public function isLazy() { return $this->lazy; } /** * Returns true if the edge is weak, meaning it shouldn't prevent removing the target service. * * @return bool */ public function isWeak() { return $this->weak; } /** * Returns true if the edge links with a constructor argument. * * @return bool */ public function isReferencedByConstructor() { return $this->byConstructor; } } Compiler/ServiceReferenceGraphNode.php 0000644 00000004373 15120140333 0014035 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Definition; /** * Represents a node in your service graph. * * Value is typically a definition, or an alias. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ServiceReferenceGraphNode { private $id; private $inEdges = []; private $outEdges = []; private $value; /** * @param string $id The node identifier * @param mixed $value The node value */ public function __construct(string $id, $value) { $this->id = $id; $this->value = $value; } public function addInEdge(ServiceReferenceGraphEdge $edge) { $this->inEdges[] = $edge; } public function addOutEdge(ServiceReferenceGraphEdge $edge) { $this->outEdges[] = $edge; } /** * Checks if the value of this node is an Alias. * * @return bool */ public function isAlias() { return $this->value instanceof Alias; } /** * Checks if the value of this node is a Definition. * * @return bool */ public function isDefinition() { return $this->value instanceof Definition; } /** * Returns the identifier. * * @return string */ public function getId() { return $this->id; } /** * Returns the in edges. * * @return ServiceReferenceGraphEdge[] */ public function getInEdges() { return $this->inEdges; } /** * Returns the out edges. * * @return ServiceReferenceGraphEdge[] */ public function getOutEdges() { return $this->outEdges; } /** * Returns the value of this Node. * * @return mixed */ public function getValue() { return $this->value; } /** * Clears all edges. */ public function clear() { $this->inEdges = $this->outEdges = []; } } Compiler/ValidateEnvPlaceholdersPass.php 0000644 00000007125 15120140333 0014403 0 ustar 00 <?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\Compiler; use Symfony\Component\Config\Definition\BaseNode; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Processor; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; /** * Validates environment variable placeholders used in extension configuration with dummy values. * * @author Roland Franssen <franssen.roland@gmail.com> */ class ValidateEnvPlaceholdersPass implements CompilerPassInterface { private const TYPE_FIXTURES = ['array' => [], 'bool' => false, 'float' => 0.0, 'int' => 0, 'string' => '']; private $extensionConfig = []; /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { $this->extensionConfig = []; if (!class_exists(BaseNode::class) || !$extensions = $container->getExtensions()) { return; } $resolvingBag = $container->getParameterBag(); if (!$resolvingBag instanceof EnvPlaceholderParameterBag) { return; } $defaultBag = new ParameterBag($resolvingBag->all()); $envTypes = $resolvingBag->getProvidedTypes(); foreach ($resolvingBag->getEnvPlaceholders() + $resolvingBag->getUnusedEnvPlaceholders() as $env => $placeholders) { $values = []; if (false === $i = strpos($env, ':')) { $default = $defaultBag->has("env($env)") ? $defaultBag->get("env($env)") : self::TYPE_FIXTURES['string']; $defaultType = null !== $default ? get_debug_type($default) : 'string'; $values[$defaultType] = $default; } else { $prefix = substr($env, 0, $i); foreach ($envTypes[$prefix] ?? ['string'] as $type) { $values[$type] = self::TYPE_FIXTURES[$type] ?? null; } } foreach ($placeholders as $placeholder) { BaseNode::setPlaceholder($placeholder, $values); } } $processor = new Processor(); foreach ($extensions as $name => $extension) { if (!($extension instanceof ConfigurationExtensionInterface || $extension instanceof ConfigurationInterface) || !$config = array_filter($container->getExtensionConfig($name)) ) { // this extension has no semantic configuration or was not called continue; } $config = $resolvingBag->resolveValue($config); if ($extension instanceof ConfigurationInterface) { $configuration = $extension; } elseif (null === $configuration = $extension->getConfiguration($config, $container)) { continue; } $this->extensionConfig[$name] = $processor->processConfiguration($configuration, $config); } $resolvingBag->clearUnusedEnvPlaceholders(); } /** * @internal */ public function getExtensionConfig(): array { try { return $this->extensionConfig; } finally { $this->extensionConfig = []; } } } Compiler/AbstractRecursivePass.php 0000644 00000022163 15120140333 0013305 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\ExpressionLanguage; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; /** * @author Nicolas Grekas <p@tchwork.com> */ abstract class AbstractRecursivePass implements CompilerPassInterface { /** * @var ContainerBuilder */ protected $container; protected $currentId; private $processExpressions = false; private $expressionLanguage; private $inExpression = false; /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { $this->container = $container; try { $this->processValue($container->getDefinitions(), true); } finally { $this->container = null; } } protected function enableExpressionProcessing() { $this->processExpressions = true; } protected function inExpression(bool $reset = true): bool { $inExpression = $this->inExpression; if ($reset) { $this->inExpression = false; } return $inExpression; } /** * Processes a value found in a definition tree. * * @param mixed $value * * @return mixed */ protected function processValue($value, bool $isRoot = false) { if (\is_array($value)) { foreach ($value as $k => $v) { if ($isRoot) { $this->currentId = $k; } if ($v !== $processedValue = $this->processValue($v, $isRoot)) { $value[$k] = $processedValue; } } } elseif ($value instanceof ArgumentInterface) { $value->setValues($this->processValue($value->getValues())); } elseif ($value instanceof Expression && $this->processExpressions) { $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']); } elseif ($value instanceof Definition) { $value->setArguments($this->processValue($value->getArguments())); $value->setProperties($this->processValue($value->getProperties())); $value->setMethodCalls($this->processValue($value->getMethodCalls())); $changes = $value->getChanges(); if (isset($changes['factory'])) { $value->setFactory($this->processValue($value->getFactory())); } if (isset($changes['configurator'])) { $value->setConfigurator($this->processValue($value->getConfigurator())); } } return $value; } /** * @return \ReflectionFunctionAbstract|null * * @throws RuntimeException */ protected function getConstructor(Definition $definition, bool $required) { if ($definition->isSynthetic()) { return null; } if (\is_string($factory = $definition->getFactory())) { if (!\function_exists($factory)) { throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.', $this->currentId, $factory)); } $r = new \ReflectionFunction($factory); if (false !== $r->getFileName() && file_exists($r->getFileName())) { $this->container->fileExists($r->getFileName()); } return $r; } if ($factory) { [$class, $method] = $factory; if ('__construct' === $method) { throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId)); } if ($class instanceof Reference) { $factoryDefinition = $this->container->findDefinition((string) $class); while ((null === $class = $factoryDefinition->getClass()) && $factoryDefinition instanceof ChildDefinition) { $factoryDefinition = $this->container->findDefinition($factoryDefinition->getParent()); } } elseif ($class instanceof Definition) { $class = $class->getClass(); } elseif (null === $class) { $class = $definition->getClass(); } return $this->getReflectionMethod(new Definition($class), $method); } while ((null === $class = $definition->getClass()) && $definition instanceof ChildDefinition) { $definition = $this->container->findDefinition($definition->getParent()); } try { if (!$r = $this->container->getReflectionClass($class)) { if (null === $class) { throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId)); } throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); } } catch (\ReflectionException $e) { throw new RuntimeException(sprintf('Invalid service "%s": ', $this->currentId).lcfirst($e->getMessage())); } if (!$r = $r->getConstructor()) { if ($required) { throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class))); } } elseif (!$r->isPublic()) { throw new RuntimeException(sprintf('Invalid service "%s": ', $this->currentId).sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class).' must be public.'); } return $r; } /** * @return \ReflectionFunctionAbstract * * @throws RuntimeException */ protected function getReflectionMethod(Definition $definition, string $method) { if ('__construct' === $method) { return $this->getConstructor($definition, true); } while ((null === $class = $definition->getClass()) && $definition instanceof ChildDefinition) { $definition = $this->container->findDefinition($definition->getParent()); } if (null === $class) { throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId)); } if (!$r = $this->container->getReflectionClass($class)) { throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); } if (!$r->hasMethod($method)) { if ($r->hasMethod('__call') && ($r = $r->getMethod('__call')) && $r->isPublic()) { return new \ReflectionMethod(static function (...$arguments) {}, '__invoke'); } throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); } $r = $r->getMethod($method); if (!$r->isPublic()) { throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); } return $r; } private function getExpressionLanguage(): ExpressionLanguage { if (null === $this->expressionLanguage) { if (!class_exists(ExpressionLanguage::class)) { throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed. Try running "composer require symfony/expression-language".'); } $providers = $this->container->getExpressionLanguageProviders(); $this->expressionLanguage = new ExpressionLanguage(null, $providers, function (string $arg): string { if ('""' === substr_replace($arg, '', 1, -1)) { $id = stripcslashes(substr($arg, 1, -1)); $this->inExpression = true; $arg = $this->processValue(new Reference($id)); $this->inExpression = false; if (!$arg instanceof Reference) { throw new RuntimeException(sprintf('"%s::processValue()" must return a Reference when processing an expression, "%s" returned for service("%s").', static::class, get_debug_type($arg), $id)); } $arg = sprintf('"%s"', $arg); } return sprintf('$this->get(%s)', $arg); }); } return $this->expressionLanguage; } } Compiler/AliasDeprecatedPublicServicesPass.php 0000644 00000004777 15120140333 0015542 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; final class AliasDeprecatedPublicServicesPass extends AbstractRecursivePass { private $tagName; private $aliases = []; public function __construct(string $tagName = 'container.private') { if (0 < \func_num_args()) { trigger_deprecation('symfony/dependency-injection', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); } $this->tagName = $tagName; } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if ($value instanceof Reference && isset($this->aliases[$id = (string) $value])) { return new Reference($this->aliases[$id], $value->getInvalidBehavior()); } return parent::processValue($value, $isRoot); } /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { foreach ($container->findTaggedServiceIds($this->tagName) as $id => $tags) { if (null === $package = $tags[0]['package'] ?? null) { throw new InvalidArgumentException(sprintf('The "package" attribute is mandatory for the "%s" tag on the "%s" service.', $this->tagName, $id)); } if (null === $version = $tags[0]['version'] ?? null) { throw new InvalidArgumentException(sprintf('The "version" attribute is mandatory for the "%s" tag on the "%s" service.', $this->tagName, $id)); } $definition = $container->getDefinition($id); if (!$definition->isPublic() || $definition->isPrivate()) { continue; } $container ->setAlias($id, $aliasId = '.'.$this->tagName.'.'.$id) ->setPublic(true) ->setDeprecated($package, $version, 'Accessing the "%alias_id%" service directly from the container is deprecated, use dependency injection instead.'); $container->setDefinition($aliasId, $definition); $this->aliases[$id] = $aliasId; } parent::process($container); } } Compiler/AnalyzeServiceReferencesPass.php 0000644 00000014267 15120140333 0014606 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; /** * Run this pass before passes that need to know more about the relation of * your services. * * This class will populate the ServiceReferenceGraph with information. You can * retrieve the graph in other passes from the compiler. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ class AnalyzeServiceReferencesPass extends AbstractRecursivePass { private $graph; private $currentDefinition; private $onlyConstructorArguments; private $hasProxyDumper; private $lazy; private $byConstructor; private $byFactory; private $definitions; private $aliases; /** * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls */ public function __construct(bool $onlyConstructorArguments = false, bool $hasProxyDumper = true) { $this->onlyConstructorArguments = $onlyConstructorArguments; $this->hasProxyDumper = $hasProxyDumper; $this->enableExpressionProcessing(); } /** * Processes a ContainerBuilder object to populate the service reference graph. */ public function process(ContainerBuilder $container) { $this->container = $container; $this->graph = $container->getCompiler()->getServiceReferenceGraph(); $this->graph->clear(); $this->lazy = false; $this->byConstructor = false; $this->byFactory = false; $this->definitions = $container->getDefinitions(); $this->aliases = $container->getAliases(); foreach ($this->aliases as $id => $alias) { $targetId = $this->getDefinitionId((string) $alias); $this->graph->connect($id, $alias, $targetId, null !== $targetId ? $this->container->getDefinition($targetId) : null, null); } try { parent::process($container); } finally { $this->aliases = $this->definitions = []; } } protected function processValue($value, bool $isRoot = false) { $lazy = $this->lazy; $inExpression = $this->inExpression(); if ($value instanceof ArgumentInterface) { $this->lazy = !$this->byFactory || !$value instanceof IteratorArgument; parent::processValue($value->getValues()); $this->lazy = $lazy; return $value; } if ($value instanceof Reference) { $targetId = $this->getDefinitionId((string) $value); $targetDefinition = null !== $targetId ? $this->container->getDefinition($targetId) : null; $this->graph->connect( $this->currentId, $this->currentDefinition, $targetId, $targetDefinition, $value, $this->lazy || ($this->hasProxyDumper && $targetDefinition && $targetDefinition->isLazy()), ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior(), $this->byConstructor ); if ($inExpression) { $this->graph->connect( '.internal.reference_in_expression', null, $targetId, $targetDefinition, $value, $this->lazy || ($targetDefinition && $targetDefinition->isLazy()), true ); } return $value; } if (!$value instanceof Definition) { return parent::processValue($value, $isRoot); } if ($isRoot) { if ($value->isSynthetic() || $value->isAbstract()) { return $value; } $this->currentDefinition = $value; } elseif ($this->currentDefinition === $value) { return $value; } $this->lazy = false; $byConstructor = $this->byConstructor; $this->byConstructor = $isRoot || $byConstructor; $byFactory = $this->byFactory; $this->byFactory = true; $this->processValue($value->getFactory()); $this->byFactory = $byFactory; $this->processValue($value->getArguments()); $properties = $value->getProperties(); $setters = $value->getMethodCalls(); // Any references before a "wither" are part of the constructor-instantiation graph $lastWitherIndex = null; foreach ($setters as $k => $call) { if ($call[2] ?? false) { $lastWitherIndex = $k; } } if (null !== $lastWitherIndex) { $this->processValue($properties); $setters = $properties = []; foreach ($value->getMethodCalls() as $k => $call) { if (null === $lastWitherIndex) { $setters[] = $call; continue; } if ($lastWitherIndex === $k) { $lastWitherIndex = null; } $this->processValue($call); } } $this->byConstructor = $byConstructor; if (!$this->onlyConstructorArguments) { $this->processValue($properties); $this->processValue($setters); $this->processValue($value->getConfigurator()); } $this->lazy = $lazy; return $value; } private function getDefinitionId(string $id): ?string { while (isset($this->aliases[$id])) { $id = (string) $this->aliases[$id]; } return isset($this->definitions[$id]) ? $id : null; } } Compiler/AttributeAutoconfigurationPass.php 0000644 00000016706 15120140333 0015244 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * @author Alexander M. Turek <me@derrabus.de> */ final class AttributeAutoconfigurationPass extends AbstractRecursivePass { private $classAttributeConfigurators = []; private $methodAttributeConfigurators = []; private $propertyAttributeConfigurators = []; private $parameterAttributeConfigurators = []; public function process(ContainerBuilder $container): void { if (80000 > \PHP_VERSION_ID || !$container->getAutoconfiguredAttributes()) { return; } foreach ($container->getAutoconfiguredAttributes() as $attributeName => $callable) { $callableReflector = new \ReflectionFunction(\Closure::fromCallable($callable)); if ($callableReflector->getNumberOfParameters() <= 2) { $this->classAttributeConfigurators[$attributeName] = $callable; continue; } $reflectorParameter = $callableReflector->getParameters()[2]; $parameterType = $reflectorParameter->getType(); $types = []; if ($parameterType instanceof \ReflectionUnionType) { foreach ($parameterType->getTypes() as $type) { $types[] = $type->getName(); } } elseif ($parameterType instanceof \ReflectionNamedType) { $types[] = $parameterType->getName(); } else { throw new LogicException(sprintf('Argument "$%s" of attribute autoconfigurator should have a type, use one or more of "\ReflectionClass|\ReflectionMethod|\ReflectionProperty|\ReflectionParameter|\Reflector" in "%s" on line "%d".', $reflectorParameter->getName(), $callableReflector->getFileName(), $callableReflector->getStartLine())); } try { $attributeReflector = new \ReflectionClass($attributeName); } catch (\ReflectionException $e) { continue; } $targets = $attributeReflector->getAttributes(\Attribute::class)[0] ?? 0; $targets = $targets ? $targets->getArguments()[0] ?? -1 : 0; foreach (['class', 'method', 'property', 'parameter'] as $symbol) { if (['Reflector'] !== $types) { if (!\in_array('Reflection'.ucfirst($symbol), $types, true)) { continue; } if (!($targets & \constant('Attribute::TARGET_'.strtoupper($symbol)))) { throw new LogicException(sprintf('Invalid type "Reflection%s" on argument "$%s": attribute "%s" cannot target a '.$symbol.' in "%s" on line "%d".', ucfirst($symbol), $reflectorParameter->getName(), $attributeName, $callableReflector->getFileName(), $callableReflector->getStartLine())); } } $this->{$symbol.'AttributeConfigurators'}[$attributeName] = $callable; } } parent::process($container); } protected function processValue($value, bool $isRoot = false) { if (!$value instanceof Definition || !$value->isAutoconfigured() || $value->isAbstract() || $value->hasTag('container.ignore_attributes') || !($classReflector = $this->container->getReflectionClass($value->getClass(), false)) ) { return parent::processValue($value, $isRoot); } $instanceof = $value->getInstanceofConditionals(); $conditionals = $instanceof[$classReflector->getName()] ?? new ChildDefinition(''); if ($this->classAttributeConfigurators) { foreach ($classReflector->getAttributes() as $attribute) { if ($configurator = $this->classAttributeConfigurators[$attribute->getName()] ?? null) { $configurator($conditionals, $attribute->newInstance(), $classReflector); } } } if ($this->parameterAttributeConfigurators) { try { $constructorReflector = $this->getConstructor($value, false); } catch (RuntimeException $e) { $constructorReflector = null; } if ($constructorReflector) { foreach ($constructorReflector->getParameters() as $parameterReflector) { foreach ($parameterReflector->getAttributes() as $attribute) { if ($configurator = $this->parameterAttributeConfigurators[$attribute->getName()] ?? null) { $configurator($conditionals, $attribute->newInstance(), $parameterReflector); } } } } } if ($this->methodAttributeConfigurators || $this->parameterAttributeConfigurators) { foreach ($classReflector->getMethods(\ReflectionMethod::IS_PUBLIC) as $methodReflector) { if ($methodReflector->isStatic() || $methodReflector->isConstructor() || $methodReflector->isDestructor()) { continue; } if ($this->methodAttributeConfigurators) { foreach ($methodReflector->getAttributes() as $attribute) { if ($configurator = $this->methodAttributeConfigurators[$attribute->getName()] ?? null) { $configurator($conditionals, $attribute->newInstance(), $methodReflector); } } } if ($this->parameterAttributeConfigurators) { foreach ($methodReflector->getParameters() as $parameterReflector) { foreach ($parameterReflector->getAttributes() as $attribute) { if ($configurator = $this->parameterAttributeConfigurators[$attribute->getName()] ?? null) { $configurator($conditionals, $attribute->newInstance(), $parameterReflector); } } } } } } if ($this->propertyAttributeConfigurators) { foreach ($classReflector->getProperties(\ReflectionProperty::IS_PUBLIC) as $propertyReflector) { if ($propertyReflector->isStatic()) { continue; } foreach ($propertyReflector->getAttributes() as $attribute) { if ($configurator = $this->propertyAttributeConfigurators[$attribute->getName()] ?? null) { $configurator($conditionals, $attribute->newInstance(), $propertyReflector); } } } } if (!isset($instanceof[$classReflector->getName()]) && new ChildDefinition('') != $conditionals) { $instanceof[$classReflector->getName()] = $conditionals; $value->setInstanceofConditionals($instanceof); } return parent::processValue($value, $isRoot); } } Compiler/AutoAliasServicePass.php 0000644 00000003564 15120140333 0013061 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * Sets a service to be an alias of another one, given a format pattern. */ class AutoAliasServicePass implements CompilerPassInterface { private $privateAliases = []; /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { foreach ($container->findTaggedServiceIds('auto_alias') as $serviceId => $tags) { foreach ($tags as $tag) { if (!isset($tag['format'])) { throw new InvalidArgumentException(sprintf('Missing tag information "format" on auto_alias service "%s".', $serviceId)); } $aliasId = $container->getParameterBag()->resolveValue($tag['format']); if ($container->hasDefinition($aliasId) || $container->hasAlias($aliasId)) { $alias = new Alias($aliasId, $container->getDefinition($serviceId)->isPublic()); $container->setAlias($serviceId, $alias); if (!$alias->isPublic()) { $alias->setPublic(true); $this->privateAliases[] = $alias; } } } } } /** * @internal to be removed in Symfony 6.0 */ public function getPrivateAliases(): array { $privateAliases = $this->privateAliases; $this->privateAliases = []; return $privateAliases; } } Compiler/AutowirePass.php 0000644 00000060074 15120140333 0011454 0 ustar 00 <?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\Compiler; use Symfony\Component\Config\Resource\ClassExistenceResource; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use Symfony\Component\DependencyInjection\Attribute\TaggedLocator; use Symfony\Component\DependencyInjection\Attribute\Target; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; use Symfony\Component\DependencyInjection\TypedReference; /** * Inspects existing service definitions and wires the autowired ones using the type hints of their classes. * * @author Kévin Dunglas <dunglas@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ class AutowirePass extends AbstractRecursivePass { private $types; private $ambiguousServiceTypes; private $autowiringAliases; private $lastFailure; private $throwOnAutowiringException; private $decoratedClass; private $decoratedId; private $methodCalls; private $defaultArgument; private $getPreviousValue; private $decoratedMethodIndex; private $decoratedMethodArgumentIndex; private $typesClone; public function __construct(bool $throwOnAutowireException = true) { $this->throwOnAutowiringException = $throwOnAutowireException; $this->defaultArgument = new class() { public $value; public $names; }; } /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { try { $this->typesClone = clone $this; parent::process($container); } finally { $this->decoratedClass = null; $this->decoratedId = null; $this->methodCalls = null; $this->defaultArgument->names = null; $this->getPreviousValue = null; $this->decoratedMethodIndex = null; $this->decoratedMethodArgumentIndex = null; $this->typesClone = null; } } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { try { return $this->doProcessValue($value, $isRoot); } catch (AutowiringFailedException $e) { if ($this->throwOnAutowiringException) { throw $e; } $this->container->getDefinition($this->currentId)->addError($e->getMessageCallback() ?? $e->getMessage()); return parent::processValue($value, $isRoot); } } /** * @return mixed */ private function doProcessValue($value, bool $isRoot = false) { if ($value instanceof TypedReference) { if ($ref = $this->getAutowiredReference($value, true)) { return $ref; } if (ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) { $message = $this->createTypeNotFoundMessageCallback($value, 'it'); // since the error message varies by referenced id and $this->currentId, so should the id of the dummy errored definition $this->container->register($id = sprintf('.errored.%s.%s', $this->currentId, (string) $value), $value->getType()) ->addError($message); return new TypedReference($id, $value->getType(), $value->getInvalidBehavior(), $value->getName()); } } $value = parent::processValue($value, $isRoot); if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { return $value; } if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { $this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" cannot be loaded.', $this->currentId, $value->getClass())); return $value; } $this->methodCalls = $value->getMethodCalls(); try { $constructor = $this->getConstructor($value, false); } catch (RuntimeException $e) { throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e); } if ($constructor) { array_unshift($this->methodCalls, [$constructor, $value->getArguments()]); } $checkAttributes = 80000 <= \PHP_VERSION_ID && !$value->hasTag('container.ignore_attributes'); $this->methodCalls = $this->autowireCalls($reflectionClass, $isRoot, $checkAttributes); if ($constructor) { [, $arguments] = array_shift($this->methodCalls); if ($arguments !== $value->getArguments()) { $value->setArguments($arguments); } } if ($this->methodCalls !== $value->getMethodCalls()) { $value->setMethodCalls($this->methodCalls); } return $value; } private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, bool $checkAttributes): array { $this->decoratedId = null; $this->decoratedClass = null; $this->getPreviousValue = null; if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && null !== ($this->decoratedId = $definition->innerServiceId) && $this->container->has($this->decoratedId)) { $this->decoratedClass = $this->container->findDefinition($this->decoratedId)->getClass(); } $patchedIndexes = []; foreach ($this->methodCalls as $i => $call) { [$method, $arguments] = $call; if ($method instanceof \ReflectionFunctionAbstract) { $reflectionMethod = $method; } else { $definition = new Definition($reflectionClass->name); try { $reflectionMethod = $this->getReflectionMethod($definition, $method); } catch (RuntimeException $e) { if ($definition->getFactory()) { continue; } throw $e; } } $arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes, $i); if ($arguments !== $call[1]) { $this->methodCalls[$i][1] = $arguments; $patchedIndexes[] = $i; } } // use named arguments to skip complex default values foreach ($patchedIndexes as $i) { $namedArguments = null; $arguments = $this->methodCalls[$i][1]; foreach ($arguments as $j => $value) { if ($namedArguments && !$value instanceof $this->defaultArgument) { unset($arguments[$j]); $arguments[$namedArguments[$j]] = $value; } if ($namedArguments || !$value instanceof $this->defaultArgument) { continue; } if (\PHP_VERSION_ID >= 80100 && (\is_array($value->value) ? $value->value : \is_object($value->value))) { unset($arguments[$j]); $namedArguments = $value->names; } else { $arguments[$j] = $value->value; } } $this->methodCalls[$i][1] = $arguments; } return $this->methodCalls; } /** * Autowires the constructor or a method. * * @throws AutowiringFailedException */ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes, int $methodIndex): array { $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId; $method = $reflectionMethod->name; $parameters = $reflectionMethod->getParameters(); if ($reflectionMethod->isVariadic()) { array_pop($parameters); } $this->defaultArgument->names = new \ArrayObject(); foreach ($parameters as $index => $parameter) { $this->defaultArgument->names[$index] = $parameter->name; if (\array_key_exists($parameter->name, $arguments)) { $arguments[$index] = $arguments[$parameter->name]; unset($arguments[$parameter->name]); } if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) { continue; } $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true); if ($checkAttributes) { foreach ($parameter->getAttributes() as $attribute) { if (TaggedIterator::class === $attribute->getName()) { $attribute = $attribute->newInstance(); $arguments[$index] = new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, $attribute->defaultIndexMethod, false, $attribute->defaultPriorityMethod); break; } if (TaggedLocator::class === $attribute->getName()) { $attribute = $attribute->newInstance(); $arguments[$index] = new ServiceLocatorArgument(new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, $attribute->defaultIndexMethod, true, $attribute->defaultPriorityMethod)); break; } } if ('' !== ($arguments[$index] ?? '')) { continue; } } if (!$type) { if (isset($arguments[$index])) { continue; } // no default value? Then fail if (!$parameter->isDefaultValueAvailable()) { // For core classes, isDefaultValueAvailable() can // be false when isOptional() returns true. If the // argument *is* optional, allow it to be missing if ($parameter->isOptional()) { --$index; break; } $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false); $type = $type ? sprintf('is type-hinted "%s"', ltrim($type, '\\')) : 'has no type-hint'; throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method, $type)); } // specifically pass the default value $arguments[$index] = clone $this->defaultArgument; $arguments[$index]->value = $parameter->getDefaultValue(); continue; } $getValue = function () use ($type, $parameter, $class, $method) { if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, Target::parseName($parameter)), true)) { $failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); if ($parameter->isDefaultValueAvailable()) { $value = clone $this->defaultArgument; $value->value = $parameter->getDefaultValue(); } elseif (!$parameter->allowsNull()) { throw new AutowiringFailedException($this->currentId, $failureMessage); } } return $value; }; if ($this->decoratedClass && $isDecorated = is_a($this->decoratedClass, $type, true)) { if ($this->getPreviousValue) { // The inner service is injected only if there is only 1 argument matching the type of the decorated class // across all arguments of all autowired methods. // If a second matching argument is found, the default behavior is restored. $getPreviousValue = $this->getPreviousValue; $this->methodCalls[$this->decoratedMethodIndex][1][$this->decoratedMethodArgumentIndex] = $getPreviousValue(); $this->decoratedClass = null; // Prevent further checks } else { $arguments[$index] = new TypedReference($this->decoratedId, $this->decoratedClass); $this->getPreviousValue = $getValue; $this->decoratedMethodIndex = $methodIndex; $this->decoratedMethodArgumentIndex = $index; continue; } } $arguments[$index] = $getValue(); } if ($parameters && !isset($arguments[++$index])) { while (0 <= --$index) { if (!$arguments[$index] instanceof $this->defaultArgument) { break; } unset($arguments[$index]); } } // it's possible index 1 was set, then index 0, then 2, etc // make sure that we re-order so they're injected as expected ksort($arguments, \SORT_NATURAL); return $arguments; } /** * Returns a reference to the service matching the given type, if any. */ private function getAutowiredReference(TypedReference $reference, bool $filterType): ?TypedReference { $this->lastFailure = null; $type = $reference->getType(); if ($type !== (string) $reference) { return $reference; } if ($filterType && false !== $m = strpbrk($type, '&|')) { $types = array_diff(explode($m[0], $type), ['int', 'string', 'array', 'bool', 'float', 'iterable', 'object', 'callable', 'null']); sort($types); $type = implode($m[0], $types); } if (null !== $name = $reference->getName()) { if ($this->container->has($alias = $type.' $'.$name) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } if (null !== ($alias = $this->getCombinedAlias($type, $name) ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) { foreach ($this->container->getAliases() as $id => $alias) { if ($name === (string) $alias && str_starts_with($id, $type.' $')) { return new TypedReference($name, $type, $reference->getInvalidBehavior()); } } } } if ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract()) { return new TypedReference($type, $type, $reference->getInvalidBehavior()); } if (null !== ($alias = $this->getCombinedAlias($type) ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } return null; } /** * Populates the list of available types. */ private function populateAvailableTypes(ContainerBuilder $container) { $this->types = []; $this->ambiguousServiceTypes = []; $this->autowiringAliases = []; foreach ($container->getDefinitions() as $id => $definition) { $this->populateAvailableType($container, $id, $definition); } foreach ($container->getAliases() as $id => $alias) { $this->populateAutowiringAlias($id); } } /** * Populates the list of available types for a given definition. */ private function populateAvailableType(ContainerBuilder $container, string $id, Definition $definition) { // Never use abstract services if ($definition->isAbstract()) { return; } if ('' === $id || '.' === $id[0] || $definition->isDeprecated() || !$reflectionClass = $container->getReflectionClass($definition->getClass(), false)) { return; } foreach ($reflectionClass->getInterfaces() as $reflectionInterface) { $this->set($reflectionInterface->name, $id); } do { $this->set($reflectionClass->name, $id); } while ($reflectionClass = $reflectionClass->getParentClass()); $this->populateAutowiringAlias($id); } /** * Associates a type and a service id if applicable. */ private function set(string $type, string $id) { // is this already a type/class that is known to match multiple services? if (isset($this->ambiguousServiceTypes[$type])) { $this->ambiguousServiceTypes[$type][] = $id; return; } // check to make sure the type doesn't match multiple services if (!isset($this->types[$type]) || $this->types[$type] === $id) { $this->types[$type] = $id; return; } // keep an array of all services matching this type if (!isset($this->ambiguousServiceTypes[$type])) { $this->ambiguousServiceTypes[$type] = [$this->types[$type]]; unset($this->types[$type]); } $this->ambiguousServiceTypes[$type][] = $id; } private function createTypeNotFoundMessageCallback(TypedReference $reference, string $label): \Closure { if (null === $this->typesClone->container) { $this->typesClone->container = new ContainerBuilder($this->container->getParameterBag()); $this->typesClone->container->setAliases($this->container->getAliases()); $this->typesClone->container->setDefinitions($this->container->getDefinitions()); $this->typesClone->container->setResourceTracking(false); } $currentId = $this->currentId; return (function () use ($reference, $label, $currentId) { return $this->createTypeNotFoundMessage($reference, $label, $currentId); })->bindTo($this->typesClone); } private function createTypeNotFoundMessage(TypedReference $reference, string $label, string $currentId): string { if (!$r = $this->container->getReflectionClass($type = $reference->getType(), false)) { // either $type does not exist or a parent class does not exist try { $resource = new ClassExistenceResource($type, false); // isFresh() will explode ONLY if a parent class/trait does not exist $resource->isFresh(0); $parentMsg = false; } catch (\ReflectionException $e) { $parentMsg = $e->getMessage(); } $message = sprintf('has type "%s" but this class %s.', $type, $parentMsg ? sprintf('is missing a parent class (%s)', $parentMsg) : 'was not found'); } else { $alternatives = $this->createTypeAlternatives($this->container, $reference); $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists'; $message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $alternatives); if ($r->isInterface() && !$alternatives) { $message .= ' Did you create a class that implements this interface?'; } } $message = sprintf('Cannot autowire service "%s": %s %s', $currentId, $label, $message); if (null !== $this->lastFailure) { $message = $this->lastFailure."\n".$message; $this->lastFailure = null; } return $message; } private function createTypeAlternatives(ContainerBuilder $container, TypedReference $reference): string { // try suggesting available aliases first if ($message = $this->getAliasesSuggestionForType($container, $type = $reference->getType())) { return ' '.$message; } if (null === $this->ambiguousServiceTypes) { $this->populateAvailableTypes($container); } $servicesAndAliases = $container->getServiceIds(); if (null !== ($autowiringAliases = $this->autowiringAliases[$type] ?? null) && !isset($autowiringAliases[''])) { return sprintf(' Available autowiring aliases for this %s are: "$%s".', class_exists($type, false) ? 'class' : 'interface', implode('", "$', $autowiringAliases)); } if (!$container->has($type) && false !== $key = array_search(strtolower($type), array_map('strtolower', $servicesAndAliases))) { return sprintf(' Did you mean "%s"?', $servicesAndAliases[$key]); } elseif (isset($this->ambiguousServiceTypes[$type])) { $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type])); } elseif (isset($this->types[$type])) { $message = sprintf('the existing "%s" service', $this->types[$type]); } else { return ''; } return sprintf(' You should maybe alias this %s to %s.', class_exists($type, false) ? 'class' : 'interface', $message); } private function getAliasesSuggestionForType(ContainerBuilder $container, string $type): ?string { $aliases = []; foreach (class_parents($type) + class_implements($type) as $parent) { if ($container->has($parent) && !$container->findDefinition($parent)->isAbstract()) { $aliases[] = $parent; } } if (1 < $len = \count($aliases)) { $message = 'Try changing the type-hint to one of its parents: '; for ($i = 0, --$len; $i < $len; ++$i) { $message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]); } $message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]); return $message; } if ($aliases) { return sprintf('Try changing the type-hint to "%s" instead.', $aliases[0]); } return null; } private function populateAutowiringAlias(string $id): void { if (!preg_match('/(?(DEFINE)(?<V>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))^((?&V)(?:\\\\(?&V))*+)(?: \$((?&V)))?$/', $id, $m)) { return; } $type = $m[2]; $name = $m[3] ?? ''; if (class_exists($type, false) || interface_exists($type, false)) { $this->autowiringAliases[$type][$name] = $name; } } private function getCombinedAlias(string $type, string $name = null): ?string { if (str_contains($type, '&')) { $types = explode('&', $type); } elseif (str_contains($type, '|')) { $types = explode('|', $type); } else { return null; } $alias = null; $suffix = $name ? ' $'.$name : ''; foreach ($types as $type) { if (!$this->container->hasAlias($type.$suffix)) { return null; } if (null === $alias) { $alias = (string) $this->container->getAlias($type.$suffix); } elseif ((string) $this->container->getAlias($type.$suffix) !== $alias) { return null; } } return $alias; } } Compiler/AutowireRequiredMethodsPass.php 0000644 00000007433 15120140333 0014501 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Definition; use Symfony\Contracts\Service\Attribute\Required; /** * Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters. * * @author Nicolas Grekas <p@tchwork.com> */ class AutowireRequiredMethodsPass extends AbstractRecursivePass { /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { $value = parent::processValue($value, $isRoot); if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { return $value; } if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { return $value; } $alreadyCalledMethods = []; $withers = []; foreach ($value->getMethodCalls() as [$method]) { $alreadyCalledMethods[strtolower($method)] = true; } foreach ($reflectionClass->getMethods() as $reflectionMethod) { $r = $reflectionMethod; if ($r->isConstructor() || isset($alreadyCalledMethods[strtolower($r->name)])) { continue; } while (true) { if (\PHP_VERSION_ID >= 80000 && $r->getAttributes(Required::class)) { if ($this->isWither($r, $r->getDocComment() ?: '')) { $withers[] = [$r->name, [], true]; } else { $value->addMethodCall($r->name, []); } break; } if (false !== $doc = $r->getDocComment()) { if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { if ($this->isWither($reflectionMethod, $doc)) { $withers[] = [$reflectionMethod->name, [], true]; } else { $value->addMethodCall($reflectionMethod->name, []); } break; } if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) { break; } } try { $r = $r->getPrototype(); } catch (\ReflectionException $e) { break; // method has no prototype } } } if ($withers) { // Prepend withers to prevent creating circular loops $setters = $value->getMethodCalls(); $value->setMethodCalls($withers); foreach ($setters as $call) { $value->addMethodCall($call[0], $call[1], $call[2] ?? false); } } return $value; } private function isWither(\ReflectionMethod $reflectionMethod, string $doc): bool { $match = preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@return\s++(static|\$this)[\s\*]#i', $doc, $matches); if ($match && 'static' === $matches[1]) { return true; } if ($match && '$this' === $matches[1]) { return false; } $reflectionType = $reflectionMethod->hasReturnType() ? $reflectionMethod->getReturnType() : null; return $reflectionType instanceof \ReflectionNamedType && 'static' === $reflectionType->getName(); } } Compiler/AutowireRequiredPropertiesPass.php 0000644 00000004356 15120140333 0015233 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Contracts\Service\Attribute\Required; /** * Looks for definitions with autowiring enabled and registers their corresponding "@required" properties. * * @author Sebastien Morel (Plopix) <morel.seb@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ class AutowireRequiredPropertiesPass extends AbstractRecursivePass { /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if (\PHP_VERSION_ID < 70400) { return $value; } $value = parent::processValue($value, $isRoot); if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { return $value; } if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { return $value; } $properties = $value->getProperties(); foreach ($reflectionClass->getProperties() as $reflectionProperty) { if (!($type = $reflectionProperty->getType()) instanceof \ReflectionNamedType) { continue; } if ((\PHP_VERSION_ID < 80000 || !$reflectionProperty->getAttributes(Required::class)) && ((false === $doc = $reflectionProperty->getDocComment()) || false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) ) { continue; } if (\array_key_exists($name = $reflectionProperty->getName(), $properties)) { continue; } $type = $type->getName(); $value->setProperty($name, new TypedReference($type, $type, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $name)); } return $value; } } Compiler/CheckArgumentsValidityPass.php 0000644 00000010442 15120140333 0014260 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * Checks if arguments of methods are properly configured. * * @author Kévin Dunglas <dunglas@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ class CheckArgumentsValidityPass extends AbstractRecursivePass { private $throwExceptions; public function __construct(bool $throwExceptions = true) { $this->throwExceptions = $throwExceptions; } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if (!$value instanceof Definition) { return parent::processValue($value, $isRoot); } $i = 0; $hasNamedArgs = false; foreach ($value->getArguments() as $k => $v) { if (\PHP_VERSION_ID >= 80000 && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $k)) { $hasNamedArgs = true; continue; } if ($k !== $i++) { if (!\is_int($k)) { $msg = sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k); $value->addError($msg); if ($this->throwExceptions) { throw new RuntimeException($msg); } break; } $msg = sprintf('Invalid constructor argument %d for service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $this->currentId, $i); $value->addError($msg); if ($this->throwExceptions) { throw new RuntimeException($msg); } } if ($hasNamedArgs) { $msg = sprintf('Invalid constructor argument for service "%s": cannot use positional argument after named argument. Check your service definition.', $this->currentId); $value->addError($msg); if ($this->throwExceptions) { throw new RuntimeException($msg); } break; } } foreach ($value->getMethodCalls() as $methodCall) { $i = 0; $hasNamedArgs = false; foreach ($methodCall[1] as $k => $v) { if (\PHP_VERSION_ID >= 80000 && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $k)) { $hasNamedArgs = true; continue; } if ($k !== $i++) { if (!\is_int($k)) { $msg = sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k); $value->addError($msg); if ($this->throwExceptions) { throw new RuntimeException($msg); } break; } $msg = sprintf('Invalid argument %d for method call "%s" of service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $methodCall[0], $this->currentId, $i); $value->addError($msg); if ($this->throwExceptions) { throw new RuntimeException($msg); } } if ($hasNamedArgs) { $msg = sprintf('Invalid argument for method call "%s" of service "%s": cannot use positional argument after named argument. Check your service definition.', $methodCall[0], $this->currentId); $value->addError($msg); if ($this->throwExceptions) { throw new RuntimeException($msg); } break; } } } return null; } } Compiler/CheckCircularReferencesPass.php 0000644 00000004620 15120140333 0014354 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; /** * Checks your services for circular references. * * References from method calls are ignored since we might be able to resolve * these references depending on the order in which services are called. * * Circular reference from method calls will only be detected at run-time. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class CheckCircularReferencesPass implements CompilerPassInterface { private $currentPath; private $checkedNodes; /** * Checks the ContainerBuilder object for circular references. */ public function process(ContainerBuilder $container) { $graph = $container->getCompiler()->getServiceReferenceGraph(); $this->checkedNodes = []; foreach ($graph->getNodes() as $id => $node) { $this->currentPath = [$id]; $this->checkOutEdges($node->getOutEdges()); } } /** * Checks for circular references. * * @param ServiceReferenceGraphEdge[] $edges An array of Edges * * @throws ServiceCircularReferenceException when a circular reference is found */ private function checkOutEdges(array $edges) { foreach ($edges as $edge) { $node = $edge->getDestNode(); $id = $node->getId(); if (empty($this->checkedNodes[$id])) { // Don't check circular references for lazy edges if (!$node->getValue() || (!$edge->isLazy() && !$edge->isWeak())) { $searchKey = array_search($id, $this->currentPath); $this->currentPath[] = $id; if (false !== $searchKey) { throw new ServiceCircularReferenceException($id, \array_slice($this->currentPath, $searchKey)); } $this->checkOutEdges($node->getOutEdges()); } $this->checkedNodes[$id] = true; array_pop($this->currentPath); } } } } Compiler/CheckDefinitionValidityPass.php 0000644 00000011206 15120140333 0014402 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\EnvParameterException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Loader\FileLoader; /** * This pass validates each definition individually only taking the information * into account which is contained in the definition itself. * * Later passes can rely on the following, and specifically do not need to * perform these checks themselves: * * - non synthetic, non abstract services always have a class set * - synthetic services are always public * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class CheckDefinitionValidityPass implements CompilerPassInterface { /** * Processes the ContainerBuilder to validate the Definition. * * @throws RuntimeException When the Definition is invalid */ public function process(ContainerBuilder $container) { foreach ($container->getDefinitions() as $id => $definition) { // synthetic service is public if ($definition->isSynthetic() && !$definition->isPublic()) { throw new RuntimeException(sprintf('A synthetic service ("%s") must be public.', $id)); } // non-synthetic, non-abstract service has class if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass() && !$definition->hasTag('container.service_locator') && (!$definition->getFactory() || !preg_match(FileLoader::ANONYMOUS_ID_REGEXP, $id))) { if ($definition->getFactory()) { throw new RuntimeException(sprintf('Please add the class to service "%s" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.', $id)); } if (class_exists($id) || interface_exists($id, false)) { if (str_starts_with($id, '\\') && 1 < substr_count($id, '\\')) { throw new RuntimeException(sprintf('The definition for "%s" has no class attribute, and appears to reference a class or interface. Please specify the class attribute explicitly or remove the leading backslash by renaming the service to "%s" to get rid of this error.', $id, substr($id, 1))); } throw new RuntimeException(sprintf('The definition for "%s" has no class attribute, and appears to reference a class or interface in the global namespace. Leaving out the "class" attribute is only allowed for namespaced classes. Please specify the class attribute explicitly to get rid of this error.', $id)); } throw new RuntimeException(sprintf('The definition for "%s" has no class. If you intend to inject this service dynamically at runtime, please mark it as synthetic=true. If this is an abstract definition solely used by child definitions, please add abstract=true, otherwise specify a class to get rid of this error.', $id)); } // tag attribute values must be scalars foreach ($definition->getTags() as $name => $tags) { foreach ($tags as $attributes) { foreach ($attributes as $attribute => $value) { if (!\is_scalar($value) && null !== $value) { throw new RuntimeException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $id, $name, $attribute)); } } } } if ($definition->isPublic() && !$definition->isPrivate()) { $resolvedId = $container->resolveEnvPlaceholders($id, null, $usedEnvs); if (null !== $usedEnvs) { throw new EnvParameterException([$resolvedId], null, 'A service name ("%s") cannot contain dynamic values.'); } } } foreach ($container->getAliases() as $id => $alias) { if ($alias->isPublic() && !$alias->isPrivate()) { $resolvedId = $container->resolveEnvPlaceholders($id, null, $usedEnvs); if (null !== $usedEnvs) { throw new EnvParameterException([$resolvedId], null, 'An alias name ("%s") cannot contain dynamic values.'); } } } } } Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php 0000644 00000007117 15120140333 0017653 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Reference; /** * Checks that all references are pointing to a valid service. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass { private $serviceLocatorContextIds = []; /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { $this->serviceLocatorContextIds = []; foreach ($container->findTaggedServiceIds('container.service_locator_context') as $id => $tags) { $this->serviceLocatorContextIds[$id] = $tags[0]['id']; $container->getDefinition($id)->clearTag('container.service_locator_context'); } try { return parent::process($container); } finally { $this->serviceLocatorContextIds = []; } } protected function processValue($value, bool $isRoot = false) { if (!$value instanceof Reference) { return parent::processValue($value, $isRoot); } if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE < $value->getInvalidBehavior() || $this->container->has($id = (string) $value)) { return $value; } $currentId = $this->currentId; $graph = $this->container->getCompiler()->getServiceReferenceGraph(); if (isset($this->serviceLocatorContextIds[$currentId])) { $currentId = $this->serviceLocatorContextIds[$currentId]; $locator = $this->container->getDefinition($this->currentId)->getFactory()[0]; foreach ($locator->getArgument(0) as $k => $v) { if ($v->getValues()[0] === $value) { if ($k !== $id) { $currentId = $k.'" in the container provided to "'.$currentId; } throw new ServiceNotFoundException($id, $currentId, null, $this->getAlternatives($id)); } } } if ('.' === $currentId[0] && $graph->hasNode($currentId)) { foreach ($graph->getNode($currentId)->getInEdges() as $edge) { if (!$edge->getValue() instanceof Reference || ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE < $edge->getValue()->getInvalidBehavior()) { continue; } $sourceId = $edge->getSourceNode()->getId(); if ('.' !== $sourceId[0]) { $currentId = $sourceId; break; } } } throw new ServiceNotFoundException($id, $currentId, null, $this->getAlternatives($id)); } private function getAlternatives(string $id): array { $alternatives = []; foreach ($this->container->getServiceIds() as $knownId) { if ('' === $knownId || '.' === $knownId[0]) { continue; } $lev = levenshtein($id, $knownId); if ($lev <= \strlen($id) / 3 || false !== strpos($knownId, $id)) { $alternatives[] = $knownId; } } return $alternatives; } } Compiler/CheckReferenceValidityPass.php 0000644 00000002705 15120140333 0014214 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; /** * Checks the validity of references. * * The following checks are performed by this pass: * - target definitions are not abstract * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class CheckReferenceValidityPass extends AbstractRecursivePass { protected function processValue($value, bool $isRoot = false) { if ($isRoot && $value instanceof Definition && ($value->isSynthetic() || $value->isAbstract())) { return $value; } if ($value instanceof Reference && $this->container->hasDefinition((string) $value)) { $targetDefinition = $this->container->getDefinition((string) $value); if ($targetDefinition->isAbstract()) { throw new RuntimeException(sprintf('The definition "%s" has a reference to an abstract definition "%s". Abstract definitions cannot be the target of references.', $this->currentId, $value)); } } return parent::processValue($value, $isRoot); } } Compiler/CheckTypeDeclarationsPass.php 0000644 00000030150 15120140333 0014055 0 ustar 00 <?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\Compiler; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\ExpressionLanguage; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\ExpressionLanguage\Expression; /** * Checks whether injected parameters are compatible with type declarations. * * This pass should be run after all optimization passes. * * It can be added either: * * before removing passes to check all services even if they are not currently used, * * after removing passes to check only services are used in the app. * * @author Nicolas Grekas <p@tchwork.com> * @author Julien Maulny <jmaulny@darkmira.fr> */ final class CheckTypeDeclarationsPass extends AbstractRecursivePass { private const SCALAR_TYPES = [ 'int' => true, 'float' => true, 'bool' => true, 'string' => true, ]; private const BUILTIN_TYPES = [ 'array' => true, 'bool' => true, 'callable' => true, 'float' => true, 'int' => true, 'iterable' => true, 'object' => true, 'string' => true, ]; private $autoload; private $skippedIds; private $expressionLanguage; /** * @param bool $autoload Whether services who's class in not loaded should be checked or not. * Defaults to false to save loading code during compilation. * @param array $skippedIds An array indexed by the service ids to skip */ public function __construct(bool $autoload = false, array $skippedIds = []) { $this->autoload = $autoload; $this->skippedIds = $skippedIds; } /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { if (isset($this->skippedIds[$this->currentId])) { return $value; } if (!$value instanceof Definition || $value->hasErrors() || $value->isDeprecated()) { return parent::processValue($value, $isRoot); } if (!$this->autoload) { if (!$class = $value->getClass()) { return parent::processValue($value, $isRoot); } if (!class_exists($class, false) && !interface_exists($class, false)) { return parent::processValue($value, $isRoot); } } if (ServiceLocator::class === $value->getClass()) { return parent::processValue($value, $isRoot); } if ($constructor = $this->getConstructor($value, false)) { $this->checkTypeDeclarations($value, $constructor, $value->getArguments()); } foreach ($value->getMethodCalls() as $methodCall) { try { $reflectionMethod = $this->getReflectionMethod($value, $methodCall[0]); } catch (RuntimeException $e) { if ($value->getFactory()) { continue; } throw $e; } $this->checkTypeDeclarations($value, $reflectionMethod, $methodCall[1]); } return parent::processValue($value, $isRoot); } /** * @throws InvalidArgumentException When not enough parameters are defined for the method */ private function checkTypeDeclarations(Definition $checkedDefinition, \ReflectionFunctionAbstract $reflectionFunction, array $values): void { $numberOfRequiredParameters = $reflectionFunction->getNumberOfRequiredParameters(); if (\count($values) < $numberOfRequiredParameters) { throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": "%s::%s()" requires %d arguments, %d passed.', $this->currentId, $reflectionFunction->class, $reflectionFunction->name, $numberOfRequiredParameters, \count($values))); } $reflectionParameters = $reflectionFunction->getParameters(); $checksCount = min($reflectionFunction->getNumberOfParameters(), \count($values)); $envPlaceholderUniquePrefix = $this->container->getParameterBag() instanceof EnvPlaceholderParameterBag ? $this->container->getParameterBag()->getEnvPlaceholderUniquePrefix() : null; for ($i = 0; $i < $checksCount; ++$i) { if (!$reflectionParameters[$i]->hasType() || $reflectionParameters[$i]->isVariadic()) { continue; } $this->checkType($checkedDefinition, $values[$i], $reflectionParameters[$i], $envPlaceholderUniquePrefix); } if ($reflectionFunction->isVariadic() && ($lastParameter = end($reflectionParameters))->hasType()) { $variadicParameters = \array_slice($values, $lastParameter->getPosition()); foreach ($variadicParameters as $variadicParameter) { $this->checkType($checkedDefinition, $variadicParameter, $lastParameter, $envPlaceholderUniquePrefix); } } } /** * @throws InvalidParameterTypeException When a parameter is not compatible with the declared type */ private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix, \ReflectionType $reflectionType = null): void { $reflectionType = $reflectionType ?? $parameter->getType(); if ($reflectionType instanceof \ReflectionUnionType) { foreach ($reflectionType->getTypes() as $t) { try { $this->checkType($checkedDefinition, $value, $parameter, $envPlaceholderUniquePrefix, $t); return; } catch (InvalidParameterTypeException $e) { } } throw new InvalidParameterTypeException($this->currentId, $e->getCode(), $parameter); } if ($reflectionType instanceof \ReflectionIntersectionType) { foreach ($reflectionType->getTypes() as $t) { $this->checkType($checkedDefinition, $value, $parameter, $envPlaceholderUniquePrefix, $t); } return; } if (!$reflectionType instanceof \ReflectionNamedType) { return; } $type = $reflectionType->getName(); if ($value instanceof Reference) { if (!$this->container->has($value = (string) $value)) { return; } if ('service_container' === $value && is_a($type, Container::class, true)) { return; } $value = $this->container->findDefinition($value); } if ('self' === $type) { $type = $parameter->getDeclaringClass()->getName(); } if ('static' === $type) { $type = $checkedDefinition->getClass(); } $class = null; if ($value instanceof Definition) { if ($value->hasErrors() || $value->getFactory()) { return; } $class = $value->getClass(); if ($class && isset(self::BUILTIN_TYPES[strtolower($class)])) { $class = strtolower($class); } elseif (!$class || (!$this->autoload && !class_exists($class, false) && !interface_exists($class, false))) { return; } } elseif ($value instanceof Parameter) { $value = $this->container->getParameter($value); } elseif ($value instanceof Expression) { try { $value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this->container]); } catch (\Exception $e) { // If a service from the expression cannot be fetched from the container, we skip the validation. return; } } elseif (\is_string($value)) { if ('%' === ($value[0] ?? '') && preg_match('/^%([^%]+)%$/', $value, $match)) { $value = $this->container->getParameter(substr($value, 1, -1)); } if ($envPlaceholderUniquePrefix && \is_string($value) && str_contains($value, 'env_')) { // If the value is an env placeholder that is either mixed with a string or with another env placeholder, then its resolved value will always be a string, so we don't need to resolve it. // We don't need to change the value because it is already a string. if ('' === preg_replace('/'.$envPlaceholderUniquePrefix.'_\w+_[a-f0-9]{32}/U', '', $value, -1, $c) && 1 === $c) { try { $value = $this->container->resolveEnvPlaceholders($value, true); } catch (\Exception $e) { // If an env placeholder cannot be resolved, we skip the validation. return; } } } } if (null === $value && $parameter->allowsNull()) { return; } if (null === $class) { if ($value instanceof IteratorArgument) { $class = RewindableGenerator::class; } elseif ($value instanceof ServiceClosureArgument) { $class = \Closure::class; } elseif ($value instanceof ServiceLocatorArgument) { $class = ServiceLocator::class; } elseif (\is_object($value)) { $class = \get_class($value); } else { $class = \gettype($value); $class = ['integer' => 'int', 'double' => 'float', 'boolean' => 'bool'][$class] ?? $class; } } if (isset(self::SCALAR_TYPES[$type]) && isset(self::SCALAR_TYPES[$class])) { return; } if ('string' === $type && method_exists($class, '__toString')) { return; } if ('callable' === $type && (\Closure::class === $class || method_exists($class, '__invoke'))) { return; } if ('callable' === $type && \is_array($value) && isset($value[0]) && ($value[0] instanceof Reference || $value[0] instanceof Definition || \is_string($value[0]))) { return; } if ('iterable' === $type && (\is_array($value) || 'array' === $class || is_subclass_of($class, \Traversable::class))) { return; } if ($type === $class) { return; } if ('object' === $type && !isset(self::BUILTIN_TYPES[$class])) { return; } if ('mixed' === $type) { return; } if (is_a($class, $type, true)) { return; } if ('false' === $type) { if (false === $value) { return; } } elseif ($reflectionType->isBuiltin()) { $checkFunction = sprintf('is_%s', $type); if ($checkFunction($value)) { return; } } throw new InvalidParameterTypeException($this->currentId, \is_object($value) ? $class : get_debug_type($value), $parameter); } private function getExpressionLanguage(): ExpressionLanguage { if (null === $this->expressionLanguage) { $this->expressionLanguage = new ExpressionLanguage(null, $this->container->getExpressionLanguageProviders()); } return $this->expressionLanguage; } } Config/ContainerParametersResource.php 0000644 00000001711 15120140333 0014130 0 ustar 00 <?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\Config; use Symfony\Component\Config\Resource\ResourceInterface; /** * Tracks container parameters. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class ContainerParametersResource implements ResourceInterface { private $parameters; /** * @param array $parameters The container parameters to track */ public function __construct(array $parameters) { $this->parameters = $parameters; } public function __toString(): string { return 'container_parameters_'.md5(serialize($this->parameters)); } public function getParameters(): array { return $this->parameters; } } Config/ContainerParametersResourceChecker.php 0000644 00000002475 15120140333 0015425 0 ustar 00 <?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\Config; use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\Config\ResourceCheckerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ class ContainerParametersResourceChecker implements ResourceCheckerInterface { /** @var ContainerInterface */ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } /** * {@inheritdoc} */ public function supports(ResourceInterface $metadata) { return $metadata instanceof ContainerParametersResource; } /** * {@inheritdoc} */ public function isFresh(ResourceInterface $resource, int $timestamp) { foreach ($resource->getParameters() as $key => $value) { if (!$this->container->hasParameter($key) || $this->container->getParameter($key) !== $value) { return false; } } return true; } } Dumper/Dumper.php 0000644 00000001232 15120140333 0007733 0 ustar 00 <?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\Dumper; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Dumper is the abstract class for all built-in dumpers. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class Dumper implements DumperInterface { protected $container; public function __construct(ContainerBuilder $container) { $this->container = $container; } } Dumper/DumperInterface.php 0000644 00000001134 15120140333 0011555 0 ustar 00 <?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\Dumper; /** * DumperInterface is the interface implemented by service container dumper classes. * * @author Fabien Potencier <fabien@symfony.com> */ interface DumperInterface { /** * Dumps the service container. * * @return string|array */ public function dump(array $options = []); } Dumper/GraphvizDumper.php 0000644 00000022140 15120140333 0011447 0 ustar 00 <?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\Dumper; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; /** * GraphvizDumper dumps a service container as a graphviz file. * * You can convert the generated dot file with the dot utility (http://www.graphviz.org/): * * dot -Tpng container.dot > foo.png * * @author Fabien Potencier <fabien@symfony.com> */ class GraphvizDumper extends Dumper { private $nodes; private $edges; // All values should be strings private $options = [ 'graph' => ['ratio' => 'compress'], 'node' => ['fontsize' => '11', 'fontname' => 'Arial', 'shape' => 'record'], 'edge' => ['fontsize' => '9', 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => '0.5'], 'node.instance' => ['fillcolor' => '#9999ff', 'style' => 'filled'], 'node.definition' => ['fillcolor' => '#eeeeee'], 'node.missing' => ['fillcolor' => '#ff9999', 'style' => 'filled'], ]; /** * Dumps the service container as a graphviz graph. * * Available options: * * * graph: The default options for the whole graph * * node: The default options for nodes * * edge: The default options for edges * * node.instance: The default options for services that are defined directly by object instances * * node.definition: The default options for services that are defined via service definition instances * * node.missing: The default options for missing services * * @return string */ public function dump(array $options = []) { foreach (['graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing'] as $key) { if (isset($options[$key])) { $this->options[$key] = array_merge($this->options[$key], $options[$key]); } } $this->nodes = $this->findNodes(); $this->edges = []; foreach ($this->container->getDefinitions() as $id => $definition) { $this->edges[$id] = array_merge( $this->findEdges($id, $definition->getArguments(), true, ''), $this->findEdges($id, $definition->getProperties(), false, '') ); foreach ($definition->getMethodCalls() as $call) { $this->edges[$id] = array_merge( $this->edges[$id], $this->findEdges($id, $call[1], false, $call[0].'()') ); } } return $this->container->resolveEnvPlaceholders($this->startDot().$this->addNodes().$this->addEdges().$this->endDot(), '__ENV_%s__'); } private function addNodes(): string { $code = ''; foreach ($this->nodes as $id => $node) { $aliases = $this->getAliases($id); $code .= sprintf(" node_%s [label=\"%s\\n%s\\n\", shape=%s%s];\n", $this->dotize($id), $id.($aliases ? ' ('.implode(', ', $aliases).')' : ''), $node['class'], $this->options['node']['shape'], $this->addAttributes($node['attributes'])); } return $code; } private function addEdges(): string { $code = ''; foreach ($this->edges as $id => $edges) { foreach ($edges as $edge) { $code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"%s];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed', $edge['lazy'] ? ' color="#9999ff"' : ''); } } return $code; } /** * Finds all edges belonging to a specific service id. */ private function findEdges(string $id, array $arguments, bool $required, string $name, bool $lazy = false): array { $edges = []; foreach ($arguments as $argument) { if ($argument instanceof Parameter) { $argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null; } elseif (\is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match)) { $argument = $this->container->hasParameter($match[1]) ? $this->container->getParameter($match[1]) : null; } if ($argument instanceof Reference) { $lazyEdge = $lazy; if (!$this->container->has((string) $argument)) { $this->nodes[(string) $argument] = ['name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']]; } elseif ('service_container' !== (string) $argument) { $lazyEdge = $lazy || $this->container->getDefinition((string) $argument)->isLazy(); } $edges[] = [['name' => $name, 'required' => $required, 'to' => $argument, 'lazy' => $lazyEdge]]; } elseif ($argument instanceof ArgumentInterface) { $edges[] = $this->findEdges($id, $argument->getValues(), $required, $name, true); } elseif ($argument instanceof Definition) { $edges[] = $this->findEdges($id, $argument->getArguments(), $required, ''); $edges[] = $this->findEdges($id, $argument->getProperties(), false, ''); foreach ($argument->getMethodCalls() as $call) { $edges[] = $this->findEdges($id, $call[1], false, $call[0].'()'); } } elseif (\is_array($argument)) { $edges[] = $this->findEdges($id, $argument, $required, $name, $lazy); } } return array_merge([], ...$edges); } private function findNodes(): array { $nodes = []; $container = $this->cloneContainer(); foreach ($container->getDefinitions() as $id => $definition) { $class = $definition->getClass(); if ('\\' === substr($class, 0, 1)) { $class = substr($class, 1); } try { $class = $this->container->getParameterBag()->resolveValue($class); } catch (ParameterNotFoundException $e) { } $nodes[$id] = ['class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], ['style' => $definition->isShared() ? 'filled' : 'dotted'])]; $container->setDefinition($id, new Definition('stdClass')); } foreach ($container->getServiceIds() as $id) { if (\array_key_exists($id, $container->getAliases())) { continue; } if (!$container->hasDefinition($id)) { $nodes[$id] = ['class' => str_replace('\\', '\\\\', \get_class($container->get($id))), 'attributes' => $this->options['node.instance']]; } } return $nodes; } private function cloneContainer(): ContainerBuilder { $parameterBag = new ParameterBag($this->container->getParameterBag()->all()); $container = new ContainerBuilder($parameterBag); $container->setDefinitions($this->container->getDefinitions()); $container->setAliases($this->container->getAliases()); $container->setResources($this->container->getResources()); foreach ($this->container->getExtensions() as $extension) { $container->registerExtension($extension); } return $container; } private function startDot(): string { return sprintf("digraph sc {\n %s\n node [%s];\n edge [%s];\n\n", $this->addOptions($this->options['graph']), $this->addOptions($this->options['node']), $this->addOptions($this->options['edge']) ); } private function endDot(): string { return "}\n"; } private function addAttributes(array $attributes): string { $code = []; foreach ($attributes as $k => $v) { $code[] = sprintf('%s="%s"', $k, $v); } return $code ? ', '.implode(', ', $code) : ''; } private function addOptions(array $options): string { $code = []; foreach ($options as $k => $v) { $code[] = sprintf('%s="%s"', $k, $v); } return implode(' ', $code); } private function dotize(string $id): string { return preg_replace('/\W/i', '_', $id); } private function getAliases(string $id): array { $aliases = []; foreach ($this->container->getAliases() as $alias => $origin) { if ($id == $origin) { $aliases[] = $alias; } } return $aliases; } } Dumper/PhpDumper.php 0000644 00000262424 15120140333 0010417 0 ustar 00 <?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\Dumper; use Composer\Autoload\ClassLoader; use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocator; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass; use Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphNode; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\EnvParameterException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\ExpressionLanguage; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; use Symfony\Component\DependencyInjection\Loader\FileLoader; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\ErrorHandler\DebugClassLoader; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\HttpKernel\Kernel; /** * PhpDumper dumps a service container as a PHP class. * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class PhpDumper extends Dumper { /** * Characters that might appear in the generated variable name as first character. */ public const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; /** * Characters that might appear in the generated variable name as any but the first character. */ public const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; /** * @var \SplObjectStorage<Definition, Variable>|null */ private $definitionVariables; private $referenceVariables; private $variableCount; private $inlinedDefinitions; private $serviceCalls; private $reservedVariables = ['instance', 'class', 'this', 'container']; private $expressionLanguage; private $targetDirRegex; private $targetDirMaxMatches; private $docStar; private $serviceIdToMethodNameMap; private $usedMethodNames; private $namespace; private $asFiles; private $hotPathTag; private $preloadTags; private $inlineFactories; private $inlineRequires; private $inlinedRequires = []; private $circularReferences = []; private $singleUsePrivateIds = []; private $preload = []; private $addThrow = false; private $addGetService = false; private $locatedIds = []; private $serviceLocatorTag; private $exportedVariables = []; private $dynamicParameters = []; private $baseClass; /** * @var DumperInterface */ private $proxyDumper; private $hasProxyDumper = false; /** * {@inheritdoc} */ public function __construct(ContainerBuilder $container) { if (!$container->isCompiled()) { throw new LogicException('Cannot dump an uncompiled container.'); } parent::__construct($container); } /** * Sets the dumper to be used when dumping proxies in the generated container. */ public function setProxyDumper(DumperInterface $proxyDumper) { $this->proxyDumper = $proxyDumper; $this->hasProxyDumper = !$proxyDumper instanceof NullDumper; } /** * Dumps the service container as a PHP class. * * Available options: * * * class: The class name * * base_class: The base class name * * namespace: The class namespace * * as_files: To split the container in several files * * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set * * @throws EnvParameterException When an env var exists but has not been dumped */ public function dump(array $options = []) { $this->locatedIds = []; $this->targetDirRegex = null; $this->inlinedRequires = []; $this->exportedVariables = []; $this->dynamicParameters = []; $options = array_merge([ 'class' => 'ProjectServiceContainer', 'base_class' => 'Container', 'namespace' => '', 'as_files' => false, 'debug' => true, 'hot_path_tag' => 'container.hot_path', 'preload_tags' => ['container.preload', 'container.no_preload'], 'inline_factories_parameter' => 'container.dumper.inline_factories', 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', 'preload_classes' => [], 'service_locator_tag' => 'container.service_locator', 'build_time' => time(), ], $options); $this->addThrow = $this->addGetService = false; $this->namespace = $options['namespace']; $this->asFiles = $options['as_files']; $this->hotPathTag = $options['hot_path_tag']; $this->preloadTags = $options['preload_tags']; $this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && $this->container->hasParameter($options['inline_factories_parameter']) && $this->container->getParameter($options['inline_factories_parameter']); $this->inlineRequires = $options['inline_class_loader_parameter'] && ($this->container->hasParameter($options['inline_class_loader_parameter']) ? $this->container->getParameter($options['inline_class_loader_parameter']) : (\PHP_VERSION_ID < 70400 || $options['debug'])); $this->serviceLocatorTag = $options['service_locator_tag']; if (!str_starts_with($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) { $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass); $this->baseClass = $baseClass; } elseif ('Container' === $baseClass) { $this->baseClass = Container::class; } else { $this->baseClass = $baseClass; } $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass); if (!$this->hasProxyDumper) { (new AnalyzeServiceReferencesPass(true, false))->process($this->container); try { (new CheckCircularReferencesPass())->process($this->container); } catch (ServiceCircularReferenceException $e) { $path = $e->getPath(); end($path); $path[key($path)] .= '". Try running "composer require symfony/proxy-manager-bridge'; throw new ServiceCircularReferenceException($e->getServiceId(), $path); } } $this->analyzeReferences(); $this->docStar = $options['debug'] ? '*' : ''; if (!empty($options['file']) && is_dir($dir = \dirname($options['file']))) { // Build a regexp where the first root dirs are mandatory, // but every other sub-dir is optional up to the full path in $dir // Mandate at least 1 root dir and not more than 5 optional dirs. $dir = explode(\DIRECTORY_SEPARATOR, realpath($dir)); $i = \count($dir); if (2 + (int) ('\\' === \DIRECTORY_SEPARATOR) <= $i) { $regex = ''; $lastOptionalDir = $i > 8 ? $i - 5 : (2 + (int) ('\\' === \DIRECTORY_SEPARATOR)); $this->targetDirMaxMatches = $i - $lastOptionalDir; while (--$i >= $lastOptionalDir) { $regex = sprintf('(%s%s)?', preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex); } do { $regex = preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; } while (0 < --$i); $this->targetDirRegex = '#(^|file://|[:;, \|\r\n])'.preg_quote($dir[0], '#').$regex.'#'; } } $proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null; if ($options['preload_classes']) { $this->preload = array_combine($options['preload_classes'], $options['preload_classes']); } $code = $this->addDefaultParametersMethod(); $code = $this->startClass($options['class'], $baseClass, $this->inlineFactories && $proxyClasses). $this->addServices($services). $this->addDeprecatedAliases(). $code ; $proxyClasses = $proxyClasses ?? $this->generateProxyClasses(); if ($this->addGetService) { $code = preg_replace( "/(\r?\n\r?\n public function __construct.+?\\{\r?\n)/s", "\n protected \$getService;$1 \$this->getService = \\Closure::fromCallable([\$this, 'getService']);\n", $code, 1 ); } if ($this->asFiles) { $fileTemplate = <<<EOF <?php use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /*{$this->docStar} * @internal This class has been auto-generated by the Symfony Dependency Injection Component. */ class %s extends {$options['class']} {%s} EOF; $files = []; $preloadedFiles = []; $ids = $this->container->getRemovedIds(); foreach ($this->container->getDefinitions() as $id => $definition) { if (!$definition->isPublic()) { $ids[$id] = true; } } if ($ids = array_keys($ids)) { sort($ids); $c = "<?php\n\nreturn [\n"; foreach ($ids as $id) { $c .= ' '.$this->doExport($id)." => true,\n"; } $files['removed-ids.php'] = $c."];\n"; } if (!$this->inlineFactories) { foreach ($this->generateServiceFiles($services) as $file => [$c, $preload]) { $files[$file] = sprintf($fileTemplate, substr($file, 0, -4), $c); if ($preload) { $preloadedFiles[$file] = $file; } } foreach ($proxyClasses as $file => $c) { $files[$file] = "<?php\n".$c; $preloadedFiles[$file] = $file; } } $code .= $this->endClass(); if ($this->inlineFactories && $proxyClasses) { $files['proxy-classes.php'] = "<?php\n\n"; foreach ($proxyClasses as $c) { $files['proxy-classes.php'] .= $c; } } $files[$options['class'].'.php'] = $code; $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx')); $code = []; foreach ($files as $file => $c) { $code["Container{$hash}/{$file}"] = substr_replace($c, "<?php\n\nnamespace Container{$hash};\n", 0, 6); if (isset($preloadedFiles[$file])) { $preloadedFiles[$file] = "Container{$hash}/{$file}"; } } $namespaceLine = $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; $time = $options['build_time']; $id = hash('crc32', $hash.$time); $this->asFiles = false; if ($this->preload && null !== $autoloadFile = $this->getAutoloadFile()) { $autoloadFile = trim($this->export($autoloadFile), '()\\'); $preloadedFiles = array_reverse($preloadedFiles); if ('' !== $preloadedFiles = implode("';\nrequire __DIR__.'/", $preloadedFiles)) { $preloadedFiles = "require __DIR__.'/$preloadedFiles';\n"; } $code[$options['class'].'.preload.php'] = <<<EOF <?php // This file has been auto-generated by the Symfony Dependency Injection Component // You can reference it in the "opcache.preload" php.ini setting on PHP >= 7.4 when preloading is desired use Symfony\Component\DependencyInjection\Dumper\Preloader; if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { return; } require $autoloadFile; (require __DIR__.'/{$options['class']}.php')->set(\\Container{$hash}\\{$options['class']}::class, null); $preloadedFiles \$classes = []; EOF; foreach ($this->preload as $class) { if (!$class || str_contains($class, '$') || \in_array($class, ['int', 'float', 'string', 'bool', 'resource', 'object', 'array', 'null', 'callable', 'iterable', 'mixed', 'void'], true)) { continue; } if (!(class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) || ((new \ReflectionClass($class))->isUserDefined() && !\in_array($class, ['Attribute', 'JsonException', 'ReturnTypeWillChange', 'Stringable', 'UnhandledMatchError', 'ValueError'], true))) { $code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class); } } $code[$options['class'].'.preload.php'] .= <<<'EOF' $preloaded = Preloader::preload($classes); EOF; } $code[$options['class'].'.php'] = <<<EOF <?php {$namespaceLine} // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. if (\\class_exists(\\Container{$hash}\\{$options['class']}::class, false)) { // no-op } elseif (!include __DIR__.'/Container{$hash}/{$options['class']}.php') { touch(__DIR__.'/Container{$hash}.legacy'); return; } if (!\\class_exists({$options['class']}::class, false)) { \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false); } return new \\Container{$hash}\\{$options['class']}([ 'container.build_hash' => '$hash', 'container.build_id' => '$id', 'container.build_time' => $time, ], __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}'); EOF; } else { $code .= $this->endClass(); foreach ($proxyClasses as $c) { $code .= $c; } } $this->targetDirRegex = null; $this->inlinedRequires = []; $this->circularReferences = []; $this->locatedIds = []; $this->exportedVariables = []; $this->dynamicParameters = []; $this->preload = []; $unusedEnvs = []; foreach ($this->container->getEnvCounters() as $env => $use) { if (!$use) { $unusedEnvs[] = $env; } } if ($unusedEnvs) { throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.'); } return $code; } /** * Retrieves the currently set proxy dumper or instantiates one. */ private function getProxyDumper(): DumperInterface { if (!$this->proxyDumper) { $this->proxyDumper = new NullDumper(); } return $this->proxyDumper; } private function analyzeReferences() { (new AnalyzeServiceReferencesPass(false, $this->hasProxyDumper))->process($this->container); $checkedNodes = []; $this->circularReferences = []; $this->singleUsePrivateIds = []; foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { if (!$node->getValue() instanceof Definition) { continue; } if ($this->isSingleUsePrivateNode($node)) { $this->singleUsePrivateIds[$id] = $id; } $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes); } $this->container->getCompiler()->getServiceReferenceGraph()->clear(); $this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences); } private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$loops = [], array $path = [], bool $byConstructor = true): void { $path[$sourceId] = $byConstructor; $checkedNodes[$sourceId] = true; foreach ($edges as $edge) { $node = $edge->getDestNode(); $id = $node->getId(); if ($sourceId === $id || !$node->getValue() instanceof Definition || $edge->isWeak()) { continue; } if (isset($path[$id])) { $loop = null; $loopByConstructor = $edge->isReferencedByConstructor() && !$edge->isLazy(); $pathInLoop = [$id, []]; foreach ($path as $k => $pathByConstructor) { if (null !== $loop) { $loop[] = $k; $pathInLoop[1][$k] = $pathByConstructor; $loops[$k][] = &$pathInLoop; $loopByConstructor = $loopByConstructor && $pathByConstructor; } elseif ($k === $id) { $loop = []; } } $this->addCircularReferences($id, $loop, $loopByConstructor); } elseif (!isset($checkedNodes[$id])) { $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $loops, $path, $edge->isReferencedByConstructor() && !$edge->isLazy()); } elseif (isset($loops[$id])) { // we already had detected loops for this edge // let's check if we have a common ancestor in one of the detected loops foreach ($loops[$id] as [$first, $loopPath]) { if (!isset($path[$first])) { continue; } // We have a common ancestor, let's fill the current path $fillPath = null; foreach ($loopPath as $k => $pathByConstructor) { if (null !== $fillPath) { $fillPath[$k] = $pathByConstructor; } elseif ($k === $id) { $fillPath = $path; $fillPath[$k] = $pathByConstructor; } } // we can now build the loop $loop = null; $loopByConstructor = $edge->isReferencedByConstructor() && !$edge->isLazy(); foreach ($fillPath as $k => $pathByConstructor) { if (null !== $loop) { $loop[] = $k; $loopByConstructor = $loopByConstructor && $pathByConstructor; } elseif ($k === $first) { $loop = []; } } $this->addCircularReferences($first, $loop, $loopByConstructor); break; } } } unset($path[$sourceId]); } private function addCircularReferences(string $sourceId, array $currentPath, bool $byConstructor) { $currentId = $sourceId; $currentPath = array_reverse($currentPath); $currentPath[] = $currentId; foreach ($currentPath as $parentId) { if (empty($this->circularReferences[$parentId][$currentId])) { $this->circularReferences[$parentId][$currentId] = $byConstructor; } $currentId = $parentId; } } private function collectLineage(string $class, array &$lineage) { if (isset($lineage[$class])) { return; } if (!$r = $this->container->getReflectionClass($class, false)) { return; } if (is_a($class, $this->baseClass, true)) { return; } $file = $r->getFileName(); if (str_ends_with($file, ') : eval()\'d code')) { $file = substr($file, 0, strrpos($file, '(', -17)); } if (!$file || $this->doExport($file) === $exportedFile = $this->export($file)) { return; } $lineage[$class] = substr($exportedFile, 1, -1); if ($parent = $r->getParentClass()) { $this->collectLineage($parent->name, $lineage); } foreach ($r->getInterfaces() as $parent) { $this->collectLineage($parent->name, $lineage); } foreach ($r->getTraits() as $parent) { $this->collectLineage($parent->name, $lineage); } unset($lineage[$class]); $lineage[$class] = substr($exportedFile, 1, -1); } private function generateProxyClasses(): array { $proxyClasses = []; $alreadyGenerated = []; $definitions = $this->container->getDefinitions(); $strip = '' === $this->docStar && method_exists(Kernel::class, 'stripComments'); $proxyDumper = $this->getProxyDumper(); ksort($definitions); foreach ($definitions as $definition) { if (!$proxyDumper->isProxyCandidate($definition)) { continue; } if (isset($alreadyGenerated[$class = $definition->getClass()])) { continue; } $alreadyGenerated[$class] = true; // register class' reflector for resource tracking $this->container->getReflectionClass($class); if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) { continue; } if ($this->inlineRequires) { $lineage = []; $this->collectLineage($class, $lineage); $code = ''; foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { if ($this->inlineFactories) { $this->inlinedRequires[$file] = true; } $code .= sprintf("include_once %s;\n", $file); } $proxyCode = $code.$proxyCode; } if ($strip) { $proxyCode = "<?php\n".$proxyCode; $proxyCode = substr(Kernel::stripComments($proxyCode), 5); } $proxyClass = explode(' ', $this->inlineRequires ? substr($proxyCode, \strlen($code)) : $proxyCode, 3)[1]; if ($this->asFiles || $this->namespace) { $proxyCode .= "\nif (!\\class_exists('$proxyClass', false)) {\n \\class_alias(__NAMESPACE__.'\\\\$proxyClass', '$proxyClass', false);\n}\n"; } $proxyClasses[$proxyClass.'.php'] = $proxyCode; } return $proxyClasses; } private function addServiceInclude(string $cId, Definition $definition): string { $code = ''; if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) { $lineage = []; foreach ($this->inlinedDefinitions as $def) { if (!$def->isDeprecated()) { foreach ($this->getClasses($def, $cId) as $class) { $this->collectLineage($class, $lineage); } } } foreach ($this->serviceCalls as $id => [$callCount, $behavior]) { if ('service_container' !== $id && $id !== $cId && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior && $this->container->has($id) && $this->isTrivialInstance($def = $this->container->findDefinition($id)) ) { foreach ($this->getClasses($def, $cId) as $class) { $this->collectLineage($class, $lineage); } } } foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { $code .= sprintf(" include_once %s;\n", $file); } } foreach ($this->inlinedDefinitions as $def) { if ($file = $def->getFile()) { $file = $this->dumpValue($file); $file = '(' === $file[0] ? substr($file, 1, -1) : $file; $code .= sprintf(" include_once %s;\n", $file); } } if ('' !== $code) { $code .= "\n"; } return $code; } /** * @throws InvalidArgumentException * @throws RuntimeException */ private function addServiceInstance(string $id, Definition $definition, bool $isSimpleInstance): string { $class = $this->dumpValue($definition->getClass()); if (str_starts_with($class, "'") && !str_contains($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); } $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); $instantiation = ''; $lastWitherIndex = null; foreach ($definition->getMethodCalls() as $k => $call) { if ($call[2] ?? false) { $lastWitherIndex = $k; } } if (!$isProxyCandidate && $definition->isShared() && !isset($this->singleUsePrivateIds[$id]) && null === $lastWitherIndex) { $instantiation = sprintf('$this->%s[%s] = %s', $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $this->doExport($id), $isSimpleInstance ? '' : '$instance'); } elseif (!$isSimpleInstance) { $instantiation = '$instance'; } $return = ''; if ($isSimpleInstance) { $return = 'return '; } else { $instantiation .= ' = '; } return $this->addNewInstance($definition, ' '.$return.$instantiation, $id); } private function isTrivialInstance(Definition $definition): bool { if ($definition->hasErrors()) { return true; } if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) { return false; } if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < \count($definition->getArguments())) { return false; } foreach ($definition->getArguments() as $arg) { if (!$arg || $arg instanceof Parameter) { continue; } if (\is_array($arg) && 3 >= \count($arg)) { foreach ($arg as $k => $v) { if ($this->dumpValue($k) !== $this->dumpValue($k, false)) { return false; } if (!$v || $v instanceof Parameter) { continue; } if ($v instanceof Reference && $this->container->has($id = (string) $v) && $this->container->findDefinition($id)->isSynthetic()) { continue; } if (!\is_scalar($v) || $this->dumpValue($v) !== $this->dumpValue($v, false)) { return false; } } } elseif ($arg instanceof Reference && $this->container->has($id = (string) $arg) && $this->container->findDefinition($id)->isSynthetic()) { continue; } elseif (!\is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) { return false; } } return true; } private function addServiceMethodCalls(Definition $definition, string $variableName, ?string $sharedNonLazyId): string { $lastWitherIndex = null; foreach ($definition->getMethodCalls() as $k => $call) { if ($call[2] ?? false) { $lastWitherIndex = $k; } } $calls = ''; foreach ($definition->getMethodCalls() as $k => $call) { $arguments = []; foreach ($call[1] as $i => $value) { $arguments[] = (\is_string($i) ? $i.': ' : '').$this->dumpValue($value); } $witherAssignation = ''; if ($call[2] ?? false) { if (null !== $sharedNonLazyId && $lastWitherIndex === $k && 'instance' === $variableName) { $witherAssignation = sprintf('$this->%s[\'%s\'] = ', $definition->isPublic() ? 'services' : 'privates', $sharedNonLazyId); } $witherAssignation .= sprintf('$%s = ', $variableName); } $calls .= $this->wrapServiceConditionals($call[1], sprintf(" %s\$%s->%s(%s);\n", $witherAssignation, $variableName, $call[0], implode(', ', $arguments))); } return $calls; } private function addServiceProperties(Definition $definition, string $variableName = 'instance'): string { $code = ''; foreach ($definition->getProperties() as $name => $value) { $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value)); } return $code; } private function addServiceConfigurator(Definition $definition, string $variableName = 'instance'): string { if (!$callable = $definition->getConfigurator()) { return ''; } if (\is_array($callable)) { if ($callable[0] instanceof Reference || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0])) ) { return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } $class = $this->dumpValue($callable[0]); // If the class is a string we can optimize away if (str_starts_with($class, "'") && !str_contains($class, '$')) { return sprintf(" %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName); } if (str_starts_with($class, 'new ')) { return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } return sprintf(" [%s, '%s'](\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } return sprintf(" %s(\$%s);\n", $callable, $variableName); } private function addService(string $id, Definition $definition): array { $this->definitionVariables = new \SplObjectStorage(); $this->referenceVariables = []; $this->variableCount = 0; $this->referenceVariables[$id] = new Variable('instance'); $return = []; if ($class = $definition->getClass()) { $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class); $return[] = sprintf(str_starts_with($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\')); } elseif ($definition->getFactory()) { $factory = $definition->getFactory(); if (\is_string($factory)) { $return[] = sprintf('@return object An instance returned by %s()', $factory); } elseif (\is_array($factory) && (\is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) { $class = $factory[0] instanceof Definition ? $factory[0]->getClass() : (string) $factory[0]; $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class); $return[] = sprintf('@return object An instance returned by %s::%s()', $class, $factory[1]); } } if ($definition->isDeprecated()) { if ($return && str_starts_with($return[\count($return) - 1], '@return')) { $return[] = ''; } $deprecation = $definition->getDeprecation($id); $return[] = sprintf('@deprecated %s', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); } $return = str_replace("\n * \n", "\n *\n", implode("\n * ", $return)); $return = $this->container->resolveEnvPlaceholders($return); $shared = $definition->isShared() ? ' shared' : ''; $public = $definition->isPublic() ? 'public' : 'private'; $autowired = $definition->isAutowired() ? ' autowired' : ''; $asFile = $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition); $methodName = $this->generateMethodName($id); if ($asFile || $definition->isLazy()) { $lazyInitialization = '$lazyLoad = true'; } else { $lazyInitialization = ''; } $code = <<<EOF /*{$this->docStar} * Gets the $public '$id'$shared$autowired service. * * $return EOF; $code = str_replace('*/', ' ', $code).<<<EOF */ protected function {$methodName}($lazyInitialization) { EOF; if ($asFile) { $file = $methodName.'.php'; $code = str_replace("protected function {$methodName}(", 'public static function do($container, ', $code); } else { $file = null; } if ($definition->hasErrors() && $e = $definition->getErrors()) { $this->addThrow = true; $code .= sprintf(" \$this->throw(%s);\n", $this->export(reset($e))); } else { $this->serviceCalls = []; $this->inlinedDefinitions = $this->getDefinitionsFromArguments([$definition], null, $this->serviceCalls); if ($definition->isDeprecated()) { $deprecation = $definition->getDeprecation($id); $code .= sprintf(" trigger_deprecation(%s, %s, %s);\n\n", $this->export($deprecation['package']), $this->export($deprecation['version']), $this->export($deprecation['message'])); } elseif ($definition->hasTag($this->hotPathTag) || !$definition->hasTag($this->preloadTags[1])) { foreach ($this->inlinedDefinitions as $def) { foreach ($this->getClasses($def, $id) as $class) { $this->preload[$class] = $class; } } } if (!$definition->isShared()) { $factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id)); } if ($isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition)) { if (!$definition->isShared()) { $code .= sprintf(' %s = %1$s ?? ', $factory); if ($asFile) { $code .= "function () {\n"; $code .= " return self::do(\$container);\n"; $code .= " };\n\n"; } else { $code .= sprintf("\\Closure::fromCallable([\$this, '%s']);\n\n", $methodName); } } $factoryCode = $asFile ? 'self::do($container, false)' : sprintf('$this->%s(false)', $methodName); $factoryCode = $this->getProxyDumper()->getProxyFactoryCode($definition, $id, $factoryCode); $code .= $asFile ? preg_replace('/function \(([^)]*+)\)( {|:)/', 'function (\1) use ($container)\2', $factoryCode) : $factoryCode; } $c = $this->addServiceInclude($id, $definition); if ('' !== $c && $isProxyCandidate && !$definition->isShared()) { $c = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $c))); $code .= " static \$include = true;\n\n"; $code .= " if (\$include) {\n"; $code .= $c; $code .= " \$include = false;\n"; $code .= " }\n\n"; } else { $code .= $c; } $c = $this->addInlineService($id, $definition); if (!$isProxyCandidate && !$definition->isShared()) { $c = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $c))); $lazyloadInitialization = $definition->isLazy() ? '$lazyLoad = true' : ''; $c = sprintf(" %s = function (%s) {\n%s };\n\n return %1\$s();\n", $factory, $lazyloadInitialization, $c); } $code .= $c; } if ($asFile) { $code = str_replace('$this', '$container', $code); $code = preg_replace('/function \(([^)]*+)\)( {|:)/', 'function (\1) use ($container)\2', $code); } $code .= " }\n"; $this->definitionVariables = $this->inlinedDefinitions = null; $this->referenceVariables = $this->serviceCalls = null; return [$file, $code]; } private function addInlineVariables(string $id, Definition $definition, array $arguments, bool $forConstructor): string { $code = ''; foreach ($arguments as $argument) { if (\is_array($argument)) { $code .= $this->addInlineVariables($id, $definition, $argument, $forConstructor); } elseif ($argument instanceof Reference) { $code .= $this->addInlineReference($id, $definition, $argument, $forConstructor); } elseif ($argument instanceof Definition) { $code .= $this->addInlineService($id, $definition, $argument, $forConstructor); } } return $code; } private function addInlineReference(string $id, Definition $definition, string $targetId, bool $forConstructor): string { while ($this->container->hasAlias($targetId)) { $targetId = (string) $this->container->getAlias($targetId); } [$callCount, $behavior] = $this->serviceCalls[$targetId]; if ($id === $targetId) { return $this->addInlineService($id, $definition, $definition); } if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) { return ''; } if ($this->container->hasDefinition($targetId) && ($def = $this->container->getDefinition($targetId)) && !$def->isShared()) { return ''; } $hasSelfRef = isset($this->circularReferences[$id][$targetId]) && !isset($this->definitionVariables[$definition]) && !($this->hasProxyDumper && $definition->isLazy()); if ($hasSelfRef && !$forConstructor && !$forConstructor = !$this->circularReferences[$id][$targetId]) { $code = $this->addInlineService($id, $definition, $definition); } else { $code = ''; } if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) { return $code; } $name = $this->getNextVariableName(); $this->referenceVariables[$targetId] = new Variable($name); $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $behavior ? new Reference($targetId, $behavior) : null; $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference)); if (!$hasSelfRef || !$forConstructor) { return $code; } $code .= sprintf(<<<'EOTXT' if (isset($this->%s[%s])) { return $this->%1$s[%2$s]; } EOTXT , $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $this->doExport($id) ); return $code; } private function addInlineService(string $id, Definition $definition, Definition $inlineDef = null, bool $forConstructor = true): string { $code = ''; if ($isSimpleInstance = $isRootInstance = null === $inlineDef) { foreach ($this->serviceCalls as $targetId => [$callCount, $behavior, $byConstructor]) { if ($byConstructor && isset($this->circularReferences[$id][$targetId]) && !$this->circularReferences[$id][$targetId] && !($this->hasProxyDumper && $definition->isLazy())) { $code .= $this->addInlineReference($id, $definition, $targetId, $forConstructor); } } } if (isset($this->definitionVariables[$inlineDef = $inlineDef ?: $definition])) { return $code; } $arguments = [$inlineDef->getArguments(), $inlineDef->getFactory()]; $code .= $this->addInlineVariables($id, $definition, $arguments, $forConstructor); if ($arguments = array_filter([$inlineDef->getProperties(), $inlineDef->getMethodCalls(), $inlineDef->getConfigurator()])) { $isSimpleInstance = false; } elseif ($definition !== $inlineDef && 2 > $this->inlinedDefinitions[$inlineDef]) { return $code; } if (isset($this->definitionVariables[$inlineDef])) { $isSimpleInstance = false; } else { $name = $definition === $inlineDef ? 'instance' : $this->getNextVariableName(); $this->definitionVariables[$inlineDef] = new Variable($name); $code .= '' !== $code ? "\n" : ''; if ('instance' === $name) { $code .= $this->addServiceInstance($id, $definition, $isSimpleInstance); } else { $code .= $this->addNewInstance($inlineDef, ' $'.$name.' = ', $id); } if ('' !== $inline = $this->addInlineVariables($id, $definition, $arguments, false)) { $code .= "\n".$inline."\n"; } elseif ($arguments && 'instance' === $name) { $code .= "\n"; } $code .= $this->addServiceProperties($inlineDef, $name); $code .= $this->addServiceMethodCalls($inlineDef, $name, !$this->getProxyDumper()->isProxyCandidate($inlineDef) && $inlineDef->isShared() && !isset($this->singleUsePrivateIds[$id]) ? $id : null); $code .= $this->addServiceConfigurator($inlineDef, $name); } if ($isRootInstance && !$isSimpleInstance) { $code .= "\n return \$instance;\n"; } return $code; } private function addServices(array &$services = null): string { $publicServices = $privateServices = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { if (!$definition->isSynthetic()) { $services[$id] = $this->addService($id, $definition); } elseif ($definition->hasTag($this->hotPathTag) || !$definition->hasTag($this->preloadTags[1])) { $services[$id] = null; foreach ($this->getClasses($definition, $id) as $class) { $this->preload[$class] = $class; } } } foreach ($definitions as $id => $definition) { if (!([$file, $code] = $services[$id]) || null !== $file) { continue; } if ($definition->isPublic()) { $publicServices .= $code; } elseif (!$this->isTrivialInstance($definition) || isset($this->locatedIds[$id])) { $privateServices .= $code; } } return $publicServices.$privateServices; } private function generateServiceFiles(array $services): iterable { $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { if (([$file, $code] = $services[$id]) && null !== $file && ($definition->isPublic() || !$this->isTrivialInstance($definition) || isset($this->locatedIds[$id]))) { yield $file => [$code, $definition->hasTag($this->hotPathTag) || !$definition->hasTag($this->preloadTags[1]) && !$definition->isDeprecated() && !$definition->hasErrors()]; } } } private function addNewInstance(Definition $definition, string $return = '', string $id = null): string { $tail = $return ? ";\n" : ''; if (BaseServiceLocator::class === $definition->getClass() && $definition->hasTag($this->serviceLocatorTag)) { $arguments = []; foreach ($definition->getArgument(0) as $k => $argument) { $arguments[$k] = $argument->getValues()[0]; } return $return.$this->dumpValue(new ServiceLocatorArgument($arguments)).$tail; } $arguments = []; foreach ($definition->getArguments() as $i => $value) { $arguments[] = (\is_string($i) ? $i.': ' : '').$this->dumpValue($value); } if (null !== $definition->getFactory()) { $callable = $definition->getFactory(); if (\is_array($callable)) { if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) { throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s).', $callable[1] ?: 'n/a')); } if ($callable[0] instanceof Reference || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { return $return.sprintf('%s->%s(%s)', $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } $class = $this->dumpValue($callable[0]); // If the class is a string we can optimize away if (str_starts_with($class, "'") && !str_contains($class, '$')) { if ("''" === $class) { throw new RuntimeException(sprintf('Cannot dump definition: "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id ? 'The "'.$id.'"' : 'inline')); } return $return.sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } if (str_starts_with($class, 'new ')) { return $return.sprintf('(%s)->%s(%s)', $class, $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } return $return.sprintf("[%s, '%s'](%s)", $class, $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } return $return.sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '').$tail; } if (null === $class = $definition->getClass()) { throw new RuntimeException('Cannot dump definitions which have no class nor factory.'); } return $return.sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)).$tail; } private function startClass(string $class, string $baseClass, bool $hasProxyClasses): string { $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; $code = <<<EOF <?php $namespaceLine use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; /*{$this->docStar} * @internal This class has been auto-generated by the Symfony Dependency Injection Component. */ class $class extends $baseClass { protected \$parameters = []; public function __construct() { EOF; if ($this->asFiles) { $code = str_replace('$parameters = []', "\$containerDir;\n protected \$parameters = [];\n private \$buildParameters", $code); $code = str_replace('__construct()', '__construct(array $buildParameters = [], $containerDir = __DIR__)', $code); $code .= " \$this->buildParameters = \$buildParameters;\n"; $code .= " \$this->containerDir = \$containerDir;\n"; if (null !== $this->targetDirRegex) { $code = str_replace('$parameters = []', "\$targetDir;\n protected \$parameters = []", $code); $code .= ' $this->targetDir = \\dirname($containerDir);'."\n"; } } if (Container::class !== $this->baseClass) { $r = $this->container->getReflectionClass($this->baseClass, false); if (null !== $r && (null !== $constructor = $r->getConstructor()) && 0 === $constructor->getNumberOfRequiredParameters() && Container::class !== $constructor->getDeclaringClass()->name ) { $code .= " parent::__construct();\n"; $code .= " \$this->parameterBag = null;\n\n"; } } if ($this->container->getParameterBag()->all()) { $code .= " \$this->parameters = \$this->getDefaultParameters();\n\n"; } $code .= " \$this->services = \$this->privates = [];\n"; $code .= $this->addSyntheticIds(); $code .= $this->addMethodMap(); $code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : ''; $code .= $this->addAliases(); $code .= $this->addInlineRequires($hasProxyClasses); $code .= <<<EOF } public function compile(): void { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } public function isCompiled(): bool { return true; } EOF; $code .= $this->addRemovedIds(); if ($this->asFiles && !$this->inlineFactories) { $code .= <<<'EOF' protected function load($file, $lazyLoad = true) { if (class_exists($class = __NAMESPACE__.'\\'.$file, false)) { return $class::do($this, $lazyLoad); } if ('.' === $file[-4]) { $class = substr($class, 0, -4); } else { $file .= '.php'; } $service = require $this->containerDir.\DIRECTORY_SEPARATOR.$file; return class_exists($class, false) ? $class::do($this, $lazyLoad) : $service; } EOF; } $proxyDumper = $this->getProxyDumper(); foreach ($this->container->getDefinitions() as $definition) { if (!$proxyDumper->isProxyCandidate($definition)) { continue; } if ($this->asFiles && !$this->inlineFactories) { $proxyLoader = "class_exists(\$class, false) || require __DIR__.'/'.\$class.'.php';\n\n "; } else { $proxyLoader = ''; } $code .= <<<EOF protected function createProxy(\$class, \Closure \$factory) { {$proxyLoader}return \$factory(); } EOF; break; } return $code; } private function addSyntheticIds(): string { $code = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { if ($definition->isSynthetic() && 'service_container' !== $id) { $code .= ' '.$this->doExport($id)." => true,\n"; } } return $code ? " \$this->syntheticIds = [\n{$code} ];\n" : ''; } private function addRemovedIds(): string { $ids = $this->container->getRemovedIds(); foreach ($this->container->getDefinitions() as $id => $definition) { if (!$definition->isPublic()) { $ids[$id] = true; } } if (!$ids) { return ''; } if ($this->asFiles) { $code = "require \$this->containerDir.\\DIRECTORY_SEPARATOR.'removed-ids.php'"; } else { $code = ''; $ids = array_keys($ids); sort($ids); foreach ($ids as $id) { if (preg_match(FileLoader::ANONYMOUS_ID_REGEXP, $id)) { continue; } $code .= ' '.$this->doExport($id)." => true,\n"; } $code = "[\n{$code} ]"; } return <<<EOF public function getRemovedIds(): array { return {$code}; } EOF; } private function addMethodMap(): string { $code = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || $this->inlineFactories || $this->isHotPath($definition))) { $code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n"; } } $aliases = $this->container->getAliases(); foreach ($aliases as $alias => $id) { if (!$id->isDeprecated()) { continue; } $code .= ' '.$this->doExport($alias).' => '.$this->doExport($this->generateMethodName($alias)).",\n"; } return $code ? " \$this->methodMap = [\n{$code} ];\n" : ''; } private function addFileMap(): string { $code = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { if (!$definition->isSynthetic() && $definition->isPublic() && !$this->isHotPath($definition)) { $code .= sprintf(" %s => '%s',\n", $this->doExport($id), $this->generateMethodName($id)); } } return $code ? " \$this->fileMap = [\n{$code} ];\n" : ''; } private function addAliases(): string { if (!$aliases = $this->container->getAliases()) { return "\n \$this->aliases = [];\n"; } $code = " \$this->aliases = [\n"; ksort($aliases); foreach ($aliases as $alias => $id) { if ($id->isDeprecated()) { continue; } $id = (string) $id; while (isset($aliases[$id])) { $id = (string) $aliases[$id]; } $code .= ' '.$this->doExport($alias).' => '.$this->doExport($id).",\n"; } return $code." ];\n"; } private function addDeprecatedAliases(): string { $code = ''; $aliases = $this->container->getAliases(); foreach ($aliases as $alias => $definition) { if (!$definition->isDeprecated()) { continue; } $public = $definition->isPublic() ? 'public' : 'private'; $id = (string) $definition; $methodNameAlias = $this->generateMethodName($alias); $idExported = $this->export($id); $deprecation = $definition->getDeprecation($alias); $packageExported = $this->export($deprecation['package']); $versionExported = $this->export($deprecation['version']); $messageExported = $this->export($deprecation['message']); $code .= <<<EOF /*{$this->docStar} * Gets the $public '$alias' alias. * * @return object The "$id" service. */ protected function {$methodNameAlias}() { trigger_deprecation($packageExported, $versionExported, $messageExported); return \$this->get($idExported); } EOF; } return $code; } private function addInlineRequires(bool $hasProxyClasses): string { $lineage = []; $hotPathServices = $this->hotPathTag && $this->inlineRequires ? $this->container->findTaggedServiceIds($this->hotPathTag) : []; foreach ($hotPathServices as $id => $tags) { $definition = $this->container->getDefinition($id); if ($this->getProxyDumper()->isProxyCandidate($definition)) { continue; } $inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]); foreach ($inlinedDefinitions as $def) { foreach ($this->getClasses($def, $id) as $class) { $this->collectLineage($class, $lineage); } } } $code = ''; foreach ($lineage as $file) { if (!isset($this->inlinedRequires[$file])) { $this->inlinedRequires[$file] = true; $code .= sprintf("\n include_once %s;", $file); } } if ($hasProxyClasses) { $code .= "\n include_once __DIR__.'/proxy-classes.php';"; } return $code ? sprintf("\n \$this->privates['service_container'] = function () {%s\n };\n", $code) : ''; } private function addDefaultParametersMethod(): string { if (!$this->container->getParameterBag()->all()) { return ''; } $php = []; $dynamicPhp = []; foreach ($this->container->getParameterBag()->all() as $key => $value) { if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) { throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: "%s".', $resolvedKey)); } $hasEnum = false; $export = $this->exportParameters([$value], '', 12, $hasEnum); $export = explode('0 => ', substr(rtrim($export, " ]\n"), 2, -1), 2); if ($hasEnum || preg_match("/\\\$this->(?:getEnv\('(?:[-.\w]*+:)*+\w++'\)|targetDir\.'')/", $export[1])) { $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]); $this->dynamicParameters[$key] = true; } else { $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]); } } $parameters = sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', 8)); $code = <<<'EOF' /** * @return array|bool|float|int|string|\UnitEnum|null */ public function getParameter(string $name) { if (isset($this->buildParameters[$name])) { return $this->buildParameters[$name]; } if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) { throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); } return $this->parameters[$name]; } public function hasParameter(string $name): bool { if (isset($this->buildParameters[$name])) { return true; } return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters); } public function setParameter(string $name, $value): void { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } public function getParameterBag(): ParameterBagInterface { if (null === $this->parameterBag) { $parameters = $this->parameters; foreach ($this->loadedDynamicParameters as $name => $loaded) { $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); } foreach ($this->buildParameters as $name => $value) { $parameters[$name] = $value; } $this->parameterBag = new FrozenParameterBag($parameters); } return $this->parameterBag; } EOF; if (!$this->asFiles) { $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n\n?/m', '', $code); } if ($dynamicPhp) { $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8); $getDynamicParameter = <<<'EOF' switch ($name) { %s default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name)); } $this->loadedDynamicParameters[$name] = true; return $this->dynamicParameters[$name] = $value; EOF; $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp)); } else { $loadedDynamicParameters = '[]'; $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));'; } $code .= <<<EOF private \$loadedDynamicParameters = {$loadedDynamicParameters}; private \$dynamicParameters = []; private function getDynamicParameter(string \$name) { {$getDynamicParameter} } protected function getDefaultParameters(): array { return $parameters; } EOF; return $code; } /** * @throws InvalidArgumentException */ private function exportParameters(array $parameters, string $path = '', int $indent = 12, bool &$hasEnum = false): string { $php = []; foreach ($parameters as $key => $value) { if (\is_array($value)) { $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4, $hasEnum); } elseif ($value instanceof ArgumentInterface) { throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain special arguments. "%s" found in "%s".', get_debug_type($value), $path.'/'.$key)); } elseif ($value instanceof Variable) { throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key)); } elseif ($value instanceof Definition) { throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain service definitions. Definition for "%s" found in "%s".', $value->getClass(), $path.'/'.$key)); } elseif ($value instanceof Reference) { throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service "%s" found in "%s").', $value, $path.'/'.$key)); } elseif ($value instanceof Expression) { throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain expressions. Expression "%s" found in "%s".', $value, $path.'/'.$key)); } elseif ($value instanceof \UnitEnum) { $hasEnum = true; $value = sprintf('\%s::%s', \get_class($value), $value->name); } else { $value = $this->export($value); } $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value); } return sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', $indent - 4)); } private function endClass(): string { if ($this->addThrow) { return <<<'EOF' protected function throw($message) { throw new RuntimeException($message); } } EOF; } return <<<'EOF' } EOF; } private function wrapServiceConditionals($value, string $code): string { if (!$condition = $this->getServiceConditionals($value)) { return $code; } // re-indent the wrapped code $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code))); return sprintf(" if (%s) {\n%s }\n", $condition, $code); } private function getServiceConditionals($value): string { $conditions = []; foreach (ContainerBuilder::getInitializedConditionals($value) as $service) { if (!$this->container->hasDefinition($service)) { return 'false'; } $conditions[] = sprintf('isset($this->%s[%s])', $this->container->getDefinition($service)->isPublic() ? 'services' : 'privates', $this->doExport($service)); } foreach (ContainerBuilder::getServiceConditionals($value) as $service) { if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) { continue; } $conditions[] = sprintf('$this->has(%s)', $this->doExport($service)); } if (!$conditions) { return ''; } return implode(' && ', $conditions); } private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null, array &$calls = [], bool $byConstructor = null): \SplObjectStorage { if (null === $definitions) { $definitions = new \SplObjectStorage(); } foreach ($arguments as $argument) { if (\is_array($argument)) { $this->getDefinitionsFromArguments($argument, $definitions, $calls, $byConstructor); } elseif ($argument instanceof Reference) { $id = (string) $argument; while ($this->container->hasAlias($id)) { $id = (string) $this->container->getAlias($id); } if (!isset($calls[$id])) { $calls[$id] = [0, $argument->getInvalidBehavior(), $byConstructor]; } else { $calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior()); } ++$calls[$id][0]; } elseif (!$argument instanceof Definition) { // no-op } elseif (isset($definitions[$argument])) { $definitions[$argument] = 1 + $definitions[$argument]; } else { $definitions[$argument] = 1; $arguments = [$argument->getArguments(), $argument->getFactory()]; $this->getDefinitionsFromArguments($arguments, $definitions, $calls, null === $byConstructor || $byConstructor); $arguments = [$argument->getProperties(), $argument->getMethodCalls(), $argument->getConfigurator()]; $this->getDefinitionsFromArguments($arguments, $definitions, $calls, null !== $byConstructor && $byConstructor); } } return $definitions; } /** * @throws RuntimeException */ private function dumpValue($value, bool $interpolate = true): string { if (\is_array($value)) { if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) { return $this->dumpValue("%$param%"); } $code = []; foreach ($value as $k => $v) { $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)); } return sprintf('[%s]', implode(', ', $code)); } elseif ($value instanceof ArgumentInterface) { $scope = [$this->definitionVariables, $this->referenceVariables]; $this->definitionVariables = $this->referenceVariables = null; try { if ($value instanceof ServiceClosureArgument) { $value = $value->getValues()[0]; $code = $this->dumpValue($value, $interpolate); $returnedType = ''; if ($value instanceof TypedReference) { $returnedType = sprintf(': %s\%s', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $value->getInvalidBehavior() ? '' : '?', str_replace(['|', '&'], ['|\\', '&\\'], $value->getType())); } $code = sprintf('return %s;', $code); return sprintf("function ()%s {\n %s\n }", $returnedType, $code); } if ($value instanceof IteratorArgument) { $operands = [0]; $code = []; $code[] = 'new RewindableGenerator(function () {'; if (!$values = $value->getValues()) { $code[] = ' return new \EmptyIterator();'; } else { $countCode = []; $countCode[] = 'function () {'; foreach ($values as $k => $v) { ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0]; $v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate))); foreach (explode("\n", $v) as $v) { if ($v) { $code[] = ' '.$v; } } } $countCode[] = sprintf(' return %s;', implode(' + ', $operands)); $countCode[] = ' }'; } $code[] = sprintf(' }, %s)', \count($operands) > 1 ? implode("\n", $countCode) : $operands[0]); return implode("\n", $code); } if ($value instanceof ServiceLocatorArgument) { $serviceMap = ''; $serviceTypes = ''; foreach ($value->getValues() as $k => $v) { if (!$v) { continue; } $id = (string) $v; while ($this->container->hasAlias($id)) { $id = (string) $this->container->getAlias($id); } $definition = $this->container->getDefinition($id); $load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) : reset($e); $serviceMap .= sprintf("\n %s => [%s, %s, %s, %s],", $this->export($k), $this->export($definition->isShared() ? ($definition->isPublic() ? 'services' : 'privates') : false), $this->doExport($id), $this->export(ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $v->getInvalidBehavior() && !\is_string($load) ? $this->generateMethodName($id) : null), $this->export($load) ); $serviceTypes .= sprintf("\n %s => %s,", $this->export($k), $this->export($v instanceof TypedReference ? $v->getType() : '?')); $this->locatedIds[$id] = true; } $this->addGetService = true; return sprintf('new \%s($this->getService, [%s%s], [%s%s])', ServiceLocator::class, $serviceMap, $serviceMap ? "\n " : '', $serviceTypes, $serviceTypes ? "\n " : ''); } } finally { [$this->definitionVariables, $this->referenceVariables] = $scope; } } elseif ($value instanceof Definition) { if ($value->hasErrors() && $e = $value->getErrors()) { $this->addThrow = true; return sprintf('$this->throw(%s)', $this->export(reset($e))); } if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { return $this->dumpValue($this->definitionVariables[$value], $interpolate); } if ($value->getMethodCalls()) { throw new RuntimeException('Cannot dump definitions which have method calls.'); } if ($value->getProperties()) { throw new RuntimeException('Cannot dump definitions which have properties.'); } if (null !== $value->getConfigurator()) { throw new RuntimeException('Cannot dump definitions which have a configurator.'); } return $this->addNewInstance($value); } elseif ($value instanceof Variable) { return '$'.$value; } elseif ($value instanceof Reference) { $id = (string) $value; while ($this->container->hasAlias($id)) { $id = (string) $this->container->getAlias($id); } if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) { return $this->dumpValue($this->referenceVariables[$id], $interpolate); } return $this->getServiceCall($id, $value); } elseif ($value instanceof Expression) { return $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']); } elseif ($value instanceof Parameter) { return $this->dumpParameter($value); } elseif (true === $interpolate && \is_string($value)) { if (preg_match('/^%([^%]+)%$/', $value, $match)) { // we do this to deal with non string values (Boolean, integer, ...) // the preg_replace_callback converts them to strings return $this->dumpParameter($match[1]); } else { $replaceParameters = function ($match) { return "'.".$this->dumpParameter($match[2]).".'"; }; $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value))); return $code; } } elseif ($value instanceof \UnitEnum) { return sprintf('\%s::%s', \get_class($value), $value->name); } elseif ($value instanceof AbstractArgument) { throw new RuntimeException($value->getTextWithContext()); } elseif (\is_object($value) || \is_resource($value)) { throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); } return $this->export($value); } /** * Dumps a string to a literal (aka PHP Code) class value. * * @throws RuntimeException */ private function dumpLiteralClass(string $class): string { if (str_contains($class, '$')) { return sprintf('${($_ = %s) && false ?: "_"}', $class); } if (!str_starts_with($class, "'") || !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s).', $class ?: 'n/a')); } $class = substr(str_replace('\\\\', '\\', $class), 1, -1); return str_starts_with($class, '\\') ? $class : '\\'.$class; } private function dumpParameter(string $name): string { if (!$this->container->hasParameter($name) || ($this->dynamicParameters[$name] ?? false)) { return sprintf('$this->getParameter(%s)', $this->doExport($name)); } $value = $this->container->getParameter($name); $dumpedValue = $this->dumpValue($value, false); if (!$value || !\is_array($value)) { return $dumpedValue; } return sprintf('$this->parameters[%s]', $this->doExport($name)); } private function getServiceCall(string $id, Reference $reference = null): string { while ($this->container->hasAlias($id)) { $id = (string) $this->container->getAlias($id); } if ('service_container' === $id) { return '$this'; } if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) { if ($definition->isSynthetic()) { $code = sprintf('$this->get(%s%s)', $this->doExport($id), null !== $reference ? ', '.$reference->getInvalidBehavior() : ''); } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { $code = 'null'; if (!$definition->isShared()) { return $code; } } elseif ($this->isTrivialInstance($definition)) { if ($definition->hasErrors() && $e = $definition->getErrors()) { $this->addThrow = true; return sprintf('$this->throw(%s)', $this->export(reset($e))); } $code = $this->addNewInstance($definition, '', $id); if ($definition->isShared() && !isset($this->singleUsePrivateIds[$id])) { $code = sprintf('$this->%s[%s] = %s', $definition->isPublic() ? 'services' : 'privates', $this->doExport($id), $code); } $code = "($code)"; } else { $code = $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) ? "\$this->load('%s')" : '$this->%s()'; $code = sprintf($code, $this->generateMethodName($id)); if (!$definition->isShared()) { $factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id)); $code = sprintf('(isset(%s) ? %1$s() : %s)', $factory, $code); } } if ($definition->isShared() && !isset($this->singleUsePrivateIds[$id])) { $code = sprintf('($this->%s[%s] ?? %s)', $definition->isPublic() ? 'services' : 'privates', $this->doExport($id), $code); } return $code; } if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { return 'null'; } if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE < $reference->getInvalidBehavior()) { $code = sprintf('$this->get(%s, /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ %d)', $this->doExport($id), ContainerInterface::NULL_ON_INVALID_REFERENCE); } else { $code = sprintf('$this->get(%s)', $this->doExport($id)); } return sprintf('($this->services[%s] ?? %s)', $this->doExport($id), $code); } /** * Initializes the method names map to avoid conflicts with the Container methods. */ private function initializeMethodNamesMap(string $class) { $this->serviceIdToMethodNameMap = []; $this->usedMethodNames = []; if ($reflectionClass = $this->container->getReflectionClass($class)) { foreach ($reflectionClass->getMethods() as $method) { $this->usedMethodNames[strtolower($method->getName())] = true; } } } /** * @throws InvalidArgumentException */ private function generateMethodName(string $id): string { if (isset($this->serviceIdToMethodNameMap[$id])) { return $this->serviceIdToMethodNameMap[$id]; } $i = strrpos($id, '\\'); $name = Container::camelize(false !== $i && isset($id[1 + $i]) ? substr($id, 1 + $i) : $id); $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name); $methodName = 'get'.$name.'Service'; $suffix = 1; while (isset($this->usedMethodNames[strtolower($methodName)])) { ++$suffix; $methodName = 'get'.$name.$suffix.'Service'; } $this->serviceIdToMethodNameMap[$id] = $methodName; $this->usedMethodNames[strtolower($methodName)] = true; return $methodName; } private function getNextVariableName(): string { $firstChars = self::FIRST_CHARS; $firstCharsLength = \strlen($firstChars); $nonFirstChars = self::NON_FIRST_CHARS; $nonFirstCharsLength = \strlen($nonFirstChars); while (true) { $name = ''; $i = $this->variableCount; if ('' === $name) { $name .= $firstChars[$i % $firstCharsLength]; $i = (int) ($i / $firstCharsLength); } while ($i > 0) { --$i; $name .= $nonFirstChars[$i % $nonFirstCharsLength]; $i = (int) ($i / $nonFirstCharsLength); } ++$this->variableCount; // check that the name is not reserved if (\in_array($name, $this->reservedVariables, true)) { continue; } return $name; } } private function getExpressionLanguage(): ExpressionLanguage { if (null === $this->expressionLanguage) { if (!class_exists(\Symfony\Component\ExpressionLanguage\ExpressionLanguage::class)) { throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); } $providers = $this->container->getExpressionLanguageProviders(); $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) { $id = '""' === substr_replace($arg, '', 1, -1) ? stripcslashes(substr($arg, 1, -1)) : null; if (null !== $id && ($this->container->hasAlias($id) || $this->container->hasDefinition($id))) { return $this->getServiceCall($id); } return sprintf('$this->get(%s)', $arg); }); if ($this->container->isTrackingResources()) { foreach ($providers as $provider) { $this->container->addObjectResource($provider); } } } return $this->expressionLanguage; } private function isHotPath(Definition $definition): bool { return $this->hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated(); } private function isSingleUsePrivateNode(ServiceReferenceGraphNode $node): bool { if ($node->getValue()->isPublic()) { return false; } $ids = []; foreach ($node->getInEdges() as $edge) { if (!$value = $edge->getSourceNode()->getValue()) { continue; } if ($edge->isLazy() || !$value instanceof Definition || !$value->isShared()) { return false; } $ids[$edge->getSourceNode()->getId()] = true; } return 1 === \count($ids); } /** * @return mixed */ private function export($value) { if (null !== $this->targetDirRegex && \is_string($value) && preg_match($this->targetDirRegex, $value, $matches, \PREG_OFFSET_CAPTURE)) { $suffix = $matches[0][1] + \strlen($matches[0][0]); $matches[0][1] += \strlen($matches[1][0]); $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : ''; if ('\\' === \DIRECTORY_SEPARATOR && isset($value[$suffix])) { $cookie = '\\'.random_int(100000, \PHP_INT_MAX); $suffix = '.'.$this->doExport(str_replace('\\', $cookie, substr($value, $suffix)), true); $suffix = str_replace('\\'.$cookie, "'.\\DIRECTORY_SEPARATOR.'", $suffix); } else { $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : ''; } $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__'; $offset = 2 + $this->targetDirMaxMatches - \count($matches); if (0 < $offset) { $dirname = sprintf('\dirname(__DIR__, %d)', $offset + (int) $this->asFiles); } elseif ($this->asFiles) { $dirname = "\$this->targetDir.''"; // empty string concatenation on purpose } if ($prefix || $suffix) { return sprintf('(%s%s%s)', $prefix, $dirname, $suffix); } return $dirname; } return $this->doExport($value, true); } /** * @return mixed */ private function doExport($value, bool $resolveEnv = false) { $shouldCacheValue = $resolveEnv && \is_string($value); if ($shouldCacheValue && isset($this->exportedVariables[$value])) { return $this->exportedVariables[$value]; } if (\is_string($value) && str_contains($value, "\n")) { $cleanParts = explode("\n", $value); $cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts); $export = implode('."\n".', $cleanParts); } else { $export = var_export($value, true); } if ($this->asFiles) { if (false !== strpos($export, '$this')) { $export = str_replace('$this', "$'.'this", $export); } if (false !== strpos($export, 'function () {')) { $export = str_replace('function () {', "function ('.') {", $export); } } if ($resolveEnv && "'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) { $export = $resolvedExport; if (str_ends_with($export, ".''")) { $export = substr($export, 0, -3); if ("'" === $export[1]) { $export = substr_replace($export, '', 18, 7); } } if ("'" === $export[1]) { $export = substr($export, 3); } } if ($shouldCacheValue) { $this->exportedVariables[$value] = $export; } return $export; } private function getAutoloadFile(): ?string { $file = null; foreach (spl_autoload_functions() as $autoloader) { if (!\is_array($autoloader)) { continue; } if ($autoloader[0] instanceof DebugClassLoader || $autoloader[0] instanceof LegacyDebugClassLoader) { $autoloader = $autoloader[0]->getClassLoader(); } if (!\is_array($autoloader) || !$autoloader[0] instanceof ClassLoader || !$autoloader[0]->findFile(__CLASS__)) { continue; } foreach (get_declared_classes() as $class) { if (str_starts_with($class, 'ComposerAutoloaderInit') && $class::getLoader() === $autoloader[0]) { $file = \dirname((new \ReflectionClass($class))->getFileName(), 2).'/autoload.php'; if (null !== $this->targetDirRegex && preg_match($this->targetDirRegex.'A', $file)) { return $file; } } } } return $file; } private function getClasses(Definition $definition, string $id): array { $classes = []; while ($definition instanceof Definition) { foreach ($definition->getTag($this->preloadTags[0]) as $tag) { if (!isset($tag['class'])) { throw new InvalidArgumentException(sprintf('Missing attribute "class" on tag "%s" for service "%s".', $this->preloadTags[0], $id)); } $classes[] = trim($tag['class'], '\\'); } if ($class = $definition->getClass()) { $classes[] = trim($class, '\\'); } $factory = $definition->getFactory(); if (!\is_array($factory)) { $factory = [$factory]; } if (\is_string($factory[0])) { if (false !== $i = strrpos($factory[0], '::')) { $factory[0] = substr($factory[0], 0, $i); } $classes[] = trim($factory[0], '\\'); } $definition = $factory[0]; } return $classes; } } Dumper/Preloader.php 0000644 00000010024 15120140333 0010413 0 ustar 00 <?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\Dumper; /** * @author Nicolas Grekas <p@tchwork.com> */ final class Preloader { public static function append(string $file, array $list): void { if (!file_exists($file)) { throw new \LogicException(sprintf('File "%s" does not exist.', $file)); } $cacheDir = \dirname($file); $classes = []; foreach ($list as $item) { if (0 === strpos($item, $cacheDir)) { file_put_contents($file, sprintf("require_once __DIR__.%s;\n", var_export(strtr(substr($item, \strlen($cacheDir)), \DIRECTORY_SEPARATOR, '/'), true)), \FILE_APPEND); continue; } $classes[] = sprintf("\$classes[] = %s;\n", var_export($item, true)); } file_put_contents($file, sprintf("\n\$classes = [];\n%s\$preloaded = Preloader::preload(\$classes, \$preloaded);\n", implode('', $classes)), \FILE_APPEND); } public static function preload(array $classes, array $preloaded = []): array { set_error_handler(function ($t, $m, $f, $l) { if (error_reporting() & $t) { if (__FILE__ !== $f) { throw new \ErrorException($m, 0, $t, $f, $l); } throw new \ReflectionException($m); } }); $prev = []; try { while ($prev !== $classes) { $prev = $classes; foreach ($classes as $c) { if (!isset($preloaded[$c])) { self::doPreload($c, $preloaded); } } $classes = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); } } finally { restore_error_handler(); } return $preloaded; } private static function doPreload(string $class, array &$preloaded): void { if (isset($preloaded[$class]) || \in_array($class, ['self', 'static', 'parent'], true)) { return; } $preloaded[$class] = true; try { if (!class_exists($class) && !interface_exists($class, false) && !trait_exists($class, false)) { return; } $r = new \ReflectionClass($class); if ($r->isInternal()) { return; } $r->getConstants(); $r->getDefaultProperties(); if (\PHP_VERSION_ID >= 70400) { foreach ($r->getProperties(\ReflectionProperty::IS_PUBLIC) as $p) { self::preloadType($p->getType(), $preloaded); } } foreach ($r->getMethods(\ReflectionMethod::IS_PUBLIC) as $m) { foreach ($m->getParameters() as $p) { if ($p->isDefaultValueAvailable() && $p->isDefaultValueConstant()) { $c = $p->getDefaultValueConstantName(); if ($i = strpos($c, '::')) { self::doPreload(substr($c, 0, $i), $preloaded); } } self::preloadType($p->getType(), $preloaded); } self::preloadType($m->getReturnType(), $preloaded); } } catch (\Throwable $e) { // ignore missing classes } } private static function preloadType(?\ReflectionType $t, array &$preloaded): void { if (!$t) { return; } foreach (($t instanceof \ReflectionUnionType || $t instanceof \ReflectionIntersectionType) ? $t->getTypes() : [$t] as $t) { if (!$t->isBuiltin()) { self::doPreload($t instanceof \ReflectionNamedType ? $t->getName() : $t, $preloaded); } } } } Dumper/XmlDumper.php 0000644 00000037737 15120140333 0010437 0 ustar 00 <?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\Dumper; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; /** * XmlDumper dumps a service container as an XML string. * * @author Fabien Potencier <fabien@symfony.com> * @author Martin Hasoň <martin.hason@gmail.com> */ class XmlDumper extends Dumper { /** * @var \DOMDocument */ private $document; /** * Dumps the service container as an XML string. * * @return string */ public function dump(array $options = []) { $this->document = new \DOMDocument('1.0', 'utf-8'); $this->document->formatOutput = true; $container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container'); $container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd'); $this->addParameters($container); $this->addServices($container); $this->document->appendChild($container); $xml = $this->document->saveXML(); $this->document = null; return $this->container->resolveEnvPlaceholders($xml); } private function addParameters(\DOMElement $parent) { $data = $this->container->getParameterBag()->all(); if (!$data) { return; } if ($this->container->isCompiled()) { $data = $this->escape($data); } $parameters = $this->document->createElement('parameters'); $parent->appendChild($parameters); $this->convertParameters($data, 'parameter', $parameters); } private function addMethodCalls(array $methodcalls, \DOMElement $parent) { foreach ($methodcalls as $methodcall) { $call = $this->document->createElement('call'); $call->setAttribute('method', $methodcall[0]); if (\count($methodcall[1])) { $this->convertParameters($methodcall[1], 'argument', $call); } if ($methodcall[2] ?? false) { $call->setAttribute('returns-clone', 'true'); } $parent->appendChild($call); } } private function addService(Definition $definition, ?string $id, \DOMElement $parent) { $service = $this->document->createElement('service'); if (null !== $id) { $service->setAttribute('id', $id); } if ($class = $definition->getClass()) { if ('\\' === substr($class, 0, 1)) { $class = substr($class, 1); } $service->setAttribute('class', $class); } if (!$definition->isShared()) { $service->setAttribute('shared', 'false'); } if ($definition->isPublic()) { $service->setAttribute('public', 'true'); } if ($definition->isSynthetic()) { $service->setAttribute('synthetic', 'true'); } if ($definition->isLazy()) { $service->setAttribute('lazy', 'true'); } if (null !== $decoratedService = $definition->getDecoratedService()) { [$decorated, $renamedId, $priority] = $decoratedService; $service->setAttribute('decorates', $decorated); $decorationOnInvalid = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; if (\in_array($decorationOnInvalid, [ContainerInterface::IGNORE_ON_INVALID_REFERENCE, ContainerInterface::NULL_ON_INVALID_REFERENCE], true)) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE === $decorationOnInvalid ? 'null' : 'ignore'; $service->setAttribute('decoration-on-invalid', $invalidBehavior); } if (null !== $renamedId) { $service->setAttribute('decoration-inner-name', $renamedId); } if (0 !== $priority) { $service->setAttribute('decoration-priority', $priority); } } foreach ($definition->getTags() as $name => $tags) { foreach ($tags as $attributes) { $tag = $this->document->createElement('tag'); if (!\array_key_exists('name', $attributes)) { $tag->setAttribute('name', $name); } else { $tag->appendChild($this->document->createTextNode($name)); } foreach ($attributes as $key => $value) { $tag->setAttribute($key, $value ?? ''); } $service->appendChild($tag); } } if ($definition->getFile()) { $file = $this->document->createElement('file'); $file->appendChild($this->document->createTextNode($definition->getFile())); $service->appendChild($file); } if ($parameters = $definition->getArguments()) { $this->convertParameters($parameters, 'argument', $service); } if ($parameters = $definition->getProperties()) { $this->convertParameters($parameters, 'property', $service, 'name'); } $this->addMethodCalls($definition->getMethodCalls(), $service); if ($callable = $definition->getFactory()) { $factory = $this->document->createElement('factory'); if (\is_array($callable) && $callable[0] instanceof Definition) { $this->addService($callable[0], null, $factory); $factory->setAttribute('method', $callable[1]); } elseif (\is_array($callable)) { if (null !== $callable[0]) { $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); } $factory->setAttribute('method', $callable[1]); } else { $factory->setAttribute('function', $callable); } $service->appendChild($factory); } if ($definition->isDeprecated()) { $deprecation = $definition->getDeprecation('%service_id%'); $deprecated = $this->document->createElement('deprecated'); $deprecated->appendChild($this->document->createTextNode($definition->getDeprecation('%service_id%')['message'])); $deprecated->setAttribute('package', $deprecation['package']); $deprecated->setAttribute('version', $deprecation['version']); $service->appendChild($deprecated); } if ($definition->isAutowired()) { $service->setAttribute('autowire', 'true'); } if ($definition->isAutoconfigured()) { $service->setAttribute('autoconfigure', 'true'); } if ($definition->isAbstract()) { $service->setAttribute('abstract', 'true'); } if ($callable = $definition->getConfigurator()) { $configurator = $this->document->createElement('configurator'); if (\is_array($callable) && $callable[0] instanceof Definition) { $this->addService($callable[0], null, $configurator); $configurator->setAttribute('method', $callable[1]); } elseif (\is_array($callable)) { $configurator->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); $configurator->setAttribute('method', $callable[1]); } else { $configurator->setAttribute('function', $callable); } $service->appendChild($configurator); } $parent->appendChild($service); } private function addServiceAlias(string $alias, Alias $id, \DOMElement $parent) { $service = $this->document->createElement('service'); $service->setAttribute('id', $alias); $service->setAttribute('alias', $id); if ($id->isPublic()) { $service->setAttribute('public', 'true'); } if ($id->isDeprecated()) { $deprecation = $id->getDeprecation('%alias_id%'); $deprecated = $this->document->createElement('deprecated'); $deprecated->appendChild($this->document->createTextNode($deprecation['message'])); $deprecated->setAttribute('package', $deprecation['package']); $deprecated->setAttribute('version', $deprecation['version']); $service->appendChild($deprecated); } $parent->appendChild($service); } private function addServices(\DOMElement $parent) { $definitions = $this->container->getDefinitions(); if (!$definitions) { return; } $services = $this->document->createElement('services'); foreach ($definitions as $id => $definition) { $this->addService($definition, $id, $services); } $aliases = $this->container->getAliases(); foreach ($aliases as $alias => $id) { while (isset($aliases[(string) $id])) { $id = $aliases[(string) $id]; } $this->addServiceAlias($alias, $id, $services); } $parent->appendChild($services); } private function convertParameters(array $parameters, string $type, \DOMElement $parent, string $keyAttribute = 'key') { $withKeys = !array_is_list($parameters); foreach ($parameters as $key => $value) { $element = $this->document->createElement($type); if ($withKeys) { $element->setAttribute($keyAttribute, $key); } if (\is_array($tag = $value)) { $element->setAttribute('type', 'collection'); $this->convertParameters($value, $type, $element, 'key'); } elseif ($value instanceof TaggedIteratorArgument || ($value instanceof ServiceLocatorArgument && $tag = $value->getTaggedIteratorArgument())) { $element->setAttribute('type', $value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator'); $element->setAttribute('tag', $tag->getTag()); if (null !== $tag->getIndexAttribute()) { $element->setAttribute('index-by', $tag->getIndexAttribute()); if (null !== $tag->getDefaultIndexMethod()) { $element->setAttribute('default-index-method', $tag->getDefaultIndexMethod()); } if (null !== $tag->getDefaultPriorityMethod()) { $element->setAttribute('default-priority-method', $tag->getDefaultPriorityMethod()); } } } elseif ($value instanceof IteratorArgument) { $element->setAttribute('type', 'iterator'); $this->convertParameters($value->getValues(), $type, $element, 'key'); } elseif ($value instanceof ServiceLocatorArgument) { $element->setAttribute('type', 'service_locator'); $this->convertParameters($value->getValues(), $type, $element, 'key'); } elseif ($value instanceof Reference || $value instanceof ServiceClosureArgument) { $element->setAttribute('type', 'service'); if ($value instanceof ServiceClosureArgument) { $element->setAttribute('type', 'service_closure'); $value = $value->getValues()[0]; } $element->setAttribute('id', (string) $value); $behavior = $value->getInvalidBehavior(); if (ContainerInterface::NULL_ON_INVALID_REFERENCE == $behavior) { $element->setAttribute('on-invalid', 'null'); } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE == $behavior) { $element->setAttribute('on-invalid', 'ignore'); } elseif (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE == $behavior) { $element->setAttribute('on-invalid', 'ignore_uninitialized'); } } elseif ($value instanceof Definition) { $element->setAttribute('type', 'service'); $this->addService($value, null, $element); } elseif ($value instanceof Expression) { $element->setAttribute('type', 'expression'); $text = $this->document->createTextNode(self::phpToXml((string) $value)); $element->appendChild($text); } elseif (\is_string($value) && !preg_match('/^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*+$/u', $value)) { $element->setAttribute('type', 'binary'); $text = $this->document->createTextNode(self::phpToXml(base64_encode($value))); $element->appendChild($text); } elseif ($value instanceof \UnitEnum) { $element->setAttribute('type', 'constant'); $element->appendChild($this->document->createTextNode(self::phpToXml($value))); } elseif ($value instanceof AbstractArgument) { $element->setAttribute('type', 'abstract'); $text = $this->document->createTextNode(self::phpToXml($value->getText())); $element->appendChild($text); } else { if (\in_array($value, ['null', 'true', 'false'], true)) { $element->setAttribute('type', 'string'); } if (\is_string($value) && (is_numeric($value) || preg_match('/^0b[01]*$/', $value) || preg_match('/^0x[0-9a-f]++$/i', $value))) { $element->setAttribute('type', 'string'); } $text = $this->document->createTextNode(self::phpToXml($value)); $element->appendChild($text); } $parent->appendChild($element); } } /** * Escapes arguments. */ private function escape(array $arguments): array { $args = []; foreach ($arguments as $k => $v) { if (\is_array($v)) { $args[$k] = $this->escape($v); } elseif (\is_string($v)) { $args[$k] = str_replace('%', '%%', $v); } else { $args[$k] = $v; } } return $args; } /** * Converts php types to xml types. * * @param mixed $value Value to convert * * @throws RuntimeException When trying to dump object or resource */ public static function phpToXml($value): string { switch (true) { case null === $value: return 'null'; case true === $value: return 'true'; case false === $value: return 'false'; case $value instanceof Parameter: return '%'.$value.'%'; case $value instanceof \UnitEnum: return sprintf('%s::%s', \get_class($value), $value->name); case \is_object($value) || \is_resource($value): throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); default: return (string) $value; } } } Dumper/YamlDumper.php 0000644 00000032100 15120140333 0010554 0 ustar 00 <?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\Dumper; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\Yaml\Dumper as YmlDumper; use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Tag\TaggedValue; use Symfony\Component\Yaml\Yaml; /** * YamlDumper dumps a service container as a YAML string. * * @author Fabien Potencier <fabien@symfony.com> */ class YamlDumper extends Dumper { private $dumper; /** * Dumps the service container as an YAML string. * * @return string */ public function dump(array $options = []) { if (!class_exists(\Symfony\Component\Yaml\Dumper::class)) { throw new LogicException('Unable to dump the container as the Symfony Yaml Component is not installed.'); } if (null === $this->dumper) { $this->dumper = new YmlDumper(); } return $this->container->resolveEnvPlaceholders($this->addParameters()."\n".$this->addServices()); } private function addService(string $id, Definition $definition): string { $code = " $id:\n"; if ($class = $definition->getClass()) { if ('\\' === substr($class, 0, 1)) { $class = substr($class, 1); } $code .= sprintf(" class: %s\n", $this->dumper->dump($class)); } if (!$definition->isPrivate()) { $code .= sprintf(" public: %s\n", $definition->isPublic() ? 'true' : 'false'); } $tagsCode = ''; foreach ($definition->getTags() as $name => $tags) { foreach ($tags as $attributes) { $att = []; foreach ($attributes as $key => $value) { $att[] = sprintf('%s: %s', $this->dumper->dump($key), $this->dumper->dump($value)); } $att = $att ? ': { '.implode(', ', $att).' }' : ''; $tagsCode .= sprintf(" - %s%s\n", $this->dumper->dump($name), $att); } } if ($tagsCode) { $code .= " tags:\n".$tagsCode; } if ($definition->getFile()) { $code .= sprintf(" file: %s\n", $this->dumper->dump($definition->getFile())); } if ($definition->isSynthetic()) { $code .= " synthetic: true\n"; } if ($definition->isDeprecated()) { $code .= " deprecated:\n"; foreach ($definition->getDeprecation('%service_id%') as $key => $value) { if ('' !== $value) { $code .= sprintf(" %s: %s\n", $key, $this->dumper->dump($value)); } } } if ($definition->isAutowired()) { $code .= " autowire: true\n"; } if ($definition->isAutoconfigured()) { $code .= " autoconfigure: true\n"; } if ($definition->isAbstract()) { $code .= " abstract: true\n"; } if ($definition->isLazy()) { $code .= " lazy: true\n"; } if ($definition->getArguments()) { $code .= sprintf(" arguments: %s\n", $this->dumper->dump($this->dumpValue($definition->getArguments()), 0)); } if ($definition->getProperties()) { $code .= sprintf(" properties: %s\n", $this->dumper->dump($this->dumpValue($definition->getProperties()), 0)); } if ($definition->getMethodCalls()) { $code .= sprintf(" calls:\n%s\n", $this->dumper->dump($this->dumpValue($definition->getMethodCalls()), 1, 12)); } if (!$definition->isShared()) { $code .= " shared: false\n"; } if (null !== $decoratedService = $definition->getDecoratedService()) { [$decorated, $renamedId, $priority] = $decoratedService; $code .= sprintf(" decorates: %s\n", $decorated); if (null !== $renamedId) { $code .= sprintf(" decoration_inner_name: %s\n", $renamedId); } if (0 !== $priority) { $code .= sprintf(" decoration_priority: %s\n", $priority); } $decorationOnInvalid = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; if (\in_array($decorationOnInvalid, [ContainerInterface::IGNORE_ON_INVALID_REFERENCE, ContainerInterface::NULL_ON_INVALID_REFERENCE])) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE === $decorationOnInvalid ? 'null' : 'ignore'; $code .= sprintf(" decoration_on_invalid: %s\n", $invalidBehavior); } } if ($callable = $definition->getFactory()) { $code .= sprintf(" factory: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0)); } if ($callable = $definition->getConfigurator()) { $code .= sprintf(" configurator: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0)); } return $code; } private function addServiceAlias(string $alias, Alias $id): string { $deprecated = ''; if ($id->isDeprecated()) { $deprecated = " deprecated:\n"; foreach ($id->getDeprecation('%alias_id%') as $key => $value) { if ('' !== $value) { $deprecated .= sprintf(" %s: %s\n", $key, $value); } } } if (!$id->isDeprecated() && $id->isPrivate()) { return sprintf(" %s: '@%s'\n", $alias, $id); } if ($id->isPublic()) { $deprecated = " public: true\n".$deprecated; } return sprintf(" %s:\n alias: %s\n%s", $alias, $id, $deprecated); } private function addServices(): string { if (!$this->container->getDefinitions()) { return ''; } $code = "services:\n"; foreach ($this->container->getDefinitions() as $id => $definition) { $code .= $this->addService($id, $definition); } $aliases = $this->container->getAliases(); foreach ($aliases as $alias => $id) { while (isset($aliases[(string) $id])) { $id = $aliases[(string) $id]; } $code .= $this->addServiceAlias($alias, $id); } return $code; } private function addParameters(): string { if (!$this->container->getParameterBag()->all()) { return ''; } $parameters = $this->prepareParameters($this->container->getParameterBag()->all(), $this->container->isCompiled()); return $this->dumper->dump(['parameters' => $parameters], 2); } /** * Dumps callable to YAML format. * * @param mixed $callable * * @return mixed */ private function dumpCallable($callable) { if (\is_array($callable)) { if ($callable[0] instanceof Reference) { $callable = [$this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]]; } else { $callable = [$callable[0], $callable[1]]; } } return $callable; } /** * Dumps the value to YAML format. * * @return mixed * * @throws RuntimeException When trying to dump object or resource */ private function dumpValue($value) { if ($value instanceof ServiceClosureArgument) { $value = $value->getValues()[0]; return new TaggedValue('service_closure', $this->getServiceCall((string) $value, $value)); } if ($value instanceof ArgumentInterface) { $tag = $value; if ($value instanceof TaggedIteratorArgument || ($value instanceof ServiceLocatorArgument && $tag = $value->getTaggedIteratorArgument())) { if (null === $tag->getIndexAttribute()) { $content = $tag->getTag(); } else { $content = [ 'tag' => $tag->getTag(), 'index_by' => $tag->getIndexAttribute(), ]; if (null !== $tag->getDefaultIndexMethod()) { $content['default_index_method'] = $tag->getDefaultIndexMethod(); } if (null !== $tag->getDefaultPriorityMethod()) { $content['default_priority_method'] = $tag->getDefaultPriorityMethod(); } } return new TaggedValue($value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator', $content); } if ($value instanceof IteratorArgument) { $tag = 'iterator'; } elseif ($value instanceof ServiceLocatorArgument) { $tag = 'service_locator'; } else { throw new RuntimeException(sprintf('Unspecified Yaml tag for type "%s".', get_debug_type($value))); } return new TaggedValue($tag, $this->dumpValue($value->getValues())); } if (\is_array($value)) { $code = []; foreach ($value as $k => $v) { $code[$k] = $this->dumpValue($v); } return $code; } elseif ($value instanceof Reference) { return $this->getServiceCall((string) $value, $value); } elseif ($value instanceof Parameter) { return $this->getParameterCall((string) $value); } elseif ($value instanceof Expression) { return $this->getExpressionCall((string) $value); } elseif ($value instanceof Definition) { return new TaggedValue('service', (new Parser())->parse("_:\n".$this->addService('_', $value), Yaml::PARSE_CUSTOM_TAGS)['_']['_']); } elseif ($value instanceof \UnitEnum) { return new TaggedValue('php/const', sprintf('%s::%s', \get_class($value), $value->name)); } elseif ($value instanceof AbstractArgument) { return new TaggedValue('abstract', $value->getText()); } elseif (\is_object($value) || \is_resource($value)) { throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); } return $value; } private function getServiceCall(string $id, Reference $reference = null): string { if (null !== $reference) { switch ($reference->getInvalidBehavior()) { case ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE: break; case ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE: break; case ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE: return sprintf('@!%s', $id); default: return sprintf('@?%s', $id); } } return sprintf('@%s', $id); } private function getParameterCall(string $id): string { return sprintf('%%%s%%', $id); } private function getExpressionCall(string $expression): string { return sprintf('@=%s', $expression); } private function prepareParameters(array $parameters, bool $escape = true): array { $filtered = []; foreach ($parameters as $key => $value) { if (\is_array($value)) { $value = $this->prepareParameters($value, $escape); } elseif ($value instanceof Reference || \is_string($value) && str_starts_with($value, '@')) { $value = '@'.$value; } $filtered[$key] = $value; } return $escape ? $this->escape($filtered) : $filtered; } private function escape(array $arguments): array { $args = []; foreach ($arguments as $k => $v) { if (\is_array($v)) { $args[$k] = $this->escape($v); } elseif (\is_string($v)) { $args[$k] = str_replace('%', '%%', $v); } else { $args[$k] = $v; } } return $args; } } Exception/AutowiringFailedException.php 0000644 00000003735 15120140333 0014327 0 ustar 00 <?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\Exception; /** * Thrown when a definition cannot be autowired. */ class AutowiringFailedException extends RuntimeException { private $serviceId; private $messageCallback; public function __construct(string $serviceId, $message = '', int $code = 0, \Throwable $previous = null) { $this->serviceId = $serviceId; if ($message instanceof \Closure && (\function_exists('xdebug_is_enabled') ? xdebug_is_enabled() : \function_exists('xdebug_info')) ) { $message = $message(); } if (!$message instanceof \Closure) { parent::__construct($message, $code, $previous); return; } $this->messageCallback = $message; parent::__construct('', $code, $previous); $this->message = new class($this->message, $this->messageCallback) { private $message; private $messageCallback; public function __construct(&$message, &$messageCallback) { $this->message = &$message; $this->messageCallback = &$messageCallback; } public function __toString(): string { $messageCallback = $this->messageCallback; $this->messageCallback = null; try { return $this->message = $messageCallback(); } catch (\Throwable $e) { return $this->message = $e->getMessage(); } } }; } public function getMessageCallback(): ?\Closure { return $this->messageCallback; } public function getServiceId() { return $this->serviceId; } } Exception/BadMethodCallException.php 0000644 00000000724 15120140333 0013510 0 ustar 00 <?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\Exception; /** * Base BadMethodCallException for Dependency Injection component. */ class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface { } Exception/EnvNotFoundException.php 0000644 00000000746 15120140333 0013276 0 ustar 00 <?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\Exception; /** * This exception is thrown when an environment variable is not found. * * @author Nicolas Grekas <p@tchwork.com> */ class EnvNotFoundException extends InvalidArgumentException { } Exception/EnvParameterException.php 0000644 00000001410 15120140333 0013447 0 ustar 00 <?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\Exception; /** * This exception wraps exceptions whose messages contain a reference to an env parameter. * * @author Nicolas Grekas <p@tchwork.com> */ class EnvParameterException extends InvalidArgumentException { public function __construct(array $envs, \Throwable $previous = null, string $message = 'Incompatible use of dynamic environment variables "%s" found in parameters.') { parent::__construct(sprintf($message, implode('", "', $envs)), 0, $previous); } } Exception/ExceptionInterface.php 0000644 00000001142 15120140333 0012760 0 ustar 00 <?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\Exception; use Psr\Container\ContainerExceptionInterface; /** * Base ExceptionInterface for Dependency Injection component. * * @author Fabien Potencier <fabien@symfony.com> * @author Bulat Shakirzyanov <bulat@theopenskyproject.com> */ interface ExceptionInterface extends ContainerExceptionInterface, \Throwable { } Exception/InvalidArgumentException.php 0000644 00000001031 15120140333 0014146 0 ustar 00 <?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\Exception; /** * Base InvalidArgumentException for Dependency Injection component. * * @author Bulat Shakirzyanov <bulat@theopenskyproject.com> */ class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } Exception/InvalidParameterTypeException.php 0000644 00000002472 15120140333 0015160 0 ustar 00 <?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\Exception; /** * Thrown when trying to inject a parameter into a constructor/method with an incompatible type. * * @author Nicolas Grekas <p@tchwork.com> * @author Julien Maulny <jmaulny@darkmira.fr> */ class InvalidParameterTypeException extends InvalidArgumentException { public function __construct(string $serviceId, string $type, \ReflectionParameter $parameter) { $acceptedType = $parameter->getType(); $acceptedType = $acceptedType instanceof \ReflectionNamedType ? $acceptedType->getName() : (string) $acceptedType; $this->code = $type; $function = $parameter->getDeclaringFunction(); $functionName = $function instanceof \ReflectionMethod ? sprintf('%s::%s', $function->getDeclaringClass()->getName(), $function->getName()) : $function->getName(); parent::__construct(sprintf('Invalid definition for service "%s": argument %d of "%s()" accepts "%s", "%s" passed.', $serviceId, 1 + $parameter->getPosition(), $functionName, $acceptedType, $type)); } } Exception/LogicException.php 0000644 00000000674 15120140333 0012126 0 ustar 00 <?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\Exception; /** * Base LogicException for Dependency Injection component. */ class LogicException extends \LogicException implements ExceptionInterface { } Exception/OutOfBoundsException.php 0000644 00000000716 15120140333 0013275 0 ustar 00 <?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\Exception; /** * Base OutOfBoundsException for Dependency Injection component. */ class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface { } Exception/ParameterCircularReferenceException.php 0000644 00000001646 15120140333 0016315 0 ustar 00 <?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\Exception; /** * This exception is thrown when a circular reference in a parameter is detected. * * @author Fabien Potencier <fabien@symfony.com> */ class ParameterCircularReferenceException extends RuntimeException { private $parameters; public function __construct(array $parameters, \Throwable $previous = null) { parent::__construct(sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], implode('" > "', $parameters), $parameters[0]), 0, $previous); $this->parameters = $parameters; } public function getParameters() { return $this->parameters; } } Exception/ParameterNotFoundException.php 0000644 00000006463 15120140333 0014470 0 ustar 00 <?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\Exception; use Psr\Container\NotFoundExceptionInterface; /** * This exception is thrown when a non-existent parameter is used. * * @author Fabien Potencier <fabien@symfony.com> */ class ParameterNotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface { private $key; private $sourceId; private $sourceKey; private $alternatives; private $nonNestedAlternative; /** * @param string $key The requested parameter key * @param string|null $sourceId The service id that references the non-existent parameter * @param string|null $sourceKey The parameter key that references the non-existent parameter * @param \Throwable|null $previous The previous exception * @param string[] $alternatives Some parameter name alternatives * @param string|null $nonNestedAlternative The alternative parameter name when the user expected dot notation for nested parameters */ public function __construct(string $key, string $sourceId = null, string $sourceKey = null, \Throwable $previous = null, array $alternatives = [], string $nonNestedAlternative = null) { $this->key = $key; $this->sourceId = $sourceId; $this->sourceKey = $sourceKey; $this->alternatives = $alternatives; $this->nonNestedAlternative = $nonNestedAlternative; parent::__construct('', 0, $previous); $this->updateRepr(); } public function updateRepr() { if (null !== $this->sourceId) { $this->message = sprintf('The service "%s" has a dependency on a non-existent parameter "%s".', $this->sourceId, $this->key); } elseif (null !== $this->sourceKey) { $this->message = sprintf('The parameter "%s" has a dependency on a non-existent parameter "%s".', $this->sourceKey, $this->key); } else { $this->message = sprintf('You have requested a non-existent parameter "%s".', $this->key); } if ($this->alternatives) { if (1 == \count($this->alternatives)) { $this->message .= ' Did you mean this: "'; } else { $this->message .= ' Did you mean one of these: "'; } $this->message .= implode('", "', $this->alternatives).'"?'; } elseif (null !== $this->nonNestedAlternative) { $this->message .= ' You cannot access nested array items, do you want to inject "'.$this->nonNestedAlternative.'" instead?'; } } public function getKey() { return $this->key; } public function getSourceId() { return $this->sourceId; } public function getSourceKey() { return $this->sourceKey; } public function setSourceId(?string $sourceId) { $this->sourceId = $sourceId; $this->updateRepr(); } public function setSourceKey(?string $sourceKey) { $this->sourceKey = $sourceKey; $this->updateRepr(); } } Exception/RuntimeException.php 0000644 00000000773 15120140333 0012514 0 ustar 00 <?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\Exception; /** * Base RuntimeException for Dependency Injection component. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class RuntimeException extends \RuntimeException implements ExceptionInterface { } Exception/ServiceCircularReferenceException.php 0000644 00000001772 15120140333 0015775 0 ustar 00 <?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\Exception; /** * This exception is thrown when a circular reference is detected. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ServiceCircularReferenceException extends RuntimeException { private $serviceId; private $path; public function __construct(string $serviceId, array $path, \Throwable $previous = null) { parent::__construct(sprintf('Circular reference detected for service "%s", path: "%s".', $serviceId, implode(' -> ', $path)), 0, $previous); $this->serviceId = $serviceId; $this->path = $path; } public function getServiceId() { return $this->serviceId; } public function getPath() { return $this->path; } } Exception/ServiceNotFoundException.php 0000644 00000003407 15120140333 0014143 0 ustar 00 <?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\Exception; use Psr\Container\NotFoundExceptionInterface; /** * This exception is thrown when a non-existent service is requested. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ServiceNotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface { private $id; private $sourceId; private $alternatives; public function __construct(string $id, string $sourceId = null, \Throwable $previous = null, array $alternatives = [], string $msg = null) { if (null !== $msg) { // no-op } elseif (null === $sourceId) { $msg = sprintf('You have requested a non-existent service "%s".', $id); } else { $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s".', $sourceId, $id); } if ($alternatives) { if (1 == \count($alternatives)) { $msg .= ' Did you mean this: "'; } else { $msg .= ' Did you mean one of these: "'; } $msg .= implode('", "', $alternatives).'"?'; } parent::__construct($msg, 0, $previous); $this->id = $id; $this->sourceId = $sourceId; $this->alternatives = $alternatives; } public function getId() { return $this->id; } public function getSourceId() { return $this->sourceId; } public function getAlternatives() { return $this->alternatives; } } Extension/ConfigurationExtensionInterface.php 0000644 00000001450 15120140333 0015546 0 ustar 00 <?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\Extension; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * ConfigurationExtensionInterface is the interface implemented by container extension classes. * * @author Kevin Bond <kevinbond@gmail.com> */ interface ConfigurationExtensionInterface { /** * Returns extension configuration. * * @return ConfigurationInterface|null */ public function getConfiguration(array $config, ContainerBuilder $container); } Extension/Extension.php 0000644 00000010020 15120140333 0011166 0 ustar 00 <?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\Extension; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Processor; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; /** * Provides useful features shared by many extensions. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface { private $processedConfigs = []; /** * {@inheritdoc} */ public function getXsdValidationBasePath() { return false; } /** * {@inheritdoc} */ public function getNamespace() { return 'http://example.org/schema/dic/'.$this->getAlias(); } /** * Returns the recommended alias to use in XML. * * This alias is also the mandatory prefix to use when using YAML. * * This convention is to remove the "Extension" postfix from the class * name and then lowercase and underscore the result. So: * * AcmeHelloExtension * * becomes * * acme_hello * * This can be overridden in a sub-class to specify the alias manually. * * @return string * * @throws BadMethodCallException When the extension name does not follow conventions */ public function getAlias() { $className = static::class; if (!str_ends_with($className, 'Extension')) { throw new BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.'); } $classBaseName = substr(strrchr($className, '\\'), 1, -9); return Container::underscore($classBaseName); } /** * {@inheritdoc} */ public function getConfiguration(array $config, ContainerBuilder $container) { $class = static::class; if (str_contains($class, "\0")) { return null; // ignore anonymous classes } $class = substr_replace($class, '\Configuration', strrpos($class, '\\')); $class = $container->getReflectionClass($class); if (!$class) { return null; } if (!$class->implementsInterface(ConfigurationInterface::class)) { throw new LogicException(sprintf('The extension configuration class "%s" must implement "%s".', $class->getName(), ConfigurationInterface::class)); } if (!($constructor = $class->getConstructor()) || !$constructor->getNumberOfRequiredParameters()) { return $class->newInstance(); } return null; } final protected function processConfiguration(ConfigurationInterface $configuration, array $configs): array { $processor = new Processor(); return $this->processedConfigs[] = $processor->processConfiguration($configuration, $configs); } /** * @internal */ final public function getProcessedConfigs(): array { try { return $this->processedConfigs; } finally { $this->processedConfigs = []; } } /** * @return bool * * @throws InvalidArgumentException When the config is not enableable */ protected function isConfigEnabled(ContainerBuilder $container, array $config) { if (!\array_key_exists('enabled', $config)) { throw new InvalidArgumentException("The config array has no 'enabled' key."); } return (bool) $container->getParameterBag()->resolveValue($config['enabled']); } } Extension/ExtensionInterface.php 0000644 00000002372 15120140333 0013022 0 ustar 00 <?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\Extension; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * ExtensionInterface is the interface implemented by container extension classes. * * @author Fabien Potencier <fabien@symfony.com> */ interface ExtensionInterface { /** * Loads a specific configuration. * * @throws \InvalidArgumentException When provided tag is not defined in this extension */ public function load(array $configs, ContainerBuilder $container); /** * Returns the namespace to be used for this extension (XML namespace). * * @return string */ public function getNamespace(); /** * Returns the base path for the XSD files. * * @return string|false */ public function getXsdValidationBasePath(); /** * Returns the recommended alias to use in XML. * * This alias is also the mandatory prefix to use when using YAML. * * @return string */ public function getAlias(); } Extension/PrependExtensionInterface.php 0000644 00000001034 15120140333 0014332 0 ustar 00 <?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\Extension; use Symfony\Component\DependencyInjection\ContainerBuilder; interface PrependExtensionInterface { /** * Allow an extension to prepend the extension configurations. */ public function prepend(ContainerBuilder $container); } LazyProxy/Instantiator/InstantiatorInterface.php 0000644 00000002115 15120140333 0016204 0 ustar 00 <?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\LazyProxy\Instantiator; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; /** * Lazy proxy instantiator, capable of instantiating a proxy given a container, the * service definitions and a callback that produces the real service instance. * * @author Marco Pivetta <ocramius@gmail.com> */ interface InstantiatorInterface { /** * Instantiates a proxy object. * * @param string $id Identifier of the requested service * @param callable $realInstantiator Zero-argument callback that is capable of producing the real service instance * * @return object */ public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator); } LazyProxy/Instantiator/RealServiceInstantiator.php 0000644 00000001551 15120140333 0016513 0 ustar 00 <?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\LazyProxy\Instantiator; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; /** * {@inheritdoc} * * Noop proxy instantiator - produces the real service instead of a proxy instance. * * @author Marco Pivetta <ocramius@gmail.com> */ class RealServiceInstantiator implements InstantiatorInterface { /** * {@inheritdoc} */ public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator) { return $realInstantiator(); } } LazyProxy/PhpDumper/DumperInterface.php 0000644 00000002173 15120140333 0014212 0 ustar 00 <?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\LazyProxy\PhpDumper; use Symfony\Component\DependencyInjection\Definition; /** * Lazy proxy dumper capable of generating the instantiation logic PHP code for proxied services. * * @author Marco Pivetta <ocramius@gmail.com> */ interface DumperInterface { /** * Inspects whether the given definitions should produce proxy instantiation logic in the dumped container. * * @return bool */ public function isProxyCandidate(Definition $definition); /** * Generates the code to be used to instantiate a proxy in the dumped factory code. * * @return string */ public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode); /** * Generates the code for the lazy proxy. * * @return string */ public function getProxyCode(Definition $definition); } LazyProxy/PhpDumper/NullDumper.php 0000644 00000001763 15120140333 0013230 0 ustar 00 <?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\LazyProxy\PhpDumper; use Symfony\Component\DependencyInjection\Definition; /** * Null dumper, negates any proxy code generation for any given service definition. * * @author Marco Pivetta <ocramius@gmail.com> * * @final */ class NullDumper implements DumperInterface { /** * {@inheritdoc} */ public function isProxyCandidate(Definition $definition): bool { return false; } /** * {@inheritdoc} */ public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode): string { return ''; } /** * {@inheritdoc} */ public function getProxyCode(Definition $definition): string { return ''; } } LazyProxy/ProxyHelper.php 0000644 00000005346 15120140333 0011517 0 ustar 00 <?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\LazyProxy; /** * @author Nicolas Grekas <p@tchwork.com> * * @internal */ class ProxyHelper { /** * @return string|null The FQCN or builtin name of the type hint, or null when the type hint references an invalid self|parent context */ public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionParameter $p = null, bool $noBuiltin = false): ?string { if ($p instanceof \ReflectionParameter) { $type = $p->getType(); } else { $type = $r->getReturnType(); } if (!$type) { return null; } return self::getTypeHintForType($type, $r, $noBuiltin); } private static function getTypeHintForType(\ReflectionType $type, \ReflectionFunctionAbstract $r, bool $noBuiltin): ?string { $types = []; $glue = '|'; if ($type instanceof \ReflectionUnionType) { $reflectionTypes = $type->getTypes(); } elseif ($type instanceof \ReflectionIntersectionType) { $reflectionTypes = $type->getTypes(); $glue = '&'; } elseif ($type instanceof \ReflectionNamedType) { $reflectionTypes = [$type]; } else { return null; } foreach ($reflectionTypes as $type) { if ($type instanceof \ReflectionIntersectionType) { $typeHint = self::getTypeHintForType($type, $r, $noBuiltin); if (null === $typeHint) { return null; } $types[] = sprintf('(%s)', $typeHint); continue; } if ($type->isBuiltin()) { if (!$noBuiltin) { $types[] = $type->getName(); } continue; } $lcName = strtolower($type->getName()); $prefix = $noBuiltin ? '' : '\\'; if ('self' !== $lcName && 'parent' !== $lcName) { $types[] = $prefix.$type->getName(); continue; } if (!$r instanceof \ReflectionMethod) { continue; } if ('self' === $lcName) { $types[] = $prefix.$r->getDeclaringClass()->name; } else { $types[] = ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null; } } sort($types); return $types ? implode($glue, $types) : null; } } Loader/Configurator/Traits/AbstractTrait.php 0000644 00000001211 15120140333 0015125 0 ustar 00 <?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\Configurator\Traits; trait AbstractTrait { /** * Whether this definition is abstract, that means it merely serves as a * template for other definitions. * * @return $this */ final public function abstract(bool $abstract = true): self { $this->definition->setAbstract($abstract); return $this; } } Loader/Configurator/Traits/ArgumentTrait.php 0000644 00000001734 15120140333 0015156 0 ustar 00 <?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\Configurator\Traits; trait ArgumentTrait { /** * Sets the arguments to pass to the service constructor/factory method. * * @return $this */ final public function args(array $arguments): self { $this->definition->setArguments(static::processValue($arguments, true)); return $this; } /** * Sets one argument to pass to the service constructor/factory method. * * @param string|int $key * @param mixed $value * * @return $this */ final public function arg($key, $value): self { $this->definition->setArgument($key, static::processValue($value, true)); return $this; } } Loader/Configurator/Traits/AutoconfigureTrait.php 0000644 00000001446 15120140334 0016207 0 ustar 00 <?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\Configurator\Traits; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; trait AutoconfigureTrait { /** * Sets whether or not instanceof conditionals should be prepended with a global set. * * @return $this * * @throws InvalidArgumentException when a parent is already set */ final public function autoconfigure(bool $autoconfigured = true): self { $this->definition->setAutoconfigured($autoconfigured); return $this; } } Loader/Configurator/Traits/AutowireTrait.php 0000644 00000001074 15120140334 0015171 0 ustar 00 <?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\Configurator\Traits; trait AutowireTrait { /** * Enables/disables autowiring. * * @return $this */ final public function autowire(bool $autowired = true): self { $this->definition->setAutowired($autowired); return $this; } } Loader/Configurator/Traits/BindTrait.php 0000644 00000003027 15120140334 0014246 0 ustar 00 <?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\Configurator\Traits; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Loader\Configurator\DefaultsConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\InstanceofConfigurator; trait BindTrait { /** * Sets bindings. * * Bindings map $named or FQCN arguments to values that should be * injected in the matching parameters (of the constructor, of methods * called and of controller actions). * * @param string $nameOrFqcn A parameter name with its "$" prefix, or an FQCN * @param mixed $valueOrRef The value or reference to bind * * @return $this */ final public function bind(string $nameOrFqcn, $valueOrRef): self { $valueOrRef = static::processValue($valueOrRef, true); $bindings = $this->definition->getBindings(); $type = $this instanceof DefaultsConfigurator ? BoundArgument::DEFAULTS_BINDING : ($this instanceof InstanceofConfigurator ? BoundArgument::INSTANCEOF_BINDING : BoundArgument::SERVICE_BINDING); $bindings[$nameOrFqcn] = new BoundArgument($valueOrRef, true, $type, $this->path ?? null); $this->definition->setBindings($bindings); return $this; } } Loader/Configurator/Traits/CallTrait.php 0000644 00000002056 15120140334 0014246 0 ustar 00 <?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\Configurator\Traits; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; trait CallTrait { /** * Adds a method to call after service initialization. * * @param string $method The method name to call * @param array $arguments An array of arguments to pass to the method call * @param bool $returnsClone Whether the call returns the service instance or not * * @return $this * * @throws InvalidArgumentException on empty $method param */ final public function call(string $method, array $arguments = [], bool $returnsClone = false): self { $this->definition->addMethodCall($method, static::processValue($arguments, true), $returnsClone); return $this; } } Loader/Configurator/Traits/ClassTrait.php 0000644 00000001041 15120140334 0014431 0 ustar 00 <?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\Configurator\Traits; trait ClassTrait { /** * Sets the service class. * * @return $this */ final public function class(?string $class): self { $this->definition->setClass($class); return $this; } } Loader/Configurator/Traits/ConfiguratorTrait.php 0000644 00000001315 15120140334 0016032 0 ustar 00 <?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\Configurator\Traits; trait ConfiguratorTrait { /** * Sets a configurator to call after the service is fully initialized. * * @param string|array $configurator A PHP callable reference * * @return $this */ final public function configurator($configurator): self { $this->definition->setConfigurator(static::processValue($configurator, true)); return $this; } } Loader/Configurator/Traits/DecorateTrait.php 0000644 00000002115 15120140334 0015115 0 ustar 00 <?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\Configurator\Traits; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; trait DecorateTrait { /** * Sets the service that this service is decorating. * * @param string|null $id The decorated service id, use null to remove decoration * * @return $this * * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals */ final public function decorate(?string $id, string $renamedId = null, int $priority = 0, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): self { $this->definition->setDecoratedService($id, $renamedId, $priority, $invalidBehavior); return $this; } } Loader/Configurator/Traits/DeprecateTrait.php 0000644 00000003157 15120140334 0015272 0 ustar 00 <?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\Configurator\Traits; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; trait DeprecateTrait { /** * Whether this definition is deprecated, that means it should not be called anymore. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message The deprecation message to use * * @return $this * * @throws InvalidArgumentException when the message template is invalid */ final public function deprecate(/* string $package, string $version, string $message */): self { $args = \func_get_args(); $package = $version = $message = ''; if (\func_num_args() < 3) { trigger_deprecation('symfony/dependency-injection', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); $message = (string) ($args[0] ?? null); } else { $package = (string) $args[0]; $version = (string) $args[1]; $message = (string) $args[2]; } $this->definition->setDeprecated($package, $version, $message); return $this; } } Loader/Configurator/Traits/FactoryTrait.php 0000644 00000002275 15120140334 0015005 0 ustar 00 <?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\Configurator\Traits; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator; trait FactoryTrait { /** * Sets a factory. * * @param string|array|ReferenceConfigurator $factory A PHP callable reference * * @return $this */ final public function factory($factory): self { if (\is_string($factory) && 1 === substr_count($factory, ':')) { $factoryParts = explode(':', $factory); throw new InvalidArgumentException(sprintf('Invalid factory "%s": the "service:method" notation is not available when using PHP-based DI configuration. Use "[service(\'%s\'), \'%s\']" instead.', $factory, $factoryParts[0], $factoryParts[1])); } $this->definition->setFactory(static::processValue($factory, true)); return $this; } } Loader/Configurator/Traits/FileTrait.php 0000644 00000001067 15120140334 0014253 0 ustar 00 <?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\Configurator\Traits; trait FileTrait { /** * Sets a file to require before creating the service. * * @return $this */ final public function file(string $file): self { $this->definition->setFile($file); return $this; } } Loader/Configurator/Traits/LazyTrait.php 0000644 00000001447 15120140334 0014315 0 ustar 00 <?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\Configurator\Traits; trait LazyTrait { /** * Sets the lazy flag of this service. * * @param bool|string $lazy A FQCN to derivate the lazy proxy from or `true` to make it extend from the definition's class * * @return $this */ final public function lazy($lazy = true): self { $this->definition->setLazy((bool) $lazy); if (\is_string($lazy)) { $this->definition->addTag('proxy', ['interface' => $lazy]); } return $this; } } Loader/Configurator/Traits/ParentTrait.php 0000644 00000002660 15120140334 0014625 0 ustar 00 <?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\Configurator\Traits; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; trait ParentTrait { /** * Sets the Definition to inherit from. * * @return $this * * @throws InvalidArgumentException when parent cannot be set */ final public function parent(string $parent): self { if (!$this->allowParent) { throw new InvalidArgumentException(sprintf('A parent cannot be defined when either "_instanceof" or "_defaults" are also defined for service prototype "%s".', $this->id)); } if ($this->definition instanceof ChildDefinition) { $this->definition->setParent($parent); } else { // cast Definition to ChildDefinition $definition = serialize($this->definition); $definition = substr_replace($definition, '53', 2, 2); $definition = substr_replace($definition, 'Child', 44, 0); $definition = unserialize($definition); $this->definition = $definition->setParent($parent); } return $this; } } Loader/Configurator/Traits/PropertyTrait.php 0000644 00000001125 15120140334 0015213 0 ustar 00 <?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\Configurator\Traits; trait PropertyTrait { /** * Sets a specific property. * * @return $this */ final public function property(string $name, $value): self { $this->definition->setProperty($name, static::processValue($value, true)); return $this; } } Loader/Configurator/Traits/PublicTrait.php 0000644 00000001216 15120140334 0014606 0 ustar 00 <?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\Configurator\Traits; trait PublicTrait { /** * @return $this */ final public function public(): self { $this->definition->setPublic(true); return $this; } /** * @return $this */ final public function private(): self { $this->definition->setPublic(false); return $this; } } Loader/Configurator/Traits/ShareTrait.php 0000644 00000001073 15120140334 0014433 0 ustar 00 <?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\Configurator\Traits; trait ShareTrait { /** * Sets if the service must be shared or not. * * @return $this */ final public function share(bool $shared = true): self { $this->definition->setShared($shared); return $this; } } Loader/Configurator/Traits/SyntheticTrait.php 0000644 00000001227 15120140334 0015344 0 ustar 00 <?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\Configurator\Traits; trait SyntheticTrait { /** * Sets whether this definition is synthetic, that is not constructed by the * container, but dynamically injected. * * @return $this */ final public function synthetic(bool $synthetic = true): self { $this->definition->setSynthetic($synthetic); return $this; } } Loader/Configurator/Traits/TagTrait.php 0000644 00000002172 15120140334 0014105 0 ustar 00 <?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\Configurator\Traits; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; trait TagTrait { /** * Adds a tag for this definition. * * @return $this */ final public function tag(string $name, array $attributes = []): self { if ('' === $name) { throw new InvalidArgumentException(sprintf('The tag name for service "%s" must be a non-empty string.', $this->id)); } foreach ($attributes as $attribute => $value) { if (!\is_scalar($value) && null !== $value) { throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $this->id, $name, $attribute)); } } $this->definition->addTag($name, $attributes); return $this; } } Loader/Configurator/AbstractConfigurator.php 0000644 00000007524 15120140334 0015254 0 ustar 00 <?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\Configurator; use Symfony\Component\Config\Loader\ParamConfigurator; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; abstract class AbstractConfigurator { public const FACTORY = 'unknown'; /** * @var callable(mixed, bool)|null */ public static $valuePreProcessor; /** @internal */ protected $definition; public function __call(string $method, array $args) { if (method_exists($this, 'set'.$method)) { return $this->{'set'.$method}(...$args); } throw new \BadMethodCallException(sprintf('Call to undefined method "%s::%s()".', static::class, $method)); } /** * @return array */ public function __sleep() { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } public function __wakeup() { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } /** * Checks that a value is valid, optionally replacing Definition and Reference configurators by their configure value. * * @param mixed $value * @param bool $allowServices whether Definition and Reference are allowed; by default, only scalars and arrays are * * @return mixed the value, optionally cast to a Definition/Reference */ public static function processValue($value, $allowServices = false) { if (\is_array($value)) { foreach ($value as $k => $v) { $value[$k] = static::processValue($v, $allowServices); } return self::$valuePreProcessor ? (self::$valuePreProcessor)($value, $allowServices) : $value; } if (self::$valuePreProcessor) { $value = (self::$valuePreProcessor)($value, $allowServices); } if ($value instanceof ReferenceConfigurator) { $reference = new Reference($value->id, $value->invalidBehavior); return $value instanceof ClosureReferenceConfigurator ? new ServiceClosureArgument($reference) : $reference; } if ($value instanceof InlineServiceConfigurator) { $def = $value->definition; $value->definition = null; return $def; } if ($value instanceof ParamConfigurator) { return (string) $value; } if ($value instanceof self) { throw new InvalidArgumentException(sprintf('"%s()" can be used only at the root of service configuration files.', $value::FACTORY)); } switch (true) { case null === $value: case \is_scalar($value): return $value; case $value instanceof ArgumentInterface: case $value instanceof Definition: case $value instanceof Expression: case $value instanceof Parameter: case $value instanceof AbstractArgument: case $value instanceof Reference: if ($allowServices) { return $value; } } throw new InvalidArgumentException(sprintf('Cannot use values of type "%s" in service configuration files.', get_debug_type($value))); } } Loader/Configurator/AbstractServiceConfigurator.php 0000644 00000005660 15120140334 0016574 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; abstract class AbstractServiceConfigurator extends AbstractConfigurator { protected $parent; protected $id; private $defaultTags = []; public function __construct(ServicesConfigurator $parent, Definition $definition, string $id = null, array $defaultTags = []) { $this->parent = $parent; $this->definition = $definition; $this->id = $id; $this->defaultTags = $defaultTags; } public function __destruct() { // default tags should be added last foreach ($this->defaultTags as $name => $attributes) { foreach ($attributes as $attribute) { $this->definition->addTag($name, $attribute); } } $this->defaultTags = []; } /** * Registers a service. */ final public function set(?string $id, string $class = null): ServiceConfigurator { $this->__destruct(); return $this->parent->set($id, $class); } /** * Creates an alias. */ final public function alias(string $id, string $referencedId): AliasConfigurator { $this->__destruct(); return $this->parent->alias($id, $referencedId); } /** * Registers a PSR-4 namespace using a glob pattern. */ final public function load(string $namespace, string $resource): PrototypeConfigurator { $this->__destruct(); return $this->parent->load($namespace, $resource); } /** * Gets an already defined service definition. * * @throws ServiceNotFoundException if the service definition does not exist */ final public function get(string $id): ServiceConfigurator { $this->__destruct(); return $this->parent->get($id); } /** * Removes an already defined service definition or alias. */ final public function remove(string $id): ServicesConfigurator { $this->__destruct(); return $this->parent->remove($id); } /** * Registers a stack of decorator services. * * @param InlineServiceConfigurator[]|ReferenceConfigurator[] $services */ final public function stack(string $id, array $services): AliasConfigurator { $this->__destruct(); return $this->parent->stack($id, $services); } /** * Registers a service. */ final public function __invoke(string $id, string $class = null): ServiceConfigurator { $this->__destruct(); return $this->parent->set($id, $class); } } Loader/Configurator/AliasConfigurator.php 0000644 00000001326 15120140334 0014534 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\Alias; /** * @author Nicolas Grekas <p@tchwork.com> */ class AliasConfigurator extends AbstractServiceConfigurator { use Traits\DeprecateTrait; use Traits\PublicTrait; public const FACTORY = 'alias'; public function __construct(ServicesConfigurator $parent, Alias $alias) { $this->parent = $parent; $this->definition = $alias; } } Loader/Configurator/ClosureReferenceConfigurator.php 0000644 00000000571 15120140334 0016737 0 ustar 00 <?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\Configurator; class ClosureReferenceConfigurator extends ReferenceConfigurator { } Loader/Configurator/ContainerConfigurator.php 0000644 00000014255 15120140334 0015432 0 ustar 00 <?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\Configurator; use Symfony\Component\Config\Loader\ParamConfigurator; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\ExpressionLanguage\Expression; /** * @author Nicolas Grekas <p@tchwork.com> */ class ContainerConfigurator extends AbstractConfigurator { public const FACTORY = 'container'; private $container; private $loader; private $instanceof; private $path; private $file; private $anonymousCount = 0; private $env; public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path, string $file, string $env = null) { $this->container = $container; $this->loader = $loader; $this->instanceof = &$instanceof; $this->path = $path; $this->file = $file; $this->env = $env; } final public function extension(string $namespace, array $config) { if (!$this->container->hasExtension($namespace)) { $extensions = array_filter(array_map(function (ExtensionInterface $ext) { return $ext->getAlias(); }, $this->container->getExtensions())); throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $this->file, $namespace, $extensions ? implode('", "', $extensions) : 'none')); } $this->container->loadFromExtension($namespace, static::processValue($config)); } final public function import(string $resource, string $type = null, $ignoreErrors = false) { $this->loader->setCurrentDir(\dirname($this->path)); $this->loader->import($resource, $type, $ignoreErrors, $this->file); } final public function parameters(): ParametersConfigurator { return new ParametersConfigurator($this->container); } final public function services(): ServicesConfigurator { return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount); } /** * Get the current environment to be able to write conditional configuration. */ final public function env(): ?string { return $this->env; } /** * @return static */ final public function withPath(string $path): self { $clone = clone $this; $clone->path = $clone->file = $path; $clone->loader->setCurrentDir(\dirname($path)); return $clone; } } /** * Creates a parameter. */ function param(string $name): ParamConfigurator { return new ParamConfigurator($name); } /** * Creates a service reference. * * @deprecated since Symfony 5.1, use service() instead. */ function ref(string $id): ReferenceConfigurator { trigger_deprecation('symfony/dependency-injection', '5.1', '"%s()" is deprecated, use "service()" instead.', __FUNCTION__); return new ReferenceConfigurator($id); } /** * Creates a reference to a service. */ function service(string $serviceId): ReferenceConfigurator { return new ReferenceConfigurator($serviceId); } /** * Creates an inline service. * * @deprecated since Symfony 5.1, use inline_service() instead. */ function inline(string $class = null): InlineServiceConfigurator { trigger_deprecation('symfony/dependency-injection', '5.1', '"%s()" is deprecated, use "inline_service()" instead.', __FUNCTION__); return new InlineServiceConfigurator(new Definition($class)); } /** * Creates an inline service. */ function inline_service(string $class = null): InlineServiceConfigurator { return new InlineServiceConfigurator(new Definition($class)); } /** * Creates a service locator. * * @param ReferenceConfigurator[] $values */ function service_locator(array $values): ServiceLocatorArgument { return new ServiceLocatorArgument(AbstractConfigurator::processValue($values, true)); } /** * Creates a lazy iterator. * * @param ReferenceConfigurator[] $values */ function iterator(array $values): IteratorArgument { return new IteratorArgument(AbstractConfigurator::processValue($values, true)); } /** * Creates a lazy iterator by tag name. */ function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null): TaggedIteratorArgument { return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, false, $defaultPriorityMethod); } /** * Creates a service locator by tag name. */ function tagged_locator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null): ServiceLocatorArgument { return new ServiceLocatorArgument(new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, true, $defaultPriorityMethod)); } /** * Creates an expression. */ function expr(string $expression): Expression { return new Expression($expression); } /** * Creates an abstract argument. */ function abstract_arg(string $description): AbstractArgument { return new AbstractArgument($description); } /** * Creates an environment variable reference. */ function env(string $name): EnvConfigurator { return new EnvConfigurator($name); } /** * Creates a closure service reference. */ function service_closure(string $serviceId): ClosureReferenceConfigurator { return new ClosureReferenceConfigurator($serviceId); } Loader/Configurator/DefaultsConfigurator.php 0000644 00000003661 15120140334 0015256 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * @author Nicolas Grekas <p@tchwork.com> */ class DefaultsConfigurator extends AbstractServiceConfigurator { use Traits\AutoconfigureTrait; use Traits\AutowireTrait; use Traits\BindTrait; use Traits\PublicTrait; public const FACTORY = 'defaults'; private $path; public function __construct(ServicesConfigurator $parent, Definition $definition, string $path = null) { parent::__construct($parent, $definition, null, []); $this->path = $path; } /** * Adds a tag for this definition. * * @return $this * * @throws InvalidArgumentException when an invalid tag name or attribute is provided */ final public function tag(string $name, array $attributes = []): self { if ('' === $name) { throw new InvalidArgumentException('The tag name in "_defaults" must be a non-empty string.'); } foreach ($attributes as $attribute => $value) { if (null !== $value && !\is_scalar($value)) { throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type.', $name, $attribute)); } } $this->definition->addTag($name, $attributes); return $this; } /** * Defines an instanceof-conditional to be applied to following service definitions. */ final public function instanceof(string $fqcn): InstanceofConfigurator { return $this->parent->instanceof($fqcn); } } Loader/Configurator/EnvConfigurator.php 0000644 00000007376 15120140334 0014246 0 ustar 00 <?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\Configurator; use Symfony\Component\Config\Loader\ParamConfigurator; class EnvConfigurator extends ParamConfigurator { /** * @var string[] */ private $stack; public function __construct(string $name) { $this->stack = explode(':', $name); } public function __toString(): string { return '%env('.implode(':', $this->stack).')%'; } /** * @return $this */ public function __call(string $name, array $arguments): self { $processor = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $name)); $this->custom($processor, ...$arguments); return $this; } /** * @return $this */ public function custom(string $processor, ...$args): self { array_unshift($this->stack, $processor, ...$args); return $this; } /** * @return $this */ public function base64(): self { array_unshift($this->stack, 'base64'); return $this; } /** * @return $this */ public function bool(): self { array_unshift($this->stack, 'bool'); return $this; } /** * @return $this */ public function not(): self { array_unshift($this->stack, 'not'); return $this; } /** * @return $this */ public function const(): self { array_unshift($this->stack, 'const'); return $this; } /** * @return $this */ public function csv(): self { array_unshift($this->stack, 'csv'); return $this; } /** * @return $this */ public function file(): self { array_unshift($this->stack, 'file'); return $this; } /** * @return $this */ public function float(): self { array_unshift($this->stack, 'float'); return $this; } /** * @return $this */ public function int(): self { array_unshift($this->stack, 'int'); return $this; } /** * @return $this */ public function json(): self { array_unshift($this->stack, 'json'); return $this; } /** * @return $this */ public function key(string $key): self { array_unshift($this->stack, 'key', $key); return $this; } /** * @return $this */ public function url(): self { array_unshift($this->stack, 'url'); return $this; } /** * @return $this */ public function queryString(): self { array_unshift($this->stack, 'query_string'); return $this; } /** * @return $this */ public function resolve(): self { array_unshift($this->stack, 'resolve'); return $this; } /** * @return $this */ public function default(string $fallbackParam): self { array_unshift($this->stack, 'default', $fallbackParam); return $this; } /** * @return $this */ public function string(): self { array_unshift($this->stack, 'string'); return $this; } /** * @return $this */ public function trim(): self { array_unshift($this->stack, 'trim'); return $this; } /** * @return $this */ public function require(): self { array_unshift($this->stack, 'require'); return $this; } } Loader/Configurator/InlineServiceConfigurator.php 0000644 00000002003 15120140334 0016233 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\Definition; /** * @author Nicolas Grekas <p@tchwork.com> */ class InlineServiceConfigurator extends AbstractConfigurator { use Traits\ArgumentTrait; use Traits\AutowireTrait; use Traits\BindTrait; use Traits\CallTrait; use Traits\ConfiguratorTrait; use Traits\FactoryTrait; use Traits\FileTrait; use Traits\LazyTrait; use Traits\ParentTrait; use Traits\PropertyTrait; use Traits\TagTrait; public const FACTORY = 'service'; private $id = '[inline]'; private $allowParent = true; private $path = null; public function __construct(Definition $definition) { $this->definition = $definition; } } Loader/Configurator/InstanceofConfigurator.php 0000644 00000002332 15120140334 0015572 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\Definition; /** * @author Nicolas Grekas <p@tchwork.com> */ class InstanceofConfigurator extends AbstractServiceConfigurator { use Traits\AutowireTrait; use Traits\BindTrait; use Traits\CallTrait; use Traits\ConfiguratorTrait; use Traits\LazyTrait; use Traits\PropertyTrait; use Traits\PublicTrait; use Traits\ShareTrait; use Traits\TagTrait; public const FACTORY = 'instanceof'; private $path; public function __construct(ServicesConfigurator $parent, Definition $definition, string $id, string $path = null) { parent::__construct($parent, $definition, $id, []); $this->path = $path; } /** * Defines an instanceof-conditional to be applied to following service definitions. */ final public function instanceof(string $fqcn): self { return $this->parent->instanceof($fqcn); } } Loader/Configurator/ParametersConfigurator.php 0000644 00000002576 15120140334 0015616 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\ExpressionLanguage\Expression; /** * @author Nicolas Grekas <p@tchwork.com> */ class ParametersConfigurator extends AbstractConfigurator { public const FACTORY = 'parameters'; private $container; public function __construct(ContainerBuilder $container) { $this->container = $container; } /** * Creates a parameter. * * @return $this */ final public function set(string $name, $value): self { if ($value instanceof Expression) { throw new InvalidArgumentException(sprintf('Using an expression in parameter "%s" is not allowed.', $name)); } $this->container->setParameter($name, static::processValue($value, true)); return $this; } /** * Creates a parameter. * * @return $this */ final public function __invoke(string $name, $value): self { return $this->set($name, $value); } } Loader/Configurator/PrototypeConfigurator.php 0000644 00000004747 15120140334 0015522 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; /** * @author Nicolas Grekas <p@tchwork.com> */ class PrototypeConfigurator extends AbstractServiceConfigurator { use Traits\AbstractTrait; use Traits\ArgumentTrait; use Traits\AutoconfigureTrait; use Traits\AutowireTrait; use Traits\BindTrait; use Traits\CallTrait; use Traits\ConfiguratorTrait; use Traits\DeprecateTrait; use Traits\FactoryTrait; use Traits\LazyTrait; use Traits\ParentTrait; use Traits\PropertyTrait; use Traits\PublicTrait; use Traits\ShareTrait; use Traits\TagTrait; public const FACTORY = 'load'; private $loader; private $resource; private $excludes; private $allowParent; public function __construct(ServicesConfigurator $parent, PhpFileLoader $loader, Definition $defaults, string $namespace, string $resource, bool $allowParent) { $definition = new Definition(); if (!$defaults->isPublic() || !$defaults->isPrivate()) { $definition->setPublic($defaults->isPublic()); } $definition->setAutowired($defaults->isAutowired()); $definition->setAutoconfigured($defaults->isAutoconfigured()); // deep clone, to avoid multiple process of the same instance in the passes $definition->setBindings(unserialize(serialize($defaults->getBindings()))); $definition->setChanges([]); $this->loader = $loader; $this->resource = $resource; $this->allowParent = $allowParent; parent::__construct($parent, $definition, $namespace, $defaults->getTags()); } public function __destruct() { parent::__destruct(); if ($this->loader) { $this->loader->registerClasses($this->definition, $this->id, $this->resource, $this->excludes); } $this->loader = null; } /** * Excludes files from registration using glob patterns. * * @param string[]|string $excludes * * @return $this */ final public function exclude($excludes): self { $this->excludes = (array) $excludes; return $this; } } Loader/Configurator/ReferenceConfigurator.php 0000644 00000002644 15120140334 0015405 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\ContainerInterface; /** * @author Nicolas Grekas <p@tchwork.com> */ class ReferenceConfigurator extends AbstractConfigurator { /** @internal */ protected $id; /** @internal */ protected $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; public function __construct(string $id) { $this->id = $id; } /** * @return $this */ final public function ignoreOnInvalid(): self { $this->invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; return $this; } /** * @return $this */ final public function nullOnInvalid(): self { $this->invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; return $this; } /** * @return $this */ final public function ignoreOnUninitialized(): self { $this->invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; return $this; } /** * @return string */ public function __toString() { return $this->id; } } Loader/Configurator/ServiceConfigurator.php 0000644 00000003774 15120140334 0015114 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; /** * @author Nicolas Grekas <p@tchwork.com> */ class ServiceConfigurator extends AbstractServiceConfigurator { use Traits\AbstractTrait; use Traits\ArgumentTrait; use Traits\AutoconfigureTrait; use Traits\AutowireTrait; use Traits\BindTrait; use Traits\CallTrait; use Traits\ClassTrait; use Traits\ConfiguratorTrait; use Traits\DecorateTrait; use Traits\DeprecateTrait; use Traits\FactoryTrait; use Traits\FileTrait; use Traits\LazyTrait; use Traits\ParentTrait; use Traits\PropertyTrait; use Traits\PublicTrait; use Traits\ShareTrait; use Traits\SyntheticTrait; use Traits\TagTrait; public const FACTORY = 'services'; private $container; private $instanceof; private $allowParent; private $path; private $destructed = false; public function __construct(ContainerBuilder $container, array $instanceof, bool $allowParent, ServicesConfigurator $parent, Definition $definition, ?string $id, array $defaultTags, string $path = null) { $this->container = $container; $this->instanceof = $instanceof; $this->allowParent = $allowParent; $this->path = $path; parent::__construct($parent, $definition, $id, $defaultTags); } public function __destruct() { if ($this->destructed) { return; } $this->destructed = true; parent::__destruct(); $this->container->removeBindings($this->id); $this->container->setDefinition($this->id, $this->definition->setInstanceofConditionals($this->instanceof)); } } Loader/Configurator/ServicesConfigurator.php 0000644 00000015633 15120140334 0015274 0 ustar 00 <?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\Configurator; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; /** * @author Nicolas Grekas <p@tchwork.com> */ class ServicesConfigurator extends AbstractConfigurator { public const FACTORY = 'services'; private $defaults; private $container; private $loader; private $instanceof; private $path; private $anonymousHash; private $anonymousCount; public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path = null, int &$anonymousCount = 0) { $this->defaults = new Definition(); $this->container = $container; $this->loader = $loader; $this->instanceof = &$instanceof; $this->path = $path; $this->anonymousHash = ContainerBuilder::hash($path ?: mt_rand()); $this->anonymousCount = &$anonymousCount; $instanceof = []; } /** * Defines a set of defaults for following service definitions. */ final public function defaults(): DefaultsConfigurator { return new DefaultsConfigurator($this, $this->defaults = new Definition(), $this->path); } /** * Defines an instanceof-conditional to be applied to following service definitions. */ final public function instanceof(string $fqcn): InstanceofConfigurator { $this->instanceof[$fqcn] = $definition = new ChildDefinition(''); return new InstanceofConfigurator($this, $definition, $fqcn, $this->path); } /** * Registers a service. * * @param string|null $id The service id, or null to create an anonymous service * @param string|null $class The class of the service, or null when $id is also the class name */ final public function set(?string $id, string $class = null): ServiceConfigurator { $defaults = $this->defaults; $definition = new Definition(); if (null === $id) { if (!$class) { throw new \LogicException('Anonymous services must have a class name.'); } $id = sprintf('.%d_%s', ++$this->anonymousCount, preg_replace('/^.*\\\\/', '', $class).'~'.$this->anonymousHash); } elseif (!$defaults->isPublic() || !$defaults->isPrivate()) { $definition->setPublic($defaults->isPublic() && !$defaults->isPrivate()); } $definition->setAutowired($defaults->isAutowired()); $definition->setAutoconfigured($defaults->isAutoconfigured()); // deep clone, to avoid multiple process of the same instance in the passes $definition->setBindings(unserialize(serialize($defaults->getBindings()))); $definition->setChanges([]); $configurator = new ServiceConfigurator($this->container, $this->instanceof, true, $this, $definition, $id, $defaults->getTags(), $this->path); return null !== $class ? $configurator->class($class) : $configurator; } /** * Removes an already defined service definition or alias. * * @return $this */ final public function remove(string $id): self { $this->container->removeDefinition($id); $this->container->removeAlias($id); return $this; } /** * Creates an alias. */ final public function alias(string $id, string $referencedId): AliasConfigurator { $ref = static::processValue($referencedId, true); $alias = new Alias((string) $ref); if (!$this->defaults->isPublic() || !$this->defaults->isPrivate()) { $alias->setPublic($this->defaults->isPublic()); } $this->container->setAlias($id, $alias); return new AliasConfigurator($this, $alias); } /** * Registers a PSR-4 namespace using a glob pattern. */ final public function load(string $namespace, string $resource): PrototypeConfigurator { return new PrototypeConfigurator($this, $this->loader, $this->defaults, $namespace, $resource, true); } /** * Gets an already defined service definition. * * @throws ServiceNotFoundException if the service definition does not exist */ final public function get(string $id): ServiceConfigurator { $definition = $this->container->getDefinition($id); return new ServiceConfigurator($this->container, $definition->getInstanceofConditionals(), true, $this, $definition, $id, []); } /** * Registers a stack of decorator services. * * @param InlineServiceConfigurator[]|ReferenceConfigurator[] $services */ final public function stack(string $id, array $services): AliasConfigurator { foreach ($services as $i => $service) { if ($service instanceof InlineServiceConfigurator) { $definition = $service->definition->setInstanceofConditionals($this->instanceof); $changes = $definition->getChanges(); $definition->setAutowired((isset($changes['autowired']) ? $definition : $this->defaults)->isAutowired()); $definition->setAutoconfigured((isset($changes['autoconfigured']) ? $definition : $this->defaults)->isAutoconfigured()); $definition->setBindings(array_merge($this->defaults->getBindings(), $definition->getBindings())); $definition->setChanges($changes); $services[$i] = $definition; } elseif (!$service instanceof ReferenceConfigurator) { throw new InvalidArgumentException(sprintf('"%s()" expects a list of definitions as returned by "%s()" or "%s()", "%s" given at index "%s" for service "%s".', __METHOD__, InlineServiceConfigurator::FACTORY, ReferenceConfigurator::FACTORY, $service instanceof AbstractConfigurator ? $service::FACTORY.'()' : get_debug_type($service), $i, $id)); } } $alias = $this->alias($id, ''); $alias->definition = $this->set($id) ->parent('') ->args($services) ->tag('container.stack') ->definition; return $alias; } /** * Registers a service. */ final public function __invoke(string $id, string $class = null): ServiceConfigurator { return $this->set($id, $class); } public function __destruct() { $this->loader->registerAliasesForSinglyImplementedInterfaces(); } } Loader/schema/dic/services/services-1.0.xsd 0000644 00000034562 15120140334 0014436 0 ustar 00 <?xml version="1.0" encoding="UTF-8" ?> <xsd:schema xmlns="http://symfony.com/schema/dic/services" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://symfony.com/schema/dic/services" elementFormDefault="qualified"> <xsd:annotation> <xsd:documentation><![CDATA[ Symfony XML Services Schema, version 1.0 Authors: Fabien Potencier This defines a way to describe PHP objects (services) and their dependencies. ]]></xsd:documentation> </xsd:annotation> <xsd:element name="container" type="container" /> <xsd:complexType name="container"> <xsd:annotation> <xsd:documentation><![CDATA[ The root element of a service file. ]]></xsd:documentation> </xsd:annotation> <xsd:sequence> <xsd:group ref="foreign" /> <xsd:sequence minOccurs="0"> <xsd:element name="imports" type="imports" /> <xsd:group ref="foreign" /> </xsd:sequence> <xsd:sequence minOccurs="0"> <xsd:element name="parameters" type="parameters" /> <xsd:group ref="foreign" /> </xsd:sequence> <xsd:sequence minOccurs="0"> <xsd:element name="services" type="services" /> <xsd:group ref="foreign" /> </xsd:sequence> <xsd:sequence minOccurs="0" maxOccurs="unbounded"> <xsd:element name="when" type="when" /> </xsd:sequence> </xsd:sequence> </xsd:complexType> <xsd:complexType name="when"> <xsd:sequence> <xsd:group ref="foreign" /> <xsd:sequence minOccurs="0"> <xsd:element name="imports" type="imports" /> <xsd:group ref="foreign" /> </xsd:sequence> <xsd:sequence minOccurs="0"> <xsd:element name="parameters" type="parameters" /> <xsd:group ref="foreign" /> </xsd:sequence> <xsd:sequence minOccurs="0"> <xsd:element name="services" type="services" /> <xsd:group ref="foreign" /> </xsd:sequence> </xsd:sequence> <xsd:attribute name="env" type="xsd:string" use="required" /> </xsd:complexType> <xsd:group name="foreign"> <xsd:sequence> <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> </xsd:group> <xsd:complexType name="services"> <xsd:annotation> <xsd:documentation><![CDATA[ Enclosing element for the definition of all services ]]></xsd:documentation> </xsd:annotation> <xsd:choice maxOccurs="unbounded"> <xsd:element name="service" type="service" minOccurs="1" /> <xsd:element name="prototype" type="prototype" minOccurs="0" /> <xsd:element name="defaults" type="defaults" minOccurs="0" maxOccurs="1" /> <xsd:element name="instanceof" type="instanceof" minOccurs="0" /> <xsd:element name="stack" type="stack" minOccurs="0" /> </xsd:choice> </xsd:complexType> <xsd:complexType name="imports"> <xsd:annotation> <xsd:documentation><![CDATA[ Enclosing element for the import elements ]]></xsd:documentation> </xsd:annotation> <xsd:choice minOccurs="1" maxOccurs="unbounded"> <xsd:element name="import" type="import" /> </xsd:choice> </xsd:complexType> <xsd:complexType name="import"> <xsd:annotation> <xsd:documentation><![CDATA[ Import an external resource defining other services or parameters ]]></xsd:documentation> </xsd:annotation> <xsd:attribute name="resource" type="xsd:string" use="required" /> <xsd:attribute name="ignore-errors" type="ignore_errors" /> <xsd:attribute name="type" type="xsd:string" /> </xsd:complexType> <xsd:complexType name="callable"> <xsd:choice minOccurs="0" maxOccurs="1"> <xsd:element name="service" type="service" minOccurs="0" maxOccurs="1" /> </xsd:choice> <xsd:attribute name="service" type="xsd:string" /> <xsd:attribute name="class" type="xsd:string" /> <xsd:attribute name="method" type="xsd:string" /> <xsd:attribute name="function" type="xsd:string" /> </xsd:complexType> <xsd:complexType name="defaults"> <xsd:annotation> <xsd:documentation><![CDATA[ Enclosing element for the service definitions' defaults for the current file ]]></xsd:documentation> </xsd:annotation> <xsd:choice maxOccurs="unbounded"> <xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" /> </xsd:choice> <xsd:attribute name="public" type="boolean" /> <xsd:attribute name="autowire" type="boolean" /> <xsd:attribute name="autoconfigure" type="boolean" /> </xsd:complexType> <xsd:complexType name="service"> <xsd:choice maxOccurs="unbounded"> <xsd:element name="file" type="xsd:string" minOccurs="0" maxOccurs="1" /> <xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" /> <xsd:element name="factory" type="callable" minOccurs="0" maxOccurs="1" /> <xsd:element name="deprecated" type="deprecated" minOccurs="0" maxOccurs="1" /> <xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" /> </xsd:choice> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="class" type="xsd:string" /> <xsd:attribute name="shared" type="boolean" /> <xsd:attribute name="public" type="boolean" /> <xsd:attribute name="synthetic" type="boolean" /> <xsd:attribute name="lazy" type="xsd:string" /> <xsd:attribute name="abstract" type="boolean" /> <xsd:attribute name="alias" type="xsd:string" /> <xsd:attribute name="parent" type="xsd:string" /> <xsd:attribute name="decorates" type="xsd:string" /> <xsd:attribute name="decoration-on-invalid" type="invalid_decorated_service_sequence" /> <xsd:attribute name="decoration-inner-name" type="xsd:string" /> <xsd:attribute name="decoration-priority" type="xsd:integer" /> <xsd:attribute name="autowire" type="boolean" /> <xsd:attribute name="autoconfigure" type="boolean" /> </xsd:complexType> <xsd:complexType name="instanceof"> <xsd:choice maxOccurs="unbounded"> <xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" /> <xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" /> </xsd:choice> <xsd:attribute name="id" type="xsd:string" use="required" /> <xsd:attribute name="shared" type="boolean" /> <xsd:attribute name="public" type="boolean" /> <xsd:attribute name="lazy" type="xsd:string" /> <xsd:attribute name="autowire" type="boolean" /> <xsd:attribute name="autoconfigure" type="boolean" /> </xsd:complexType> <xsd:complexType name="prototype"> <xsd:choice maxOccurs="unbounded"> <xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" /> <xsd:element name="factory" type="callable" minOccurs="0" maxOccurs="1" /> <xsd:element name="deprecated" type="deprecated" minOccurs="0" maxOccurs="1" /> <xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="exclude" type="xsd:string" minOccurs="0" maxOccurs="unbounded" /> </xsd:choice> <xsd:attribute name="namespace" type="xsd:string" use="required" /> <xsd:attribute name="resource" type="xsd:string" use="required" /> <xsd:attribute name="exclude" type="xsd:string" /> <xsd:attribute name="shared" type="boolean" /> <xsd:attribute name="public" type="boolean" /> <xsd:attribute name="lazy" type="xsd:string" /> <xsd:attribute name="abstract" type="boolean" /> <xsd:attribute name="parent" type="xsd:string" /> <xsd:attribute name="autowire" type="boolean" /> <xsd:attribute name="autoconfigure" type="boolean" /> </xsd:complexType> <xsd:complexType name="stack"> <xsd:choice maxOccurs="unbounded"> <xsd:element name="service" type="service" minOccurs="1" /> <xsd:element name="deprecated" type="deprecated" minOccurs="0" maxOccurs="1" /> </xsd:choice> <xsd:attribute name="id" type="xsd:string" use="required" /> <xsd:attribute name="public" type="boolean" /> </xsd:complexType> <xsd:complexType name="tag"> <xsd:simpleContent> <xsd:extension base="xsd:string"> <xsd:anyAttribute namespace="##any" processContents="lax" /> </xsd:extension> </xsd:simpleContent> </xsd:complexType> <xsd:complexType name="deprecated"> <xsd:simpleContent> <xsd:extension base="xsd:string"> <!-- In Symfony 6, make these attributes required --> <xsd:attribute name="package" type="xsd:string" /> <xsd:attribute name="version" type="xsd:string" /> </xsd:extension> </xsd:simpleContent> </xsd:complexType> <xsd:complexType name="parameters"> <xsd:choice minOccurs="1" maxOccurs="unbounded"> <xsd:element name="parameter" type="parameter" /> </xsd:choice> <xsd:attribute name="type" type="parameter_type" /> <xsd:attribute name="key" type="xsd:string" /> </xsd:complexType> <xsd:complexType name="parameter" mixed="true"> <xsd:choice minOccurs="0" maxOccurs="unbounded"> <xsd:element name="parameter" type="parameter" /> </xsd:choice> <xsd:attribute name="type" type="parameter_type" /> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="key" type="xsd:string" /> <xsd:attribute name="on-invalid" type="invalid_sequence" /> </xsd:complexType> <xsd:complexType name="property" mixed="true"> <xsd:choice minOccurs="0"> <xsd:element name="property" type="property" maxOccurs="unbounded" /> <xsd:element name="service" type="service" /> </xsd:choice> <xsd:attribute name="type" type="argument_type" /> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="key" type="xsd:string" /> <xsd:attribute name="name" type="xsd:string" /> <xsd:attribute name="on-invalid" type="invalid_sequence" /> <xsd:attribute name="tag" type="xsd:string" /> </xsd:complexType> <xsd:complexType name="bind" mixed="true"> <xsd:choice maxOccurs="unbounded"> <xsd:element name="bind" type="argument" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="service" type="service" /> </xsd:choice> <xsd:attribute name="type" type="argument_type" /> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="key" type="xsd:string" use="required" /> <xsd:attribute name="on-invalid" type="invalid_sequence" /> <xsd:attribute name="method" type="xsd:string" /> <xsd:attribute name="tag" type="xsd:string" /> </xsd:complexType> <xsd:complexType name="argument" mixed="true"> <xsd:choice minOccurs="0"> <xsd:element name="argument" type="argument" maxOccurs="unbounded" /> <xsd:element name="service" type="service" /> </xsd:choice> <xsd:attribute name="type" type="argument_type" /> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="key" type="xsd:string" /> <xsd:attribute name="index" type="xsd:integer" /> <xsd:attribute name="on-invalid" type="invalid_sequence" /> <xsd:attribute name="tag" type="xsd:string" /> <xsd:attribute name="index-by" type="xsd:string" /> <xsd:attribute name="default-index-method" type="xsd:string" /> <xsd:attribute name="default-priority-method" type="xsd:string" /> </xsd:complexType> <xsd:complexType name="call"> <xsd:choice minOccurs="0"> <xsd:element name="argument" type="argument" maxOccurs="unbounded" /> </xsd:choice> <xsd:attribute name="method" type="xsd:string" /> <xsd:attribute name="returns-clone" type="boolean" /> </xsd:complexType> <xsd:simpleType name="parameter_type"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="collection" /> <xsd:enumeration value="string" /> <xsd:enumeration value="constant" /> <xsd:enumeration value="binary" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="argument_type"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="abstract" /> <xsd:enumeration value="collection" /> <xsd:enumeration value="service" /> <xsd:enumeration value="expression" /> <xsd:enumeration value="string" /> <xsd:enumeration value="constant" /> <xsd:enumeration value="binary" /> <xsd:enumeration value="iterator" /> <xsd:enumeration value="service_closure" /> <xsd:enumeration value="service_locator" /> <!-- "tagged" is an alias of "tagged_iterator", using "tagged_iterator" is preferred. --> <xsd:enumeration value="tagged" /> <xsd:enumeration value="tagged_iterator" /> <xsd:enumeration value="tagged_locator" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="ignore_errors"> <xsd:restriction base="xsd:string"> <xsd:pattern value="(true|false|not_found)" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="invalid_sequence"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="null" /> <xsd:enumeration value="ignore" /> <xsd:enumeration value="exception" /> <xsd:enumeration value="ignore_uninitialized" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="invalid_decorated_service_sequence"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="null" /> <xsd:enumeration value="ignore" /> <xsd:enumeration value="exception" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="boolean"> <xsd:restriction base="xsd:string"> <xsd:pattern value="(%.+%|true|false)" /> </xsd:restriction> </xsd:simpleType> </xsd:schema> Loader/ClosureLoader.php 0000644 00000002167 15120140334 0011225 0 ustar 00 <?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\Config\Loader\Loader; use Symfony\Component\DependencyInjection\ContainerBuilder; /** * ClosureLoader loads service definitions from a PHP closure. * * The Closure has access to the container as its first argument. * * @author Fabien Potencier <fabien@symfony.com> */ class ClosureLoader extends Loader { private $container; public function __construct(ContainerBuilder $container, string $env = null) { $this->container = $container; parent::__construct($env); } /** * {@inheritdoc} */ public function load($resource, string $type = null) { return $resource($this->container, $this->env); } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { return $resource instanceof \Closure; } } Loader/DirectoryLoader.php 0000644 00000002470 15120140334 0011552 0 ustar 00 <?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; /** * DirectoryLoader is a recursive loader to go through directories. * * @author Sebastien Lavoie <seb@wemakecustom.com> */ class DirectoryLoader extends FileLoader { /** * {@inheritdoc} */ public function load($file, string $type = null) { $file = rtrim($file, '/'); $path = $this->locator->locate($file); $this->container->fileExists($path, false); foreach (scandir($path) as $dir) { if ('.' !== $dir[0]) { if (is_dir($path.'/'.$dir)) { $dir .= '/'; // append / to allow recursion } $this->setCurrentDir($path); $this->import($dir, null, false, $path); } } return null; } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { if ('directory' === $type) { return true; } return null === $type && \is_string($resource) && str_ends_with($resource, '/'); } } Loader/FileLoader.php 0000644 00000024127 15120140334 0010470 0 ustar 00 <?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\Config\Exception\FileLocatorFileNotFoundException; use Symfony\Component\Config\Exception\LoaderLoadException; use Symfony\Component\Config\FileLocatorInterface; use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Config\Resource\GlobResource; use Symfony\Component\DependencyInjection\Attribute\When; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\RegisterAutoconfigureAttributesPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * FileLoader is the abstract class used by all built-in loaders that are file based. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class FileLoader extends BaseFileLoader { public const ANONYMOUS_ID_REGEXP = '/^\.\d+_[^~]*+~[._a-zA-Z\d]{7}$/'; protected $container; protected $isLoadingInstanceof = false; protected $instanceof = []; protected $interfaces = []; protected $singlyImplemented = []; protected $autoRegisterAliasesForSinglyImplementedInterfaces = true; public function __construct(ContainerBuilder $container, FileLocatorInterface $locator, string $env = null) { $this->container = $container; parent::__construct($locator, $env); } /** * {@inheritdoc} * * @param bool|string $ignoreErrors Whether errors should be ignored; pass "not_found" to ignore only when the loaded resource is not found */ public function import($resource, string $type = null, $ignoreErrors = false, string $sourceResource = null, $exclude = null) { $args = \func_get_args(); if ($ignoreNotFound = 'not_found' === $ignoreErrors) { $args[2] = false; } elseif (!\is_bool($ignoreErrors)) { throw new \TypeError(sprintf('Invalid argument $ignoreErrors provided to "%s::import()": boolean or "not_found" expected, "%s" given.', static::class, get_debug_type($ignoreErrors))); } try { return parent::import(...$args); } catch (LoaderLoadException $e) { if (!$ignoreNotFound || !($prev = $e->getPrevious()) instanceof FileLocatorFileNotFoundException) { throw $e; } foreach ($prev->getTrace() as $frame) { if ('import' === ($frame['function'] ?? null) && is_a($frame['class'] ?? '', Loader::class, true)) { break; } } if (__FILE__ !== $frame['file']) { throw $e; } } return null; } /** * Registers a set of classes as services using PSR-4 for discovery. * * @param Definition $prototype A definition to use as template * @param string $namespace The namespace prefix of classes in the scanned directory * @param string $resource The directory to look for classes, glob-patterns allowed * @param string|string[]|null $exclude A globbed path of files to exclude or an array of globbed paths of files to exclude */ public function registerClasses(Definition $prototype, string $namespace, string $resource, $exclude = null) { if (!str_ends_with($namespace, '\\')) { throw new InvalidArgumentException(sprintf('Namespace prefix must end with a "\\": "%s".', $namespace)); } if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+\\\\)++$/', $namespace)) { throw new InvalidArgumentException(sprintf('Namespace is not a valid PSR-4 prefix: "%s".', $namespace)); } $autoconfigureAttributes = new RegisterAutoconfigureAttributesPass(); $autoconfigureAttributes = $autoconfigureAttributes->accept($prototype) ? $autoconfigureAttributes : null; $classes = $this->findClasses($namespace, $resource, (array) $exclude, $autoconfigureAttributes); // prepare for deep cloning $serializedPrototype = serialize($prototype); foreach ($classes as $class => $errorMessage) { if (null === $errorMessage && $autoconfigureAttributes && $this->env) { $r = $this->container->getReflectionClass($class); $attribute = null; foreach ($r->getAttributes(When::class) as $attribute) { if ($this->env === $attribute->newInstance()->env) { $attribute = null; break; } } if (null !== $attribute) { continue; } } if (interface_exists($class, false)) { $this->interfaces[] = $class; } else { $this->setDefinition($class, $definition = unserialize($serializedPrototype)); if (null !== $errorMessage) { $definition->addError($errorMessage); continue; } foreach (class_implements($class, false) as $interface) { $this->singlyImplemented[$interface] = ($this->singlyImplemented[$interface] ?? $class) !== $class ? false : $class; } } } if ($this->autoRegisterAliasesForSinglyImplementedInterfaces) { $this->registerAliasesForSinglyImplementedInterfaces(); } } public function registerAliasesForSinglyImplementedInterfaces() { foreach ($this->interfaces as $interface) { if (!empty($this->singlyImplemented[$interface]) && !$this->container->has($interface)) { $this->container->setAlias($interface, $this->singlyImplemented[$interface]); } } $this->interfaces = $this->singlyImplemented = []; } /** * Registers a definition in the container with its instanceof-conditionals. */ protected function setDefinition(string $id, Definition $definition) { $this->container->removeBindings($id); if ($this->isLoadingInstanceof) { if (!$definition instanceof ChildDefinition) { throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, get_debug_type($definition))); } $this->instanceof[$id] = $definition; } else { $this->container->setDefinition($id, $definition->setInstanceofConditionals($this->instanceof)); } } private function findClasses(string $namespace, string $pattern, array $excludePatterns, ?RegisterAutoconfigureAttributesPass $autoconfigureAttributes): array { $parameterBag = $this->container->getParameterBag(); $excludePaths = []; $excludePrefix = null; $excludePatterns = $parameterBag->unescapeValue($parameterBag->resolveValue($excludePatterns)); foreach ($excludePatterns as $excludePattern) { foreach ($this->glob($excludePattern, true, $resource, true, true) as $path => $info) { if (null === $excludePrefix) { $excludePrefix = $resource->getPrefix(); } // normalize Windows slashes and remove trailing slashes $excludePaths[rtrim(str_replace('\\', '/', $path), '/')] = true; } } $pattern = $parameterBag->unescapeValue($parameterBag->resolveValue($pattern)); $classes = []; $extRegexp = '/\\.php$/'; $prefixLen = null; foreach ($this->glob($pattern, true, $resource, false, false, $excludePaths) as $path => $info) { if (null === $prefixLen) { $prefixLen = \strlen($resource->getPrefix()); if ($excludePrefix && !str_starts_with($excludePrefix, $resource->getPrefix())) { 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)); } } if (isset($excludePaths[str_replace('\\', '/', $path)])) { continue; } if (!preg_match($extRegexp, $path, $m) || !$info->isReadable()) { continue; } $class = $namespace.ltrim(str_replace('/', '\\', substr($path, $prefixLen, -\strlen($m[0]))), '\\'); 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)) { continue; } try { $r = $this->container->getReflectionClass($class); } catch (\ReflectionException $e) { $classes[$class] = $e->getMessage(); continue; } // check to make sure the expected class exists if (!$r) { 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)); } if ($r->isInstantiable() || $r->isInterface()) { $classes[$class] = null; } if ($autoconfigureAttributes && !$r->isInstantiable()) { $autoconfigureAttributes->processClass($this->container, $r); } } // track only for new & removed files if ($resource instanceof GlobResource) { $this->container->addResource($resource); } else { foreach ($resource as $path) { $this->container->fileExists($path, false); } } return $classes; } } Loader/GlobFileLoader.php 0000644 00000001601 15120140334 0011264 0 ustar 00 <?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; /** * GlobFileLoader loads files from a glob pattern. * * @author Nicolas Grekas <p@tchwork.com> */ class GlobFileLoader extends FileLoader { /** * {@inheritdoc} */ public function load($resource, string $type = null) { foreach ($this->glob($resource, false, $globResource) as $path => $info) { $this->import($path); } $this->container->addResource($globResource); return null; } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { return 'glob' === $type; } } Loader/IniFileLoader.php 0000644 00000006217 15120140334 0011130 0 ustar 00 <?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\Config\Util\XmlUtils; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * IniFileLoader loads parameters from INI files. * * @author Fabien Potencier <fabien@symfony.com> */ class IniFileLoader extends FileLoader { /** * {@inheritdoc} */ public function load($resource, string $type = null) { $path = $this->locator->locate($resource); $this->container->fileExists($path); // first pass to catch parsing errors $result = parse_ini_file($path, true); if (false === $result || [] === $result) { throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $resource)); } // real raw parsing $result = parse_ini_file($path, true, \INI_SCANNER_RAW); if (isset($result['parameters']) && \is_array($result['parameters'])) { foreach ($result['parameters'] as $key => $value) { $this->container->setParameter($key, $this->phpize($value)); } } if ($this->env && \is_array($result['parameters@'.$this->env] ?? null)) { foreach ($result['parameters@'.$this->env] as $key => $value) { $this->container->setParameter($key, $this->phpize($value)); } } return null; } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { if (!\is_string($resource)) { return false; } if (null === $type && 'ini' === pathinfo($resource, \PATHINFO_EXTENSION)) { return true; } return 'ini' === $type; } /** * Note that the following features are not supported: * * strings with escaped quotes are not supported "foo\"bar"; * * string concatenation ("foo" "bar"). * * @return mixed */ private function phpize(string $value) { // trim on the right as comments removal keep whitespaces if ($value !== $v = rtrim($value)) { $value = '""' === substr_replace($v, '', 1, -1) ? substr($v, 1, -1) : $v; } $lowercaseValue = strtolower($value); switch (true) { case \defined($value): return \constant($value); case 'yes' === $lowercaseValue || 'on' === $lowercaseValue: return true; case 'no' === $lowercaseValue || 'off' === $lowercaseValue || 'none' === $lowercaseValue: return false; case isset($value[1]) && ( ("'" === $value[0] && "'" === $value[\strlen($value) - 1]) || ('"' === $value[0] && '"' === $value[\strlen($value) - 1]) ): // quoted string return substr($value, 1, -1); default: return XmlUtils::phpize($value); } } } Loader/PhpFileLoader.php 0000644 00000020214 15120140334 0011131 0 ustar 00 <?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\Config\Builder\ConfigBuilderGenerator; use Symfony\Component\Config\Builder\ConfigBuilderGeneratorInterface; use Symfony\Component\Config\Builder\ConfigBuilderInterface; use Symfony\Component\Config\FileLocatorInterface; use Symfony\Component\DependencyInjection\Attribute\When; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; 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 { protected $autoRegisterAliasesForSinglyImplementedInterfaces = false; private $generator; public function __construct(ContainerBuilder $container, FileLocatorInterface $locator, string $env = null, ConfigBuilderGeneratorInterface $generator = null) { parent::__construct($container, $locator, $env); $this->generator = $generator; } /** * {@inheritdoc} */ public function load($resource, string $type = null) { // 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); // the closure forbids access to the private scope in the included file $load = \Closure::bind(function ($path, $env) use ($container, $loader, $resource, $type) { return include $path; }, $this, ProtectedPhpFileLoader::class); try { $callback = $load($path, $this->env); if (\is_object($callback) && \is_callable($callback)) { $this->executeCallback($callback, new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource, $this->env), $path); } } finally { $this->instanceof = []; $this->registerAliasesForSinglyImplementedInterfaces(); } return null; } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { 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. */ private function executeCallback(callable $callback, ContainerConfigurator $containerConfigurator, string $path) { if (!$callback instanceof \Closure) { $callback = \Closure::fromCallable($callback); } $arguments = []; $configBuilders = []; $r = new \ReflectionFunction($callback); if (\PHP_VERSION_ID >= 80000) { $attribute = null; foreach ($r->getAttributes(When::class) as $attribute) { if ($this->env === $attribute->newInstance()->env) { $attribute = null; break; } } if (null !== $attribute) { 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; default: try { $configBuilder = $this->configBuilder($type); } catch (InvalidArgumentException|\LogicException $e) { throw new \InvalidArgumentException(sprintf('Could not resolve argument "%s" for "%s".', $type.' $'.$parameter->getName(), $path), 0, $e); } $configBuilders[] = $configBuilder; $arguments[] = $configBuilder; } } // Force load ContainerConfigurator to make env(), param() etc available. class_exists(ContainerConfigurator::class); $callback(...$arguments); /** @var ConfigBuilderInterface $configBuilder */ foreach ($configBuilders as $configBuilder) { $containerConfigurator->extension($configBuilder->getExtensionAlias(), $configBuilder->toArray()); } } /** * @param string $namespace FQCN string for a class implementing ConfigBuilderInterface */ private function configBuilder(string $namespace): ConfigBuilderInterface { if (!class_exists(ConfigBuilderGenerator::class)) { throw new \LogicException('You cannot use the config builder as the Config component is not installed. Try running "composer require symfony/config".'); } if (null === $this->generator) { throw new \LogicException('You cannot use the ConfigBuilders without providing a class implementing ConfigBuilderGeneratorInterface.'); } // If class exists and implements ConfigBuilderInterface if (class_exists($namespace) && is_subclass_of($namespace, ConfigBuilderInterface::class)) { return new $namespace(); } // If it does not start with Symfony\Config\ we dont know how to handle this if ('Symfony\\Config\\' !== substr($namespace, 0, 15)) { throw new InvalidArgumentException(sprintf('Could not find or generate class "%s".', $namespace)); } // Try to get the extension alias $alias = Container::underscore(substr($namespace, 15, -6)); if (false !== strpos($alias, '\\')) { throw new InvalidArgumentException('You can only use "root" ConfigBuilders from "Symfony\\Config\\" namespace. Nested classes like "Symfony\\Config\\Framework\\CacheConfig" cannot be used.'); } if (!$this->container->hasExtension($alias)) { $extensions = array_filter(array_map(function (ExtensionInterface $ext) { return $ext->getAlias(); }, $this->container->getExtensions())); 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')); } $extension = $this->container->getExtension($alias); if (!$extension instanceof ConfigurationExtensionInterface) { throw new \LogicException(sprintf('You cannot use the config builder for "%s" because the extension does not implement "%s".', $namespace, ConfigurationExtensionInterface::class)); } $configuration = $extension->getConfiguration([], $this->container); $loader = $this->generator->build($configuration); return $loader(); } } /** * @internal */ final class ProtectedPhpFileLoader extends PhpFileLoader { } Loader/XmlFileLoader.php 0000644 00000101224 15120140334 0011143 0 ustar 00 <?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\Config\Util\XmlUtils; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; /** * XmlFileLoader loads XML files service definitions. * * @author Fabien Potencier <fabien@symfony.com> */ class XmlFileLoader extends FileLoader { public const NS = 'http://symfony.com/schema/dic/services'; protected $autoRegisterAliasesForSinglyImplementedInterfaces = false; /** * {@inheritdoc} */ public function load($resource, string $type = null) { $path = $this->locator->locate($resource); $xml = $this->parseFileToDOM($path); $this->container->fileExists($path); $this->loadXml($xml, $path); if ($this->env) { $xpath = new \DOMXPath($xml); $xpath->registerNamespace('container', self::NS); foreach ($xpath->query(sprintf('//container:when[@env="%s"]', $this->env)) ?: [] as $root) { $env = $this->env; $this->env = null; try { $this->loadXml($xml, $path, $root); } finally { $this->env = $env; } } } return null; } private function loadXml(\DOMDocument $xml, string $path, \DOMNode $root = null): void { $defaults = $this->getServiceDefaults($xml, $path, $root); // anonymous services $this->processAnonymousServices($xml, $path, $root); // imports $this->parseImports($xml, $path, $root); // parameters $this->parseParameters($xml, $path, $root); // extensions $this->loadFromExtensions($xml, $root); // services try { $this->parseDefinitions($xml, $path, $defaults, $root); } finally { $this->instanceof = []; $this->registerAliasesForSinglyImplementedInterfaces(); } } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { if (!\is_string($resource)) { return false; } if (null === $type && 'xml' === pathinfo($resource, \PATHINFO_EXTENSION)) { return true; } return 'xml' === $type; } private function parseParameters(\DOMDocument $xml, string $file, \DOMNode $root = null) { if ($parameters = $this->getChildren($root ?? $xml->documentElement, 'parameters')) { $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter', $file)); } } private function parseImports(\DOMDocument $xml, string $file, \DOMNode $root = null) { $xpath = new \DOMXPath($xml); $xpath->registerNamespace('container', self::NS); if (false === $imports = $xpath->query('.//container:imports/container:import', $root)) { return; } $defaultDirectory = \dirname($file); foreach ($imports as $import) { $this->setCurrentDir($defaultDirectory); $this->import($import->getAttribute('resource'), XmlUtils::phpize($import->getAttribute('type')) ?: null, XmlUtils::phpize($import->getAttribute('ignore-errors')) ?: false, $file); } } private function parseDefinitions(\DOMDocument $xml, string $file, Definition $defaults, \DOMNode $root = null) { $xpath = new \DOMXPath($xml); $xpath->registerNamespace('container', self::NS); if (false === $services = $xpath->query('.//container:services/container:service|.//container:services/container:prototype|.//container:services/container:stack', $root)) { return; } $this->setCurrentDir(\dirname($file)); $this->instanceof = []; $this->isLoadingInstanceof = true; $instanceof = $xpath->query('.//container:services/container:instanceof', $root); foreach ($instanceof as $service) { $this->setDefinition((string) $service->getAttribute('id'), $this->parseDefinition($service, $file, new Definition())); } $this->isLoadingInstanceof = false; foreach ($services as $service) { if ('stack' === $service->tagName) { $service->setAttribute('parent', '-'); $definition = $this->parseDefinition($service, $file, $defaults) ->setTags(array_merge_recursive(['container.stack' => [[]]], $defaults->getTags())) ; $this->setDefinition($id = (string) $service->getAttribute('id'), $definition); $stack = []; foreach ($this->getChildren($service, 'service') as $k => $frame) { $k = $frame->getAttribute('id') ?: $k; $frame->setAttribute('id', $id.'" at index "'.$k); if ($alias = $frame->getAttribute('alias')) { $this->validateAlias($frame, $file); $stack[$k] = new Reference($alias); } else { $stack[$k] = $this->parseDefinition($frame, $file, $defaults) ->setInstanceofConditionals($this->instanceof); } } $definition->setArguments($stack); } elseif (null !== $definition = $this->parseDefinition($service, $file, $defaults)) { if ('prototype' === $service->tagName) { $excludes = array_column($this->getChildren($service, 'exclude'), 'nodeValue'); if ($service->hasAttribute('exclude')) { if (\count($excludes) > 0) { throw new InvalidArgumentException('You cannot use both the attribute "exclude" and <exclude> tags at the same time.'); } $excludes = [$service->getAttribute('exclude')]; } $this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'), $excludes); } else { $this->setDefinition((string) $service->getAttribute('id'), $definition); } } } } private function getServiceDefaults(\DOMDocument $xml, string $file, \DOMNode $root = null): Definition { $xpath = new \DOMXPath($xml); $xpath->registerNamespace('container', self::NS); if (null === $defaultsNode = $xpath->query('.//container:services/container:defaults', $root)->item(0)) { return new Definition(); } $defaultsNode->setAttribute('id', '<defaults>'); return $this->parseDefinition($defaultsNode, $file, new Definition()); } /** * Parses an individual Definition. */ private function parseDefinition(\DOMElement $service, string $file, Definition $defaults): ?Definition { if ($alias = $service->getAttribute('alias')) { $this->validateAlias($service, $file); $this->container->setAlias($service->getAttribute('id'), $alias = new Alias($alias)); if ($publicAttr = $service->getAttribute('public')) { $alias->setPublic(XmlUtils::phpize($publicAttr)); } elseif ($defaults->getChanges()['public'] ?? false) { $alias->setPublic($defaults->isPublic()); } if ($deprecated = $this->getChildren($service, 'deprecated')) { $message = $deprecated[0]->nodeValue ?: ''; $package = $deprecated[0]->getAttribute('package') ?: ''; $version = $deprecated[0]->getAttribute('version') ?: ''; if (!$deprecated[0]->hasAttribute('package')) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the node "deprecated" in "%s" is deprecated.', $file); } if (!$deprecated[0]->hasAttribute('version')) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the node "deprecated" in "%s" is deprecated.', $file); } $alias->setDeprecated($package, $version, $message); } return null; } if ($this->isLoadingInstanceof) { $definition = new ChildDefinition(''); } elseif ($parent = $service->getAttribute('parent')) { $definition = new ChildDefinition($parent); } else { $definition = new Definition(); } if ($defaults->getChanges()['public'] ?? false) { $definition->setPublic($defaults->isPublic()); } $definition->setAutowired($defaults->isAutowired()); $definition->setAutoconfigured($defaults->isAutoconfigured()); $definition->setChanges([]); foreach (['class', 'public', 'shared', 'synthetic', 'abstract'] as $key) { if ($value = $service->getAttribute($key)) { $method = 'set'.$key; $definition->$method($value = XmlUtils::phpize($value)); } } if ($value = $service->getAttribute('lazy')) { $definition->setLazy((bool) $value = XmlUtils::phpize($value)); if (\is_string($value)) { $definition->addTag('proxy', ['interface' => $value]); } } if ($value = $service->getAttribute('autowire')) { $definition->setAutowired(XmlUtils::phpize($value)); } if ($value = $service->getAttribute('autoconfigure')) { $definition->setAutoconfigured(XmlUtils::phpize($value)); } if ($files = $this->getChildren($service, 'file')) { $definition->setFile($files[0]->nodeValue); } if ($deprecated = $this->getChildren($service, 'deprecated')) { $message = $deprecated[0]->nodeValue ?: ''; $package = $deprecated[0]->getAttribute('package') ?: ''; $version = $deprecated[0]->getAttribute('version') ?: ''; if ('' === $package) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the node "deprecated" in "%s" is deprecated.', $file); } if ('' === $version) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the node "deprecated" in "%s" is deprecated.', $file); } $definition->setDeprecated($package, $version, $message); } $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, $definition instanceof ChildDefinition)); $definition->setProperties($this->getArgumentsAsPhp($service, 'property', $file)); if ($factories = $this->getChildren($service, 'factory')) { $factory = $factories[0]; if ($function = $factory->getAttribute('function')) { $definition->setFactory($function); } else { if ($childService = $factory->getAttribute('service')) { $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); } else { $class = $factory->hasAttribute('class') ? $factory->getAttribute('class') : null; } $definition->setFactory([$class, $factory->getAttribute('method') ?: '__invoke']); } } if ($configurators = $this->getChildren($service, 'configurator')) { $configurator = $configurators[0]; if ($function = $configurator->getAttribute('function')) { $definition->setConfigurator($function); } else { if ($childService = $configurator->getAttribute('service')) { $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); } else { $class = $configurator->getAttribute('class'); } $definition->setConfigurator([$class, $configurator->getAttribute('method') ?: '__invoke']); } } foreach ($this->getChildren($service, 'call') as $call) { $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument', $file), XmlUtils::phpize($call->getAttribute('returns-clone'))); } $tags = $this->getChildren($service, 'tag'); foreach ($tags as $tag) { $parameters = []; $tagName = $tag->nodeValue; foreach ($tag->attributes as $name => $node) { if ('name' === $name && '' === $tagName) { continue; } if (str_contains($name, '-') && !str_contains($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { $parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue); } // keep not normalized key $parameters[$name] = XmlUtils::phpize($node->nodeValue); } if ('' === $tagName && '' === $tagName = $tag->getAttribute('name')) { throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $service->getAttribute('id'), $file)); } $definition->addTag($tagName, $parameters); } $definition->setTags(array_merge_recursive($definition->getTags(), $defaults->getTags())); $bindings = $this->getArgumentsAsPhp($service, 'bind', $file); $bindingType = $this->isLoadingInstanceof ? BoundArgument::INSTANCEOF_BINDING : BoundArgument::SERVICE_BINDING; foreach ($bindings as $argument => $value) { $bindings[$argument] = new BoundArgument($value, true, $bindingType, $file); } // deep clone, to avoid multiple process of the same instance in the passes $bindings = array_merge(unserialize(serialize($defaults->getBindings())), $bindings); if ($bindings) { $definition->setBindings($bindings); } if ($decorates = $service->getAttribute('decorates')) { $decorationOnInvalid = $service->getAttribute('decoration-on-invalid') ?: 'exception'; if ('exception' === $decorationOnInvalid) { $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; } elseif ('ignore' === $decorationOnInvalid) { $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; } elseif ('null' === $decorationOnInvalid) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; } else { throw new InvalidArgumentException(sprintf('Invalid value "%s" for attribute "decoration-on-invalid" on service "%s". Did you mean "exception", "ignore" or "null" in "%s"?', $decorationOnInvalid, $service->getAttribute('id'), $file)); } $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; $definition->setDecoratedService($decorates, $renameId, $priority, $invalidBehavior); } return $definition; } /** * Parses an XML file to a \DOMDocument. * * @throws InvalidArgumentException When loading of XML file returns error */ private function parseFileToDOM(string $file): \DOMDocument { try { $dom = XmlUtils::loadFile($file, [$this, 'validateSchema']); } catch (\InvalidArgumentException $e) { throw new InvalidArgumentException(sprintf('Unable to parse file "%s": ', $file).$e->getMessage(), $e->getCode(), $e); } $this->validateExtensions($dom, $file); return $dom; } /** * Processes anonymous services. */ private function processAnonymousServices(\DOMDocument $xml, string $file, \DOMNode $root = null) { $definitions = []; $count = 0; $suffix = '~'.ContainerBuilder::hash($file); $xpath = new \DOMXPath($xml); $xpath->registerNamespace('container', self::NS); // anonymous services as arguments/properties if (false !== $nodes = $xpath->query('.//container:argument[@type="service"][not(@id)]|.//container:property[@type="service"][not(@id)]|.//container:bind[not(@id)]|.//container:factory[not(@service)]|.//container:configurator[not(@service)]', $root)) { foreach ($nodes as $node) { if ($services = $this->getChildren($node, 'service')) { // give it a unique name $id = sprintf('.%d_%s', ++$count, preg_replace('/^.*\\\\/', '', $services[0]->getAttribute('class')).$suffix); $node->setAttribute('id', $id); $node->setAttribute('service', $id); $definitions[$id] = [$services[0], $file]; $services[0]->setAttribute('id', $id); // anonymous services are always private // we could not use the constant false here, because of XML parsing $services[0]->setAttribute('public', 'false'); } } } // anonymous services "in the wild" if (false !== $nodes = $xpath->query('.//container:services/container:service[not(@id)]', $root)) { foreach ($nodes as $node) { throw new InvalidArgumentException(sprintf('Top-level services must have "id" attribute, none found in "%s" at line %d.', $file, $node->getLineNo())); } } // resolve definitions uksort($definitions, 'strnatcmp'); foreach (array_reverse($definitions) as $id => [$domElement, $file]) { if (null !== $definition = $this->parseDefinition($domElement, $file, new Definition())) { $this->setDefinition($id, $definition); } } } private function getArgumentsAsPhp(\DOMElement $node, string $name, string $file, bool $isChildDefinition = false): array { $arguments = []; foreach ($this->getChildren($node, $name) as $arg) { if ($arg->hasAttribute('name')) { $arg->setAttribute('key', $arg->getAttribute('name')); } // this is used by ChildDefinition to overwrite a specific // argument of the parent definition if ($arg->hasAttribute('index')) { $key = ($isChildDefinition ? 'index_' : '').$arg->getAttribute('index'); } elseif (!$arg->hasAttribute('key')) { // Append an empty argument, then fetch its key to overwrite it later $arguments[] = null; $keys = array_keys($arguments); $key = array_pop($keys); } else { $key = $arg->getAttribute('key'); } $onInvalid = $arg->getAttribute('on-invalid'); $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; if ('ignore' == $onInvalid) { $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; } elseif ('ignore_uninitialized' == $onInvalid) { $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; } elseif ('null' == $onInvalid) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; } switch ($arg->getAttribute('type')) { case 'service': if ('' === $arg->getAttribute('id')) { throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service" has no or empty "id" attribute in "%s".', $name, $file)); } $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior); break; case 'expression': if (!class_exists(Expression::class)) { throw new \LogicException('The type="expression" attribute cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'); } $arguments[$key] = new Expression($arg->nodeValue); break; case 'collection': $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file); break; case 'iterator': $arg = $this->getArgumentsAsPhp($arg, $name, $file); try { $arguments[$key] = new IteratorArgument($arg); } catch (InvalidArgumentException $e) { throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file)); } break; case 'service_closure': if ('' === $arg->getAttribute('id')) { throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service_closure" has no or empty "id" attribute in "%s".', $name, $file)); } $arguments[$key] = new ServiceClosureArgument(new Reference($arg->getAttribute('id'), $invalidBehavior)); break; case 'service_locator': $arg = $this->getArgumentsAsPhp($arg, $name, $file); try { $arguments[$key] = new ServiceLocatorArgument($arg); } catch (InvalidArgumentException $e) { throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service_locator" only accepts maps of type="service" references in "%s".', $name, $file)); } break; case 'tagged': case 'tagged_iterator': case 'tagged_locator': $type = $arg->getAttribute('type'); $forLocator = 'tagged_locator' === $type; if (!$arg->getAttribute('tag')) { throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="%s" has no or empty "tag" attribute in "%s".', $name, $type, $file)); } $arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'), $arg->getAttribute('index-by') ?: null, $arg->getAttribute('default-index-method') ?: null, $forLocator, $arg->getAttribute('default-priority-method') ?: null); if ($forLocator) { $arguments[$key] = new ServiceLocatorArgument($arguments[$key]); } break; case 'binary': if (false === $value = base64_decode($arg->nodeValue)) { throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="binary" is not a valid base64 encoded string.', $name)); } $arguments[$key] = $value; break; case 'abstract': $arguments[$key] = new AbstractArgument($arg->nodeValue); break; case 'string': $arguments[$key] = $arg->nodeValue; break; case 'constant': $arguments[$key] = \constant(trim($arg->nodeValue)); break; default: $arguments[$key] = XmlUtils::phpize($arg->nodeValue); } } return $arguments; } /** * Get child elements by name. * * @return \DOMElement[] */ private function getChildren(\DOMNode $node, string $name): array { $children = []; foreach ($node->childNodes as $child) { if ($child instanceof \DOMElement && $child->localName === $name && self::NS === $child->namespaceURI) { $children[] = $child; } } return $children; } /** * Validates a documents XML schema. * * @return bool * * @throws RuntimeException When extension references a non-existent XSD file */ public function validateSchema(\DOMDocument $dom) { $schemaLocations = ['http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd')]; if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) { $items = preg_split('/\s+/', $element); for ($i = 0, $nb = \count($items); $i < $nb; $i += 2) { if (!$this->container->hasExtension($items[$i])) { continue; } if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) { $ns = $extension->getNamespace(); $path = str_replace([$ns, str_replace('http://', 'https://', $ns)], str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]); if (!is_file($path)) { throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s".', get_debug_type($extension), $path)); } $schemaLocations[$items[$i]] = $path; } } } $tmpfiles = []; $imports = ''; foreach ($schemaLocations as $namespace => $location) { $parts = explode('/', $location); $locationstart = 'file:///'; if (0 === stripos($location, 'phar://')) { $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); if ($tmpfile) { copy($location, $tmpfile); $tmpfiles[] = $tmpfile; $parts = explode('/', str_replace('\\', '/', $tmpfile)); } else { array_shift($parts); $locationstart = 'phar:///'; } } elseif ('\\' === \DIRECTORY_SEPARATOR && str_starts_with($location, '\\\\')) { $locationstart = ''; } $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; $location = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts)); $imports .= sprintf(' <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location); } $source = <<<EOF <?xml version="1.0" encoding="utf-8" ?> <xsd:schema xmlns="http://symfony.com/schema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://symfony.com/schema" elementFormDefault="qualified"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> $imports </xsd:schema> EOF ; if ($this->shouldEnableEntityLoader()) { $disableEntities = libxml_disable_entity_loader(false); $valid = @$dom->schemaValidateSource($source); libxml_disable_entity_loader($disableEntities); } else { $valid = @$dom->schemaValidateSource($source); } foreach ($tmpfiles as $tmpfile) { @unlink($tmpfile); } return $valid; } private function shouldEnableEntityLoader(): bool { // Version prior to 8.0 can be enabled without deprecation if (\PHP_VERSION_ID < 80000) { return true; } static $dom, $schema; if (null === $dom) { $dom = new \DOMDocument(); $dom->loadXML('<?xml version="1.0"?><test/>'); $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); register_shutdown_function(static function () use ($tmpfile) { @unlink($tmpfile); }); $schema = '<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:include schemaLocation="file:///'.rawurlencode(str_replace('\\', '/', $tmpfile)).'" /> </xsd:schema>'; file_put_contents($tmpfile, '<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="test" type="testType" /> <xsd:complexType name="testType"/> </xsd:schema>'); } return !@$dom->schemaValidateSource($schema); } private function validateAlias(\DOMElement $alias, string $file) { foreach ($alias->attributes as $name => $node) { if (!\in_array($name, ['alias', 'id', 'public'])) { throw new InvalidArgumentException(sprintf('Invalid attribute "%s" defined for alias "%s" in "%s".', $name, $alias->getAttribute('id'), $file)); } } foreach ($alias->childNodes as $child) { if (!$child instanceof \DOMElement || self::NS !== $child->namespaceURI) { continue; } if (!\in_array($child->localName, ['deprecated'], true)) { throw new InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $alias->getAttribute('id'), $file)); } } } /** * Validates an extension. * * @throws InvalidArgumentException When no extension is found corresponding to a tag */ private function validateExtensions(\DOMDocument $dom, string $file) { foreach ($dom->documentElement->childNodes as $node) { if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) { continue; } // can it be handled by an extension? if (!$this->container->hasExtension($node->namespaceURI)) { $extensionNamespaces = array_filter(array_map(function (ExtensionInterface $ext) { return $ext->getNamespace(); }, $this->container->getExtensions())); throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $node->tagName, $file, $node->namespaceURI, $extensionNamespaces ? implode('", "', $extensionNamespaces) : 'none')); } } } /** * Loads from an extension. */ private function loadFromExtensions(\DOMDocument $xml) { foreach ($xml->documentElement->childNodes as $node) { if (!$node instanceof \DOMElement || self::NS === $node->namespaceURI) { continue; } $values = static::convertDomElementToArray($node); if (!\is_array($values)) { $values = []; } $this->container->loadFromExtension($node->namespaceURI, $values); } } /** * Converts a \DOMElement object to a PHP array. * * The following rules applies during the conversion: * * * Each tag is converted to a key value or an array * if there is more than one "value" * * * The content of a tag is set under a "value" key (<foo>bar</foo>) * if the tag also has some nested tags * * * The attributes are converted to keys (<foo foo="bar"/>) * * * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>) * * @param \DOMElement $element A \DOMElement instance * * @return mixed */ public static function convertDomElementToArray(\DOMElement $element) { return XmlUtils::convertDomElementToArray($element); } } Loader/YamlFileLoader.php 0000644 00000120363 15120140334 0011312 0 ustar 00 <?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\Alias; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser as YamlParser; use Symfony\Component\Yaml\Tag\TaggedValue; use Symfony\Component\Yaml\Yaml; /** * YamlFileLoader loads YAML files service definitions. * * @author Fabien Potencier <fabien@symfony.com> */ class YamlFileLoader extends FileLoader { private const SERVICE_KEYWORDS = [ 'alias' => 'alias', 'parent' => 'parent', 'class' => 'class', 'shared' => 'shared', 'synthetic' => 'synthetic', 'lazy' => 'lazy', 'public' => 'public', 'abstract' => 'abstract', 'deprecated' => 'deprecated', 'factory' => 'factory', 'file' => 'file', 'arguments' => 'arguments', 'properties' => 'properties', 'configurator' => 'configurator', 'calls' => 'calls', 'tags' => 'tags', 'decorates' => 'decorates', 'decoration_inner_name' => 'decoration_inner_name', 'decoration_priority' => 'decoration_priority', 'decoration_on_invalid' => 'decoration_on_invalid', 'autowire' => 'autowire', 'autoconfigure' => 'autoconfigure', 'bind' => 'bind', ]; private const PROTOTYPE_KEYWORDS = [ 'resource' => 'resource', 'namespace' => 'namespace', 'exclude' => 'exclude', 'parent' => 'parent', 'shared' => 'shared', 'lazy' => 'lazy', 'public' => 'public', 'abstract' => 'abstract', 'deprecated' => 'deprecated', 'factory' => 'factory', 'arguments' => 'arguments', 'properties' => 'properties', 'configurator' => 'configurator', 'calls' => 'calls', 'tags' => 'tags', 'autowire' => 'autowire', 'autoconfigure' => 'autoconfigure', 'bind' => 'bind', ]; private const INSTANCEOF_KEYWORDS = [ 'shared' => 'shared', 'lazy' => 'lazy', 'public' => 'public', 'properties' => 'properties', 'configurator' => 'configurator', 'calls' => 'calls', 'tags' => 'tags', 'autowire' => 'autowire', 'bind' => 'bind', ]; private const DEFAULTS_KEYWORDS = [ 'public' => 'public', 'tags' => 'tags', 'autowire' => 'autowire', 'autoconfigure' => 'autoconfigure', 'bind' => 'bind', ]; private $yamlParser; private $anonymousServicesCount; private $anonymousServicesSuffix; protected $autoRegisterAliasesForSinglyImplementedInterfaces = false; /** * {@inheritdoc} */ public function load($resource, string $type = null) { $path = $this->locator->locate($resource); $content = $this->loadFile($path); $this->container->fileExists($path); // empty file if (null === $content) { return null; } $this->loadContent($content, $path); // per-env configuration if ($this->env && isset($content['when@'.$this->env])) { if (!\is_array($content['when@'.$this->env])) { throw new InvalidArgumentException(sprintf('The "when@%s" key should contain an array in "%s". Check your YAML syntax.', $this->env, $path)); } $env = $this->env; $this->env = null; try { $this->loadContent($content['when@'.$env], $path); } finally { $this->env = $env; } } return null; } private function loadContent(array $content, string $path) { // imports $this->parseImports($content, $path); // parameters if (isset($content['parameters'])) { if (!\is_array($content['parameters'])) { throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in "%s". Check your YAML syntax.', $path)); } foreach ($content['parameters'] as $key => $value) { $this->container->setParameter($key, $this->resolveServices($value, $path, true)); } } // extensions $this->loadFromExtensions($content); // services $this->anonymousServicesCount = 0; $this->anonymousServicesSuffix = '~'.ContainerBuilder::hash($path); $this->setCurrentDir(\dirname($path)); try { $this->parseDefinitions($content, $path); } finally { $this->instanceof = []; $this->registerAliasesForSinglyImplementedInterfaces(); } } /** * {@inheritdoc} */ public function supports($resource, string $type = null) { if (!\is_string($resource)) { return false; } if (null === $type && \in_array(pathinfo($resource, \PATHINFO_EXTENSION), ['yaml', 'yml'], true)) { return true; } return \in_array($type, ['yaml', 'yml'], true); } private function parseImports(array $content, string $file) { if (!isset($content['imports'])) { return; } if (!\is_array($content['imports'])) { throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in "%s". Check your YAML syntax.', $file)); } $defaultDirectory = \dirname($file); foreach ($content['imports'] as $import) { if (!\is_array($import)) { $import = ['resource' => $import]; } if (!isset($import['resource'])) { throw new InvalidArgumentException(sprintf('An import should provide a resource in "%s". Check your YAML syntax.', $file)); } $this->setCurrentDir($defaultDirectory); $this->import($import['resource'], $import['type'] ?? null, $import['ignore_errors'] ?? false, $file); } } private function parseDefinitions(array $content, string $file, bool $trackBindings = true) { if (!isset($content['services'])) { return; } if (!\is_array($content['services'])) { throw new InvalidArgumentException(sprintf('The "services" key should contain an array in "%s". Check your YAML syntax.', $file)); } if (\array_key_exists('_instanceof', $content['services'])) { $instanceof = $content['services']['_instanceof']; unset($content['services']['_instanceof']); if (!\is_array($instanceof)) { throw new InvalidArgumentException(sprintf('Service "_instanceof" key must be an array, "%s" given in "%s".', get_debug_type($instanceof), $file)); } $this->instanceof = []; $this->isLoadingInstanceof = true; foreach ($instanceof as $id => $service) { if (!$service || !\is_array($service)) { throw new InvalidArgumentException(sprintf('Type definition "%s" must be a non-empty array within "_instanceof" in "%s". Check your YAML syntax.', $id, $file)); } if (\is_string($service) && str_starts_with($service, '@')) { throw new InvalidArgumentException(sprintf('Type definition "%s" cannot be an alias within "_instanceof" in "%s". Check your YAML syntax.', $id, $file)); } $this->parseDefinition($id, $service, $file, [], false, $trackBindings); } } $this->isLoadingInstanceof = false; $defaults = $this->parseDefaults($content, $file); foreach ($content['services'] as $id => $service) { $this->parseDefinition($id, $service, $file, $defaults, false, $trackBindings); } } /** * @throws InvalidArgumentException */ private function parseDefaults(array &$content, string $file): array { if (!\array_key_exists('_defaults', $content['services'])) { return []; } $defaults = $content['services']['_defaults']; unset($content['services']['_defaults']); if (!\is_array($defaults)) { throw new InvalidArgumentException(sprintf('Service "_defaults" key must be an array, "%s" given in "%s".', get_debug_type($defaults), $file)); } foreach ($defaults as $key => $default) { if (!isset(self::DEFAULTS_KEYWORDS[$key])) { throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', self::DEFAULTS_KEYWORDS))); } } if (isset($defaults['tags'])) { if (!\is_array($tags = $defaults['tags'])) { throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file)); } foreach ($tags as $tag) { if (!\is_array($tag)) { $tag = ['name' => $tag]; } if (1 === \count($tag) && \is_array(current($tag))) { $name = key($tag); $tag = current($tag); } else { if (!isset($tag['name'])) { throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in "%s".', $file)); } $name = $tag['name']; unset($tag['name']); } if (!\is_string($name) || '' === $name) { throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in "%s".', $file)); } foreach ($tag as $attribute => $value) { if (!\is_scalar($value) && null !== $value) { throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, $attribute, $file)); } } } } if (isset($defaults['bind'])) { if (!\is_array($defaults['bind'])) { throw new InvalidArgumentException(sprintf('Parameter "bind" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file)); } foreach ($this->resolveServices($defaults['bind'], $file) as $argument => $value) { $defaults['bind'][$argument] = new BoundArgument($value, true, BoundArgument::DEFAULTS_BINDING, $file); } } return $defaults; } private function isUsingShortSyntax(array $service): bool { foreach ($service as $key => $value) { if (\is_string($key) && ('' === $key || ('$' !== $key[0] && !str_contains($key, '\\')))) { return false; } } return true; } /** * Parses a definition. * * @param array|string|null $service * * @throws InvalidArgumentException When tags are invalid */ private function parseDefinition(string $id, $service, string $file, array $defaults, bool $return = false, bool $trackBindings = true) { if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) { throw new InvalidArgumentException(sprintf('Service names that start with an underscore are reserved. Rename the "%s" service or define it in XML instead.', $id)); } if (\is_string($service) && str_starts_with($service, '@')) { $alias = new Alias(substr($service, 1)); if (isset($defaults['public'])) { $alias->setPublic($defaults['public']); } return $return ? $alias : $this->container->setAlias($id, $alias); } if (\is_array($service) && $this->isUsingShortSyntax($service)) { $service = ['arguments' => $service]; } if (null === $service) { $service = []; } if (!\is_array($service)) { throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but "%s" found for service "%s" in "%s". Check your YAML syntax.', get_debug_type($service), $id, $file)); } if (isset($service['stack'])) { if (!\is_array($service['stack'])) { throw new InvalidArgumentException(sprintf('A stack must be an array of definitions, "%s" given for service "%s" in "%s". Check your YAML syntax.', get_debug_type($service), $id, $file)); } $stack = []; foreach ($service['stack'] as $k => $frame) { if (\is_array($frame) && 1 === \count($frame) && !isset(self::SERVICE_KEYWORDS[key($frame)])) { $frame = [ 'class' => key($frame), 'arguments' => current($frame), ]; } if (\is_array($frame) && isset($frame['stack'])) { throw new InvalidArgumentException(sprintf('Service stack "%s" cannot contain another stack in "%s".', $id, $file)); } $definition = $this->parseDefinition($id.'" at index "'.$k, $frame, $file, $defaults, true); if ($definition instanceof Definition) { $definition->setInstanceofConditionals($this->instanceof); } $stack[$k] = $definition; } if ($diff = array_diff(array_keys($service), ['stack', 'public', 'deprecated'])) { throw new InvalidArgumentException(sprintf('Invalid attribute "%s"; supported ones are "public" and "deprecated" for service "%s" in "%s". Check your YAML syntax.', implode('", "', $diff), $id, $file)); } $service = [ 'parent' => '', 'arguments' => $stack, 'tags' => ['container.stack'], 'public' => $service['public'] ?? null, 'deprecated' => $service['deprecated'] ?? null, ]; } $definition = isset($service[0]) && $service[0] instanceof Definition ? array_shift($service) : null; $return = null === $definition ? $return : true; $this->checkDefinition($id, $service, $file); if (isset($service['alias'])) { $alias = new Alias($service['alias']); if (isset($service['public'])) { $alias->setPublic($service['public']); } elseif (isset($defaults['public'])) { $alias->setPublic($defaults['public']); } foreach ($service as $key => $value) { if (!\in_array($key, ['alias', 'public', 'deprecated'])) { throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias", "public" and "deprecated".', $key, $id, $file)); } if ('deprecated' === $key) { $deprecation = \is_array($value) ? $value : ['message' => $value]; if (!isset($deprecation['package'])) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the "deprecated" option in "%s" is deprecated.', $file); } if (!isset($deprecation['version'])) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.', $file); } $alias->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message'] ?? ''); } } return $return ? $alias : $this->container->setAlias($id, $alias); } if (null !== $definition) { // no-op } elseif ($this->isLoadingInstanceof) { $definition = new ChildDefinition(''); } elseif (isset($service['parent'])) { if ('' !== $service['parent'] && '@' === $service['parent'][0]) { throw new InvalidArgumentException(sprintf('The value of the "parent" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['parent'], substr($service['parent'], 1))); } $definition = new ChildDefinition($service['parent']); } else { $definition = new Definition(); } if (isset($defaults['public'])) { $definition->setPublic($defaults['public']); } if (isset($defaults['autowire'])) { $definition->setAutowired($defaults['autowire']); } if (isset($defaults['autoconfigure'])) { $definition->setAutoconfigured($defaults['autoconfigure']); } $definition->setChanges([]); if (isset($service['class'])) { $definition->setClass($service['class']); } if (isset($service['shared'])) { $definition->setShared($service['shared']); } if (isset($service['synthetic'])) { $definition->setSynthetic($service['synthetic']); } if (isset($service['lazy'])) { $definition->setLazy((bool) $service['lazy']); if (\is_string($service['lazy'])) { $definition->addTag('proxy', ['interface' => $service['lazy']]); } } if (isset($service['public'])) { $definition->setPublic($service['public']); } if (isset($service['abstract'])) { $definition->setAbstract($service['abstract']); } if (isset($service['deprecated'])) { $deprecation = \is_array($service['deprecated']) ? $service['deprecated'] : ['message' => $service['deprecated']]; if (!isset($deprecation['package'])) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the "deprecated" option in "%s" is deprecated.', $file); } if (!isset($deprecation['version'])) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.', $file); } $definition->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message'] ?? ''); } if (isset($service['factory'])) { $definition->setFactory($this->parseCallable($service['factory'], 'factory', $id, $file)); } if (isset($service['file'])) { $definition->setFile($service['file']); } if (isset($service['arguments'])) { $definition->setArguments($this->resolveServices($service['arguments'], $file)); } if (isset($service['properties'])) { $definition->setProperties($this->resolveServices($service['properties'], $file)); } if (isset($service['configurator'])) { $definition->setConfigurator($this->parseCallable($service['configurator'], 'configurator', $id, $file)); } if (isset($service['calls'])) { if (!\is_array($service['calls'])) { throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file)); } foreach ($service['calls'] as $k => $call) { if (!\is_array($call) && (!\is_string($k) || !$call instanceof TaggedValue)) { throw new InvalidArgumentException(sprintf('Invalid method call for service "%s": expected map or array, "%s" given in "%s".', $id, $call instanceof TaggedValue ? '!'.$call->getTag() : get_debug_type($call), $file)); } if (\is_string($k)) { throw new InvalidArgumentException(sprintf('Invalid method call for service "%s", did you forgot a leading dash before "%s: ..." in "%s"?', $id, $k, $file)); } if (isset($call['method']) && \is_string($call['method'])) { $method = $call['method']; $args = $call['arguments'] ?? []; $returnsClone = $call['returns_clone'] ?? false; } else { if (1 === \count($call) && \is_string(key($call))) { $method = key($call); $args = $call[$method]; if ($args instanceof TaggedValue) { if ('returns_clone' !== $args->getTag()) { throw new InvalidArgumentException(sprintf('Unsupported tag "!%s", did you mean "!returns_clone" for service "%s" in "%s"?', $args->getTag(), $id, $file)); } $returnsClone = true; $args = $args->getValue(); } else { $returnsClone = false; } } elseif (empty($call[0])) { throw new InvalidArgumentException(sprintf('Invalid call for service "%s": the method must be defined as the first index of an array or as the only key of a map in "%s".', $id, $file)); } else { $method = $call[0]; $args = $call[1] ?? []; $returnsClone = $call[2] ?? false; } } if (!\is_array($args)) { throw new InvalidArgumentException(sprintf('The second parameter for function call "%s" must be an array of its arguments for service "%s" in "%s". Check your YAML syntax.', $method, $id, $file)); } $args = $this->resolveServices($args, $file); $definition->addMethodCall($method, $args, $returnsClone); } } $tags = $service['tags'] ?? []; if (!\is_array($tags)) { throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file)); } if (isset($defaults['tags'])) { $tags = array_merge($tags, $defaults['tags']); } foreach ($tags as $tag) { if (!\is_array($tag)) { $tag = ['name' => $tag]; } if (1 === \count($tag) && \is_array(current($tag))) { $name = key($tag); $tag = current($tag); } else { if (!isset($tag['name'])) { throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in "%s".', $id, $file)); } $name = $tag['name']; unset($tag['name']); } if (!\is_string($name) || '' === $name) { throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file)); } foreach ($tag as $attribute => $value) { if (!\is_scalar($value) && null !== $value) { throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, $attribute, $file)); } } $definition->addTag($name, $tag); } if (null !== $decorates = $service['decorates'] ?? null) { if ('' !== $decorates && '@' === $decorates[0]) { throw new InvalidArgumentException(sprintf('The value of the "decorates" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['decorates'], substr($decorates, 1))); } $decorationOnInvalid = \array_key_exists('decoration_on_invalid', $service) ? $service['decoration_on_invalid'] : 'exception'; if ('exception' === $decorationOnInvalid) { $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; } elseif ('ignore' === $decorationOnInvalid) { $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; } elseif (null === $decorationOnInvalid) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; } elseif ('null' === $decorationOnInvalid) { throw new InvalidArgumentException(sprintf('Invalid value "%s" for attribute "decoration_on_invalid" on service "%s". Did you mean null (without quotes) in "%s"?', $decorationOnInvalid, $id, $file)); } else { throw new InvalidArgumentException(sprintf('Invalid value "%s" for attribute "decoration_on_invalid" on service "%s". Did you mean "exception", "ignore" or null in "%s"?', $decorationOnInvalid, $id, $file)); } $renameId = $service['decoration_inner_name'] ?? null; $priority = $service['decoration_priority'] ?? 0; $definition->setDecoratedService($decorates, $renameId, $priority, $invalidBehavior); } if (isset($service['autowire'])) { $definition->setAutowired($service['autowire']); } if (isset($defaults['bind']) || isset($service['bind'])) { // deep clone, to avoid multiple process of the same instance in the passes $bindings = $definition->getBindings(); $bindings += isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : []; if (isset($service['bind'])) { if (!\is_array($service['bind'])) { throw new InvalidArgumentException(sprintf('Parameter "bind" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file)); } $bindings = array_merge($bindings, $this->resolveServices($service['bind'], $file)); $bindingType = $this->isLoadingInstanceof ? BoundArgument::INSTANCEOF_BINDING : BoundArgument::SERVICE_BINDING; foreach ($bindings as $argument => $value) { if (!$value instanceof BoundArgument) { $bindings[$argument] = new BoundArgument($value, $trackBindings, $bindingType, $file); } } } $definition->setBindings($bindings); } if (isset($service['autoconfigure'])) { $definition->setAutoconfigured($service['autoconfigure']); } if (\array_key_exists('namespace', $service) && !\array_key_exists('resource', $service)) { throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in "%s". Check your YAML syntax.', $id, $file)); } if ($return) { if (\array_key_exists('resource', $service)) { throw new InvalidArgumentException(sprintf('Invalid "resource" attribute found for service "%s" in "%s". Check your YAML syntax.', $id, $file)); } return $definition; } if (\array_key_exists('resource', $service)) { if (!\is_string($service['resource'])) { throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file)); } $exclude = $service['exclude'] ?? null; $namespace = $service['namespace'] ?? $id; $this->registerClasses($definition, $namespace, $service['resource'], $exclude); } else { $this->setDefinition($id, $definition); } } /** * Parses a callable. * * @param string|array $callable A callable reference * * @return string|array|Reference * * @throws InvalidArgumentException When errors occur */ private function parseCallable($callable, string $parameter, string $id, string $file) { if (\is_string($callable)) { if ('' !== $callable && '@' === $callable[0]) { if (!str_contains($callable, ':')) { return [$this->resolveServices($callable, $file), '__invoke']; } throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s" in "%s").', $parameter, $id, $callable, substr($callable, 1), $file)); } return $callable; } if (\is_array($callable)) { if (isset($callable[0]) && isset($callable[1])) { return [$this->resolveServices($callable[0], $file), $callable[1]]; } if ('factory' === $parameter && isset($callable[1]) && null === $callable[0]) { return $callable; } throw new InvalidArgumentException(sprintf('Parameter "%s" must contain an array with two elements for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file)); } throw new InvalidArgumentException(sprintf('Parameter "%s" must be a string or an array for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file)); } /** * Loads a YAML file. * * @return array|null * * @throws InvalidArgumentException when the given file is not a local file or when it does not exist */ protected function loadFile(string $file) { if (!class_exists(\Symfony\Component\Yaml\Parser::class)) { throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.'); } if (!stream_is_local($file)) { throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file)); } if (!is_file($file)) { throw new InvalidArgumentException(sprintf('The file "%s" does not exist.', $file)); } if (null === $this->yamlParser) { $this->yamlParser = new YamlParser(); } try { $configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS); } catch (ParseException $e) { throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $file).$e->getMessage(), 0, $e); } return $this->validate($configuration, $file); } /** * Validates a YAML file. * * @throws InvalidArgumentException When service file is not valid */ private function validate($content, string $file): ?array { if (null === $content) { return $content; } if (!\is_array($content)) { throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file)); } foreach ($content as $namespace => $data) { if (\in_array($namespace, ['imports', 'parameters', 'services']) || 0 === strpos($namespace, 'when@')) { continue; } if (!$this->container->hasExtension($namespace)) { $extensionNamespaces = array_filter(array_map(function (ExtensionInterface $ext) { return $ext->getAlias(); }, $this->container->getExtensions())); throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $file, $namespace, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none')); } } return $content; } /** * @return mixed */ private function resolveServices($value, string $file, bool $isParameter = false) { if ($value instanceof TaggedValue) { $argument = $value->getValue(); if ('iterator' === $value->getTag()) { if (!\is_array($argument)) { throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts sequences in "%s".', $file)); } $argument = $this->resolveServices($argument, $file, $isParameter); try { return new IteratorArgument($argument); } catch (InvalidArgumentException $e) { throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file)); } } if ('service_closure' === $value->getTag()) { $argument = $this->resolveServices($argument, $file, $isParameter); if (!$argument instanceof Reference) { throw new InvalidArgumentException(sprintf('"!service_closure" tag only accepts service references in "%s".', $file)); } return new ServiceClosureArgument($argument); } if ('service_locator' === $value->getTag()) { if (!\is_array($argument)) { throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps in "%s".', $file)); } $argument = $this->resolveServices($argument, $file, $isParameter); try { return new ServiceLocatorArgument($argument); } catch (InvalidArgumentException $e) { throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps of "@service" references in "%s".', $file)); } } if (\in_array($value->getTag(), ['tagged', 'tagged_iterator', 'tagged_locator'], true)) { $forLocator = 'tagged_locator' === $value->getTag(); if (\is_array($argument) && isset($argument['tag']) && $argument['tag']) { if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method', 'default_priority_method'])) { throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by", "default_index_method", and "default_priority_method".', $value->getTag(), implode('", "', $diff))); } $argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null); } elseif (\is_string($argument) && $argument) { $argument = new TaggedIteratorArgument($argument, null, null, $forLocator); } else { throw new InvalidArgumentException(sprintf('"!%s" tags only accept a non empty string or an array with a key "tag" in "%s".', $value->getTag(), $file)); } if ($forLocator) { $argument = new ServiceLocatorArgument($argument); } return $argument; } if ('service' === $value->getTag()) { if ($isParameter) { throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file)); } $isLoadingInstanceof = $this->isLoadingInstanceof; $this->isLoadingInstanceof = false; $instanceof = $this->instanceof; $this->instanceof = []; $id = sprintf('.%d_%s', ++$this->anonymousServicesCount, preg_replace('/^.*\\\\/', '', $argument['class'] ?? '').$this->anonymousServicesSuffix); $this->parseDefinition($id, $argument, $file, []); if (!$this->container->hasDefinition($id)) { throw new InvalidArgumentException(sprintf('Creating an alias using the tag "!service" is not allowed in "%s".', $file)); } $this->container->getDefinition($id); $this->isLoadingInstanceof = $isLoadingInstanceof; $this->instanceof = $instanceof; return new Reference($id); } if ('abstract' === $value->getTag()) { return new AbstractArgument($value->getValue()); } throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag())); } if (\is_array($value)) { foreach ($value as $k => $v) { $value[$k] = $this->resolveServices($v, $file, $isParameter); } } elseif (\is_string($value) && str_starts_with($value, '@=')) { if ($isParameter) { throw new InvalidArgumentException(sprintf('Using expressions in parameters is not allowed in "%s".', $file)); } if (!class_exists(Expression::class)) { throw new \LogicException('The "@=" expression syntax cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'); } return new Expression(substr($value, 2)); } elseif (\is_string($value) && str_starts_with($value, '@')) { if (str_starts_with($value, '@@')) { $value = substr($value, 1); $invalidBehavior = null; } elseif (str_starts_with($value, '@!')) { $value = substr($value, 2); $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; } elseif (str_starts_with($value, '@?')) { $value = substr($value, 2); $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; } else { $value = substr($value, 1); $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; } if (null !== $invalidBehavior) { $value = new Reference($value, $invalidBehavior); } } return $value; } private function loadFromExtensions(array $content) { foreach ($content as $namespace => $values) { if (\in_array($namespace, ['imports', 'parameters', 'services']) || 0 === strpos($namespace, 'when@')) { continue; } if (!\is_array($values) && null !== $values) { $values = []; } $this->container->loadFromExtension($namespace, $values); } } private function checkDefinition(string $id, array $definition, string $file) { if ($this->isLoadingInstanceof) { $keywords = self::INSTANCEOF_KEYWORDS; } elseif (isset($definition['resource']) || isset($definition['namespace'])) { $keywords = self::PROTOTYPE_KEYWORDS; } else { $keywords = self::SERVICE_KEYWORDS; } foreach ($definition as $key => $value) { if (!isset($keywords[$key])) { throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', $keywords))); } } } } ParameterBag/ContainerBag.php 0000644 00000002151 15120140334 0012133 0 ustar 00 <?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\ParameterBag; use Symfony\Component\DependencyInjection\Container; /** * @author Nicolas Grekas <p@tchwork.com> */ class ContainerBag extends FrozenParameterBag implements ContainerBagInterface { private $container; public function __construct(Container $container) { $this->container = $container; } /** * {@inheritdoc} */ public function all() { return $this->container->getParameterBag()->all(); } /** * {@inheritdoc} * * @return array|bool|string|int|float|\UnitEnum|null */ public function get(string $name) { return $this->container->getParameter($name); } /** * {@inheritdoc} * * @return bool */ public function has(string $name) { return $this->container->hasParameter($name); } } ParameterBag/ContainerBagInterface.php 0000644 00000002517 15120140334 0013762 0 ustar 00 <?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\ParameterBag; use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; /** * ContainerBagInterface is the interface implemented by objects that manage service container parameters. * * @author Nicolas Grekas <p@tchwork.com> */ interface ContainerBagInterface extends ContainerInterface { /** * Gets the service container parameters. * * @return array */ public function all(); /** * Replaces parameter placeholders (%name%) by their values. * * @param mixed $value A value * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist */ public function resolveValue($value); /** * Escape parameter placeholders %. * * @param mixed $value * * @return mixed */ public function escapeValue($value); /** * Unescape parameter placeholders %. * * @param mixed $value * * @return mixed */ public function unescapeValue($value); } ParameterBag/EnvPlaceholderParameterBag.php 0000644 00000011646 15120140334 0014756 0 ustar 00 <?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\ParameterBag; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * @author Nicolas Grekas <p@tchwork.com> */ class EnvPlaceholderParameterBag extends ParameterBag { private $envPlaceholderUniquePrefix; private $envPlaceholders = []; private $unusedEnvPlaceholders = []; private $providedTypes = []; private static $counter = 0; /** * {@inheritdoc} */ public function get(string $name) { if (str_starts_with($name, 'env(') && str_ends_with($name, ')') && 'env()' !== $name) { $env = substr($name, 4, -1); if (isset($this->envPlaceholders[$env])) { foreach ($this->envPlaceholders[$env] as $placeholder) { return $placeholder; // return first result } } if (isset($this->unusedEnvPlaceholders[$env])) { foreach ($this->unusedEnvPlaceholders[$env] as $placeholder) { return $placeholder; // return first result } } if (!preg_match('/^(?:[-.\w]*+:)*+\w++$/', $env)) { throw new InvalidArgumentException(sprintf('Invalid %s name: only "word" characters are allowed.', $name)); } if ($this->has($name) && null !== ($defaultValue = parent::get($name)) && !\is_string($defaultValue)) { throw new RuntimeException(sprintf('The default value of an env() parameter must be a string or null, but "%s" given to "%s".', get_debug_type($defaultValue), $name)); } $uniqueName = md5($name.'_'.self::$counter++); $placeholder = sprintf('%s_%s_%s', $this->getEnvPlaceholderUniquePrefix(), strtr($env, ':-.', '___'), $uniqueName); $this->envPlaceholders[$env][$placeholder] = $placeholder; return $placeholder; } return parent::get($name); } /** * Gets the common env placeholder prefix for env vars created by this bag. */ public function getEnvPlaceholderUniquePrefix(): string { if (null === $this->envPlaceholderUniquePrefix) { $reproducibleEntropy = unserialize(serialize($this->parameters)); array_walk_recursive($reproducibleEntropy, function (&$v) { $v = null; }); $this->envPlaceholderUniquePrefix = 'env_'.substr(md5(serialize($reproducibleEntropy)), -16); } return $this->envPlaceholderUniquePrefix; } /** * Returns the map of env vars used in the resolved parameter values to their placeholders. * * @return string[][] A map of env var names to their placeholders */ public function getEnvPlaceholders() { return $this->envPlaceholders; } public function getUnusedEnvPlaceholders(): array { return $this->unusedEnvPlaceholders; } public function clearUnusedEnvPlaceholders() { $this->unusedEnvPlaceholders = []; } /** * Merges the env placeholders of another EnvPlaceholderParameterBag. */ public function mergeEnvPlaceholders(self $bag) { if ($newPlaceholders = $bag->getEnvPlaceholders()) { $this->envPlaceholders += $newPlaceholders; foreach ($newPlaceholders as $env => $placeholders) { $this->envPlaceholders[$env] += $placeholders; } } if ($newUnusedPlaceholders = $bag->getUnusedEnvPlaceholders()) { $this->unusedEnvPlaceholders += $newUnusedPlaceholders; foreach ($newUnusedPlaceholders as $env => $placeholders) { $this->unusedEnvPlaceholders[$env] += $placeholders; } } } /** * Maps env prefixes to their corresponding PHP types. */ public function setProvidedTypes(array $providedTypes) { $this->providedTypes = $providedTypes; } /** * Gets the PHP types corresponding to env() parameter prefixes. * * @return string[][] */ public function getProvidedTypes() { return $this->providedTypes; } /** * {@inheritdoc} */ public function resolve() { if ($this->resolved) { return; } parent::resolve(); foreach ($this->envPlaceholders as $env => $placeholders) { if ($this->has($name = "env($env)") && null !== ($default = $this->parameters[$name]) && !\is_string($default)) { throw new RuntimeException(sprintf('The default value of env parameter "%s" must be a string or null, "%s" given.', $env, get_debug_type($default))); } } } } ParameterBag/FrozenParameterBag.php 0000644 00000003106 15120140334 0013316 0 ustar 00 <?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\ParameterBag; use Symfony\Component\DependencyInjection\Exception\LogicException; /** * Holds read-only parameters. * * @author Fabien Potencier <fabien@symfony.com> */ class FrozenParameterBag extends ParameterBag { /** * For performance reasons, the constructor assumes that * all keys are already lowercased. * * This is always the case when used internally. * * @param array $parameters An array of parameters */ public function __construct(array $parameters = []) { $this->parameters = $parameters; $this->resolved = true; } /** * {@inheritdoc} */ public function clear() { throw new LogicException('Impossible to call clear() on a frozen ParameterBag.'); } /** * {@inheritdoc} */ public function add(array $parameters) { throw new LogicException('Impossible to call add() on a frozen ParameterBag.'); } /** * {@inheritdoc} */ public function set(string $name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } /** * {@inheritdoc} */ public function remove(string $name) { throw new LogicException('Impossible to call remove() on a frozen ParameterBag.'); } } ParameterBag/ParameterBag.php 0000644 00000017122 15120140334 0012135 0 ustar 00 <?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\ParameterBag; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * Holds parameters. * * @author Fabien Potencier <fabien@symfony.com> */ class ParameterBag implements ParameterBagInterface { protected $parameters = []; protected $resolved = false; public function __construct(array $parameters = []) { $this->add($parameters); } /** * {@inheritdoc} */ public function clear() { $this->parameters = []; } /** * {@inheritdoc} */ public function add(array $parameters) { foreach ($parameters as $key => $value) { $this->set($key, $value); } } /** * {@inheritdoc} */ public function all() { return $this->parameters; } /** * {@inheritdoc} */ public function get(string $name) { if (!\array_key_exists($name, $this->parameters)) { if (!$name) { throw new ParameterNotFoundException($name); } $alternatives = []; foreach ($this->parameters as $key => $parameterValue) { $lev = levenshtein($name, $key); if ($lev <= \strlen($name) / 3 || str_contains($key, $name)) { $alternatives[] = $key; } } $nonNestedAlternative = null; if (!\count($alternatives) && str_contains($name, '.')) { $namePartsLength = array_map('strlen', explode('.', $name)); $key = substr($name, 0, -1 * (1 + array_pop($namePartsLength))); while (\count($namePartsLength)) { if ($this->has($key)) { if (\is_array($this->get($key))) { $nonNestedAlternative = $key; } break; } $key = substr($key, 0, -1 * (1 + array_pop($namePartsLength))); } } throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative); } return $this->parameters[$name]; } /** * {@inheritdoc} */ public function set(string $name, $value) { $this->parameters[$name] = $value; } /** * {@inheritdoc} */ public function has(string $name) { return \array_key_exists($name, $this->parameters); } /** * {@inheritdoc} */ public function remove(string $name) { unset($this->parameters[$name]); } /** * {@inheritdoc} */ public function resolve() { if ($this->resolved) { return; } $parameters = []; foreach ($this->parameters as $key => $value) { try { $value = $this->resolveValue($value); $parameters[$key] = $this->unescapeValue($value); } catch (ParameterNotFoundException $e) { $e->setSourceKey($key); throw $e; } } $this->parameters = $parameters; $this->resolved = true; } /** * Replaces parameter placeholders (%name%) by their values. * * @param mixed $value A value * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) * * @return mixed * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist * @throws ParameterCircularReferenceException if a circular reference if detected * @throws RuntimeException when a given parameter has a type problem */ public function resolveValue($value, array $resolving = []) { if (\is_array($value)) { $args = []; foreach ($value as $k => $v) { $args[\is_string($k) ? $this->resolveValue($k, $resolving) : $k] = $this->resolveValue($v, $resolving); } return $args; } if (!\is_string($value) || 2 > \strlen($value)) { return $value; } return $this->resolveString($value, $resolving); } /** * Resolves parameters inside a string. * * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) * * @return mixed * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist * @throws ParameterCircularReferenceException if a circular reference if detected * @throws RuntimeException when a given parameter has a type problem */ public function resolveString(string $value, array $resolving = []) { // we do this to deal with non string values (Boolean, integer, ...) // as the preg_replace_callback throw an exception when trying // a non-string in a parameter value if (preg_match('/^%([^%\s]+)%$/', $value, $match)) { $key = $match[1]; if (isset($resolving[$key])) { throw new ParameterCircularReferenceException(array_keys($resolving)); } $resolving[$key] = true; return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving); } return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($resolving, $value) { // skip %% if (!isset($match[1])) { return '%%'; } $key = $match[1]; if (isset($resolving[$key])) { throw new ParameterCircularReferenceException(array_keys($resolving)); } $resolved = $this->get($key); if (!\is_string($resolved) && !is_numeric($resolved)) { throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type "%s" inside string value "%s".', $key, get_debug_type($resolved), $value)); } $resolved = (string) $resolved; $resolving[$key] = true; return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving); }, $value); } public function isResolved() { return $this->resolved; } /** * {@inheritdoc} */ public function escapeValue($value) { if (\is_string($value)) { return str_replace('%', '%%', $value); } if (\is_array($value)) { $result = []; foreach ($value as $k => $v) { $result[$k] = $this->escapeValue($v); } return $result; } return $value; } /** * {@inheritdoc} */ public function unescapeValue($value) { if (\is_string($value)) { return str_replace('%%', '%', $value); } if (\is_array($value)) { $result = []; foreach ($value as $k => $v) { $result[$k] = $this->unescapeValue($v); } return $result; } return $value; } } ParameterBag/ParameterBagInterface.php 0000644 00000005031 15120140334 0013752 0 ustar 00 <?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\ParameterBag; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; /** * ParameterBagInterface is the interface implemented by objects that manage service container parameters. * * @author Fabien Potencier <fabien@symfony.com> */ interface ParameterBagInterface { /** * Clears all parameters. * * @throws LogicException if the ParameterBagInterface cannot be cleared */ public function clear(); /** * Adds parameters to the service container parameters. * * @throws LogicException if the parameter cannot be added */ public function add(array $parameters); /** * Gets the service container parameters. * * @return array */ public function all(); /** * Gets a service container parameter. * * @return array|bool|string|int|float|\UnitEnum|null * * @throws ParameterNotFoundException if the parameter is not defined */ public function get(string $name); /** * Removes a parameter. */ public function remove(string $name); /** * Sets a service container parameter. * * @param array|bool|string|int|float|\UnitEnum|null $value The parameter value * * @throws LogicException if the parameter cannot be set */ public function set(string $name, $value); /** * Returns true if a parameter name is defined. * * @return bool */ public function has(string $name); /** * Replaces parameter placeholders (%name%) by their values for all parameters. */ public function resolve(); /** * Replaces parameter placeholders (%name%) by their values. * * @param mixed $value A value * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist */ public function resolveValue($value); /** * Escape parameter placeholders %. * * @param mixed $value * * @return mixed */ public function escapeValue($value); /** * Unescape parameter placeholders %. * * @param mixed $value * * @return mixed */ public function unescapeValue($value); } Alias.php 0000644 00000011024 15120140334 0006275 0 ustar 00 <?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; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; class Alias { private const DEFAULT_DEPRECATION_TEMPLATE = 'The "%alias_id%" service alias is deprecated. You should stop using it, as it will be removed in the future.'; private $id; private $public; private $deprecation = []; public function __construct(string $id, bool $public = false) { $this->id = $id; $this->public = $public; } /** * Checks if this DI Alias should be public or not. * * @return bool */ public function isPublic() { return $this->public; } /** * Sets if this Alias is public. * * @return $this */ public function setPublic(bool $boolean) { $this->public = $boolean; return $this; } /** * Sets if this Alias is private. * * @return $this * * @deprecated since Symfony 5.2, use setPublic() instead */ public function setPrivate(bool $boolean) { trigger_deprecation('symfony/dependency-injection', '5.2', 'The "%s()" method is deprecated, use "setPublic()" instead.', __METHOD__); return $this->setPublic(!$boolean); } /** * Whether this alias is private. * * @return bool */ public function isPrivate() { return !$this->public; } /** * Whether this alias is deprecated, that means it should not be referenced * anymore. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message The deprecation message to use * * @return $this * * @throws InvalidArgumentException when the message template is invalid */ public function setDeprecated(/* string $package, string $version, string $message */) { $args = \func_get_args(); if (\func_num_args() < 3) { trigger_deprecation('symfony/dependency-injection', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); $status = $args[0] ?? true; if (!$status) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Passing a null message to un-deprecate a node is deprecated.'); } $message = (string) ($args[1] ?? null); $package = $version = ''; } else { $status = true; $package = (string) $args[0]; $version = (string) $args[1]; $message = (string) $args[2]; } if ('' !== $message) { if (preg_match('#[\r\n]|\*/#', $message)) { throw new InvalidArgumentException('Invalid characters found in deprecation template.'); } if (!str_contains($message, '%alias_id%')) { throw new InvalidArgumentException('The deprecation template must contain the "%alias_id%" placeholder.'); } } $this->deprecation = $status ? ['package' => $package, 'version' => $version, 'message' => $message ?: self::DEFAULT_DEPRECATION_TEMPLATE] : []; return $this; } public function isDeprecated(): bool { return (bool) $this->deprecation; } /** * @deprecated since Symfony 5.1, use "getDeprecation()" instead. */ public function getDeprecationMessage(string $id): string { trigger_deprecation('symfony/dependency-injection', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); return $this->getDeprecation($id)['message']; } /** * @param string $id Service id relying on this definition */ public function getDeprecation(string $id): array { return [ 'package' => $this->deprecation['package'], 'version' => $this->deprecation['version'], 'message' => str_replace('%alias_id%', $id, $this->deprecation['message']), ]; } /** * Returns the Id of this alias. * * @return string */ public function __toString() { return $this->id; } } CHANGELOG.md 0000644 00000034763 15120140334 0006363 0 ustar 00 CHANGELOG ========= 5.4 --- * Add `$defaultIndexMethod` and `$defaultPriorityMethod` to `TaggedIterator` and `TaggedLocator` attributes * Add `service_closure()` to the PHP-DSL * Add support for autoconfigurable attributes on methods, properties and parameters * Make auto-aliases private by default * Add support for autowiring union and intersection types 5.3 --- * Add `ServicesConfigurator::remove()` in the PHP-DSL * Add `%env(not:...)%` processor to negate boolean values * Add support for loading autoconfiguration rules via the `#[Autoconfigure]` and `#[AutoconfigureTag]` attributes on PHP 8 * Add `#[AsTaggedItem]` attribute for defining the index and priority of classes found in tagged iterators/locators * Add autoconfigurable attributes * Add support for autowiring tagged iterators and locators via attributes on PHP 8 * Add support for per-env configuration in XML and Yaml loaders * Add `ContainerBuilder::willBeAvailable()` to help with conditional configuration * Add support an integer return value for default_index_method * Add `#[When(env: 'foo')]` to skip autoregistering a class when the env doesn't match * Add `env()` and `EnvConfigurator` in the PHP-DSL * Add support for `ConfigBuilder` in the `PhpFileLoader` * Add `ContainerConfigurator::env()` to get the current environment * Add `#[Target]` to tell how a dependency is used and hint named autowiring aliases 5.2.0 ----- * added `param()` and `abstract_arg()` in the PHP-DSL * deprecated `Definition::setPrivate()` and `Alias::setPrivate()`, use `setPublic()` instead * added support for the `#[Required]` attribute 5.1.0 ----- * deprecated `inline()` in favor of `inline_service()` and `ref()` in favor of `service()` when using the PHP-DSL * allow decorators to reference their decorated service using the special `.inner` id * added support to autowire public typed properties in php 7.4 * added support for defining method calls, a configurator, and property setters in `InlineServiceConfigurator` * added possibility to define abstract service arguments * allowed mixing "parent" and instanceof-conditionals/defaults/bindings * updated the signature of method `Definition::setDeprecated()` to `Definition::setDeprecation(string $package, string $version, string $message)` * updated the signature of method `Alias::setDeprecated()` to `Alias::setDeprecation(string $package, string $version, string $message)` * updated the signature of method `DeprecateTrait::deprecate()` to `DeprecateTrait::deprecation(string $package, string $version, string $message)` * deprecated the `Psr\Container\ContainerInterface` and `Symfony\Component\DependencyInjection\ContainerInterface` aliases of the `service_container` service, configure them explicitly instead * added class `Symfony\Component\DependencyInjection\Dumper\Preloader` to help with preloading on PHP 7.4+ * added tags `container.preload`/`.no_preload` to declare extra classes to preload/services to not preload * allowed loading and dumping tags with an attribute named "name" * deprecated `Definition::getDeprecationMessage()`, use `Definition::getDeprecation()` instead * deprecated `Alias::getDeprecationMessage()`, use `Alias::getDeprecation()` instead * added support of PHP8 static return type for withers * added `AliasDeprecatedPublicServicesPass` to deprecate public services to private 5.0.0 ----- * removed support for auto-discovered extension configuration class which does not implement `ConfigurationInterface` * removed support for non-string default env() parameters * moved `ServiceSubscriberInterface` to the `Symfony\Contracts\Service` namespace * removed `RepeatedPass` and `RepeatablePassInterface` * removed support for short factory/configurator syntax from `YamlFileLoader` * removed `ResettableContainerInterface`, use `ResetInterface` instead * added argument `$returnsClone` to `Definition::addMethodCall()` * removed `tagged`, use `tagged_iterator` instead 4.4.0 ----- * added `CheckTypeDeclarationsPass` to check injected parameters type during compilation * added support for opcache.preload by generating a preloading script in the cache folder * added support for dumping the container in one file instead of many files * deprecated support for short factories and short configurators in Yaml * added `tagged_iterator` alias for `tagged` which might be deprecated in a future version * deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition` * added support for binding iterable and tagged services * made singly-implemented interfaces detection be scoped by file * added ability to define a static priority method for tagged service * added support for improved syntax to define method calls in Yaml * made the `%env(base64:...)%` processor able to decode base64url * added ability to choose behavior of decorations on non existent decorated services 4.3.0 ----- * added `%env(trim:...)%` processor to trim a string value * added `%env(default:param_name:...)%` processor to fallback to a parameter or to null when using `%env(default::...)%` * added `%env(url:...)%` processor to convert an URL or DNS into an array of components * added `%env(query_string:...)%` processor to convert a query string into an array of key values * added support for deprecating aliases * made `ContainerParametersResource` final and not implement `Serializable` anymore * added `ReverseContainer`: a container that turns services back to their ids * added ability to define an index for a tagged collection * added ability to define an index for services in an injected service locator argument * made `ServiceLocator` implement `ServiceProviderInterface` * deprecated support for non-string default env() parameters * added `%env(require:...)%` processor to `require()` a PHP file and use the value returned from it 4.2.0 ----- * added `ContainerBuilder::registerAliasForArgument()` to support autowiring by type+name * added support for binding by type+name * added `ServiceSubscriberTrait` to ease implementing `ServiceSubscriberInterface` using methods' return types * added `ServiceLocatorArgument` and `!service_locator` config tag for creating optimized service-locators * added support for autoconfiguring bindings * added `%env(key:...)%` processor to fetch a specific key from an array * deprecated `ServiceSubscriberInterface`, use the same interface from the `Symfony\Contracts\Service` namespace instead * deprecated `ResettableContainerInterface`, use `Symfony\Contracts\Service\ResetInterface` instead 4.1.0 ----- * added support for variadics in named arguments * added PSR-11 `ContainerBagInterface` and its `ContainerBag` implementation to access parameters as-a-service * added support for service's decorators autowiring * deprecated the `TypedReference::canBeAutoregistered()` and `TypedReference::getRequiringClass()` methods * environment variables are validated when used in extension configuration * deprecated support for auto-discovered extension configuration class which does not implement `ConfigurationInterface` 4.0.0 ----- * Relying on service auto-registration while autowiring is not supported anymore. Explicitly inject your dependencies or create services whose ids are their fully-qualified class name. Before: ```php namespace App\Controller; use App\Mailer; class DefaultController { public function __construct(Mailer $mailer) { // ... } // ... } ``` ```yml services: App\Controller\DefaultController: autowire: true ``` After: ```php // same PHP code ``` ```yml services: App\Controller\DefaultController: autowire: true # or # App\Controller\DefaultController: # arguments: { $mailer: "@App\Mailer" } App\Mailer: autowire: true ``` * removed autowiring services based on the types they implement * added a third `$methodName` argument to the `getProxyFactoryCode()` method of the `DumperInterface` * removed support for autowiring types * removed `Container::isFrozen` * removed support for dumping an ucompiled container in `PhpDumper` * removed support for generating a dumped `Container` without populating the method map * removed support for case insensitive service identifiers * removed the `DefinitionDecorator` class, replaced by `ChildDefinition` * removed the `AutowireServiceResource` class and related `AutowirePass::createResourceForClass()` method * removed `LoggingFormatter`, `Compiler::getLoggingFormatter()` and `addLogMessage()` class and methods, use the `ContainerBuilder::log()` method instead * removed `FactoryReturnTypePass` * removed `ContainerBuilder::addClassResource()`, use the `addObjectResource()` or the `getReflectionClass()` method instead. * removed support for top-level anonymous services * removed silent behavior for unused attributes and elements * removed support for setting and accessing private services in `Container` * removed support for setting pre-defined services in `Container` * removed support for case insensitivity of parameter names * removed `AutowireExceptionPass` and `AutowirePass::getAutowiringExceptions()`, use `Definition::addError()` and the `DefinitionErrorExceptionPass` instead 3.4.0 ----- * moved the `ExtensionCompilerPass` to before-optimization passes with priority -1000 * deprecated "public-by-default" definitions and aliases, the new default will be "private" in 4.0 * added `EnvVarProcessorInterface` and corresponding "container.env_var_processor" tag for processing env vars * added support for ignore-on-uninitialized references * deprecated service auto-registration while autowiring * deprecated the ability to check for the initialization of a private service with the `Container::initialized()` method * deprecated support for top-level anonymous services in XML * deprecated case insensitivity of parameter names * deprecated the `ResolveDefinitionTemplatesPass` class in favor of `ResolveChildDefinitionsPass` * added `TaggedIteratorArgument` with YAML (`!tagged foo`) and XML (`<service type="tagged"/>`) support * deprecated `AutowireExceptionPass` and `AutowirePass::getAutowiringExceptions()`, use `Definition::addError()` and the `DefinitionErrorExceptionPass` instead 3.3.0 ----- * deprecated autowiring services based on the types they implement; rename (or alias) your services to their FQCN id to make them autowirable * added "ServiceSubscriberInterface" - to allow for per-class explicit service-locator definitions * added "container.service_locator" tag for defining service-locator services * added anonymous services support in YAML configuration files using the `!service` tag. * added "TypedReference" and "ServiceClosureArgument" for creating service-locator services * added `ServiceLocator` - a PSR-11 container holding a set of services to be lazily loaded * added "instanceof" section for local interface-defined configs * added prototype services for PSR4-based discovery and registration * added `ContainerBuilder::getReflectionClass()` for retrieving and tracking reflection class info * deprecated `ContainerBuilder::getClassResource()`, use `ContainerBuilder::getReflectionClass()` or `ContainerBuilder::addObjectResource()` instead * added `ContainerBuilder::fileExists()` for checking and tracking file or directory existence * deprecated autowiring-types, use aliases instead * added support for omitting the factory class name in a service definition if the definition class is set * deprecated case insensitivity of service identifiers * added "iterator" argument type for lazy iteration over a set of values and services * added file-wide configurable defaults for service attributes "public", "tags", "autowire" and "autoconfigure" * made the "class" attribute optional, using the "id" as fallback * using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and will not be supported anymore in 4.0 * deprecated the `DefinitionDecorator` class in favor of `ChildDefinition` * allow config files to be loaded using a glob pattern * [BC BREAK] the `NullDumper` class is now final 3.2.0 ----- * allowed to prioritize compiler passes by introducing a third argument to `PassConfig::addPass()`, to `Compiler::addPass` and to `ContainerBuilder::addCompilerPass()` * added support for PHP constants in YAML configuration files * deprecated the ability to set or unset a private service with the `Container::set()` method * deprecated the ability to check for the existence of a private service with the `Container::has()` method * deprecated the ability to request a private service with the `Container::get()` method * deprecated support for generating a dumped `Container` without populating the method map 3.0.0 ----- * removed all deprecated codes from 2.x versions 2.8.0 ----- * deprecated the abstract ContainerAware class in favor of ContainerAwareTrait * deprecated IntrospectableContainerInterface, to be merged with ContainerInterface in 3.0 * allowed specifying a directory to recursively load all configuration files it contains * deprecated the concept of scopes * added `Definition::setShared()` and `Definition::isShared()` * added ResettableContainerInterface to be able to reset the container to release memory on shutdown * added a way to define the priority of service decoration * added support for service autowiring 2.7.0 ----- * deprecated synchronized services 2.6.0 ----- * added new factory syntax and deprecated the old one 2.5.0 ----- * added DecoratorServicePass and a way to override a service definition (Definition::setDecoratedService()) * deprecated SimpleXMLElement class. 2.4.0 ----- * added support for expressions in service definitions * added ContainerAwareTrait to add default container aware behavior to a class 2.2.0 ----- * added Extension::isConfigEnabled() to ease working with enableable configurations * added an Extension base class with sensible defaults to be used in conjunction with the Config component. * added PrependExtensionInterface (to be able to allow extensions to prepend application configuration settings for any Bundle) 2.1.0 ----- * added IntrospectableContainerInterface (to be able to check if a service has been initialized or not) * added ConfigurationExtensionInterface * added Definition::clearTag() * component exceptions that inherit base SPL classes are now used exclusively (this includes dumped containers) * [BC BREAK] fixed unescaping of class arguments, method ParameterBag::unescapeValue() was made public ChildDefinition.php 0000644 00000005247 15120140334 0010312 0 ustar 00 <?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; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; /** * This definition extends another definition. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class ChildDefinition extends Definition { private $parent; /** * @param string $parent The id of Definition instance to decorate */ public function __construct(string $parent) { $this->parent = $parent; } /** * Returns the Definition to inherit from. * * @return string */ public function getParent() { return $this->parent; } /** * Sets the Definition to inherit from. * * @return $this */ public function setParent(string $parent) { $this->parent = $parent; return $this; } /** * Gets an argument to pass to the service constructor/factory method. * * If replaceArgument() has been used to replace an argument, this method * will return the replacement value. * * @param int|string $index * * @return mixed * * @throws OutOfBoundsException When the argument does not exist */ public function getArgument($index) { if (\array_key_exists('index_'.$index, $this->arguments)) { return $this->arguments['index_'.$index]; } return parent::getArgument($index); } /** * You should always use this method when overwriting existing arguments * of the parent definition. * * If you directly call setArguments() keep in mind that you must follow * certain conventions when you want to overwrite the arguments of the * parent definition, otherwise your arguments will only be appended. * * @param int|string $index * @param mixed $value * * @return $this * * @throws InvalidArgumentException when $index isn't an integer */ public function replaceArgument($index, $value) { if (\is_int($index)) { $this->arguments['index_'.$index] = $value; } elseif (str_starts_with($index, '$')) { $this->arguments[$index] = $value; } else { throw new InvalidArgumentException('The argument must be an existing index or the name of a constructor\'s parameter.'); } return $this; } } Container.php 0000644 00000033357 15120140334 0007203 0 ustar 00 <?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; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceLocator as ArgumentServiceLocator; use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Contracts\Service\ResetInterface; // Help opcache.preload discover always-needed symbols class_exists(RewindableGenerator::class); class_exists(ArgumentServiceLocator::class); /** * Container is a dependency injection container. * * It gives access to object instances (services). * Services and parameters are simple key/pair stores. * The container can have four possible behaviors when a service * does not exist (or is not initialized for the last case): * * * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception at compilation time (the default) * * NULL_ON_INVALID_REFERENCE: Returns null * * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference * (for instance, ignore a setter if the service does not exist) * * IGNORE_ON_UNINITIALIZED_REFERENCE: Ignores/returns null for uninitialized services or invalid references * * RUNTIME_EXCEPTION_ON_INVALID_REFERENCE: Throws an exception at runtime * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class Container implements ContainerInterface, ResetInterface { protected $parameterBag; protected $services = []; protected $privates = []; protected $fileMap = []; protected $methodMap = []; protected $factories = []; protected $aliases = []; protected $loading = []; protected $resolving = []; protected $syntheticIds = []; private $envCache = []; private $compiled = false; private $getEnv; public function __construct(ParameterBagInterface $parameterBag = null) { $this->parameterBag = $parameterBag ?? new EnvPlaceholderParameterBag(); } /** * Compiles the container. * * This method does two things: * * * Parameter values are resolved; * * The parameter bag is frozen. */ public function compile() { $this->parameterBag->resolve(); $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); $this->compiled = true; } /** * Returns true if the container is compiled. * * @return bool */ public function isCompiled() { return $this->compiled; } /** * Gets the service container parameter bag. * * @return ParameterBagInterface */ public function getParameterBag() { return $this->parameterBag; } /** * Gets a parameter. * * @return array|bool|string|int|float|\UnitEnum|null * * @throws InvalidArgumentException if the parameter is not defined */ public function getParameter(string $name) { return $this->parameterBag->get($name); } /** * @return bool */ public function hasParameter(string $name) { return $this->parameterBag->has($name); } /** * Sets a parameter. * * @param string $name The parameter name * @param array|bool|string|int|float|\UnitEnum|null $value The parameter value */ public function setParameter(string $name, $value) { $this->parameterBag->set($name, $value); } /** * Sets a service. * * Setting a synthetic service to null resets it: has() returns false and get() * behaves in the same way as if the service was never created. */ public function set(string $id, ?object $service) { // Runs the internal initializer; used by the dumped container to include always-needed files if (isset($this->privates['service_container']) && $this->privates['service_container'] instanceof \Closure) { $initialize = $this->privates['service_container']; unset($this->privates['service_container']); $initialize(); } if ('service_container' === $id) { throw new InvalidArgumentException('You cannot set service "service_container".'); } if (!(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) { if (isset($this->syntheticIds[$id]) || !isset($this->getRemovedIds()[$id])) { // no-op } elseif (null === $service) { throw new InvalidArgumentException(sprintf('The "%s" service is private, you cannot unset it.', $id)); } else { throw new InvalidArgumentException(sprintf('The "%s" service is private, you cannot replace it.', $id)); } } elseif (isset($this->services[$id])) { throw new InvalidArgumentException(sprintf('The "%s" service is already initialized, you cannot replace it.', $id)); } if (isset($this->aliases[$id])) { unset($this->aliases[$id]); } if (null === $service) { unset($this->services[$id]); return; } $this->services[$id] = $service; } /** * Returns true if the given service is defined. * * @param string $id The service identifier * * @return bool */ public function has(string $id) { if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } if (isset($this->services[$id])) { return true; } if ('service_container' === $id) { return true; } return isset($this->fileMap[$id]) || isset($this->methodMap[$id]); } /** * Gets a service. * * @return object|null * * @throws ServiceCircularReferenceException When a circular reference is detected * @throws ServiceNotFoundException When the service is not defined * @throws \Exception if an exception has been thrown when the service has been resolved * * @see Reference */ public function get(string $id, int $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1) { return $this->services[$id] ?? $this->services[$id = $this->aliases[$id] ?? $id] ?? ('service_container' === $id ? $this : ($this->factories[$id] ?? [$this, 'make'])($id, $invalidBehavior)); } /** * Creates a service. * * As a separate method to allow "get()" to use the really fast `??` operator. */ private function make(string $id, int $invalidBehavior) { if (isset($this->loading[$id])) { throw new ServiceCircularReferenceException($id, array_merge(array_keys($this->loading), [$id])); } $this->loading[$id] = true; try { if (isset($this->fileMap[$id])) { return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]); } elseif (isset($this->methodMap[$id])) { return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}(); } } catch (\Exception $e) { unset($this->services[$id]); throw $e; } finally { unset($this->loading[$id]); } if (/* self::EXCEPTION_ON_INVALID_REFERENCE */ 1 === $invalidBehavior) { if (!$id) { throw new ServiceNotFoundException($id); } if (isset($this->syntheticIds[$id])) { throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service is synthetic, it needs to be set at boot time before it can be used.', $id)); } if (isset($this->getRemovedIds()[$id])) { throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.', $id)); } $alternatives = []; foreach ($this->getServiceIds() as $knownId) { if ('' === $knownId || '.' === $knownId[0]) { continue; } $lev = levenshtein($id, $knownId); if ($lev <= \strlen($id) / 3 || str_contains($knownId, $id)) { $alternatives[] = $knownId; } } throw new ServiceNotFoundException($id, null, null, $alternatives); } return null; } /** * Returns true if the given service has actually been initialized. * * @return bool */ public function initialized(string $id) { if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } if ('service_container' === $id) { return false; } return isset($this->services[$id]); } /** * {@inheritdoc} */ public function reset() { $services = $this->services + $this->privates; $this->services = $this->factories = $this->privates = []; foreach ($services as $service) { try { if ($service instanceof ResetInterface) { $service->reset(); } } catch (\Throwable $e) { continue; } } } /** * Gets all service ids. * * @return string[] */ public function getServiceIds() { return array_map('strval', array_unique(array_merge(['service_container'], array_keys($this->fileMap), array_keys($this->methodMap), array_keys($this->aliases), array_keys($this->services)))); } /** * Gets service ids that existed at compile time. * * @return array */ public function getRemovedIds() { return []; } /** * Camelizes a string. * * @return string */ public static function camelize(string $id) { return strtr(ucwords(strtr($id, ['_' => ' ', '.' => '_ ', '\\' => '_ '])), [' ' => '']); } /** * A string to underscore. * * @return string */ public static function underscore(string $id) { return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], str_replace('_', '.', $id))); } /** * Creates a service by requiring its factory file. */ protected function load(string $file) { return require $file; } /** * Fetches a variable from the environment. * * @return mixed * * @throws EnvNotFoundException When the environment variable is not found and has no default value */ protected function getEnv(string $name) { if (isset($this->resolving[$envName = "env($name)"])) { throw new ParameterCircularReferenceException(array_keys($this->resolving)); } if (isset($this->envCache[$name]) || \array_key_exists($name, $this->envCache)) { return $this->envCache[$name]; } if (!$this->has($id = 'container.env_var_processors_locator')) { $this->set($id, new ServiceLocator([])); } if (!$this->getEnv) { $this->getEnv = \Closure::fromCallable([$this, 'getEnv']); } $processors = $this->get($id); if (false !== $i = strpos($name, ':')) { $prefix = substr($name, 0, $i); $localName = substr($name, 1 + $i); } else { $prefix = 'string'; $localName = $name; } $processor = $processors->has($prefix) ? $processors->get($prefix) : new EnvVarProcessor($this); $this->resolving[$envName] = true; try { return $this->envCache[$name] = $processor->getEnv($prefix, $localName, $this->getEnv); } finally { unset($this->resolving[$envName]); } } /** * @param string|false $registry * @param string|bool $load * * @return mixed * * @internal */ final protected function getService($registry, string $id, ?string $method, $load) { if ('service_container' === $id) { return $this; } if (\is_string($load)) { throw new RuntimeException($load); } if (null === $method) { return false !== $registry ? $this->{$registry}[$id] ?? null : null; } if (false !== $registry) { return $this->{$registry}[$id] ?? $this->{$registry}[$id] = $load ? $this->load($method) : $this->{$method}(); } if (!$load) { return $this->{$method}(); } return ($factory = $this->factories[$id] ?? $this->factories['service_container'][$id] ?? null) ? $factory() : $this->load($method); } private function __clone() { } } ContainerAwareInterface.php 0000644 00000001116 15120140334 0011770 0 ustar 00 <?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; /** * ContainerAwareInterface should be implemented by classes that depends on a Container. * * @author Fabien Potencier <fabien@symfony.com> */ interface ContainerAwareInterface { /** * Sets the container. */ public function setContainer(ContainerInterface $container = null); } ContainerAwareTrait.php 0000644 00000001127 15120140334 0011155 0 ustar 00 <?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; /** * ContainerAware trait. * * @author Fabien Potencier <fabien@symfony.com> */ trait ContainerAwareTrait { /** * @var ContainerInterface */ protected $container; public function setContainer(ContainerInterface $container = null) { $this->container = $container; } } ContainerBuilder.php 0000644 00000160750 15120140334 0010510 0 ustar 00 <?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; use Composer\InstalledVersions; use Psr\Container\ContainerInterface as PsrContainerInterface; use Symfony\Component\Config\Resource\ClassExistenceResource; use Symfony\Component\Config\Resource\ComposerResource; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Config\Resource\FileExistenceResource; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Resource\GlobResource; use Symfony\Component\Config\Resource\ReflectionClassResource; use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocator; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Attribute\Target; use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass; use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; /** * ContainerBuilder is a DI container that provides an API to easily describe services. * * @author Fabien Potencier <fabien@symfony.com> */ class ContainerBuilder extends Container implements TaggedContainerInterface { /** * @var array<string, ExtensionInterface> */ private $extensions = []; /** * @var array<string, ExtensionInterface> */ private $extensionsByNs = []; /** * @var array<string, Definition> */ private $definitions = []; /** * @var array<string, Alias> */ private $aliasDefinitions = []; /** * @var array<string, ResourceInterface> */ private $resources = []; /** * @var array<string, array<array<string, mixed>>> */ private $extensionConfigs = []; /** * @var Compiler */ private $compiler; /** * @var bool */ private $trackResources; /** * @var InstantiatorInterface|null */ private $proxyInstantiator; /** * @var ExpressionLanguage|null */ private $expressionLanguage; /** * @var ExpressionFunctionProviderInterface[] */ private $expressionLanguageProviders = []; /** * @var string[] with tag names used by findTaggedServiceIds */ private $usedTags = []; /** * @var string[][] a map of env var names to their placeholders */ private $envPlaceholders = []; /** * @var int[] a map of env vars to their resolution counter */ private $envCounters = []; /** * @var string[] the list of vendor directories */ private $vendors; /** * @var array<string, ChildDefinition> */ private $autoconfiguredInstanceof = []; /** * @var array<string, callable> */ private $autoconfiguredAttributes = []; /** * @var array<string, bool> */ private $removedIds = []; /** * @var array<int, bool> */ private $removedBindingIds = []; private const INTERNAL_TYPES = [ 'int' => true, 'float' => true, 'string' => true, 'bool' => true, 'resource' => true, 'object' => true, 'array' => true, 'null' => true, 'callable' => true, 'iterable' => true, 'mixed' => true, ]; public function __construct(ParameterBagInterface $parameterBag = null) { parent::__construct($parameterBag); $this->trackResources = interface_exists(ResourceInterface::class); $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true)); $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false))->setDeprecated('symfony/dependency-injection', '5.1', $deprecationMessage = 'The "%alias_id%" autowiring alias is deprecated. Define it explicitly in your app if you want to keep using it.'); $this->setAlias(ContainerInterface::class, new Alias('service_container', false))->setDeprecated('symfony/dependency-injection', '5.1', $deprecationMessage); } /** * @var array<string, \ReflectionClass> */ private $classReflectors; /** * Sets the track resources flag. * * If you are not using the loaders and therefore don't want * to depend on the Config component, set this flag to false. */ public function setResourceTracking(bool $track) { $this->trackResources = $track; } /** * Checks if resources are tracked. * * @return bool */ public function isTrackingResources() { return $this->trackResources; } /** * Sets the instantiator to be used when fetching proxies. */ public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator) { $this->proxyInstantiator = $proxyInstantiator; } public function registerExtension(ExtensionInterface $extension) { $this->extensions[$extension->getAlias()] = $extension; if (false !== $extension->getNamespace()) { $this->extensionsByNs[$extension->getNamespace()] = $extension; } } /** * Returns an extension by alias or namespace. * * @return ExtensionInterface * * @throws LogicException if the extension is not registered */ public function getExtension(string $name) { if (isset($this->extensions[$name])) { return $this->extensions[$name]; } if (isset($this->extensionsByNs[$name])) { return $this->extensionsByNs[$name]; } throw new LogicException(sprintf('Container extension "%s" is not registered.', $name)); } /** * Returns all registered extensions. * * @return array<string, ExtensionInterface> */ public function getExtensions() { return $this->extensions; } /** * Checks if we have an extension. * * @return bool */ public function hasExtension(string $name) { return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]); } /** * Returns an array of resources loaded to build this configuration. * * @return ResourceInterface[] */ public function getResources() { return array_values($this->resources); } /** * @return $this */ public function addResource(ResourceInterface $resource) { if (!$this->trackResources) { return $this; } if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) { return $this; } $this->resources[(string) $resource] = $resource; return $this; } /** * Sets the resources for this configuration. * * @param array<string, ResourceInterface> $resources * * @return $this */ public function setResources(array $resources) { if (!$this->trackResources) { return $this; } $this->resources = $resources; return $this; } /** * Adds the object class hierarchy as resources. * * @param object|string $object An object instance or class name * * @return $this */ public function addObjectResource($object) { if ($this->trackResources) { if (\is_object($object)) { $object = \get_class($object); } if (!isset($this->classReflectors[$object])) { $this->classReflectors[$object] = new \ReflectionClass($object); } $class = $this->classReflectors[$object]; foreach ($class->getInterfaceNames() as $name) { if (null === $interface = &$this->classReflectors[$name]) { $interface = new \ReflectionClass($name); } $file = $interface->getFileName(); if (false !== $file && file_exists($file)) { $this->fileExists($file); } } do { $file = $class->getFileName(); if (false !== $file && file_exists($file)) { $this->fileExists($file); } foreach ($class->getTraitNames() as $name) { $this->addObjectResource($name); } } while ($class = $class->getParentClass()); } return $this; } /** * Retrieves the requested reflection class and registers it for resource tracking. * * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true * * @final */ public function getReflectionClass(?string $class, bool $throw = true): ?\ReflectionClass { if (!$class = $this->getParameterBag()->resolveValue($class)) { return null; } if (isset(self::INTERNAL_TYPES[$class])) { return null; } $resource = $classReflector = null; try { if (isset($this->classReflectors[$class])) { $classReflector = $this->classReflectors[$class]; } elseif (class_exists(ClassExistenceResource::class)) { $resource = new ClassExistenceResource($class, false); $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class); } else { $classReflector = class_exists($class) ? new \ReflectionClass($class) : false; } } catch (\ReflectionException $e) { if ($throw) { throw $e; } } if ($this->trackResources) { if (!$classReflector) { $this->addResource($resource ?? new ClassExistenceResource($class, false)); } elseif (!$classReflector->isInternal()) { $path = $classReflector->getFileName(); if (!$this->inVendors($path)) { $this->addResource(new ReflectionClassResource($classReflector, $this->vendors)); } } $this->classReflectors[$class] = $classReflector; } return $classReflector ?: null; } /** * Checks whether the requested file or directory exists and registers the result for resource tracking. * * @param string $path The file or directory path for which to check the existence * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed, * it will be used as pattern for tracking contents of the requested directory * * @final */ public function fileExists(string $path, $trackContents = true): bool { $exists = file_exists($path); if (!$this->trackResources || $this->inVendors($path)) { return $exists; } if (!$exists) { $this->addResource(new FileExistenceResource($path)); return $exists; } if (is_dir($path)) { if ($trackContents) { $this->addResource(new DirectoryResource($path, \is_string($trackContents) ? $trackContents : null)); } else { $this->addResource(new GlobResource($path, '/*', false)); } } elseif ($trackContents) { $this->addResource(new FileResource($path)); } return $exists; } /** * Loads the configuration for an extension. * * @param string $extension The extension alias or namespace * @param array<string, mixed>|null $values An array of values that customizes the extension * * @return $this * * @throws BadMethodCallException When this ContainerBuilder is compiled * @throws \LogicException if the extension is not registered */ public function loadFromExtension(string $extension, array $values = null) { if ($this->isCompiled()) { throw new BadMethodCallException('Cannot load from an extension on a compiled container.'); } $namespace = $this->getExtension($extension)->getAlias(); $this->extensionConfigs[$namespace][] = $values ?? []; return $this; } /** * Adds a compiler pass. * * @param string $type The type of compiler pass * @param int $priority Used to sort the passes * * @return $this */ public function addCompilerPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { $this->getCompiler()->addPass($pass, $type, $priority); $this->addObjectResource($pass); return $this; } /** * Returns the compiler pass config which can then be modified. * * @return PassConfig */ public function getCompilerPassConfig() { return $this->getCompiler()->getPassConfig(); } /** * Returns the compiler. * * @return Compiler */ public function getCompiler() { if (null === $this->compiler) { $this->compiler = new Compiler(); } return $this->compiler; } /** * Sets a service. * * @throws BadMethodCallException When this ContainerBuilder is compiled */ public function set(string $id, ?object $service) { if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) { // setting a synthetic service on a compiled container is alright throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id)); } unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]); parent::set($id, $service); } /** * Removes a service definition. */ public function removeDefinition(string $id) { if (isset($this->definitions[$id])) { unset($this->definitions[$id]); $this->removedIds[$id] = true; } } /** * Returns true if the given service is defined. * * @param string $id The service identifier * * @return bool */ public function has(string $id) { return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id); } /** * @return object|null * * @throws InvalidArgumentException when no definitions are available * @throws ServiceCircularReferenceException When a circular reference is detected * @throws ServiceNotFoundException When the service is not defined * @throws \Exception * * @see Reference */ public function get(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { if ($this->isCompiled() && isset($this->removedIds[$id])) { return ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $invalidBehavior ? parent::get($id) : null; } return $this->doGet($id, $invalidBehavior); } private function doGet(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = null, bool $isConstructorArgument = false) { if (isset($inlineServices[$id])) { return $inlineServices[$id]; } if (null === $inlineServices) { $isConstructorArgument = true; $inlineServices = []; } try { if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) { return $this->privates[$id] ?? parent::get($id, $invalidBehavior); } if (null !== $service = $this->privates[$id] ?? parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { return $service; } } catch (ServiceCircularReferenceException $e) { if ($isConstructorArgument) { throw $e; } } if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { $alias = $this->aliasDefinitions[$id]; if ($alias->isDeprecated()) { $deprecation = $alias->getDeprecation($id); trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } return $this->doGet((string) $alias, $invalidBehavior, $inlineServices, $isConstructorArgument); } try { $definition = $this->getDefinition($id); } catch (ServiceNotFoundException $e) { if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE < $invalidBehavior) { return null; } throw $e; } if ($definition->hasErrors() && $e = $definition->getErrors()) { throw new RuntimeException(reset($e)); } if ($isConstructorArgument) { $this->loading[$id] = true; } try { return $this->createService($definition, $inlineServices, $isConstructorArgument, $id); } finally { if ($isConstructorArgument) { unset($this->loading[$id]); } } } /** * Merges a ContainerBuilder with the current ContainerBuilder configuration. * * Service definitions overrides the current defined ones. * * But for parameters, they are overridden by the current ones. It allows * the parameters passed to the container constructor to have precedence * over the loaded ones. * * $container = new ContainerBuilder(new ParameterBag(['foo' => 'bar'])); * $loader = new LoaderXXX($container); * $loader->load('resource_name'); * $container->register('foo', 'stdClass'); * * In the above example, even if the loaded resource defines a foo * parameter, the value will still be 'bar' as defined in the ContainerBuilder * constructor. * * @throws BadMethodCallException When this ContainerBuilder is compiled */ public function merge(self $container) { if ($this->isCompiled()) { throw new BadMethodCallException('Cannot merge on a compiled container.'); } $this->addDefinitions($container->getDefinitions()); $this->addAliases($container->getAliases()); $this->getParameterBag()->add($container->getParameterBag()->all()); if ($this->trackResources) { foreach ($container->getResources() as $resource) { $this->addResource($resource); } } foreach ($this->extensions as $name => $extension) { if (!isset($this->extensionConfigs[$name])) { $this->extensionConfigs[$name] = []; } $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name)); } if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) { $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders(); $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag()); } else { $envPlaceholders = []; } foreach ($container->envCounters as $env => $count) { if (!$count && !isset($envPlaceholders[$env])) { continue; } if (!isset($this->envCounters[$env])) { $this->envCounters[$env] = $count; } else { $this->envCounters[$env] += $count; } } foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) { if (isset($this->autoconfiguredInstanceof[$interface])) { throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface)); } $this->autoconfiguredInstanceof[$interface] = $childDefinition; } foreach ($container->getAutoconfiguredAttributes() as $attribute => $configurator) { if (isset($this->autoconfiguredAttributes[$attribute])) { throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same attribute.', $attribute)); } $this->autoconfiguredAttributes[$attribute] = $configurator; } } /** * Returns the configuration array for the given extension. * * @return array<array<string, mixed>> */ public function getExtensionConfig(string $name) { if (!isset($this->extensionConfigs[$name])) { $this->extensionConfigs[$name] = []; } return $this->extensionConfigs[$name]; } /** * Prepends a config array to the configs of the given extension. * * @param array<string, mixed> $config */ public function prependExtensionConfig(string $name, array $config) { if (!isset($this->extensionConfigs[$name])) { $this->extensionConfigs[$name] = []; } array_unshift($this->extensionConfigs[$name], $config); } /** * Compiles the container. * * This method passes the container to compiler * passes whose job is to manipulate and optimize * the container. * * The main compiler passes roughly do four things: * * * The extension configurations are merged; * * Parameter values are resolved; * * The parameter bag is frozen; * * Extension loading is disabled. * * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current * env vars or be replaced by uniquely identifiable placeholders. * Set to "true" when you want to use the current ContainerBuilder * directly, keep to "false" when the container is dumped instead. */ public function compile(bool $resolveEnvPlaceholders = false) { $compiler = $this->getCompiler(); if ($this->trackResources) { foreach ($compiler->getPassConfig()->getPasses() as $pass) { $this->addObjectResource($pass); } } $bag = $this->getParameterBag(); if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) { $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000); } $compiler->compile($this); foreach ($this->definitions as $id => $definition) { if ($this->trackResources && $definition->isLazy()) { $this->getReflectionClass($definition->getClass()); } } $this->extensionConfigs = []; if ($bag instanceof EnvPlaceholderParameterBag) { if ($resolveEnvPlaceholders) { $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true)); } $this->envPlaceholders = $bag->getEnvPlaceholders(); } parent::compile(); foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) { if (!$definition->isPublic() || $definition->isPrivate()) { $this->removedIds[$id] = true; } } } /** * {@inheritdoc} */ public function getServiceIds() { return array_map('strval', array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds()))); } /** * Gets removed service or alias ids. * * @return array<string, bool> */ public function getRemovedIds() { return $this->removedIds; } /** * Adds the service aliases. * * @param array<string, string|Alias> $aliases */ public function addAliases(array $aliases) { foreach ($aliases as $alias => $id) { $this->setAlias($alias, $id); } } /** * Sets the service aliases. * * @param array<string, string|Alias> $aliases */ public function setAliases(array $aliases) { $this->aliasDefinitions = []; $this->addAliases($aliases); } /** * Sets an alias for an existing service. * * @param string $alias The alias to create * @param string|Alias $id The service to alias * * @return Alias * * @throws InvalidArgumentException if the id is not a string or an Alias * @throws InvalidArgumentException if the alias is for itself */ public function setAlias(string $alias, $id) { if ('' === $alias || '\\' === $alias[-1] || \strlen($alias) !== strcspn($alias, "\0\r\n'")) { throw new InvalidArgumentException(sprintf('Invalid alias id: "%s".', $alias)); } if (\is_string($id)) { $id = new Alias($id); } elseif (!$id instanceof Alias) { throw new InvalidArgumentException('$id must be a string, or an Alias object.'); } if ($alias === (string) $id) { throw new InvalidArgumentException(sprintf('An alias cannot reference itself, got a circular reference on "%s".', $alias)); } unset($this->definitions[$alias], $this->removedIds[$alias]); return $this->aliasDefinitions[$alias] = $id; } public function removeAlias(string $alias) { if (isset($this->aliasDefinitions[$alias])) { unset($this->aliasDefinitions[$alias]); $this->removedIds[$alias] = true; } } /** * @return bool */ public function hasAlias(string $id) { return isset($this->aliasDefinitions[$id]); } /** * @return array<string, Alias> */ public function getAliases() { return $this->aliasDefinitions; } /** * @return Alias * * @throws InvalidArgumentException if the alias does not exist */ public function getAlias(string $id) { if (!isset($this->aliasDefinitions[$id])) { throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id)); } return $this->aliasDefinitions[$id]; } /** * Registers a service definition. * * This methods allows for simple registration of service definition * with a fluid interface. * * @return Definition */ public function register(string $id, string $class = null) { return $this->setDefinition($id, new Definition($class)); } /** * Registers an autowired service definition. * * This method implements a shortcut for using setDefinition() with * an autowired definition. * * @return Definition */ public function autowire(string $id, string $class = null) { return $this->setDefinition($id, (new Definition($class))->setAutowired(true)); } /** * Adds the service definitions. * * @param array<string, Definition> $definitions */ public function addDefinitions(array $definitions) { foreach ($definitions as $id => $definition) { $this->setDefinition($id, $definition); } } /** * Sets the service definitions. * * @param array<string, Definition> $definitions */ public function setDefinitions(array $definitions) { $this->definitions = []; $this->addDefinitions($definitions); } /** * Gets all service definitions. * * @return array<string, Definition> */ public function getDefinitions() { return $this->definitions; } /** * Sets a service definition. * * @return Definition * * @throws BadMethodCallException When this ContainerBuilder is compiled */ public function setDefinition(string $id, Definition $definition) { if ($this->isCompiled()) { throw new BadMethodCallException('Adding definition to a compiled container is not allowed.'); } if ('' === $id || '\\' === $id[-1] || \strlen($id) !== strcspn($id, "\0\r\n'")) { throw new InvalidArgumentException(sprintf('Invalid service id: "%s".', $id)); } unset($this->aliasDefinitions[$id], $this->removedIds[$id]); return $this->definitions[$id] = $definition; } /** * Returns true if a service definition exists under the given identifier. * * @return bool */ public function hasDefinition(string $id) { return isset($this->definitions[$id]); } /** * Gets a service definition. * * @return Definition * * @throws ServiceNotFoundException if the service definition does not exist */ public function getDefinition(string $id) { if (!isset($this->definitions[$id])) { throw new ServiceNotFoundException($id); } return $this->definitions[$id]; } /** * Gets a service definition by id or alias. * * The method "unaliases" recursively to return a Definition instance. * * @return Definition * * @throws ServiceNotFoundException if the service definition does not exist */ public function findDefinition(string $id) { $seen = []; while (isset($this->aliasDefinitions[$id])) { $id = (string) $this->aliasDefinitions[$id]; if (isset($seen[$id])) { $seen = array_values($seen); $seen = \array_slice($seen, array_search($id, $seen)); $seen[] = $id; throw new ServiceCircularReferenceException($id, $seen); } $seen[$id] = $id; } return $this->getDefinition($id); } /** * Creates a service for a service definition. * * @return mixed * * @throws RuntimeException When the factory definition is incomplete * @throws RuntimeException When the service is a synthetic service * @throws InvalidArgumentException When configure callable is not callable */ private function createService(Definition $definition, array &$inlineServices, bool $isConstructorArgument = false, string $id = null, bool $tryProxy = true) { if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) { return $inlineServices[$h]; } if ($definition instanceof ChildDefinition) { throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id)); } if ($definition->isSynthetic()) { throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); } if ($definition->isDeprecated()) { $deprecation = $definition->getDeprecation($id); trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } if ($tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) { $proxy = $proxy->instantiateProxy( $this, $definition, $id, function () use ($definition, &$inlineServices, $id) { return $this->createService($definition, $inlineServices, true, $id, false); } ); $this->shareService($definition, $proxy, $id, $inlineServices); return $proxy; } $parameterBag = $this->getParameterBag(); if (null !== $definition->getFile()) { require_once $parameterBag->resolveValue($definition->getFile()); } $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices, $isConstructorArgument); if (null !== $factory = $definition->getFactory()) { if (\is_array($factory)) { $factory = [$this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices, $isConstructorArgument), $factory[1]]; } elseif (!\is_string($factory)) { throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory.', $id)); } } if (null !== $id && $definition->isShared() && (isset($this->services[$id]) || isset($this->privates[$id])) && ($tryProxy || !$definition->isLazy())) { return $this->services[$id] ?? $this->privates[$id]; } if (!array_is_list($arguments)) { $arguments = array_combine(array_map(function ($k) { return preg_replace('/^.*\\$/', '', $k); }, array_keys($arguments)), $arguments); } if (null !== $factory) { $service = $factory(...$arguments); if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) { $r = new \ReflectionClass($factory[0]); if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) { trigger_deprecation('', '', 'The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name); } } } else { $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass())); $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) { trigger_deprecation('', '', 'The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name); } } $lastWitherIndex = null; foreach ($definition->getMethodCalls() as $k => $call) { if ($call[2] ?? false) { $lastWitherIndex = $k; } } if (null === $lastWitherIndex && ($tryProxy || !$definition->isLazy())) { // share only if proxying failed, or if not a proxy, and if no withers are found $this->shareService($definition, $service, $id, $inlineServices); } $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices); foreach ($properties as $name => $value) { $service->$name = $value; } foreach ($definition->getMethodCalls() as $k => $call) { $service = $this->callMethod($service, $call, $inlineServices); if ($lastWitherIndex === $k && ($tryProxy || !$definition->isLazy())) { // share only if proxying failed, or if not a proxy, and this is the last wither $this->shareService($definition, $service, $id, $inlineServices); } } if ($callable = $definition->getConfigurator()) { if (\is_array($callable)) { $callable[0] = $parameterBag->resolveValue($callable[0]); if ($callable[0] instanceof Reference) { $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices); } elseif ($callable[0] instanceof Definition) { $callable[0] = $this->createService($callable[0], $inlineServices); } } if (!\is_callable($callable)) { throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_debug_type($service))); } $callable($service); } return $service; } /** * Replaces service references by the real service instance and evaluates expressions. * * @param mixed $value * * @return mixed The same value with all service references replaced by * the real service instances and all expressions evaluated */ public function resolveServices($value) { return $this->doResolveServices($value); } private function doResolveServices($value, array &$inlineServices = [], bool $isConstructorArgument = false) { if (\is_array($value)) { foreach ($value as $k => $v) { $value[$k] = $this->doResolveServices($v, $inlineServices, $isConstructorArgument); } } elseif ($value instanceof ServiceClosureArgument) { $reference = $value->getValues()[0]; $value = function () use ($reference) { return $this->resolveServices($reference); }; } elseif ($value instanceof IteratorArgument) { $value = new RewindableGenerator(function () use ($value, &$inlineServices) { foreach ($value->getValues() as $k => $v) { foreach (self::getServiceConditionals($v) as $s) { if (!$this->has($s)) { continue 2; } } foreach (self::getInitializedConditionals($v) as $s) { if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) { continue 2; } } yield $k => $this->doResolveServices($v, $inlineServices); } }, function () use ($value): int { $count = 0; foreach ($value->getValues() as $v) { foreach (self::getServiceConditionals($v) as $s) { if (!$this->has($s)) { continue 2; } } foreach (self::getInitializedConditionals($v) as $s) { if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) { continue 2; } } ++$count; } return $count; }); } elseif ($value instanceof ServiceLocatorArgument) { $refs = $types = []; foreach ($value->getValues() as $k => $v) { if ($v) { $refs[$k] = [$v]; $types[$k] = $v instanceof TypedReference ? $v->getType() : '?'; } } $value = new ServiceLocator(\Closure::fromCallable([$this, 'resolveServices']), $refs, $types); } elseif ($value instanceof Reference) { $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices, $isConstructorArgument); } elseif ($value instanceof Definition) { $value = $this->createService($value, $inlineServices, $isConstructorArgument); } elseif ($value instanceof Parameter) { $value = $this->getParameter((string) $value); } elseif ($value instanceof Expression) { $value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this]); } elseif ($value instanceof AbstractArgument) { throw new RuntimeException($value->getTextWithContext()); } return $value; } /** * Returns service ids for a given tag. * * Example: * * $container->register('foo')->addTag('my.tag', ['hello' => 'world']); * * $serviceIds = $container->findTaggedServiceIds('my.tag'); * foreach ($serviceIds as $serviceId => $tags) { * foreach ($tags as $tag) { * echo $tag['hello']; * } * } * * @return array<string, array> An array of tags with the tagged service as key, holding a list of attribute arrays */ public function findTaggedServiceIds(string $name, bool $throwOnAbstract = false) { $this->usedTags[] = $name; $tags = []; foreach ($this->getDefinitions() as $id => $definition) { if ($definition->hasTag($name)) { if ($throwOnAbstract && $definition->isAbstract()) { throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name)); } $tags[$id] = $definition->getTag($name); } } return $tags; } /** * Returns all tags the defined services use. * * @return string[] */ public function findTags() { $tags = []; foreach ($this->getDefinitions() as $id => $definition) { $tags[] = array_keys($definition->getTags()); } return array_unique(array_merge([], ...$tags)); } /** * Returns all tags not queried by findTaggedServiceIds. * * @return string[] */ public function findUnusedTags() { return array_values(array_diff($this->findTags(), $this->usedTags)); } public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) { $this->expressionLanguageProviders[] = $provider; } /** * @return ExpressionFunctionProviderInterface[] */ public function getExpressionLanguageProviders() { return $this->expressionLanguageProviders; } /** * Returns a ChildDefinition that will be used for autoconfiguring the interface/class. * * @return ChildDefinition */ public function registerForAutoconfiguration(string $interface) { if (!isset($this->autoconfiguredInstanceof[$interface])) { $this->autoconfiguredInstanceof[$interface] = new ChildDefinition(''); } return $this->autoconfiguredInstanceof[$interface]; } /** * Registers an attribute that will be used for autoconfiguring annotated classes. * * The third argument passed to the callable is the reflector of the * class/method/property/parameter that the attribute targets. Using one or many of * \ReflectionClass|\ReflectionMethod|\ReflectionProperty|\ReflectionParameter as a type-hint * for this argument allows filtering which attributes should be passed to the callable. * * @template T * * @param class-string<T> $attributeClass * @param callable(ChildDefinition, T, \Reflector): void $configurator */ public function registerAttributeForAutoconfiguration(string $attributeClass, callable $configurator): void { $this->autoconfiguredAttributes[$attributeClass] = $configurator; } /** * Registers an autowiring alias that only binds to a specific argument name. * * The argument name is derived from $name if provided (from $id otherwise) * using camel case: "foo.bar" or "foo_bar" creates an alias bound to * "$fooBar"-named arguments with $type as type-hint. Such arguments will * receive the service $id when autowiring is used. */ public function registerAliasForArgument(string $id, string $type, string $name = null): Alias { $name = (new Target($name ?? $id))->name; if (!preg_match('/^[a-zA-Z_\x7f-\xff]/', $name)) { throw new InvalidArgumentException(sprintf('Invalid argument name "%s" for service "%s": the first character must be a letter.', $name, $id)); } return $this->setAlias($type.' $'.$name, $id); } /** * Returns an array of ChildDefinition[] keyed by interface. * * @return array<string, ChildDefinition> */ public function getAutoconfiguredInstanceof() { return $this->autoconfiguredInstanceof; } /** * @return array<string, callable> */ public function getAutoconfiguredAttributes(): array { return $this->autoconfiguredAttributes; } /** * Resolves env parameter placeholders in a string or an array. * * @param mixed $value The value to resolve * @param string|true|null $format A sprintf() format returning the replacement for each env var name or * null to resolve back to the original "%env(VAR)%" format or * true to resolve to the actual values of the referenced env vars * @param array &$usedEnvs Env vars found while resolving are added to this array * * @return mixed The value with env parameters resolved if a string or an array is passed */ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null) { if (null === $format) { $format = '%%env(%s)%%'; } $bag = $this->getParameterBag(); if (true === $format) { $value = $bag->resolveValue($value); } if ($value instanceof Definition) { $value = (array) $value; } if (\is_array($value)) { $result = []; foreach ($value as $k => $v) { $result[\is_string($k) ? $this->resolveEnvPlaceholders($k, $format, $usedEnvs) : $k] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs); } return $result; } if (!\is_string($value) || 38 > \strlen($value) || !preg_match('/env[_(]/i', $value)) { return $value; } $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; $completed = false; foreach ($envPlaceholders as $env => $placeholders) { foreach ($placeholders as $placeholder) { if (false !== stripos($value, $placeholder)) { if (true === $format) { $resolved = $bag->escapeValue($this->getEnv($env)); } else { $resolved = sprintf($format, $env); } if ($placeholder === $value) { $value = $resolved; $completed = true; } else { if (!\is_string($resolved) && !is_numeric($resolved)) { throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type "%s" inside string value "%s".', $env, get_debug_type($resolved), $this->resolveEnvPlaceholders($value))); } $value = str_ireplace($placeholder, $resolved, $value); } $usedEnvs[$env] = $env; $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1; if ($completed) { break 2; } } } } return $value; } /** * Get statistics about env usage. * * @return int[] The number of time each env vars has been resolved */ public function getEnvCounters() { $bag = $this->getParameterBag(); $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; foreach ($envPlaceholders as $env => $placeholders) { if (!isset($this->envCounters[$env])) { $this->envCounters[$env] = 0; } } return $this->envCounters; } /** * @final */ public function log(CompilerPassInterface $pass, string $message) { $this->getCompiler()->log($pass, $this->resolveEnvPlaceholders($message)); } /** * Checks whether a class is available and will remain available in the "no-dev" mode of Composer. * * When parent packages are provided and if any of them is in dev-only mode, * the class will be considered available even if it is also in dev-only mode. */ final public static function willBeAvailable(string $package, string $class, array $parentPackages): bool { $skipDeprecation = 3 < \func_num_args() && func_get_arg(3); $hasRuntimeApi = class_exists(InstalledVersions::class); if (!$hasRuntimeApi && !$skipDeprecation) { trigger_deprecation('symfony/dependency-injection', '5.4', 'Calling "%s" when dependencies have been installed with Composer 1 is deprecated. Consider upgrading to Composer 2.', __METHOD__); } if (!class_exists($class) && !interface_exists($class, false) && !trait_exists($class, false)) { return false; } if (!$hasRuntimeApi || !InstalledVersions::isInstalled($package) || InstalledVersions::isInstalled($package, false)) { return true; } // the package is installed but in dev-mode only, check if this applies to one of the parent packages too $rootPackage = InstalledVersions::getRootPackage()['name'] ?? ''; if ('symfony/symfony' === $rootPackage) { return true; } foreach ($parentPackages as $parentPackage) { if ($rootPackage === $parentPackage || (InstalledVersions::isInstalled($parentPackage) && !InstalledVersions::isInstalled($parentPackage, false))) { return true; } } return false; } /** * Gets removed binding ids. * * @return array<int, bool> * * @internal */ public function getRemovedBindingIds(): array { return $this->removedBindingIds; } /** * Removes bindings for a service. * * @internal */ public function removeBindings(string $id) { if ($this->hasDefinition($id)) { foreach ($this->getDefinition($id)->getBindings() as $key => $binding) { [, $bindingId] = $binding->getValues(); $this->removedBindingIds[(int) $bindingId] = true; } } } /** * Returns the Service Conditionals. * * @param mixed $value An array of conditionals to return * * @return string[] * * @internal */ public static function getServiceConditionals($value): array { $services = []; if (\is_array($value)) { foreach ($value as $v) { $services = array_unique(array_merge($services, self::getServiceConditionals($v))); } } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) { $services[] = (string) $value; } return $services; } /** * Returns the initialized conditionals. * * @param mixed $value An array of conditionals to return * * @return string[] * * @internal */ public static function getInitializedConditionals($value): array { $services = []; if (\is_array($value)) { foreach ($value as $v) { $services = array_unique(array_merge($services, self::getInitializedConditionals($v))); } } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()) { $services[] = (string) $value; } return $services; } /** * Computes a reasonably unique hash of a value. * * @param mixed $value A serializable value * * @return string */ public static function hash($value) { $hash = substr(base64_encode(hash('sha256', serialize($value), true)), 0, 7); return str_replace(['/', '+'], ['.', '_'], $hash); } /** * {@inheritdoc} */ protected function getEnv(string $name) { $value = parent::getEnv($name); $bag = $this->getParameterBag(); if (!\is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) { return $value; } $envPlaceholders = $bag->getEnvPlaceholders(); if (isset($envPlaceholders[$name][$value])) { $bag = new ParameterBag($bag->all()); return $bag->unescapeValue($bag->get("env($name)")); } foreach ($envPlaceholders as $env => $placeholders) { if (isset($placeholders[$value])) { return $this->getEnv($env); } } $this->resolving["env($name)"] = true; try { return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true)); } finally { unset($this->resolving["env($name)"]); } } private function callMethod(object $service, array $call, array &$inlineServices) { foreach (self::getServiceConditionals($call[1]) as $s) { if (!$this->has($s)) { return $service; } } foreach (self::getInitializedConditionals($call[1]) as $s) { if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) { return $service; } } $result = $service->{$call[0]}(...$this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices)); return empty($call[2]) ? $service : $result; } /** * Shares a given service in the container. * * @param mixed $service */ private function shareService(Definition $definition, $service, ?string $id, array &$inlineServices) { $inlineServices[$id ?? spl_object_hash($definition)] = $service; if (null !== $id && $definition->isShared()) { if ($definition->isPrivate() && $this->isCompiled()) { $this->privates[$id] = $service; } else { $this->services[$id] = $service; } unset($this->loading[$id]); } } private function getExpressionLanguage(): ExpressionLanguage { if (null === $this->expressionLanguage) { if (!class_exists(\Symfony\Component\ExpressionLanguage\ExpressionLanguage::class)) { throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); } $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders); } return $this->expressionLanguage; } private function inVendors(string $path): bool { if (null === $this->vendors) { $this->vendors = (new ComposerResource())->getVendors(); } $path = realpath($path) ?: $path; foreach ($this->vendors as $vendor) { if (str_starts_with($path, $vendor) && false !== strpbrk(substr($path, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { $this->addResource(new FileResource($vendor.'/composer/installed.json')); return true; } } return false; } } ContainerInterface.php 0000644 00000004741 15120140334 0011017 0 ustar 00 <?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; use Psr\Container\ContainerInterface as PsrContainerInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** * ContainerInterface is the interface implemented by service container classes. * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface ContainerInterface extends PsrContainerInterface { public const RUNTIME_EXCEPTION_ON_INVALID_REFERENCE = 0; public const EXCEPTION_ON_INVALID_REFERENCE = 1; public const NULL_ON_INVALID_REFERENCE = 2; public const IGNORE_ON_INVALID_REFERENCE = 3; public const IGNORE_ON_UNINITIALIZED_REFERENCE = 4; /** * Sets a service. */ public function set(string $id, ?object $service); /** * Gets a service. * * @param string $id The service identifier * @param int $invalidBehavior The behavior when the service does not exist * * @return object|null * * @throws ServiceCircularReferenceException When a circular reference is detected * @throws ServiceNotFoundException When the service is not defined * * @see Reference */ public function get(string $id, int $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); /** * @return bool */ public function has(string $id); /** * Check for whether or not a service has been initialized. * * @return bool */ public function initialized(string $id); /** * @return array|bool|string|int|float|\UnitEnum|null * * @throws InvalidArgumentException if the parameter is not defined */ public function getParameter(string $name); /** * @return bool */ public function hasParameter(string $name); /** * Sets a parameter. * * @param string $name The parameter name * @param array|bool|string|int|float|\UnitEnum|null $value The parameter value */ public function setParameter(string $name, $value); } Definition.php 0000644 00000053762 15120140334 0007353 0 ustar 00 <?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; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; /** * Definition represents a service definition. * * @author Fabien Potencier <fabien@symfony.com> */ class Definition { private const DEFAULT_DEPRECATION_TEMPLATE = 'The "%service_id%" service is deprecated. You should stop using it, as it will be removed in the future.'; private $class; private $file; private $factory; private $shared = true; private $deprecation = []; private $properties = []; private $calls = []; private $instanceof = []; private $autoconfigured = false; private $configurator; private $tags = []; private $public = false; private $synthetic = false; private $abstract = false; private $lazy = false; private $decoratedService; private $autowired = false; private $changes = []; private $bindings = []; private $errors = []; protected $arguments = []; /** * @internal * * Used to store the name of the inner id when using service decoration together with autowiring */ public $innerServiceId; /** * @internal * * Used to store the behavior to follow when using service decoration and the decorated service is invalid */ public $decorationOnInvalid; public function __construct(string $class = null, array $arguments = []) { if (null !== $class) { $this->setClass($class); } $this->arguments = $arguments; } /** * Returns all changes tracked for the Definition object. * * @return array */ public function getChanges() { return $this->changes; } /** * Sets the tracked changes for the Definition object. * * @param array $changes An array of changes for this Definition * * @return $this */ public function setChanges(array $changes) { $this->changes = $changes; return $this; } /** * Sets a factory. * * @param string|array|Reference|null $factory A PHP function, reference or an array containing a class/Reference and a method to call * * @return $this */ public function setFactory($factory) { $this->changes['factory'] = true; if (\is_string($factory) && str_contains($factory, '::')) { $factory = explode('::', $factory, 2); } elseif ($factory instanceof Reference) { $factory = [$factory, '__invoke']; } $this->factory = $factory; return $this; } /** * Gets the factory. * * @return string|array|null The PHP function or an array containing a class/Reference and a method to call */ public function getFactory() { return $this->factory; } /** * Sets the service that this service is decorating. * * @param string|null $id The decorated service id, use null to remove decoration * @param string|null $renamedId The new decorated service id * * @return $this * * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals */ public function setDecoratedService(?string $id, string $renamedId = null, int $priority = 0, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { if ($renamedId && $id === $renamedId) { throw new InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id)); } $this->changes['decorated_service'] = true; if (null === $id) { $this->decoratedService = null; } else { $this->decoratedService = [$id, $renamedId, $priority]; if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { $this->decoratedService[] = $invalidBehavior; } } return $this; } /** * Gets the service that this service is decorating. * * @return array|null An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated */ public function getDecoratedService() { return $this->decoratedService; } /** * Sets the service class. * * @return $this */ public function setClass(?string $class) { $this->changes['class'] = true; $this->class = $class; return $this; } /** * Gets the service class. * * @return string|null */ public function getClass() { return $this->class; } /** * Sets the arguments to pass to the service constructor/factory method. * * @return $this */ public function setArguments(array $arguments) { $this->arguments = $arguments; return $this; } /** * Sets the properties to define when creating the service. * * @return $this */ public function setProperties(array $properties) { $this->properties = $properties; return $this; } /** * Gets the properties to define when creating the service. * * @return array */ public function getProperties() { return $this->properties; } /** * Sets a specific property. * * @param mixed $value * * @return $this */ public function setProperty(string $name, $value) { $this->properties[$name] = $value; return $this; } /** * Adds an argument to pass to the service constructor/factory method. * * @param mixed $argument An argument * * @return $this */ public function addArgument($argument) { $this->arguments[] = $argument; return $this; } /** * Replaces a specific argument. * * @param int|string $index * @param mixed $argument * * @return $this * * @throws OutOfBoundsException When the replaced argument does not exist */ public function replaceArgument($index, $argument) { if (0 === \count($this->arguments)) { throw new OutOfBoundsException(sprintf('Cannot replace arguments for class "%s" if none have been configured yet.', $this->class)); } if (\is_int($index) && ($index < 0 || $index > \count($this->arguments) - 1)) { throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d] of the arguments of class "%s".', $index, \count($this->arguments) - 1, $this->class)); } if (!\array_key_exists($index, $this->arguments)) { throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist in class "%s".', $index, $this->class)); } $this->arguments[$index] = $argument; return $this; } /** * Sets a specific argument. * * @param int|string $key * @param mixed $value * * @return $this */ public function setArgument($key, $value) { $this->arguments[$key] = $value; return $this; } /** * Gets the arguments to pass to the service constructor/factory method. * * @return array */ public function getArguments() { return $this->arguments; } /** * Gets an argument to pass to the service constructor/factory method. * * @param int|string $index * * @return mixed * * @throws OutOfBoundsException When the argument does not exist */ public function getArgument($index) { if (!\array_key_exists($index, $this->arguments)) { throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist in class "%s".', $index, $this->class)); } return $this->arguments[$index]; } /** * Sets the methods to call after service initialization. * * @return $this */ public function setMethodCalls(array $calls = []) { $this->calls = []; foreach ($calls as $call) { $this->addMethodCall($call[0], $call[1], $call[2] ?? false); } return $this; } /** * Adds a method to call after service initialization. * * @param string $method The method name to call * @param array $arguments An array of arguments to pass to the method call * @param bool $returnsClone Whether the call returns the service instance or not * * @return $this * * @throws InvalidArgumentException on empty $method param */ public function addMethodCall(string $method, array $arguments = [], bool $returnsClone = false) { if (empty($method)) { throw new InvalidArgumentException('Method name cannot be empty.'); } $this->calls[] = $returnsClone ? [$method, $arguments, true] : [$method, $arguments]; return $this; } /** * Removes a method to call after service initialization. * * @return $this */ public function removeMethodCall(string $method) { foreach ($this->calls as $i => $call) { if ($call[0] === $method) { unset($this->calls[$i]); } } return $this; } /** * Check if the current definition has a given method to call after service initialization. * * @return bool */ public function hasMethodCall(string $method) { foreach ($this->calls as $call) { if ($call[0] === $method) { return true; } } return false; } /** * Gets the methods to call after service initialization. * * @return array */ public function getMethodCalls() { return $this->calls; } /** * Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. * * @param ChildDefinition[] $instanceof * * @return $this */ public function setInstanceofConditionals(array $instanceof) { $this->instanceof = $instanceof; return $this; } /** * Gets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. * * @return ChildDefinition[] */ public function getInstanceofConditionals() { return $this->instanceof; } /** * Sets whether or not instanceof conditionals should be prepended with a global set. * * @return $this */ public function setAutoconfigured(bool $autoconfigured) { $this->changes['autoconfigured'] = true; $this->autoconfigured = $autoconfigured; return $this; } /** * @return bool */ public function isAutoconfigured() { return $this->autoconfigured; } /** * Sets tags for this definition. * * @return $this */ public function setTags(array $tags) { $this->tags = $tags; return $this; } /** * Returns all tags. * * @return array */ public function getTags() { return $this->tags; } /** * Gets a tag by name. * * @return array */ public function getTag(string $name) { return $this->tags[$name] ?? []; } /** * Adds a tag for this definition. * * @return $this */ public function addTag(string $name, array $attributes = []) { $this->tags[$name][] = $attributes; return $this; } /** * Whether this definition has a tag with the given name. * * @return bool */ public function hasTag(string $name) { return isset($this->tags[$name]); } /** * Clears all tags for a given name. * * @return $this */ public function clearTag(string $name) { unset($this->tags[$name]); return $this; } /** * Clears the tags for this definition. * * @return $this */ public function clearTags() { $this->tags = []; return $this; } /** * Sets a file to require before creating the service. * * @return $this */ public function setFile(?string $file) { $this->changes['file'] = true; $this->file = $file; return $this; } /** * Gets the file to require before creating the service. * * @return string|null */ public function getFile() { return $this->file; } /** * Sets if the service must be shared or not. * * @return $this */ public function setShared(bool $shared) { $this->changes['shared'] = true; $this->shared = $shared; return $this; } /** * Whether this service is shared. * * @return bool */ public function isShared() { return $this->shared; } /** * Sets the visibility of this service. * * @return $this */ public function setPublic(bool $boolean) { $this->changes['public'] = true; $this->public = $boolean; return $this; } /** * Whether this service is public facing. * * @return bool */ public function isPublic() { return $this->public; } /** * Sets if this service is private. * * @return $this * * @deprecated since Symfony 5.2, use setPublic() instead */ public function setPrivate(bool $boolean) { trigger_deprecation('symfony/dependency-injection', '5.2', 'The "%s()" method is deprecated, use "setPublic()" instead.', __METHOD__); return $this->setPublic(!$boolean); } /** * Whether this service is private. * * @return bool */ public function isPrivate() { return !$this->public; } /** * Sets the lazy flag of this service. * * @return $this */ public function setLazy(bool $lazy) { $this->changes['lazy'] = true; $this->lazy = $lazy; return $this; } /** * Whether this service is lazy. * * @return bool */ public function isLazy() { return $this->lazy; } /** * Sets whether this definition is synthetic, that is not constructed by the * container, but dynamically injected. * * @return $this */ public function setSynthetic(bool $boolean) { $this->synthetic = $boolean; if (!isset($this->changes['public'])) { $this->setPublic(true); } return $this; } /** * Whether this definition is synthetic, that is not constructed by the * container, but dynamically injected. * * @return bool */ public function isSynthetic() { return $this->synthetic; } /** * Whether this definition is abstract, that means it merely serves as a * template for other definitions. * * @return $this */ public function setAbstract(bool $boolean) { $this->abstract = $boolean; return $this; } /** * Whether this definition is abstract, that means it merely serves as a * template for other definitions. * * @return bool */ public function isAbstract() { return $this->abstract; } /** * Whether this definition is deprecated, that means it should not be called * anymore. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message The deprecation message to use * * @return $this * * @throws InvalidArgumentException when the message template is invalid */ public function setDeprecated(/* string $package, string $version, string $message */) { $args = \func_get_args(); if (\func_num_args() < 3) { trigger_deprecation('symfony/dependency-injection', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); $status = $args[0] ?? true; if (!$status) { trigger_deprecation('symfony/dependency-injection', '5.1', 'Passing a null message to un-deprecate a node is deprecated.'); } $message = (string) ($args[1] ?? null); $package = $version = ''; } else { $status = true; $package = (string) $args[0]; $version = (string) $args[1]; $message = (string) $args[2]; } if ('' !== $message) { if (preg_match('#[\r\n]|\*/#', $message)) { throw new InvalidArgumentException('Invalid characters found in deprecation template.'); } if (!str_contains($message, '%service_id%')) { throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.'); } } $this->changes['deprecated'] = true; $this->deprecation = $status ? ['package' => $package, 'version' => $version, 'message' => $message ?: self::DEFAULT_DEPRECATION_TEMPLATE] : []; return $this; } /** * Whether this definition is deprecated, that means it should not be called * anymore. * * @return bool */ public function isDeprecated() { return (bool) $this->deprecation; } /** * Message to use if this definition is deprecated. * * @deprecated since Symfony 5.1, use "getDeprecation()" instead. * * @param string $id Service id relying on this definition * * @return string */ public function getDeprecationMessage(string $id) { trigger_deprecation('symfony/dependency-injection', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); return $this->getDeprecation($id)['message']; } /** * @param string $id Service id relying on this definition */ public function getDeprecation(string $id): array { return [ 'package' => $this->deprecation['package'], 'version' => $this->deprecation['version'], 'message' => str_replace('%service_id%', $id, $this->deprecation['message']), ]; } /** * Sets a configurator to call after the service is fully initialized. * * @param string|array|Reference|null $configurator A PHP function, reference or an array containing a class/Reference and a method to call * * @return $this */ public function setConfigurator($configurator) { $this->changes['configurator'] = true; if (\is_string($configurator) && str_contains($configurator, '::')) { $configurator = explode('::', $configurator, 2); } elseif ($configurator instanceof Reference) { $configurator = [$configurator, '__invoke']; } $this->configurator = $configurator; return $this; } /** * Gets the configurator to call after the service is fully initialized. * * @return string|array|null */ public function getConfigurator() { return $this->configurator; } /** * Is the definition autowired? * * @return bool */ public function isAutowired() { return $this->autowired; } /** * Enables/disables autowiring. * * @return $this */ public function setAutowired(bool $autowired) { $this->changes['autowired'] = true; $this->autowired = $autowired; return $this; } /** * Gets bindings. * * @return BoundArgument[] */ public function getBindings() { return $this->bindings; } /** * Sets bindings. * * Bindings map $named or FQCN arguments to values that should be * injected in the matching parameters (of the constructor, of methods * called and of controller actions). * * @return $this */ public function setBindings(array $bindings) { foreach ($bindings as $key => $binding) { if (0 < strpos($key, '$') && $key !== $k = preg_replace('/[ \t]*\$/', ' $', $key)) { unset($bindings[$key]); $bindings[$key = $k] = $binding; } if (!$binding instanceof BoundArgument) { $bindings[$key] = new BoundArgument($binding); } } $this->bindings = $bindings; return $this; } /** * Add an error that occurred when building this Definition. * * @param string|\Closure|self $error * * @return $this */ public function addError($error) { if ($error instanceof self) { $this->errors = array_merge($this->errors, $error->errors); } else { $this->errors[] = $error; } return $this; } /** * Returns any errors that occurred while building this Definition. * * @return array */ public function getErrors() { foreach ($this->errors as $i => $error) { if ($error instanceof \Closure) { $this->errors[$i] = (string) $error(); } elseif (!\is_string($error)) { $this->errors[$i] = (string) $error; } } return $this->errors; } public function hasErrors(): bool { return (bool) $this->errors; } } EnvVarLoaderInterface.php 0000644 00000001175 15120140334 0011423 0 ustar 00 <?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; /** * EnvVarLoaderInterface objects return key/value pairs that are added to the list of available env vars. * * @author Nicolas Grekas <p@tchwork.com> */ interface EnvVarLoaderInterface { /** * @return string[] Key/value pairs that can be accessed using the regular "%env()%" syntax */ public function loadEnvVars(): array; } EnvVarProcessor.php 0000644 00000024634 15120140334 0010360 0 ustar 00 <?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; use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * @author Nicolas Grekas <p@tchwork.com> */ class EnvVarProcessor implements EnvVarProcessorInterface { private $container; private $loaders; private $loadedVars = []; /** * @param EnvVarLoaderInterface[] $loaders */ public function __construct(ContainerInterface $container, \Traversable $loaders = null) { $this->container = $container; $this->loaders = $loaders ?? new \ArrayIterator(); } /** * {@inheritdoc} */ public static function getProvidedTypes() { return [ 'base64' => 'string', 'bool' => 'bool', 'not' => 'bool', 'const' => 'bool|int|float|string|array', 'csv' => 'array', 'file' => 'string', 'float' => 'float', 'int' => 'int', 'json' => 'array', 'key' => 'bool|int|float|string|array', 'url' => 'array', 'query_string' => 'array', 'resolve' => 'string', 'default' => 'bool|int|float|string|array', 'string' => 'string', 'trim' => 'string', 'require' => 'bool|int|float|string|array', ]; } /** * {@inheritdoc} */ public function getEnv(string $prefix, string $name, \Closure $getEnv) { $i = strpos($name, ':'); if ('key' === $prefix) { if (false === $i) { throw new RuntimeException(sprintf('Invalid env "key:%s": a key specifier should be provided.', $name)); } $next = substr($name, $i + 1); $key = substr($name, 0, $i); $array = $getEnv($next); if (!\is_array($array)) { throw new RuntimeException(sprintf('Resolved value of "%s" did not result in an array value.', $next)); } if (!isset($array[$key]) && !\array_key_exists($key, $array)) { throw new EnvNotFoundException(sprintf('Key "%s" not found in %s (resolved from "%s").', $key, json_encode($array), $next)); } return $array[$key]; } if ('default' === $prefix) { if (false === $i) { throw new RuntimeException(sprintf('Invalid env "default:%s": a fallback parameter should be provided.', $name)); } $next = substr($name, $i + 1); $default = substr($name, 0, $i); if ('' !== $default && !$this->container->hasParameter($default)) { throw new RuntimeException(sprintf('Invalid env fallback in "default:%s": parameter "%s" not found.', $name, $default)); } try { $env = $getEnv($next); if ('' !== $env && null !== $env) { return $env; } } catch (EnvNotFoundException $e) { // no-op } return '' === $default ? null : $this->container->getParameter($default); } if ('file' === $prefix || 'require' === $prefix) { if (!\is_scalar($file = $getEnv($name))) { throw new RuntimeException(sprintf('Invalid file name: env var "%s" is non-scalar.', $name)); } if (!is_file($file)) { throw new EnvNotFoundException(sprintf('File "%s" not found (resolved from "%s").', $file, $name)); } if ('file' === $prefix) { return file_get_contents($file); } else { return require $file; } } if (false !== $i || 'string' !== $prefix) { $env = $getEnv($name); } elseif (isset($_ENV[$name])) { $env = $_ENV[$name]; } elseif (isset($_SERVER[$name]) && !str_starts_with($name, 'HTTP_')) { $env = $_SERVER[$name]; } elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues foreach ($this->loadedVars as $vars) { if (false !== $env = ($vars[$name] ?? false)) { break; } } if (false === $env || null === $env) { $loaders = $this->loaders; $this->loaders = new \ArrayIterator(); try { $i = 0; $ended = true; $count = $loaders instanceof \Countable ? $loaders->count() : 0; foreach ($loaders as $loader) { if (\count($this->loadedVars) > $i++) { continue; } $this->loadedVars[] = $vars = $loader->loadEnvVars(); if (false !== $env = $vars[$name] ?? false) { $ended = false; break; } } if ($ended || $count === $i) { $loaders = $this->loaders; } } catch (ParameterCircularReferenceException $e) { // skip loaders that need an env var that is not defined } finally { $this->loaders = $loaders; } } if (false === $env || null === $env) { if (!$this->container->hasParameter("env($name)")) { throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name)); } $env = $this->container->getParameter("env($name)"); } } if (null === $env) { if (!isset($this->getProvidedTypes()[$prefix])) { throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix)); } return null; } if (!\is_scalar($env)) { throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to "%s".', $name, $prefix)); } if ('string' === $prefix) { return (string) $env; } if (\in_array($prefix, ['bool', 'not'], true)) { $env = (bool) (filter_var($env, \FILTER_VALIDATE_BOOLEAN) ?: filter_var($env, \FILTER_VALIDATE_INT) ?: filter_var($env, \FILTER_VALIDATE_FLOAT)); return 'not' === $prefix ? !$env : $env; } if ('int' === $prefix) { if (false === $env = filter_var($env, \FILTER_VALIDATE_INT) ?: filter_var($env, \FILTER_VALIDATE_FLOAT)) { throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to int.', $name)); } return (int) $env; } if ('float' === $prefix) { if (false === $env = filter_var($env, \FILTER_VALIDATE_FLOAT)) { throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to float.', $name)); } return (float) $env; } if ('const' === $prefix) { if (!\defined($env)) { throw new RuntimeException(sprintf('Env var "%s" maps to undefined constant "%s".', $name, $env)); } return \constant($env); } if ('base64' === $prefix) { return base64_decode(strtr($env, '-_', '+/')); } if ('json' === $prefix) { $env = json_decode($env, true); if (\JSON_ERROR_NONE !== json_last_error()) { throw new RuntimeException(sprintf('Invalid JSON in env var "%s": ', $name).json_last_error_msg()); } if (null !== $env && !\is_array($env)) { throw new RuntimeException(sprintf('Invalid JSON env var "%s": array or null expected, "%s" given.', $name, get_debug_type($env))); } return $env; } if ('url' === $prefix) { $parsedEnv = parse_url($env); if (false === $parsedEnv) { throw new RuntimeException(sprintf('Invalid URL in env var "%s".', $name)); } if (!isset($parsedEnv['scheme'], $parsedEnv['host'])) { throw new RuntimeException(sprintf('Invalid URL env var "%s": schema and host expected, "%s" given.', $name, $env)); } $parsedEnv += [ 'port' => null, 'user' => null, 'pass' => null, 'path' => null, 'query' => null, 'fragment' => null, ]; // remove the '/' separator $parsedEnv['path'] = '/' === ($parsedEnv['path'] ?? '/') ? '' : substr($parsedEnv['path'], 1); return $parsedEnv; } if ('query_string' === $prefix) { $queryString = parse_url($env, \PHP_URL_QUERY) ?: $env; parse_str($queryString, $result); return $result; } if ('resolve' === $prefix) { return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name, $getEnv) { if (!isset($match[1])) { return '%'; } if (str_starts_with($match[1], 'env(') && str_ends_with($match[1], ')') && 'env()' !== $match[1]) { $value = $getEnv(substr($match[1], 4, -1)); } else { $value = $this->container->getParameter($match[1]); } if (!\is_scalar($value)) { throw new RuntimeException(sprintf('Parameter "%s" found when resolving env var "%s" must be scalar, "%s" given.', $match[1], $name, get_debug_type($value))); } return $value; }, $env); } if ('csv' === $prefix) { return str_getcsv($env, ',', '"', \PHP_VERSION_ID >= 70400 ? '' : '\\'); } if ('trim' === $prefix) { return trim($env); } throw new RuntimeException(sprintf('Unsupported env var prefix "%s" for env name "%s".', $prefix, $name)); } } EnvVarProcessorInterface.php 0000644 00000002212 15120140334 0012165 0 ustar 00 <?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; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * The EnvVarProcessorInterface is implemented by objects that manage environment-like variables. * * @author Nicolas Grekas <p@tchwork.com> */ interface EnvVarProcessorInterface { /** * Returns the value of the given variable as managed by the current instance. * * @param string $prefix The namespace of the variable * @param string $name The name of the variable within the namespace * @param \Closure $getEnv A closure that allows fetching more env vars * * @return mixed * * @throws RuntimeException on error */ public function getEnv(string $prefix, string $name, \Closure $getEnv); /** * @return string[] The PHP-types managed by getEnv(), keyed by prefixes */ public static function getProvidedTypes(); } ExpressionLanguage.php 0000644 00000002067 15120140334 0011056 0 ustar 00 <?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; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; if (!class_exists(BaseExpressionLanguage::class)) { return; } /** * Adds some function to the default ExpressionLanguage. * * @author Fabien Potencier <fabien@symfony.com> * * @see ExpressionLanguageProvider */ class ExpressionLanguage extends BaseExpressionLanguage { /** * {@inheritdoc} */ public function __construct(CacheItemPoolInterface $cache = null, array $providers = [], callable $serviceCompiler = null) { // prepend the default provider to let users override it easily array_unshift($providers, new ExpressionLanguageProvider($serviceCompiler)); parent::__construct($cache, $providers); } } ExpressionLanguageProvider.php 0000644 00000002736 15120140334 0012574 0 ustar 00 <?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; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; /** * Define some ExpressionLanguage functions. * * To get a service, use service('request'). * To get a parameter, use parameter('kernel.debug'). * * @author Fabien Potencier <fabien@symfony.com> */ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface { private $serviceCompiler; public function __construct(callable $serviceCompiler = null) { $this->serviceCompiler = $serviceCompiler; } public function getFunctions() { return [ new ExpressionFunction('service', $this->serviceCompiler ?: function ($arg) { return sprintf('$this->get(%s)', $arg); }, function (array $variables, $value) { return $variables['container']->get($value); }), new ExpressionFunction('parameter', function ($arg) { return sprintf('$this->getParameter(%s)', $arg); }, function (array $variables, $value) { return $variables['container']->getParameter($value); }), ]; } } LICENSE 0000644 00000002054 15120140334 0005543 0 ustar 00 Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Parameter.php 0000644 00000001160 15120140334 0007164 0 ustar 00 <?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; /** * Parameter represents a parameter reference. * * @author Fabien Potencier <fabien@symfony.com> */ class Parameter { private $id; public function __construct(string $id) { $this->id = $id; } /** * @return string */ public function __toString() { return $this->id; } } README.md 0000644 00000001103 15120140334 0006007 0 ustar 00 DependencyInjection Component ============================= The DependencyInjection component allows you to standardize and centralize the way objects are constructed in your application. Resources --------- * [Documentation](https://symfony.com/doc/current/components/dependency_injection.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) Reference.php 0000644 00000001731 15120140334 0007146 0 ustar 00 <?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; /** * Reference represents a service reference. * * @author Fabien Potencier <fabien@symfony.com> */ class Reference { private $id; private $invalidBehavior; public function __construct(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { $this->id = $id; $this->invalidBehavior = $invalidBehavior; } /** * @return string */ public function __toString() { return $this->id; } /** * Returns the behavior to be used when the service does not exist. * * @return int */ public function getInvalidBehavior() { return $this->invalidBehavior; } } ReverseContainer.php 0000644 00000004624 15120140334 0010532 0 ustar 00 <?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; use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** * Turns public and "container.reversible" services back to their ids. * * @author Nicolas Grekas <p@tchwork.com> */ final class ReverseContainer { private $serviceContainer; private $reversibleLocator; private $tagName; private $getServiceId; public function __construct(Container $serviceContainer, ContainerInterface $reversibleLocator, string $tagName = 'container.reversible') { $this->serviceContainer = $serviceContainer; $this->reversibleLocator = $reversibleLocator; $this->tagName = $tagName; $this->getServiceId = \Closure::bind(function (object $service): ?string { return array_search($service, $this->services, true) ?: array_search($service, $this->privates, true) ?: null; }, $serviceContainer, Container::class); } /** * Returns the id of the passed object when it exists as a service. * * To be reversible, services need to be either public or be tagged with "container.reversible". */ public function getId(object $service): ?string { if ($this->serviceContainer === $service) { return 'service_container'; } if (null === $id = ($this->getServiceId)($service)) { return null; } if ($this->serviceContainer->has($id) || $this->reversibleLocator->has($id)) { return $id; } return null; } /** * @throws ServiceNotFoundException When the service is not reversible */ public function getService(string $id): object { if ($this->reversibleLocator->has($id)) { return $this->reversibleLocator->get($id); } if (isset($this->serviceContainer->getRemovedIds()[$id])) { throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service is private and cannot be accessed by reference. You should either make it public, or tag it as "%s".', $id, $this->tagName)); } return $this->serviceContainer->get($id); } } ServiceLocator.php 0000644 00000012254 15120140334 0010176 0 ustar 00 <?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; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Contracts\Service\ServiceLocatorTrait; use Symfony\Contracts\Service\ServiceProviderInterface; use Symfony\Contracts\Service\ServiceSubscriberInterface; /** * @author Robin Chalas <robin.chalas@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ class ServiceLocator implements ServiceProviderInterface { use ServiceLocatorTrait { get as private doGet; } private $externalId; private $container; /** * {@inheritdoc} * * @return mixed */ public function get(string $id) { if (!$this->externalId) { return $this->doGet($id); } try { return $this->doGet($id); } catch (RuntimeException $e) { $what = sprintf('service "%s" required by "%s"', $id, $this->externalId); $message = preg_replace('/service "\.service_locator\.[^"]++"/', $what, $e->getMessage()); if ($e->getMessage() === $message) { $message = sprintf('Cannot resolve %s: %s', $what, $message); } $r = new \ReflectionProperty($e, 'message'); $r->setAccessible(true); $r->setValue($e, $message); throw $e; } } public function __invoke(string $id) { return isset($this->factories[$id]) ? $this->get($id) : null; } /** * @internal * * @return static */ public function withContext(string $externalId, Container $container): self { $locator = clone $this; $locator->externalId = $externalId; $locator->container = $container; return $locator; } private function createNotFoundException(string $id): NotFoundExceptionInterface { if ($this->loading) { $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $this->formatAlternatives()); return new ServiceNotFoundException($id, end($this->loading) ?: null, null, [], $msg); } $class = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, 4); $class = isset($class[3]['object']) ? \get_class($class[3]['object']) : null; $externalId = $this->externalId ?: $class; $msg = []; $msg[] = sprintf('Service "%s" not found:', $id); if (!$this->container) { $class = null; } elseif ($this->container->has($id) || isset($this->container->getRemovedIds()[$id])) { $msg[] = 'even though it exists in the app\'s container,'; } else { try { $this->container->get($id); $class = null; } catch (ServiceNotFoundException $e) { if ($e->getAlternatives()) { $msg[] = sprintf('did you mean %s? Anyway,', $this->formatAlternatives($e->getAlternatives(), 'or')); } else { $class = null; } } } if ($externalId) { $msg[] = sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives()); } else { $msg[] = sprintf('the current service locator %s', $this->formatAlternatives()); } if (!$class) { // no-op } elseif (is_subclass_of($class, ServiceSubscriberInterface::class)) { $msg[] = sprintf('Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', preg_replace('/([^\\\\]++\\\\)++/', '', $class)); } else { $msg[] = 'Try using dependency injection instead.'; } return new ServiceNotFoundException($id, end($this->loading) ?: null, null, [], implode(' ', $msg)); } private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface { return new ServiceCircularReferenceException($id, $path); } private function formatAlternatives(array $alternatives = null, string $separator = 'and'): string { $format = '"%s"%s'; if (null === $alternatives) { if (!$alternatives = array_keys($this->factories)) { return 'is empty...'; } $format = sprintf('only knows about the %s service%s.', $format, 1 < \count($alternatives) ? 's' : ''); } $last = array_pop($alternatives); return sprintf($format, $alternatives ? implode('", "', $alternatives) : $last, $alternatives ? sprintf(' %s "%s"', $separator, $last) : ''); } } TaggedContainerInterface.php 0000644 00000001305 15120140334 0012124 0 ustar 00 <?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; /** * TaggedContainerInterface is the interface implemented when a container knows how to deals with tags. * * @author Fabien Potencier <fabien@symfony.com> */ interface TaggedContainerInterface extends ContainerInterface { /** * Returns service ids for a given tag. * * @param string $name The tag name * * @return array */ public function findTaggedServiceIds(string $name); } TypedReference.php 0000644 00000002362 15120140334 0010155 0 ustar 00 <?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; /** * Represents a PHP type-hinted service reference. * * @author Nicolas Grekas <p@tchwork.com> */ class TypedReference extends Reference { private $type; private $name; /** * @param string $id The service identifier * @param string $type The PHP type of the identified service * @param int $invalidBehavior The behavior when the service does not exist * @param string|null $name The name of the argument targeting the service */ public function __construct(string $id, string $type, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, string $name = null) { $this->name = $type === $id ? $name : null; parent::__construct($id, $invalidBehavior); $this->type = $type; } public function getType() { return $this->type; } public function getName(): ?string { return $this->name; } } Variable.php 0000644 00000001313 15120140334 0006771 0 ustar 00 <?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; /** * Represents a variable. * * $var = new Variable('a'); * * will be dumped as * * $a * * by the PHP dumper. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class Variable { private $name; public function __construct(string $name) { $this->name = $name; } /** * @return string */ public function __toString() { return $this->name; } } composer.json 0000644 00000003472 15120140334 0007265 0 ustar 00 { "name": "symfony/dependency-injection", "type": "library", "description": "Allows you to standardize and centralize the way objects are constructed in your application", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=7.2.5", "psr/container": "^1.1.1", "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-php80": "^1.16", "symfony/polyfill-php81": "^1.22", "symfony/service-contracts": "^1.1.6|^2" }, "require-dev": { "symfony/yaml": "^4.4.26|^5.0|^6.0", "symfony/config": "^5.3|^6.0", "symfony/expression-language": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/yaml": "", "symfony/config": "", "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", "symfony/expression-language": "For using expressions in service container configuration", "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" }, "conflict": { "ext-psr": "<1.1|>=2", "symfony/config": "<5.3", "symfony/finder": "<4.4", "symfony/proxy-manager-bridge": "<4.4", "symfony/yaml": "<4.4.26" }, "provide": { "psr/container-implementation": "1.0", "symfony/service-implementation": "1.0|2.0" }, "autoload": { "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "minimum-stability": "dev" }
Coded With 💗 by
0x6ick