4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\DependencyInjection\Compiler;
14 use Symfony\Component\DependencyInjection\ChildDefinition;
15 use Symfony\Component\DependencyInjection\ContainerBuilder;
16 use Symfony\Component\DependencyInjection\Definition;
17 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
18 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
21 * Applies instanceof conditionals to definitions.
23 * @author Nicolas Grekas <p@tchwork.com>
25 class ResolveInstanceofConditionalsPass implements CompilerPassInterface
30 public function process(ContainerBuilder $container)
32 foreach ($container->getAutoconfiguredInstanceof() as $interface => $definition) {
33 if ($definition->getArguments()) {
34 throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines arguments but these are not supported and should be removed.', $interface));
36 if ($definition->getMethodCalls()) {
37 throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines method calls but these are not supported and should be removed.', $interface));
41 foreach ($container->getDefinitions() as $id => $definition) {
42 if ($definition instanceof ChildDefinition) {
43 // don't apply "instanceof" to children: it will be applied to their parent
46 $container->setDefinition($id, $this->processDefinition($container, $id, $definition));
50 private function processDefinition(ContainerBuilder $container, $id, Definition $definition)
52 $instanceofConditionals = $definition->getInstanceofConditionals();
53 $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : array();
54 if (!$instanceofConditionals && !$autoconfiguredInstanceof) {
58 if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) {
62 $conditionals = $this->mergeConditionals($autoconfiguredInstanceof, $instanceofConditionals, $container);
64 $definition->setInstanceofConditionals(array());
65 $parent = $shared = null;
66 $instanceofTags = array();
68 foreach ($conditionals as $interface => $instanceofDefs) {
69 if ($interface !== $class && (!$container->getReflectionClass($class, false))) {
73 if ($interface !== $class && !is_subclass_of($class, $interface)) {
77 foreach ($instanceofDefs as $key => $instanceofDef) {
78 /** @var ChildDefinition $instanceofDef */
79 $instanceofDef = clone $instanceofDef;
80 $instanceofDef->setAbstract(true)->setParent($parent ?: 'abstract.instanceof.'.$id);
81 $parent = 'instanceof.'.$interface.'.'.$key.'.'.$id;
82 $container->setDefinition($parent, $instanceofDef);
83 $instanceofTags[] = $instanceofDef->getTags();
84 $instanceofDef->setTags(array());
86 if (isset($instanceofDef->getChanges()['shared'])) {
87 $shared = $instanceofDef->isShared();
93 $bindings = $definition->getBindings();
94 $abstract = $container->setDefinition('abstract.instanceof.'.$id, $definition);
96 // cast Definition to ChildDefinition
97 $definition->setBindings(array());
98 $definition = serialize($definition);
99 $definition = substr_replace($definition, '53', 2, 2);
100 $definition = substr_replace($definition, 'Child', 44, 0);
101 $definition = unserialize($definition);
102 $definition->setParent($parent);
104 if (null !== $shared && !isset($definition->getChanges()['shared'])) {
105 $definition->setShared($shared);
108 $i = \count($instanceofTags);
110 foreach ($instanceofTags[$i] as $k => $v) {
112 if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) {
115 $definition->addTag($k, $v);
120 $definition->setBindings($bindings);
122 // reset fields with "merge" behavior
124 ->setBindings(array())
125 ->setArguments(array())
126 ->setMethodCalls(array())
127 ->setDecoratedService(null)
135 private function mergeConditionals(array $autoconfiguredInstanceof, array $instanceofConditionals, ContainerBuilder $container)
137 // make each value an array of ChildDefinition
138 $conditionals = array_map(function ($childDef) { return array($childDef); }, $autoconfiguredInstanceof);
140 foreach ($instanceofConditionals as $interface => $instanceofDef) {
141 // make sure the interface/class exists (but don't validate automaticInstanceofConditionals)
142 if (!$container->getReflectionClass($interface)) {
143 throw new RuntimeException(sprintf('"%s" is set as an "instanceof" conditional, but it does not exist.', $interface));
146 if (!isset($autoconfiguredInstanceof[$interface])) {
147 $conditionals[$interface] = array();
150 $conditionals[$interface][] = $instanceofDef;
153 return $conditionals;