Chris@14
|
1 <?php
|
Chris@14
|
2
|
Chris@14
|
3 /*
|
Chris@14
|
4 * This file is part of the Symfony package.
|
Chris@14
|
5 *
|
Chris@14
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@14
|
7 *
|
Chris@14
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@14
|
9 * file that was distributed with this source code.
|
Chris@14
|
10 */
|
Chris@14
|
11
|
Chris@14
|
12 namespace Symfony\Component\DependencyInjection\Compiler;
|
Chris@14
|
13
|
Chris@14
|
14 use Symfony\Component\DependencyInjection\ChildDefinition;
|
Chris@14
|
15 use Symfony\Component\DependencyInjection\ContainerBuilder;
|
Chris@14
|
16 use Symfony\Component\DependencyInjection\Definition;
|
Chris@17
|
17 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
Chris@14
|
18 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
Chris@14
|
19
|
Chris@14
|
20 /**
|
Chris@14
|
21 * Applies instanceof conditionals to definitions.
|
Chris@14
|
22 *
|
Chris@14
|
23 * @author Nicolas Grekas <p@tchwork.com>
|
Chris@14
|
24 */
|
Chris@14
|
25 class ResolveInstanceofConditionalsPass implements CompilerPassInterface
|
Chris@14
|
26 {
|
Chris@14
|
27 /**
|
Chris@14
|
28 * {@inheritdoc}
|
Chris@14
|
29 */
|
Chris@14
|
30 public function process(ContainerBuilder $container)
|
Chris@14
|
31 {
|
Chris@14
|
32 foreach ($container->getAutoconfiguredInstanceof() as $interface => $definition) {
|
Chris@14
|
33 if ($definition->getArguments()) {
|
Chris@14
|
34 throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines arguments but these are not supported and should be removed.', $interface));
|
Chris@14
|
35 }
|
Chris@14
|
36 if ($definition->getMethodCalls()) {
|
Chris@14
|
37 throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines method calls but these are not supported and should be removed.', $interface));
|
Chris@14
|
38 }
|
Chris@14
|
39 }
|
Chris@14
|
40
|
Chris@14
|
41 foreach ($container->getDefinitions() as $id => $definition) {
|
Chris@14
|
42 if ($definition instanceof ChildDefinition) {
|
Chris@14
|
43 // don't apply "instanceof" to children: it will be applied to their parent
|
Chris@14
|
44 continue;
|
Chris@14
|
45 }
|
Chris@14
|
46 $container->setDefinition($id, $this->processDefinition($container, $id, $definition));
|
Chris@14
|
47 }
|
Chris@14
|
48 }
|
Chris@14
|
49
|
Chris@14
|
50 private function processDefinition(ContainerBuilder $container, $id, Definition $definition)
|
Chris@14
|
51 {
|
Chris@14
|
52 $instanceofConditionals = $definition->getInstanceofConditionals();
|
Chris@17
|
53 $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : [];
|
Chris@14
|
54 if (!$instanceofConditionals && !$autoconfiguredInstanceof) {
|
Chris@14
|
55 return $definition;
|
Chris@14
|
56 }
|
Chris@14
|
57
|
Chris@14
|
58 if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) {
|
Chris@14
|
59 return $definition;
|
Chris@14
|
60 }
|
Chris@14
|
61
|
Chris@14
|
62 $conditionals = $this->mergeConditionals($autoconfiguredInstanceof, $instanceofConditionals, $container);
|
Chris@14
|
63
|
Chris@17
|
64 $definition->setInstanceofConditionals([]);
|
Chris@14
|
65 $parent = $shared = null;
|
Chris@17
|
66 $instanceofTags = [];
|
Chris@14
|
67
|
Chris@14
|
68 foreach ($conditionals as $interface => $instanceofDefs) {
|
Chris@14
|
69 if ($interface !== $class && (!$container->getReflectionClass($class, false))) {
|
Chris@14
|
70 continue;
|
Chris@14
|
71 }
|
Chris@14
|
72
|
Chris@14
|
73 if ($interface !== $class && !is_subclass_of($class, $interface)) {
|
Chris@14
|
74 continue;
|
Chris@14
|
75 }
|
Chris@14
|
76
|
Chris@14
|
77 foreach ($instanceofDefs as $key => $instanceofDef) {
|
Chris@14
|
78 /** @var ChildDefinition $instanceofDef */
|
Chris@14
|
79 $instanceofDef = clone $instanceofDef;
|
Chris@14
|
80 $instanceofDef->setAbstract(true)->setParent($parent ?: 'abstract.instanceof.'.$id);
|
Chris@14
|
81 $parent = 'instanceof.'.$interface.'.'.$key.'.'.$id;
|
Chris@14
|
82 $container->setDefinition($parent, $instanceofDef);
|
Chris@14
|
83 $instanceofTags[] = $instanceofDef->getTags();
|
Chris@17
|
84 $instanceofDef->setTags([]);
|
Chris@14
|
85
|
Chris@14
|
86 if (isset($instanceofDef->getChanges()['shared'])) {
|
Chris@14
|
87 $shared = $instanceofDef->isShared();
|
Chris@14
|
88 }
|
Chris@14
|
89 }
|
Chris@14
|
90 }
|
Chris@14
|
91
|
Chris@14
|
92 if ($parent) {
|
Chris@14
|
93 $bindings = $definition->getBindings();
|
Chris@14
|
94 $abstract = $container->setDefinition('abstract.instanceof.'.$id, $definition);
|
Chris@14
|
95
|
Chris@14
|
96 // cast Definition to ChildDefinition
|
Chris@17
|
97 $definition->setBindings([]);
|
Chris@14
|
98 $definition = serialize($definition);
|
Chris@14
|
99 $definition = substr_replace($definition, '53', 2, 2);
|
Chris@14
|
100 $definition = substr_replace($definition, 'Child', 44, 0);
|
Chris@14
|
101 $definition = unserialize($definition);
|
Chris@14
|
102 $definition->setParent($parent);
|
Chris@14
|
103
|
Chris@14
|
104 if (null !== $shared && !isset($definition->getChanges()['shared'])) {
|
Chris@14
|
105 $definition->setShared($shared);
|
Chris@14
|
106 }
|
Chris@14
|
107
|
Chris@17
|
108 $i = \count($instanceofTags);
|
Chris@14
|
109 while (0 <= --$i) {
|
Chris@14
|
110 foreach ($instanceofTags[$i] as $k => $v) {
|
Chris@14
|
111 foreach ($v as $v) {
|
Chris@17
|
112 if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) {
|
Chris@14
|
113 continue;
|
Chris@14
|
114 }
|
Chris@14
|
115 $definition->addTag($k, $v);
|
Chris@14
|
116 }
|
Chris@14
|
117 }
|
Chris@14
|
118 }
|
Chris@14
|
119
|
Chris@17
|
120 $definition->setBindings($bindings);
|
Chris@17
|
121
|
Chris@14
|
122 // reset fields with "merge" behavior
|
Chris@14
|
123 $abstract
|
Chris@17
|
124 ->setBindings([])
|
Chris@17
|
125 ->setArguments([])
|
Chris@17
|
126 ->setMethodCalls([])
|
Chris@14
|
127 ->setDecoratedService(null)
|
Chris@17
|
128 ->setTags([])
|
Chris@14
|
129 ->setAbstract(true);
|
Chris@14
|
130 }
|
Chris@14
|
131
|
Chris@14
|
132 return $definition;
|
Chris@14
|
133 }
|
Chris@14
|
134
|
Chris@14
|
135 private function mergeConditionals(array $autoconfiguredInstanceof, array $instanceofConditionals, ContainerBuilder $container)
|
Chris@14
|
136 {
|
Chris@14
|
137 // make each value an array of ChildDefinition
|
Chris@17
|
138 $conditionals = array_map(function ($childDef) { return [$childDef]; }, $autoconfiguredInstanceof);
|
Chris@14
|
139
|
Chris@14
|
140 foreach ($instanceofConditionals as $interface => $instanceofDef) {
|
Chris@14
|
141 // make sure the interface/class exists (but don't validate automaticInstanceofConditionals)
|
Chris@14
|
142 if (!$container->getReflectionClass($interface)) {
|
Chris@14
|
143 throw new RuntimeException(sprintf('"%s" is set as an "instanceof" conditional, but it does not exist.', $interface));
|
Chris@14
|
144 }
|
Chris@14
|
145
|
Chris@14
|
146 if (!isset($autoconfiguredInstanceof[$interface])) {
|
Chris@17
|
147 $conditionals[$interface] = [];
|
Chris@14
|
148 }
|
Chris@14
|
149
|
Chris@14
|
150 $conditionals[$interface][] = $instanceofDef;
|
Chris@14
|
151 }
|
Chris@14
|
152
|
Chris@14
|
153 return $conditionals;
|
Chris@14
|
154 }
|
Chris@14
|
155 }
|