1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Symfony package. |
5
|
|
|
* |
6
|
|
|
* (c) Fabien Potencier <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Symfony\Component\DependencyInjection\Loader; |
13
|
|
|
|
14
|
|
|
use Symfony\Component\Config\Resource\FileResource; |
15
|
|
|
use Symfony\Component\Config\Util\XmlUtils; |
16
|
|
|
use Symfony\Component\DependencyInjection\DefinitionDecorator; |
17
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
18
|
|
|
use Symfony\Component\DependencyInjection\Alias; |
19
|
|
|
use Symfony\Component\DependencyInjection\Definition; |
20
|
|
|
use Symfony\Component\DependencyInjection\Reference; |
21
|
|
|
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; |
22
|
|
|
use Symfony\Component\DependencyInjection\Exception\RuntimeException; |
23
|
|
|
use Symfony\Component\ExpressionLanguage\Expression; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* XmlFileLoader loads XML files service definitions. |
27
|
|
|
* |
28
|
|
|
* @author Fabien Potencier <[email protected]> |
29
|
|
|
*/ |
30
|
|
|
class XmlFileLoader extends FileLoader |
31
|
|
|
{ |
32
|
|
|
const NS = 'http://symfony.com/schema/dic/services'; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* {@inheritdoc} |
36
|
|
|
*/ |
37
|
|
|
public function load($resource, $type = null) |
38
|
|
|
{ |
39
|
|
|
$path = $this->locator->locate($resource); |
40
|
|
|
|
41
|
|
|
$xml = $this->parseFileToDOM($path); |
42
|
|
|
|
43
|
|
|
$this->container->addResource(new FileResource($path)); |
44
|
|
|
|
45
|
|
|
// anonymous services |
46
|
|
|
$this->processAnonymousServices($xml, $path); |
47
|
|
|
|
48
|
|
|
// imports |
49
|
|
|
$this->parseImports($xml, $path); |
50
|
|
|
|
51
|
|
|
// parameters |
52
|
|
|
$this->parseParameters($xml, $path); |
53
|
|
|
|
54
|
|
|
// extensions |
55
|
|
|
$this->loadFromExtensions($xml); |
56
|
|
|
|
57
|
|
|
// services |
58
|
|
|
$this->parseDefinitions($xml, $path); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* {@inheritdoc} |
63
|
|
|
*/ |
64
|
|
|
public function supports($resource, $type = null) |
65
|
|
|
{ |
66
|
|
|
return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Parses parameters. |
71
|
|
|
* |
72
|
|
|
* @param \DOMDocument $xml |
73
|
|
|
* @param string $file |
74
|
|
|
*/ |
75
|
|
|
private function parseParameters(\DOMDocument $xml, $file) |
76
|
|
|
{ |
77
|
|
|
if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) { |
78
|
|
|
$this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter')); |
79
|
|
|
} |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Parses imports. |
84
|
|
|
* |
85
|
|
|
* @param \DOMDocument $xml |
86
|
|
|
* @param string $file |
87
|
|
|
*/ |
88
|
|
|
private function parseImports(\DOMDocument $xml, $file) |
89
|
|
|
{ |
90
|
|
|
$xpath = new \DOMXPath($xml); |
91
|
|
|
$xpath->registerNamespace('container', self::NS); |
92
|
|
|
|
93
|
|
|
if (false === $imports = $xpath->query('//container:imports/container:import')) { |
94
|
|
|
return; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
foreach ($imports as $import) { |
98
|
|
|
$this->setCurrentDir(dirname($file)); |
99
|
|
|
$this->import($import->getAttribute('resource'), null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file); |
100
|
|
|
} |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Parses multiple definitions. |
105
|
|
|
* |
106
|
|
|
* @param \DOMDocument $xml |
107
|
|
|
* @param string $file |
108
|
|
|
*/ |
109
|
|
|
private function parseDefinitions(\DOMDocument $xml, $file) |
110
|
|
|
{ |
111
|
|
|
$xpath = new \DOMXPath($xml); |
112
|
|
|
$xpath->registerNamespace('container', self::NS); |
113
|
|
|
|
114
|
|
|
if (false === $services = $xpath->query('//container:services/container:service')) { |
115
|
|
|
return; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
foreach ($services as $service) { |
119
|
|
|
if (null !== $definition = $this->parseDefinition($service, $file)) { |
120
|
|
|
$this->container->setDefinition((string) $service->getAttribute('id'), $definition); |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Parses an individual Definition. |
127
|
|
|
* |
128
|
|
|
* @param \DOMElement $service |
129
|
|
|
* @param string $file |
130
|
|
|
* |
131
|
|
|
* @return Definition|null |
132
|
|
|
*/ |
133
|
|
|
private function parseDefinition(\DOMElement $service, $file) |
134
|
|
|
{ |
135
|
|
|
if ($alias = $service->getAttribute('alias')) { |
136
|
|
|
$public = true; |
137
|
|
|
if ($publicAttr = $service->getAttribute('public')) { |
138
|
|
|
$public = XmlUtils::phpize($publicAttr); |
139
|
|
|
} |
140
|
|
|
$this->container->setAlias((string) $service->getAttribute('id'), new Alias($alias, $public)); |
141
|
|
|
|
142
|
|
|
return; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
View Code Duplication |
if ($parent = $service->getAttribute('parent')) { |
|
|
|
|
146
|
|
|
$definition = new DefinitionDecorator($parent); |
147
|
|
|
} else { |
148
|
|
|
$definition = new Definition(); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract') as $key) { |
152
|
|
|
if ($value = $service->getAttribute($key)) { |
153
|
|
|
if (in_array($key, array('factory-class', 'factory-method', 'factory-service'))) { |
154
|
|
|
trigger_error(sprintf('The "%s" attribute in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use the "factory" element instead.', $key, $file), E_USER_DEPRECATED); |
155
|
|
|
} |
156
|
|
|
$method = 'set'.str_replace('-', '', $key); |
157
|
|
|
$definition->$method(XmlUtils::phpize($value)); |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
if ($value = $service->getAttribute('synchronized')) { |
162
|
|
|
$triggerDeprecation = 'request' !== (string) $service->getAttribute('id'); |
163
|
|
|
|
164
|
|
|
if ($triggerDeprecation) { |
165
|
|
|
trigger_error(sprintf('The "synchronized" attribute in file "%s" is deprecated since version 2.7 and will be removed in 3.0.', $file), E_USER_DEPRECATED); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
$definition->setSynchronized(XmlUtils::phpize($value), $triggerDeprecation); |
|
|
|
|
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
if ($files = $this->getChildren($service, 'file')) { |
172
|
|
|
$definition->setFile($files[0]->nodeValue); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$definition->setArguments($this->getArgumentsAsPhp($service, 'argument')); |
176
|
|
|
$definition->setProperties($this->getArgumentsAsPhp($service, 'property')); |
177
|
|
|
|
178
|
|
View Code Duplication |
if ($factories = $this->getChildren($service, 'factory')) { |
|
|
|
|
179
|
|
|
$factory = $factories[0]; |
180
|
|
|
if ($function = $factory->getAttribute('function')) { |
181
|
|
|
$definition->setFactory($function); |
182
|
|
|
} else { |
183
|
|
|
$factoryService = $this->getChildren($factory, 'service'); |
184
|
|
|
|
185
|
|
|
if (isset($factoryService[0])) { |
186
|
|
|
$class = $this->parseDefinition($factoryService[0], $file); |
187
|
|
|
} elseif ($childService = $factory->getAttribute('service')) { |
188
|
|
|
$class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); |
189
|
|
|
} else { |
190
|
|
|
$class = $factory->getAttribute('class'); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
$definition->setFactory(array($class, $factory->getAttribute('method'))); |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
|
197
|
|
View Code Duplication |
if ($configurators = $this->getChildren($service, 'configurator')) { |
|
|
|
|
198
|
|
|
$configurator = $configurators[0]; |
199
|
|
|
if ($function = $configurator->getAttribute('function')) { |
200
|
|
|
$definition->setConfigurator($function); |
201
|
|
|
} else { |
202
|
|
|
$configuratorService = $this->getChildren($configurator, 'service'); |
203
|
|
|
|
204
|
|
|
if (isset($configuratorService[0])) { |
205
|
|
|
$class = $this->parseDefinition($configuratorService[0], $file); |
206
|
|
|
} elseif ($childService = $configurator->getAttribute('service')) { |
207
|
|
|
$class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); |
208
|
|
|
} else { |
209
|
|
|
$class = $configurator->getAttribute('class'); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
$definition->setConfigurator(array($class, $configurator->getAttribute('method'))); |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
foreach ($this->getChildren($service, 'call') as $call) { |
217
|
|
|
$definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument')); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
foreach ($this->getChildren($service, 'tag') as $tag) { |
221
|
|
|
$parameters = array(); |
222
|
|
|
foreach ($tag->attributes as $name => $node) { |
223
|
|
|
if ('name' === $name) { |
224
|
|
|
continue; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { |
228
|
|
|
$parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue); |
229
|
|
|
} |
230
|
|
|
// keep not normalized key for BC too |
231
|
|
|
$parameters[$name] = XmlUtils::phpize($node->nodeValue); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
$definition->addTag($tag->getAttribute('name'), $parameters); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
if ($value = $service->getAttribute('decorates')) { |
238
|
|
|
$renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; |
239
|
|
|
$definition->setDecoratedService($value, $renameId); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
return $definition; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Parses a XML file to a \DOMDocument. |
247
|
|
|
* |
248
|
|
|
* @param string $file Path to a file |
249
|
|
|
* |
250
|
|
|
* @return \DOMDocument |
251
|
|
|
* |
252
|
|
|
* @throws InvalidArgumentException When loading of XML file returns error |
253
|
|
|
*/ |
254
|
|
|
private function parseFileToDOM($file) |
255
|
|
|
{ |
256
|
|
|
try { |
257
|
|
|
$dom = XmlUtils::loadFile($file, array($this, 'validateSchema')); |
258
|
|
|
} catch (\InvalidArgumentException $e) { |
259
|
|
|
throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
$this->validateExtensions($dom, $file); |
263
|
|
|
|
264
|
|
|
return $dom; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Processes anonymous services. |
269
|
|
|
* |
270
|
|
|
* @param \DOMDocument $xml |
271
|
|
|
* @param string $file |
272
|
|
|
*/ |
273
|
|
|
private function processAnonymousServices(\DOMDocument $xml, $file) |
274
|
|
|
{ |
275
|
|
|
$definitions = array(); |
276
|
|
|
$count = 0; |
277
|
|
|
|
278
|
|
|
$xpath = new \DOMXPath($xml); |
279
|
|
|
$xpath->registerNamespace('container', self::NS); |
280
|
|
|
|
281
|
|
|
// anonymous services as arguments/properties |
282
|
|
View Code Duplication |
if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) { |
|
|
|
|
283
|
|
|
foreach ($nodes as $node) { |
284
|
|
|
// give it a unique name |
285
|
|
|
$id = sprintf('%s_%d', hash('sha256', $file), ++$count); |
286
|
|
|
$node->setAttribute('id', $id); |
287
|
|
|
|
288
|
|
|
if ($services = $this->getChildren($node, 'service')) { |
289
|
|
|
$definitions[$id] = array($services[0], $file, false); |
290
|
|
|
$services[0]->setAttribute('id', $id); |
291
|
|
|
} |
292
|
|
|
} |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
// anonymous services "in the wild" |
296
|
|
View Code Duplication |
if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) { |
|
|
|
|
297
|
|
|
foreach ($nodes as $node) { |
298
|
|
|
// give it a unique name |
299
|
|
|
$id = sprintf('%s_%d', hash('sha256', $file), ++$count); |
300
|
|
|
$node->setAttribute('id', $id); |
301
|
|
|
|
302
|
|
|
if ($services = $this->getChildren($node, 'service')) { |
303
|
|
|
$definitions[$id] = array($node, $file, true); |
304
|
|
|
$services[0]->setAttribute('id', $id); |
305
|
|
|
} |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
// resolve definitions |
310
|
|
|
krsort($definitions); |
311
|
|
|
foreach ($definitions as $id => $def) { |
312
|
|
|
list($domElement, $file, $wild) = $def; |
313
|
|
|
|
314
|
|
|
// anonymous services are always private |
315
|
|
|
// we could not use the constant false here, because of XML parsing |
316
|
|
|
$domElement->setAttribute('public', 'false'); |
317
|
|
|
|
318
|
|
|
if (null !== $definition = $this->parseDefinition($domElement, $file)) { |
319
|
|
|
$this->container->setDefinition($id, $definition); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
if (true === $wild) { |
323
|
|
|
$tmpDomElement = new \DOMElement('_services', null, self::NS); |
324
|
|
|
$domElement->parentNode->replaceChild($tmpDomElement, $domElement); |
325
|
|
|
$tmpDomElement->setAttribute('id', $id); |
326
|
|
|
} else { |
327
|
|
|
$domElement->parentNode->removeChild($domElement); |
328
|
|
|
} |
329
|
|
|
} |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* Returns arguments as valid php types. |
334
|
|
|
* |
335
|
|
|
* @param \DOMElement $node |
336
|
|
|
* @param string $name |
337
|
|
|
* @param bool $lowercase |
338
|
|
|
* |
339
|
|
|
* @return mixed |
340
|
|
|
*/ |
341
|
|
|
private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true) |
342
|
|
|
{ |
343
|
|
|
$arguments = array(); |
344
|
|
|
foreach ($this->getChildren($node, $name) as $arg) { |
345
|
|
|
if ($arg->hasAttribute('name')) { |
346
|
|
|
$arg->setAttribute('key', $arg->getAttribute('name')); |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
if (!$arg->hasAttribute('key')) { |
350
|
|
|
$key = !$arguments ? 0 : max(array_keys($arguments)) + 1; |
351
|
|
|
} else { |
352
|
|
|
$key = $arg->getAttribute('key'); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
// parameter keys are case insensitive |
356
|
|
|
if ('parameter' == $name && $lowercase) { |
357
|
|
|
$key = strtolower($key); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
// this is used by DefinitionDecorator to overwrite a specific |
361
|
|
|
// argument of the parent definition |
362
|
|
|
if ($arg->hasAttribute('index')) { |
363
|
|
|
$key = 'index_'.$arg->getAttribute('index'); |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
switch ($arg->getAttribute('type')) { |
367
|
|
|
case 'service': |
368
|
|
|
$onInvalid = $arg->getAttribute('on-invalid'); |
369
|
|
|
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; |
370
|
|
|
if ('ignore' == $onInvalid) { |
371
|
|
|
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; |
372
|
|
|
} elseif ('null' == $onInvalid) { |
373
|
|
|
$invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
if ($strict = $arg->getAttribute('strict')) { |
377
|
|
|
$strict = XmlUtils::phpize($strict); |
378
|
|
|
} else { |
379
|
|
|
$strict = true; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
$arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior, $strict); |
383
|
|
|
break; |
384
|
|
|
case 'expression': |
385
|
|
|
$arguments[$key] = new Expression($arg->nodeValue); |
386
|
|
|
break; |
387
|
|
|
case 'collection': |
388
|
|
|
$arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false); |
389
|
|
|
break; |
390
|
|
|
case 'string': |
391
|
|
|
$arguments[$key] = $arg->nodeValue; |
392
|
|
|
break; |
393
|
|
|
case 'constant': |
394
|
|
|
$arguments[$key] = constant($arg->nodeValue); |
395
|
|
|
break; |
396
|
|
|
default: |
397
|
|
|
$arguments[$key] = XmlUtils::phpize($arg->nodeValue); |
398
|
|
|
} |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
return $arguments; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* Get child elements by name. |
406
|
|
|
* |
407
|
|
|
* @param \DOMNode $node |
408
|
|
|
* @param mixed $name |
409
|
|
|
* |
410
|
|
|
* @return array |
411
|
|
|
*/ |
412
|
|
|
private function getChildren(\DOMNode $node, $name) |
413
|
|
|
{ |
414
|
|
|
$children = array(); |
415
|
|
|
foreach ($node->childNodes as $child) { |
416
|
|
|
if ($child instanceof \DOMElement && $child->localName === $name && $child->namespaceURI === self::NS) { |
417
|
|
|
$children[] = $child; |
418
|
|
|
} |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
return $children; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* Validates a documents XML schema. |
426
|
|
|
* |
427
|
|
|
* @param \DOMDocument $dom |
428
|
|
|
* |
429
|
|
|
* @return bool |
430
|
|
|
* |
431
|
|
|
* @throws RuntimeException When extension references a non-existent XSD file |
432
|
|
|
*/ |
433
|
|
|
public function validateSchema(\DOMDocument $dom) |
434
|
|
|
{ |
435
|
|
|
$schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd')); |
436
|
|
|
|
437
|
|
|
if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) { |
438
|
|
|
$items = preg_split('/\s+/', $element); |
439
|
|
|
for ($i = 0, $nb = count($items); $i < $nb; $i += 2) { |
440
|
|
|
if (!$this->container->hasExtension($items[$i])) { |
441
|
|
|
continue; |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) { |
445
|
|
|
$path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]); |
446
|
|
|
|
447
|
|
|
if (!is_file($path)) { |
448
|
|
|
throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', get_class($extension), $path)); |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
$schemaLocations[$items[$i]] = $path; |
452
|
|
|
} |
453
|
|
|
} |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
$tmpfiles = array(); |
457
|
|
|
$imports = ''; |
458
|
|
|
foreach ($schemaLocations as $namespace => $location) { |
459
|
|
|
$parts = explode('/', $location); |
460
|
|
|
if (0 === stripos($location, 'phar://')) { |
461
|
|
|
$tmpfile = tempnam(sys_get_temp_dir(), 'sf2'); |
462
|
|
|
if ($tmpfile) { |
463
|
|
|
copy($location, $tmpfile); |
464
|
|
|
$tmpfiles[] = $tmpfile; |
465
|
|
|
$parts = explode('/', str_replace('\\', '/', $tmpfile)); |
466
|
|
|
} |
467
|
|
|
} |
468
|
|
|
$drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; |
469
|
|
|
$location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts)); |
470
|
|
|
|
471
|
|
|
$imports .= sprintf(' <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location); |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
$source = <<<EOF |
475
|
|
|
<?xml version="1.0" encoding="utf-8" ?> |
476
|
|
|
<xsd:schema xmlns="http://symfony.com/schema" |
477
|
|
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
478
|
|
|
targetNamespace="http://symfony.com/schema" |
479
|
|
|
elementFormDefault="qualified"> |
480
|
|
|
|
481
|
|
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> |
482
|
|
|
$imports |
483
|
|
|
</xsd:schema> |
484
|
|
|
EOF |
485
|
|
|
; |
486
|
|
|
|
487
|
|
|
$valid = @$dom->schemaValidateSource($source); |
488
|
|
|
|
489
|
|
|
foreach ($tmpfiles as $tmpfile) { |
490
|
|
|
@unlink($tmpfile); |
|
|
|
|
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
return $valid; |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
/** |
497
|
|
|
* Validates an extension. |
498
|
|
|
* |
499
|
|
|
* @param \DOMDocument $dom |
500
|
|
|
* @param string $file |
501
|
|
|
* |
502
|
|
|
* @throws InvalidArgumentException When no extension is found corresponding to a tag |
503
|
|
|
*/ |
504
|
|
|
private function validateExtensions(\DOMDocument $dom, $file) |
505
|
|
|
{ |
506
|
|
|
foreach ($dom->documentElement->childNodes as $node) { |
507
|
|
|
if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) { |
508
|
|
|
continue; |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
// can it be handled by an extension? |
512
|
|
View Code Duplication |
if (!$this->container->hasExtension($node->namespaceURI)) { |
|
|
|
|
513
|
|
|
$extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions())); |
514
|
|
|
throw new InvalidArgumentException(sprintf( |
515
|
|
|
'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', |
516
|
|
|
$node->tagName, |
517
|
|
|
$file, |
518
|
|
|
$node->namespaceURI, |
519
|
|
|
$extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none' |
520
|
|
|
)); |
521
|
|
|
} |
522
|
|
|
} |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
/** |
526
|
|
|
* Loads from an extension. |
527
|
|
|
* |
528
|
|
|
* @param \DOMDocument $xml |
529
|
|
|
*/ |
530
|
|
|
private function loadFromExtensions(\DOMDocument $xml) |
531
|
|
|
{ |
532
|
|
|
foreach ($xml->documentElement->childNodes as $node) { |
533
|
|
|
if (!$node instanceof \DOMElement || $node->namespaceURI === self::NS) { |
534
|
|
|
continue; |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
$values = static::convertDomElementToArray($node); |
538
|
|
|
if (!is_array($values)) { |
539
|
|
|
$values = array(); |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
$this->container->loadFromExtension($node->namespaceURI, $values); |
543
|
|
|
} |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
/** |
547
|
|
|
* Converts a \DomElement object to a PHP array. |
548
|
|
|
* |
549
|
|
|
* The following rules applies during the conversion: |
550
|
|
|
* |
551
|
|
|
* * Each tag is converted to a key value or an array |
552
|
|
|
* if there is more than one "value" |
553
|
|
|
* |
554
|
|
|
* * The content of a tag is set under a "value" key (<foo>bar</foo>) |
555
|
|
|
* if the tag also has some nested tags |
556
|
|
|
* |
557
|
|
|
* * The attributes are converted to keys (<foo foo="bar"/>) |
558
|
|
|
* |
559
|
|
|
* * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>) |
560
|
|
|
* |
561
|
|
|
* @param \DomElement $element A \DomElement instance |
562
|
|
|
* |
563
|
|
|
* @return array A PHP array |
564
|
|
|
*/ |
565
|
|
|
public static function convertDomElementToArray(\DomElement $element) |
566
|
|
|
{ |
567
|
|
|
return XmlUtils::convertDomElementToArray($element); |
568
|
|
|
} |
569
|
|
|
} |
570
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.