[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 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\Alias; 17 use Symfony\Component\DependencyInjection\ContainerInterface; 18 use Symfony\Component\DependencyInjection\Definition; 19 use Symfony\Component\DependencyInjection\DefinitionDecorator; 20 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; 21 use Symfony\Component\DependencyInjection\Exception\RuntimeException; 22 use Symfony\Component\DependencyInjection\Reference; 23 use Symfony\Component\ExpressionLanguage\Expression; 24 25 /** 26 * XmlFileLoader loads XML files service definitions. 27 * 28 * @author Fabien Potencier <fabien@symfony.com> 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); 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 */ 74 private function parseParameters(\DOMDocument $xml) 75 { 76 if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) { 77 $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter')); 78 } 79 } 80 81 /** 82 * Parses imports. 83 * 84 * @param \DOMDocument $xml 85 * @param string $file 86 */ 87 private function parseImports(\DOMDocument $xml, $file) 88 { 89 $xpath = new \DOMXPath($xml); 90 $xpath->registerNamespace('container', self::NS); 91 92 if (false === $imports = $xpath->query('//container:imports/container:import')) { 93 return; 94 } 95 96 $defaultDirectory = \dirname($file); 97 foreach ($imports as $import) { 98 $this->setCurrentDir($defaultDirectory); 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 if ($parent = $service->getAttribute('parent')) { 146 $definition = new DefinitionDecorator($parent); 147 } else { 148 $definition = new Definition(); 149 } 150 151 foreach (array('class', 'shared', '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 of service "%s" in file "%s" is deprecated since Symfony 2.6 and will be removed in 3.0. Use the "factory" element instead.', $key, (string) $service->getAttribute('id'), $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('autowire')) { 162 $definition->setAutowired(XmlUtils::phpize($value)); 163 } 164 165 if ($value = $service->getAttribute('scope')) { 166 $triggerDeprecation = 'request' !== (string) $service->getAttribute('id'); 167 168 if ($triggerDeprecation) { 169 @trigger_error(sprintf('The "scope" attribute of service "%s" in file "%s" is deprecated since Symfony 2.8 and will be removed in 3.0.', (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED); 170 } 171 172 $definition->setScope(XmlUtils::phpize($value), false); 173 } 174 175 if ($value = $service->getAttribute('synchronized')) { 176 $triggerDeprecation = 'request' !== (string) $service->getAttribute('id'); 177 178 if ($triggerDeprecation) { 179 @trigger_error(sprintf('The "synchronized" attribute of service "%s" in file "%s" is deprecated since Symfony 2.7 and will be removed in 3.0.', (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED); 180 } 181 182 $definition->setSynchronized(XmlUtils::phpize($value), $triggerDeprecation); 183 } 184 185 if ($files = $this->getChildren($service, 'file')) { 186 $definition->setFile($files[0]->nodeValue); 187 } 188 189 if ($deprecated = $this->getChildren($service, 'deprecated')) { 190 $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); 191 } 192 193 $definition->setArguments($this->getArgumentsAsPhp($service, 'argument')); 194 $definition->setProperties($this->getArgumentsAsPhp($service, 'property')); 195 196 if ($factories = $this->getChildren($service, 'factory')) { 197 $factory = $factories[0]; 198 if ($function = $factory->getAttribute('function')) { 199 $definition->setFactory($function); 200 } else { 201 $factoryService = $this->getChildren($factory, 'service'); 202 203 if (isset($factoryService[0])) { 204 $class = $this->parseDefinition($factoryService[0], $file); 205 } elseif ($childService = $factory->getAttribute('service')) { 206 $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); 207 } else { 208 $class = $factory->getAttribute('class'); 209 } 210 211 $definition->setFactory(array($class, $factory->getAttribute('method'))); 212 } 213 } 214 215 if ($configurators = $this->getChildren($service, 'configurator')) { 216 $configurator = $configurators[0]; 217 if ($function = $configurator->getAttribute('function')) { 218 $definition->setConfigurator($function); 219 } else { 220 $configuratorService = $this->getChildren($configurator, 'service'); 221 222 if (isset($configuratorService[0])) { 223 $class = $this->parseDefinition($configuratorService[0], $file); 224 } elseif ($childService = $configurator->getAttribute('service')) { 225 $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); 226 } else { 227 $class = $configurator->getAttribute('class'); 228 } 229 230 $definition->setConfigurator(array($class, $configurator->getAttribute('method'))); 231 } 232 } 233 234 foreach ($this->getChildren($service, 'call') as $call) { 235 $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument')); 236 } 237 238 foreach ($this->getChildren($service, 'tag') as $tag) { 239 $parameters = array(); 240 foreach ($tag->attributes as $name => $node) { 241 if ('name' === $name) { 242 continue; 243 } 244 245 if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { 246 $parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue); 247 } 248 // keep not normalized key for BC too 249 $parameters[$name] = XmlUtils::phpize($node->nodeValue); 250 } 251 252 if ('' === $tag->getAttribute('name')) { 253 throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', (string) $service->getAttribute('id'), $file)); 254 } 255 256 $definition->addTag($tag->getAttribute('name'), $parameters); 257 } 258 259 foreach ($this->getChildren($service, 'autowiring-type') as $type) { 260 $definition->addAutowiringType($type->textContent); 261 } 262 263 if ($value = $service->getAttribute('decorates')) { 264 $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; 265 $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; 266 $definition->setDecoratedService($value, $renameId, $priority); 267 } 268 269 return $definition; 270 } 271 272 /** 273 * Parses a XML file to a \DOMDocument. 274 * 275 * @param string $file Path to a file 276 * 277 * @return \DOMDocument 278 * 279 * @throws InvalidArgumentException When loading of XML file returns error 280 */ 281 private function parseFileToDOM($file) 282 { 283 try { 284 $dom = XmlUtils::loadFile($file, array($this, 'validateSchema')); 285 } catch (\InvalidArgumentException $e) { 286 throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e); 287 } 288 289 $this->validateExtensions($dom, $file); 290 291 return $dom; 292 } 293 294 /** 295 * Processes anonymous services. 296 * 297 * @param \DOMDocument $xml 298 * @param string $file 299 */ 300 private function processAnonymousServices(\DOMDocument $xml, $file) 301 { 302 $definitions = array(); 303 $count = 0; 304 305 $xpath = new \DOMXPath($xml); 306 $xpath->registerNamespace('container', self::NS); 307 308 // anonymous services as arguments/properties 309 if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) { 310 foreach ($nodes as $node) { 311 // give it a unique name 312 $id = sprintf('%s_%d', hash('sha256', $file), ++$count); 313 $node->setAttribute('id', $id); 314 315 if ($services = $this->getChildren($node, 'service')) { 316 $definitions[$id] = array($services[0], $file, false); 317 $services[0]->setAttribute('id', $id); 318 319 // anonymous services are always private 320 // we could not use the constant false here, because of XML parsing 321 $services[0]->setAttribute('public', 'false'); 322 } 323 } 324 } 325 326 // anonymous services "in the wild" 327 if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) { 328 foreach ($nodes as $node) { 329 // give it a unique name 330 $id = sprintf('%s_%d', hash('sha256', $file), ++$count); 331 $node->setAttribute('id', $id); 332 $definitions[$id] = array($node, $file, true); 333 } 334 } 335 336 // resolve definitions 337 krsort($definitions); 338 foreach ($definitions as $id => $def) { 339 list($domElement, $file, $wild) = $def; 340 341 if (null !== $definition = $this->parseDefinition($domElement, $file)) { 342 $this->container->setDefinition($id, $definition); 343 } 344 345 if (true === $wild) { 346 $tmpDomElement = new \DOMElement('_services', null, self::NS); 347 $domElement->parentNode->replaceChild($tmpDomElement, $domElement); 348 $tmpDomElement->setAttribute('id', $id); 349 } else { 350 if (null !== $domElement->parentNode) { 351 $domElement->parentNode->removeChild($domElement); 352 } 353 } 354 } 355 } 356 357 /** 358 * Returns arguments as valid php types. 359 * 360 * @param \DOMElement $node 361 * @param string $name 362 * @param bool $lowercase 363 * 364 * @return mixed 365 */ 366 private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true) 367 { 368 $arguments = array(); 369 foreach ($this->getChildren($node, $name) as $arg) { 370 if ($arg->hasAttribute('name')) { 371 $arg->setAttribute('key', $arg->getAttribute('name')); 372 } 373 374 // this is used by DefinitionDecorator to overwrite a specific 375 // argument of the parent definition 376 if ($arg->hasAttribute('index')) { 377 $key = 'index_'.$arg->getAttribute('index'); 378 } elseif (!$arg->hasAttribute('key')) { 379 // Append an empty argument, then fetch its key to overwrite it later 380 $arguments[] = null; 381 $keys = array_keys($arguments); 382 $key = array_pop($keys); 383 } else { 384 $key = $arg->getAttribute('key'); 385 386 // parameter keys are case insensitive 387 if ('parameter' == $name && $lowercase) { 388 $key = strtolower($key); 389 } 390 } 391 392 switch ($arg->getAttribute('type')) { 393 case 'service': 394 $onInvalid = $arg->getAttribute('on-invalid'); 395 $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; 396 if ('ignore' == $onInvalid) { 397 $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; 398 } elseif ('null' == $onInvalid) { 399 $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; 400 } 401 402 if ($strict = $arg->getAttribute('strict')) { 403 $strict = XmlUtils::phpize($strict); 404 } else { 405 $strict = true; 406 } 407 408 $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior, $strict); 409 break; 410 case 'expression': 411 $arguments[$key] = new Expression($arg->nodeValue); 412 break; 413 case 'collection': 414 $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false); 415 break; 416 case 'string': 417 $arguments[$key] = $arg->nodeValue; 418 break; 419 case 'constant': 420 $arguments[$key] = \constant(trim($arg->nodeValue)); 421 break; 422 default: 423 $arguments[$key] = XmlUtils::phpize($arg->nodeValue); 424 } 425 } 426 427 return $arguments; 428 } 429 430 /** 431 * Get child elements by name. 432 * 433 * @param \DOMNode $node 434 * @param mixed $name 435 * 436 * @return array 437 */ 438 private function getChildren(\DOMNode $node, $name) 439 { 440 $children = array(); 441 foreach ($node->childNodes as $child) { 442 if ($child instanceof \DOMElement && $child->localName === $name && self::NS === $child->namespaceURI) { 443 $children[] = $child; 444 } 445 } 446 447 return $children; 448 } 449 450 /** 451 * Validates a documents XML schema. 452 * 453 * @param \DOMDocument $dom 454 * 455 * @return bool 456 * 457 * @throws RuntimeException When extension references a non-existent XSD file 458 */ 459 public function validateSchema(\DOMDocument $dom) 460 { 461 $schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd')); 462 463 if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) { 464 $items = preg_split('/\s+/', $element); 465 for ($i = 0, $nb = \count($items); $i < $nb; $i += 2) { 466 if (!$this->container->hasExtension($items[$i])) { 467 continue; 468 } 469 470 if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) { 471 $path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]); 472 473 if (!is_file($path)) { 474 throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', \get_class($extension), $path)); 475 } 476 477 $schemaLocations[$items[$i]] = $path; 478 } 479 } 480 } 481 482 $tmpfiles = array(); 483 $imports = ''; 484 foreach ($schemaLocations as $namespace => $location) { 485 $parts = explode('/', $location); 486 $locationstart = 'file:///'; 487 if (0 === stripos($location, 'phar://')) { 488 $tmpfile = tempnam(sys_get_temp_dir(), 'sf2'); 489 if ($tmpfile) { 490 copy($location, $tmpfile); 491 $tmpfiles[] = $tmpfile; 492 $parts = explode('/', str_replace('\\', '/', $tmpfile)); 493 } else { 494 array_shift($parts); 495 $locationstart = 'phar:///'; 496 } 497 } 498 $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; 499 $location = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts)); 500 501 $imports .= sprintf(' <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location); 502 } 503 504 $source = <<<EOF 505 <?xml version="1.0" encoding="utf-8" ?> 506 <xsd:schema xmlns="http://symfony.com/schema" 507 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 508 targetNamespace="http://symfony.com/schema" 509 elementFormDefault="qualified"> 510 511 <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> 512 $imports 513 </xsd:schema> 514 EOF 515 ; 516 517 $disableEntities = libxml_disable_entity_loader(false); 518 $valid = @$dom->schemaValidateSource($source); 519 libxml_disable_entity_loader($disableEntities); 520 521 foreach ($tmpfiles as $tmpfile) { 522 @unlink($tmpfile); 523 } 524 525 return $valid; 526 } 527 528 /** 529 * Validates an extension. 530 * 531 * @param \DOMDocument $dom 532 * @param string $file 533 * 534 * @throws InvalidArgumentException When no extension is found corresponding to a tag 535 */ 536 private function validateExtensions(\DOMDocument $dom, $file) 537 { 538 foreach ($dom->documentElement->childNodes as $node) { 539 if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) { 540 continue; 541 } 542 543 // can it be handled by an extension? 544 if (!$this->container->hasExtension($node->namespaceURI)) { 545 $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions())); 546 throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', $node->tagName, $file, $node->namespaceURI, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none')); 547 } 548 } 549 } 550 551 /** 552 * Loads from an extension. 553 * 554 * @param \DOMDocument $xml 555 */ 556 private function loadFromExtensions(\DOMDocument $xml) 557 { 558 foreach ($xml->documentElement->childNodes as $node) { 559 if (!$node instanceof \DOMElement || self::NS === $node->namespaceURI) { 560 continue; 561 } 562 563 $values = static::convertDomElementToArray($node); 564 if (!\is_array($values)) { 565 $values = array(); 566 } 567 568 $this->container->loadFromExtension($node->namespaceURI, $values); 569 } 570 } 571 572 /** 573 * Converts a \DOMElement object to a PHP array. 574 * 575 * The following rules applies during the conversion: 576 * 577 * * Each tag is converted to a key value or an array 578 * if there is more than one "value" 579 * 580 * * The content of a tag is set under a "value" key (<foo>bar</foo>) 581 * if the tag also has some nested tags 582 * 583 * * The attributes are converted to keys (<foo foo="bar"/>) 584 * 585 * * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>) 586 * 587 * @param \DOMElement $element A \DOMElement instance 588 * 589 * @return array A PHP array 590 */ 591 public static function convertDomElementToArray(\DOMElement $element) 592 { 593 return XmlUtils::convertDomElementToArray($element); 594 } 595 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |