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 {