Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace Symfony\Component\DependencyInjection\Compiler; Chris@14: Chris@14: use Symfony\Component\DependencyInjection\ChildDefinition; Chris@14: use Symfony\Component\DependencyInjection\ContainerBuilder; Chris@14: use Symfony\Component\DependencyInjection\Definition; Chris@17: use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; Chris@14: use Symfony\Component\DependencyInjection\Exception\RuntimeException; Chris@14: Chris@14: /** Chris@14: * Applies instanceof conditionals to definitions. Chris@14: * Chris@14: * @author Nicolas Grekas Chris@14: */ Chris@14: class ResolveInstanceofConditionalsPass implements CompilerPassInterface Chris@14: { Chris@14: /** Chris@14: * {@inheritdoc} Chris@14: */ Chris@14: public function process(ContainerBuilder $container) Chris@14: { Chris@14: foreach ($container->getAutoconfiguredInstanceof() as $interface => $definition) { Chris@14: if ($definition->getArguments()) { Chris@14: throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines arguments but these are not supported and should be removed.', $interface)); Chris@14: } Chris@14: if ($definition->getMethodCalls()) { Chris@14: throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines method calls but these are not supported and should be removed.', $interface)); Chris@14: } Chris@14: } Chris@14: Chris@14: foreach ($container->getDefinitions() as $id => $definition) { Chris@14: if ($definition instanceof ChildDefinition) { Chris@14: // don't apply "instanceof" to children: it will be applied to their parent Chris@14: continue; Chris@14: } Chris@14: $container->setDefinition($id, $this->processDefinition($container, $id, $definition)); Chris@14: } Chris@14: } Chris@14: Chris@14: private function processDefinition(ContainerBuilder $container, $id, Definition $definition) Chris@14: { Chris@14: $instanceofConditionals = $definition->getInstanceofConditionals(); Chris@17: $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : []; Chris@14: if (!$instanceofConditionals && !$autoconfiguredInstanceof) { Chris@14: return $definition; Chris@14: } Chris@14: Chris@14: if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) { Chris@14: return $definition; Chris@14: } Chris@14: Chris@14: $conditionals = $this->mergeConditionals($autoconfiguredInstanceof, $instanceofConditionals, $container); Chris@14: Chris@17: $definition->setInstanceofConditionals([]); Chris@14: $parent = $shared = null; Chris@17: $instanceofTags = []; Chris@14: Chris@14: foreach ($conditionals as $interface => $instanceofDefs) { Chris@14: if ($interface !== $class && (!$container->getReflectionClass($class, false))) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: if ($interface !== $class && !is_subclass_of($class, $interface)) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: foreach ($instanceofDefs as $key => $instanceofDef) { Chris@14: /** @var ChildDefinition $instanceofDef */ Chris@14: $instanceofDef = clone $instanceofDef; Chris@14: $instanceofDef->setAbstract(true)->setParent($parent ?: 'abstract.instanceof.'.$id); Chris@14: $parent = 'instanceof.'.$interface.'.'.$key.'.'.$id; Chris@14: $container->setDefinition($parent, $instanceofDef); Chris@14: $instanceofTags[] = $instanceofDef->getTags(); Chris@17: $instanceofDef->setTags([]); Chris@14: Chris@14: if (isset($instanceofDef->getChanges()['shared'])) { Chris@14: $shared = $instanceofDef->isShared(); Chris@14: } Chris@14: } Chris@14: } Chris@14: Chris@14: if ($parent) { Chris@14: $bindings = $definition->getBindings(); Chris@14: $abstract = $container->setDefinition('abstract.instanceof.'.$id, $definition); Chris@14: Chris@14: // cast Definition to ChildDefinition Chris@17: $definition->setBindings([]); Chris@14: $definition = serialize($definition); Chris@14: $definition = substr_replace($definition, '53', 2, 2); Chris@14: $definition = substr_replace($definition, 'Child', 44, 0); Chris@14: $definition = unserialize($definition); Chris@14: $definition->setParent($parent); Chris@14: Chris@14: if (null !== $shared && !isset($definition->getChanges()['shared'])) { Chris@14: $definition->setShared($shared); Chris@14: } Chris@14: Chris@17: $i = \count($instanceofTags); Chris@14: while (0 <= --$i) { Chris@14: foreach ($instanceofTags[$i] as $k => $v) { Chris@14: foreach ($v as $v) { Chris@17: if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) { Chris@14: continue; Chris@14: } Chris@14: $definition->addTag($k, $v); Chris@14: } Chris@14: } Chris@14: } Chris@14: Chris@17: $definition->setBindings($bindings); Chris@17: Chris@14: // reset fields with "merge" behavior Chris@14: $abstract Chris@17: ->setBindings([]) Chris@17: ->setArguments([]) Chris@17: ->setMethodCalls([]) Chris@14: ->setDecoratedService(null) Chris@17: ->setTags([]) Chris@14: ->setAbstract(true); Chris@14: } Chris@14: Chris@14: return $definition; Chris@14: } Chris@14: Chris@14: private function mergeConditionals(array $autoconfiguredInstanceof, array $instanceofConditionals, ContainerBuilder $container) Chris@14: { Chris@14: // make each value an array of ChildDefinition Chris@17: $conditionals = array_map(function ($childDef) { return [$childDef]; }, $autoconfiguredInstanceof); Chris@14: Chris@14: foreach ($instanceofConditionals as $interface => $instanceofDef) { Chris@14: // make sure the interface/class exists (but don't validate automaticInstanceofConditionals) Chris@14: if (!$container->getReflectionClass($interface)) { Chris@14: throw new RuntimeException(sprintf('"%s" is set as an "instanceof" conditional, but it does not exist.', $interface)); Chris@14: } Chris@14: Chris@14: if (!isset($autoconfiguredInstanceof[$interface])) { Chris@17: $conditionals[$interface] = []; Chris@14: } Chris@14: Chris@14: $conditionals[$interface][] = $instanceofDef; Chris@14: } Chris@14: Chris@14: return $conditionals; Chris@14: } Chris@14: }