123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- <?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;
- }
- }
|