Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/dependency-injection/Loader/XmlFileLoader.php @ 14:1fec387a4317
Update Drupal core to 8.5.2 via Composer
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:46:53 +0100 |
parents | 4c8ae668cc8c |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
13:5fb285c0d0e3 | 14:1fec387a4317 |
---|---|
9 * file that was distributed with this source code. | 9 * file that was distributed with this source code. |
10 */ | 10 */ |
11 | 11 |
12 namespace Symfony\Component\DependencyInjection\Loader; | 12 namespace Symfony\Component\DependencyInjection\Loader; |
13 | 13 |
14 use Symfony\Component\Config\Resource\FileResource; | |
15 use Symfony\Component\Config\Util\XmlUtils; | 14 use Symfony\Component\Config\Util\XmlUtils; |
16 use Symfony\Component\DependencyInjection\DefinitionDecorator; | 15 use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; |
17 use Symfony\Component\DependencyInjection\ContainerInterface; | 16 use Symfony\Component\DependencyInjection\ContainerInterface; |
18 use Symfony\Component\DependencyInjection\Alias; | 17 use Symfony\Component\DependencyInjection\Alias; |
18 use Symfony\Component\DependencyInjection\Argument\BoundArgument; | |
19 use Symfony\Component\DependencyInjection\Argument\IteratorArgument; | |
19 use Symfony\Component\DependencyInjection\Definition; | 20 use Symfony\Component\DependencyInjection\Definition; |
21 use Symfony\Component\DependencyInjection\ChildDefinition; | |
22 use Symfony\Component\DependencyInjection\ContainerBuilder; | |
20 use Symfony\Component\DependencyInjection\Reference; | 23 use Symfony\Component\DependencyInjection\Reference; |
21 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; | 24 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; |
22 use Symfony\Component\DependencyInjection\Exception\RuntimeException; | 25 use Symfony\Component\DependencyInjection\Exception\RuntimeException; |
23 use Symfony\Component\ExpressionLanguage\Expression; | 26 use Symfony\Component\ExpressionLanguage\Expression; |
24 | 27 |
38 { | 41 { |
39 $path = $this->locator->locate($resource); | 42 $path = $this->locator->locate($resource); |
40 | 43 |
41 $xml = $this->parseFileToDOM($path); | 44 $xml = $this->parseFileToDOM($path); |
42 | 45 |
43 $this->container->addResource(new FileResource($path)); | 46 $this->container->fileExists($path); |
47 | |
48 $defaults = $this->getServiceDefaults($xml, $path); | |
44 | 49 |
45 // anonymous services | 50 // anonymous services |
46 $this->processAnonymousServices($xml, $path); | 51 $this->processAnonymousServices($xml, $path, $defaults); |
47 | 52 |
48 // imports | 53 // imports |
49 $this->parseImports($xml, $path); | 54 $this->parseImports($xml, $path); |
50 | 55 |
51 // parameters | 56 // parameters |
52 $this->parseParameters($xml); | 57 $this->parseParameters($xml, $path); |
53 | 58 |
54 // extensions | 59 // extensions |
55 $this->loadFromExtensions($xml); | 60 $this->loadFromExtensions($xml); |
56 | 61 |
57 // services | 62 // services |
58 $this->parseDefinitions($xml, $path); | 63 try { |
64 $this->parseDefinitions($xml, $path, $defaults); | |
65 } finally { | |
66 $this->instanceof = array(); | |
67 } | |
59 } | 68 } |
60 | 69 |
61 /** | 70 /** |
62 * {@inheritdoc} | 71 * {@inheritdoc} |
63 */ | 72 */ |
64 public function supports($resource, $type = null) | 73 public function supports($resource, $type = null) |
65 { | 74 { |
66 return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION); | 75 if (!is_string($resource)) { |
76 return false; | |
77 } | |
78 | |
79 if (null === $type && 'xml' === pathinfo($resource, PATHINFO_EXTENSION)) { | |
80 return true; | |
81 } | |
82 | |
83 return 'xml' === $type; | |
67 } | 84 } |
68 | 85 |
69 /** | 86 /** |
70 * Parses parameters. | 87 * Parses parameters. |
71 * | 88 * |
72 * @param \DOMDocument $xml | 89 * @param \DOMDocument $xml |
73 */ | 90 * @param string $file |
74 private function parseParameters(\DOMDocument $xml) | 91 */ |
92 private function parseParameters(\DOMDocument $xml, $file) | |
75 { | 93 { |
76 if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) { | 94 if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) { |
77 $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter')); | 95 $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter', $file)); |
78 } | 96 } |
79 } | 97 } |
80 | 98 |
81 /** | 99 /** |
82 * Parses imports. | 100 * Parses imports. |
94 } | 112 } |
95 | 113 |
96 $defaultDirectory = dirname($file); | 114 $defaultDirectory = dirname($file); |
97 foreach ($imports as $import) { | 115 foreach ($imports as $import) { |
98 $this->setCurrentDir($defaultDirectory); | 116 $this->setCurrentDir($defaultDirectory); |
99 $this->import($import->getAttribute('resource'), null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file); | 117 $this->import($import->getAttribute('resource'), XmlUtils::phpize($import->getAttribute('type')) ?: null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file); |
100 } | 118 } |
101 } | 119 } |
102 | 120 |
103 /** | 121 /** |
104 * Parses multiple definitions. | 122 * Parses multiple definitions. |
105 * | 123 * |
106 * @param \DOMDocument $xml | 124 * @param \DOMDocument $xml |
107 * @param string $file | 125 * @param string $file |
108 */ | 126 */ |
109 private function parseDefinitions(\DOMDocument $xml, $file) | 127 private function parseDefinitions(\DOMDocument $xml, $file, $defaults) |
110 { | 128 { |
111 $xpath = new \DOMXPath($xml); | 129 $xpath = new \DOMXPath($xml); |
112 $xpath->registerNamespace('container', self::NS); | 130 $xpath->registerNamespace('container', self::NS); |
113 | 131 |
114 if (false === $services = $xpath->query('//container:services/container:service')) { | 132 if (false === $services = $xpath->query('//container:services/container:service|//container:services/container:prototype')) { |
115 return; | 133 return; |
116 } | 134 } |
117 | 135 $this->setCurrentDir(dirname($file)); |
136 | |
137 $this->instanceof = array(); | |
138 $this->isLoadingInstanceof = true; | |
139 $instanceof = $xpath->query('//container:services/container:instanceof'); | |
140 foreach ($instanceof as $service) { | |
141 $this->setDefinition((string) $service->getAttribute('id'), $this->parseDefinition($service, $file, array())); | |
142 } | |
143 | |
144 $this->isLoadingInstanceof = false; | |
118 foreach ($services as $service) { | 145 foreach ($services as $service) { |
119 if (null !== $definition = $this->parseDefinition($service, $file)) { | 146 if (null !== $definition = $this->parseDefinition($service, $file, $defaults)) { |
120 $this->container->setDefinition((string) $service->getAttribute('id'), $definition); | 147 if ('prototype' === $service->tagName) { |
121 } | 148 $this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'), (string) $service->getAttribute('exclude')); |
122 } | 149 } else { |
150 $this->setDefinition((string) $service->getAttribute('id'), $definition); | |
151 } | |
152 } | |
153 } | |
154 } | |
155 | |
156 /** | |
157 * Get service defaults. | |
158 * | |
159 * @return array | |
160 */ | |
161 private function getServiceDefaults(\DOMDocument $xml, $file) | |
162 { | |
163 $xpath = new \DOMXPath($xml); | |
164 $xpath->registerNamespace('container', self::NS); | |
165 | |
166 if (null === $defaultsNode = $xpath->query('//container:services/container:defaults')->item(0)) { | |
167 return array(); | |
168 } | |
169 $defaults = array( | |
170 'tags' => $this->getChildren($defaultsNode, 'tag'), | |
171 'bind' => array_map(function ($v) { return new BoundArgument($v); }, $this->getArgumentsAsPhp($defaultsNode, 'bind', $file)), | |
172 ); | |
173 | |
174 foreach ($defaults['tags'] as $tag) { | |
175 if ('' === $tag->getAttribute('name')) { | |
176 throw new InvalidArgumentException(sprintf('The tag name for tag "<defaults>" in %s must be a non-empty string.', $file)); | |
177 } | |
178 } | |
179 | |
180 if ($defaultsNode->hasAttribute('autowire')) { | |
181 $defaults['autowire'] = XmlUtils::phpize($defaultsNode->getAttribute('autowire')); | |
182 } | |
183 if ($defaultsNode->hasAttribute('public')) { | |
184 $defaults['public'] = XmlUtils::phpize($defaultsNode->getAttribute('public')); | |
185 } | |
186 if ($defaultsNode->hasAttribute('autoconfigure')) { | |
187 $defaults['autoconfigure'] = XmlUtils::phpize($defaultsNode->getAttribute('autoconfigure')); | |
188 } | |
189 | |
190 return $defaults; | |
123 } | 191 } |
124 | 192 |
125 /** | 193 /** |
126 * Parses an individual Definition. | 194 * Parses an individual Definition. |
127 * | 195 * |
128 * @param \DOMElement $service | 196 * @param \DOMElement $service |
129 * @param string $file | 197 * @param string $file |
198 * @param array $defaults | |
130 * | 199 * |
131 * @return Definition|null | 200 * @return Definition|null |
132 */ | 201 */ |
133 private function parseDefinition(\DOMElement $service, $file) | 202 private function parseDefinition(\DOMElement $service, $file, array $defaults) |
134 { | 203 { |
135 if ($alias = $service->getAttribute('alias')) { | 204 if ($alias = $service->getAttribute('alias')) { |
136 $this->validateAlias($service, $file); | 205 $this->validateAlias($service, $file); |
137 | 206 |
138 $public = true; | 207 $this->container->setAlias((string) $service->getAttribute('id'), $alias = new Alias($alias)); |
139 if ($publicAttr = $service->getAttribute('public')) { | 208 if ($publicAttr = $service->getAttribute('public')) { |
140 $public = XmlUtils::phpize($publicAttr); | 209 $alias->setPublic(XmlUtils::phpize($publicAttr)); |
141 } | 210 } elseif (isset($defaults['public'])) { |
142 $this->container->setAlias((string) $service->getAttribute('id'), new Alias($alias, $public)); | 211 $alias->setPublic($defaults['public']); |
212 } | |
143 | 213 |
144 return; | 214 return; |
145 } | 215 } |
146 | 216 |
147 if ($parent = $service->getAttribute('parent')) { | 217 if ($this->isLoadingInstanceof) { |
148 $definition = new DefinitionDecorator($parent); | 218 $definition = new ChildDefinition(''); |
219 } elseif ($parent = $service->getAttribute('parent')) { | |
220 if (!empty($this->instanceof)) { | |
221 throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file.', $service->getAttribute('id'))); | |
222 } | |
223 | |
224 foreach ($defaults as $k => $v) { | |
225 if ('tags' === $k) { | |
226 // since tags are never inherited from parents, there is no confusion | |
227 // thus we can safely add them as defaults to ChildDefinition | |
228 continue; | |
229 } | |
230 if ('bind' === $k) { | |
231 if ($defaults['bind']) { | |
232 throw new InvalidArgumentException(sprintf('Bound values on service "%s" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file.', $service->getAttribute('id'))); | |
233 } | |
234 | |
235 continue; | |
236 } | |
237 if (!$service->hasAttribute($k)) { | |
238 throw new InvalidArgumentException(sprintf('Attribute "%s" on service "%s" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly.', $k, $service->getAttribute('id'))); | |
239 } | |
240 } | |
241 | |
242 $definition = new ChildDefinition($parent); | |
149 } else { | 243 } else { |
150 $definition = new Definition(); | 244 $definition = new Definition(); |
151 } | 245 |
152 | 246 if (isset($defaults['public'])) { |
153 foreach (array('class', 'shared', 'public', 'synthetic', 'lazy', 'abstract') as $key) { | 247 $definition->setPublic($defaults['public']); |
248 } | |
249 if (isset($defaults['autowire'])) { | |
250 $definition->setAutowired($defaults['autowire']); | |
251 } | |
252 if (isset($defaults['autoconfigure'])) { | |
253 $definition->setAutoconfigured($defaults['autoconfigure']); | |
254 } | |
255 | |
256 $definition->setChanges(array()); | |
257 } | |
258 | |
259 foreach (array('class', 'public', 'shared', 'synthetic', 'lazy', 'abstract') as $key) { | |
154 if ($value = $service->getAttribute($key)) { | 260 if ($value = $service->getAttribute($key)) { |
155 $method = 'set'.$key; | 261 $method = 'set'.$key; |
156 $definition->$method(XmlUtils::phpize($value)); | 262 $definition->$method(XmlUtils::phpize($value)); |
157 } | 263 } |
158 } | 264 } |
159 | 265 |
160 if ($value = $service->getAttribute('autowire')) { | 266 if ($value = $service->getAttribute('autowire')) { |
161 $definition->setAutowired(XmlUtils::phpize($value)); | 267 $definition->setAutowired(XmlUtils::phpize($value)); |
162 } | 268 } |
163 | 269 |
270 if ($value = $service->getAttribute('autoconfigure')) { | |
271 if (!$definition instanceof ChildDefinition) { | |
272 $definition->setAutoconfigured(XmlUtils::phpize($value)); | |
273 } elseif ($value = XmlUtils::phpize($value)) { | |
274 throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting autoconfigure="false" for the service.', $service->getAttribute('id'))); | |
275 } | |
276 } | |
277 | |
164 if ($files = $this->getChildren($service, 'file')) { | 278 if ($files = $this->getChildren($service, 'file')) { |
165 $definition->setFile($files[0]->nodeValue); | 279 $definition->setFile($files[0]->nodeValue); |
166 } | 280 } |
167 | 281 |
168 if ($deprecated = $this->getChildren($service, 'deprecated')) { | 282 if ($deprecated = $this->getChildren($service, 'deprecated')) { |
169 $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); | 283 $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); |
170 } | 284 } |
171 | 285 |
172 $definition->setArguments($this->getArgumentsAsPhp($service, 'argument')); | 286 $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, false, $definition instanceof ChildDefinition)); |
173 $definition->setProperties($this->getArgumentsAsPhp($service, 'property')); | 287 $definition->setProperties($this->getArgumentsAsPhp($service, 'property', $file)); |
174 | 288 |
175 if ($factories = $this->getChildren($service, 'factory')) { | 289 if ($factories = $this->getChildren($service, 'factory')) { |
176 $factory = $factories[0]; | 290 $factory = $factories[0]; |
177 if ($function = $factory->getAttribute('function')) { | 291 if ($function = $factory->getAttribute('function')) { |
178 $definition->setFactory($function); | 292 $definition->setFactory($function); |
179 } else { | 293 } else { |
180 $factoryService = $this->getChildren($factory, 'service'); | 294 if ($childService = $factory->getAttribute('service')) { |
181 | |
182 if (isset($factoryService[0])) { | |
183 $class = $this->parseDefinition($factoryService[0], $file); | |
184 } elseif ($childService = $factory->getAttribute('service')) { | |
185 $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); | 295 $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); |
186 } else { | 296 } else { |
187 $class = $factory->getAttribute('class'); | 297 $class = $factory->hasAttribute('class') ? $factory->getAttribute('class') : null; |
188 } | 298 } |
189 | 299 |
190 $definition->setFactory(array($class, $factory->getAttribute('method'))); | 300 $definition->setFactory(array($class, $factory->getAttribute('method'))); |
191 } | 301 } |
192 } | 302 } |
194 if ($configurators = $this->getChildren($service, 'configurator')) { | 304 if ($configurators = $this->getChildren($service, 'configurator')) { |
195 $configurator = $configurators[0]; | 305 $configurator = $configurators[0]; |
196 if ($function = $configurator->getAttribute('function')) { | 306 if ($function = $configurator->getAttribute('function')) { |
197 $definition->setConfigurator($function); | 307 $definition->setConfigurator($function); |
198 } else { | 308 } else { |
199 $configuratorService = $this->getChildren($configurator, 'service'); | 309 if ($childService = $configurator->getAttribute('service')) { |
200 | |
201 if (isset($configuratorService[0])) { | |
202 $class = $this->parseDefinition($configuratorService[0], $file); | |
203 } elseif ($childService = $configurator->getAttribute('service')) { | |
204 $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); | 310 $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); |
205 } else { | 311 } else { |
206 $class = $configurator->getAttribute('class'); | 312 $class = $configurator->getAttribute('class'); |
207 } | 313 } |
208 | 314 |
209 $definition->setConfigurator(array($class, $configurator->getAttribute('method'))); | 315 $definition->setConfigurator(array($class, $configurator->getAttribute('method'))); |
210 } | 316 } |
211 } | 317 } |
212 | 318 |
213 foreach ($this->getChildren($service, 'call') as $call) { | 319 foreach ($this->getChildren($service, 'call') as $call) { |
214 $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument')); | 320 $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument', $file)); |
215 } | 321 } |
216 | 322 |
217 foreach ($this->getChildren($service, 'tag') as $tag) { | 323 $tags = $this->getChildren($service, 'tag'); |
324 | |
325 if (!empty($defaults['tags'])) { | |
326 $tags = array_merge($tags, $defaults['tags']); | |
327 } | |
328 | |
329 foreach ($tags as $tag) { | |
218 $parameters = array(); | 330 $parameters = array(); |
219 foreach ($tag->attributes as $name => $node) { | 331 foreach ($tag->attributes as $name => $node) { |
220 if ('name' === $name) { | 332 if ('name' === $name) { |
221 continue; | 333 continue; |
222 } | 334 } |
237 | 349 |
238 foreach ($this->getChildren($service, 'autowiring-type') as $type) { | 350 foreach ($this->getChildren($service, 'autowiring-type') as $type) { |
239 $definition->addAutowiringType($type->textContent); | 351 $definition->addAutowiringType($type->textContent); |
240 } | 352 } |
241 | 353 |
354 $bindings = $this->getArgumentsAsPhp($service, 'bind', $file); | |
355 if (isset($defaults['bind'])) { | |
356 // deep clone, to avoid multiple process of the same instance in the passes | |
357 $bindings = array_merge(unserialize(serialize($defaults['bind'])), $bindings); | |
358 } | |
359 if ($bindings) { | |
360 $definition->setBindings($bindings); | |
361 } | |
362 | |
242 if ($value = $service->getAttribute('decorates')) { | 363 if ($value = $service->getAttribute('decorates')) { |
243 $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; | 364 $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; |
244 $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; | 365 $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; |
245 $definition->setDecoratedService($value, $renameId, $priority); | 366 $definition->setDecoratedService($value, $renameId, $priority); |
246 } | 367 } |
273 /** | 394 /** |
274 * Processes anonymous services. | 395 * Processes anonymous services. |
275 * | 396 * |
276 * @param \DOMDocument $xml | 397 * @param \DOMDocument $xml |
277 * @param string $file | 398 * @param string $file |
278 */ | 399 * @param array $defaults |
279 private function processAnonymousServices(\DOMDocument $xml, $file) | 400 */ |
401 private function processAnonymousServices(\DOMDocument $xml, $file, $defaults) | |
280 { | 402 { |
281 $definitions = array(); | 403 $definitions = array(); |
282 $count = 0; | 404 $count = 0; |
405 $suffix = ContainerBuilder::hash($file); | |
283 | 406 |
284 $xpath = new \DOMXPath($xml); | 407 $xpath = new \DOMXPath($xml); |
285 $xpath->registerNamespace('container', self::NS); | 408 $xpath->registerNamespace('container', self::NS); |
286 | 409 |
287 // anonymous services as arguments/properties | 410 // anonymous services as arguments/properties |
288 if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) { | 411 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)]')) { |
289 foreach ($nodes as $node) { | 412 foreach ($nodes as $node) { |
290 // give it a unique name | |
291 $id = sprintf('%s_%d', hash('sha256', $file), ++$count); | |
292 $node->setAttribute('id', $id); | |
293 | |
294 if ($services = $this->getChildren($node, 'service')) { | 413 if ($services = $this->getChildren($node, 'service')) { |
414 // give it a unique name | |
415 $id = sprintf('%d_%s', ++$count, preg_replace('/^.*\\\\/', '', $services[0]->getAttribute('class')).'~'.$suffix); | |
416 $node->setAttribute('id', $id); | |
417 $node->setAttribute('service', $id); | |
418 | |
295 $definitions[$id] = array($services[0], $file, false); | 419 $definitions[$id] = array($services[0], $file, false); |
296 $services[0]->setAttribute('id', $id); | 420 $services[0]->setAttribute('id', $id); |
297 | 421 |
298 // anonymous services are always private | 422 // anonymous services are always private |
299 // we could not use the constant false here, because of XML parsing | 423 // we could not use the constant false here, because of XML parsing |
303 } | 427 } |
304 | 428 |
305 // anonymous services "in the wild" | 429 // anonymous services "in the wild" |
306 if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) { | 430 if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) { |
307 foreach ($nodes as $node) { | 431 foreach ($nodes as $node) { |
432 @trigger_error(sprintf('Top-level anonymous services are deprecated since Symfony 3.4, the "id" attribute will be required in version 4.0 in %s at line %d.', $file, $node->getLineNo()), E_USER_DEPRECATED); | |
433 | |
308 // give it a unique name | 434 // give it a unique name |
309 $id = sprintf('%s_%d', hash('sha256', $file), ++$count); | 435 $id = sprintf('%d_%s', ++$count, preg_replace('/^.*\\\\/', '', $node->getAttribute('class')).$suffix); |
310 $node->setAttribute('id', $id); | 436 $node->setAttribute('id', $id); |
311 $definitions[$id] = array($node, $file, true); | 437 $definitions[$id] = array($node, $file, true); |
312 } | 438 } |
313 } | 439 } |
314 | 440 |
315 // resolve definitions | 441 // resolve definitions |
316 krsort($definitions); | 442 uksort($definitions, 'strnatcmp'); |
317 foreach ($definitions as $id => list($domElement, $file, $wild)) { | 443 foreach (array_reverse($definitions) as $id => list($domElement, $file, $wild)) { |
318 if (null !== $definition = $this->parseDefinition($domElement, $file)) { | 444 if (null !== $definition = $this->parseDefinition($domElement, $file, $wild ? $defaults : array())) { |
319 $this->container->setDefinition($id, $definition); | 445 $this->setDefinition($id, $definition); |
320 } | 446 } |
321 | 447 |
322 if (true === $wild) { | 448 if (true === $wild) { |
323 $tmpDomElement = new \DOMElement('_services', null, self::NS); | 449 $tmpDomElement = new \DOMElement('_services', null, self::NS); |
324 $domElement->parentNode->replaceChild($tmpDomElement, $domElement); | 450 $domElement->parentNode->replaceChild($tmpDomElement, $domElement); |
325 $tmpDomElement->setAttribute('id', $id); | 451 $tmpDomElement->setAttribute('id', $id); |
326 } else { | |
327 $domElement->parentNode->removeChild($domElement); | |
328 } | 452 } |
329 } | 453 } |
330 } | 454 } |
331 | 455 |
332 /** | 456 /** |
333 * Returns arguments as valid php types. | 457 * Returns arguments as valid php types. |
334 * | 458 * |
335 * @param \DOMElement $node | 459 * @param \DOMElement $node |
336 * @param string $name | 460 * @param string $name |
461 * @param string $file | |
337 * @param bool $lowercase | 462 * @param bool $lowercase |
338 * | 463 * |
339 * @return mixed | 464 * @return mixed |
340 */ | 465 */ |
341 private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true) | 466 private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase = true, $isChildDefinition = false) |
342 { | 467 { |
343 $arguments = array(); | 468 $arguments = array(); |
344 foreach ($this->getChildren($node, $name) as $arg) { | 469 foreach ($this->getChildren($node, $name) as $arg) { |
345 if ($arg->hasAttribute('name')) { | 470 if ($arg->hasAttribute('name')) { |
346 $arg->setAttribute('key', $arg->getAttribute('name')); | 471 $arg->setAttribute('key', $arg->getAttribute('name')); |
347 } | 472 } |
348 | 473 |
349 // this is used by DefinitionDecorator to overwrite a specific | 474 // this is used by ChildDefinition to overwrite a specific |
350 // argument of the parent definition | 475 // argument of the parent definition |
351 if ($arg->hasAttribute('index')) { | 476 if ($arg->hasAttribute('index')) { |
352 $key = 'index_'.$arg->getAttribute('index'); | 477 $key = ($isChildDefinition ? 'index_' : '').$arg->getAttribute('index'); |
353 } elseif (!$arg->hasAttribute('key')) { | 478 } elseif (!$arg->hasAttribute('key')) { |
354 // Append an empty argument, then fetch its key to overwrite it later | 479 // Append an empty argument, then fetch its key to overwrite it later |
355 $arguments[] = null; | 480 $arguments[] = null; |
356 $keys = array_keys($arguments); | 481 $keys = array_keys($arguments); |
357 $key = array_pop($keys); | 482 $key = array_pop($keys); |
358 } else { | 483 } else { |
359 $key = $arg->getAttribute('key'); | 484 $key = $arg->getAttribute('key'); |
360 | 485 } |
361 // parameter keys are case insensitive | 486 |
362 if ('parameter' == $name && $lowercase) { | 487 $onInvalid = $arg->getAttribute('on-invalid'); |
363 $key = strtolower($key); | 488 $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; |
364 } | 489 if ('ignore' == $onInvalid) { |
490 $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; | |
491 } elseif ('ignore_uninitialized' == $onInvalid) { | |
492 $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; | |
493 } elseif ('null' == $onInvalid) { | |
494 $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; | |
365 } | 495 } |
366 | 496 |
367 switch ($arg->getAttribute('type')) { | 497 switch ($arg->getAttribute('type')) { |
368 case 'service': | 498 case 'service': |
369 $onInvalid = $arg->getAttribute('on-invalid'); | 499 if (!$arg->getAttribute('id')) { |
370 $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; | 500 throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service" has no or empty "id" attribute in "%s".', $name, $file)); |
371 if ('ignore' == $onInvalid) { | 501 } |
372 $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; | 502 if ($arg->hasAttribute('strict')) { |
373 } elseif ('null' == $onInvalid) { | 503 @trigger_error(sprintf('The "strict" attribute used when referencing the "%s" service is deprecated since Symfony 3.3 and will be removed in 4.0.', $arg->getAttribute('id')), E_USER_DEPRECATED); |
374 $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; | |
375 } | 504 } |
376 | 505 |
377 $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior); | 506 $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior); |
378 break; | 507 break; |
379 case 'expression': | 508 case 'expression': |
509 if (!class_exists(Expression::class)) { | |
510 throw new \LogicException(sprintf('The type="expression" attribute cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".')); | |
511 } | |
512 | |
380 $arguments[$key] = new Expression($arg->nodeValue); | 513 $arguments[$key] = new Expression($arg->nodeValue); |
381 break; | 514 break; |
382 case 'collection': | 515 case 'collection': |
383 $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false); | 516 $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file, false); |
517 break; | |
518 case 'iterator': | |
519 $arg = $this->getArgumentsAsPhp($arg, $name, $file, false); | |
520 try { | |
521 $arguments[$key] = new IteratorArgument($arg); | |
522 } catch (InvalidArgumentException $e) { | |
523 throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file)); | |
524 } | |
525 break; | |
526 case 'tagged': | |
527 if (!$arg->getAttribute('tag')) { | |
528 throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="tagged" has no or empty "tag" attribute in "%s".', $name, $file)); | |
529 } | |
530 $arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag')); | |
384 break; | 531 break; |
385 case 'string': | 532 case 'string': |
386 $arguments[$key] = $arg->nodeValue; | 533 $arguments[$key] = $arg->nodeValue; |
387 break; | 534 break; |
388 case 'constant': | 535 case 'constant': |
406 */ | 553 */ |
407 private function getChildren(\DOMNode $node, $name) | 554 private function getChildren(\DOMNode $node, $name) |
408 { | 555 { |
409 $children = array(); | 556 $children = array(); |
410 foreach ($node->childNodes as $child) { | 557 foreach ($node->childNodes as $child) { |
411 if ($child instanceof \DOMElement && $child->localName === $name && $child->namespaceURI === self::NS) { | 558 if ($child instanceof \DOMElement && $child->localName === $name && self::NS === $child->namespaceURI) { |
412 $children[] = $child; | 559 $children[] = $child; |
413 } | 560 } |
414 } | 561 } |
415 | 562 |
416 return $children; | 563 return $children; |
450 | 597 |
451 $tmpfiles = array(); | 598 $tmpfiles = array(); |
452 $imports = ''; | 599 $imports = ''; |
453 foreach ($schemaLocations as $namespace => $location) { | 600 foreach ($schemaLocations as $namespace => $location) { |
454 $parts = explode('/', $location); | 601 $parts = explode('/', $location); |
602 $locationstart = 'file:///'; | |
455 if (0 === stripos($location, 'phar://')) { | 603 if (0 === stripos($location, 'phar://')) { |
456 $tmpfile = tempnam(sys_get_temp_dir(), 'sf2'); | 604 $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); |
457 if ($tmpfile) { | 605 if ($tmpfile) { |
458 copy($location, $tmpfile); | 606 copy($location, $tmpfile); |
459 $tmpfiles[] = $tmpfile; | 607 $tmpfiles[] = $tmpfile; |
460 $parts = explode('/', str_replace('\\', '/', $tmpfile)); | 608 $parts = explode('/', str_replace('\\', '/', $tmpfile)); |
609 } else { | |
610 array_shift($parts); | |
611 $locationstart = 'phar:///'; | |
461 } | 612 } |
462 } | 613 } |
463 $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; | 614 $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; |
464 $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts)); | 615 $location = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts)); |
465 | 616 |
466 $imports .= sprintf(' <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location); | 617 $imports .= sprintf(' <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location); |
467 } | 618 } |
468 | 619 |
469 $source = <<<EOF | 620 $source = <<<EOF |
503 @trigger_error(sprintf('Using the attribute "%s" is deprecated for the service "%s" which is defined as an alias in "%s". Allowed attributes for service aliases are "alias", "id" and "public". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $name, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); | 654 @trigger_error(sprintf('Using the attribute "%s" is deprecated for the service "%s" which is defined as an alias in "%s". Allowed attributes for service aliases are "alias", "id" and "public". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $name, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); |
504 } | 655 } |
505 } | 656 } |
506 | 657 |
507 foreach ($alias->childNodes as $child) { | 658 foreach ($alias->childNodes as $child) { |
508 if ($child instanceof \DOMElement && $child->namespaceURI === self::NS) { | 659 if ($child instanceof \DOMElement && self::NS === $child->namespaceURI) { |
509 @trigger_error(sprintf('Using the element "%s" is deprecated for the service "%s" which is defined as an alias in "%s". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported elements.', $child->localName, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); | 660 @trigger_error(sprintf('Using the element "%s" is deprecated for the service "%s" which is defined as an alias in "%s". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported elements.', $child->localName, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); |
510 } | 661 } |
511 } | 662 } |
512 } | 663 } |
513 | 664 |
546 * @param \DOMDocument $xml | 697 * @param \DOMDocument $xml |
547 */ | 698 */ |
548 private function loadFromExtensions(\DOMDocument $xml) | 699 private function loadFromExtensions(\DOMDocument $xml) |
549 { | 700 { |
550 foreach ($xml->documentElement->childNodes as $node) { | 701 foreach ($xml->documentElement->childNodes as $node) { |
551 if (!$node instanceof \DOMElement || $node->namespaceURI === self::NS) { | 702 if (!$node instanceof \DOMElement || self::NS === $node->namespaceURI) { |
552 continue; | 703 continue; |
553 } | 704 } |
554 | 705 |
555 $values = static::convertDomElementToArray($node); | 706 $values = static::convertDomElementToArray($node); |
556 if (!is_array($values)) { | 707 if (!is_array($values)) { |
560 $this->container->loadFromExtension($node->namespaceURI, $values); | 711 $this->container->loadFromExtension($node->namespaceURI, $values); |
561 } | 712 } |
562 } | 713 } |
563 | 714 |
564 /** | 715 /** |
565 * Converts a \DomElement object to a PHP array. | 716 * Converts a \DOMElement object to a PHP array. |
566 * | 717 * |
567 * The following rules applies during the conversion: | 718 * The following rules applies during the conversion: |
568 * | 719 * |
569 * * Each tag is converted to a key value or an array | 720 * * Each tag is converted to a key value or an array |
570 * if there is more than one "value" | 721 * if there is more than one "value" |
574 * | 725 * |
575 * * The attributes are converted to keys (<foo foo="bar"/>) | 726 * * The attributes are converted to keys (<foo foo="bar"/>) |
576 * | 727 * |
577 * * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>) | 728 * * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>) |
578 * | 729 * |
579 * @param \DomElement $element A \DomElement instance | 730 * @param \DOMElement $element A \DOMElement instance |
580 * | 731 * |
581 * @return array A PHP array | 732 * @return array A PHP array |
582 */ | 733 */ |
583 public static function convertDomElementToArray(\DOMElement $element) | 734 public static function convertDomElementToArray(\DOMElement $element) |
584 { | 735 { |