Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

XmlFileLoader.php

Zuletzt modifiziert: 09.10.2024, 12:56 - Dateigröße: 21.43 KiB


 001  <?php
 002   
 003  /*
 004   * This file is part of the Symfony package.
 005   *
 006   * (c) Fabien Potencier <fabien@symfony.com>
 007   *
 008   * For the full copyright and license information, please view the LICENSE
 009   * file that was distributed with this source code.
 010   */
 011   
 012  namespace Symfony\Component\DependencyInjection\Loader;
 013   
 014  use Symfony\Component\Config\Resource\FileResource;
 015  use Symfony\Component\Config\Util\XmlUtils;
 016  use Symfony\Component\DependencyInjection\DefinitionDecorator;
 017  use Symfony\Component\DependencyInjection\ContainerInterface;
 018  use Symfony\Component\DependencyInjection\Alias;
 019  use Symfony\Component\DependencyInjection\Definition;
 020  use Symfony\Component\DependencyInjection\Reference;
 021  use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 022  use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 023  use Symfony\Component\ExpressionLanguage\Expression;
 024   
 025  /**
 026   * XmlFileLoader loads XML files service definitions.
 027   *
 028   * @author Fabien Potencier <fabien@symfony.com>
 029   */
 030  class XmlFileLoader extends FileLoader
 031  {
 032      const NS = 'http://symfony.com/schema/dic/services';
 033   
 034      /**
 035       * {@inheritdoc}
 036       */
 037      public function load($resource, $type = null)
 038      {
 039          $path = $this->locator->locate($resource);
 040   
 041          $xml = $this->parseFileToDOM($path);
 042   
 043          $this->container->addResource(new FileResource($path));
 044   
 045          // anonymous services
 046          $this->processAnonymousServices($xml, $path);
 047   
 048          // imports
 049          $this->parseImports($xml, $path);
 050   
 051          // parameters
 052          $this->parseParameters($xml);
 053   
 054          // extensions
 055          $this->loadFromExtensions($xml);
 056   
 057          // services
 058          $this->parseDefinitions($xml, $path);
 059      }
 060   
 061      /**
 062       * {@inheritdoc}
 063       */
 064      public function supports($resource, $type = null)
 065      {
 066          return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
 067      }
 068   
 069      /**
 070       * Parses parameters.
 071       *
 072       * @param \DOMDocument $xml
 073       */
 074      private function parseParameters(\DOMDocument $xml)
 075      {
 076          if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) {
 077              $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter'));
 078          }
 079      }
 080   
 081      /**
 082       * Parses imports.
 083       *
 084       * @param \DOMDocument $xml
 085       * @param string       $file
 086       */
 087      private function parseImports(\DOMDocument $xml, $file)
 088      {
 089          $xpath = new \DOMXPath($xml);
 090          $xpath->registerNamespace('container', self::NS);
 091   
 092          if (false === $imports = $xpath->query('//container:imports/container:import')) {
 093              return;
 094          }
 095   
 096          $defaultDirectory = dirname($file);
 097          foreach ($imports as $import) {
 098              $this->setCurrentDir($defaultDirectory);
 099              $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 version 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 version 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 version 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                  $domElement->parentNode->removeChild($domElement);
 351              }
 352          }
 353      }
 354   
 355      /**
 356       * Returns arguments as valid php types.
 357       *
 358       * @param \DOMElement $node
 359       * @param string      $name
 360       * @param bool        $lowercase
 361       *
 362       * @return mixed
 363       */
 364      private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true)
 365      {
 366          $arguments = array();
 367          foreach ($this->getChildren($node, $name) as $arg) {
 368              if ($arg->hasAttribute('name')) {
 369                  $arg->setAttribute('key', $arg->getAttribute('name'));
 370              }
 371   
 372              if (!$arg->hasAttribute('key')) {
 373                  $key = !$arguments ? 0 : max(array_keys($arguments)) + 1;
 374              } else {
 375                  $key = $arg->getAttribute('key');
 376              }
 377   
 378              // parameter keys are case insensitive
 379              if ('parameter' == $name && $lowercase) {
 380                  $key = strtolower($key);
 381              }
 382   
 383              // this is used by DefinitionDecorator to overwrite a specific
 384              // argument of the parent definition
 385              if ($arg->hasAttribute('index')) {
 386                  $key = 'index_'.$arg->getAttribute('index');
 387              }
 388   
 389              switch ($arg->getAttribute('type')) {
 390                  case 'service':
 391                      $onInvalid = $arg->getAttribute('on-invalid');
 392                      $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
 393                      if ('ignore' == $onInvalid) {
 394                          $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
 395                      } elseif ('null' == $onInvalid) {
 396                          $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
 397                      }
 398   
 399                      if ($strict = $arg->getAttribute('strict')) {
 400                          $strict = XmlUtils::phpize($strict);
 401                      } else {
 402                          $strict = true;
 403                      }
 404   
 405                      $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior, $strict);
 406                      break;
 407                  case 'expression':
 408                      $arguments[$key] = new Expression($arg->nodeValue);
 409                      break;
 410                  case 'collection':
 411                      $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false);
 412                      break;
 413                  case 'string':
 414                      $arguments[$key] = $arg->nodeValue;
 415                      break;
 416                  case 'constant':
 417                      $arguments[$key] = constant(trim($arg->nodeValue));
 418                      break;
 419                  default:
 420                      $arguments[$key] = XmlUtils::phpize($arg->nodeValue);
 421              }
 422          }
 423   
 424          return $arguments;
 425      }
 426   
 427      /**
 428       * Get child elements by name.
 429       *
 430       * @param \DOMNode $node
 431       * @param mixed    $name
 432       *
 433       * @return array
 434       */
 435      private function getChildren(\DOMNode $node, $name)
 436      {
 437          $children = array();
 438          foreach ($node->childNodes as $child) {
 439              if ($child instanceof \DOMElement && $child->localName === $name && $child->namespaceURI === self::NS) {
 440                  $children[] = $child;
 441              }
 442          }
 443   
 444          return $children;
 445      }
 446   
 447      /**
 448       * Validates a documents XML schema.
 449       *
 450       * @param \DOMDocument $dom
 451       *
 452       * @return bool
 453       *
 454       * @throws RuntimeException When extension references a non-existent XSD file
 455       */
 456      public function validateSchema(\DOMDocument $dom)
 457      {
 458          $schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
 459   
 460          if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) {
 461              $items = preg_split('/\s+/', $element);
 462              for ($i = 0, $nb = count($items); $i < $nb; $i += 2) {
 463                  if (!$this->container->hasExtension($items[$i])) {
 464                      continue;
 465                  }
 466   
 467                  if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) {
 468                      $path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]);
 469   
 470                      if (!is_file($path)) {
 471                          throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', get_class($extension), $path));
 472                      }
 473   
 474                      $schemaLocations[$items[$i]] = $path;
 475                  }
 476              }
 477          }
 478   
 479          $tmpfiles = array();
 480          $imports = '';
 481          foreach ($schemaLocations as $namespace => $location) {
 482              $parts = explode('/', $location);
 483              if (0 === stripos($location, 'phar://')) {
 484                  $tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
 485                  if ($tmpfile) {
 486                      copy($location, $tmpfile);
 487                      $tmpfiles[] = $tmpfile;
 488                      $parts = explode('/', str_replace('\\', '/', $tmpfile));
 489                  }
 490              }
 491              $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
 492              $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
 493   
 494              $imports .= sprintf('  <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location);
 495          }
 496   
 497          $source = <<<EOF
 498  <?xml version="1.0" encoding="utf-8" ?>
 499  <xsd:schema xmlns="http://symfony.com/schema"
 500      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 501      targetNamespace="http://symfony.com/schema"
 502      elementFormDefault="qualified">
 503   
 504      <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
 505  $imports
 506  </xsd:schema>
 507  EOF
 508          ;
 509   
 510          $disableEntities = libxml_disable_entity_loader(false);
 511          $valid = @$dom->schemaValidateSource($source);
 512          libxml_disable_entity_loader($disableEntities);
 513   
 514          foreach ($tmpfiles as $tmpfile) {
 515              @unlink($tmpfile);
 516          }
 517   
 518          return $valid;
 519      }
 520   
 521      /**
 522       * Validates an extension.
 523       *
 524       * @param \DOMDocument $dom
 525       * @param string       $file
 526       *
 527       * @throws InvalidArgumentException When no extension is found corresponding to a tag
 528       */
 529      private function validateExtensions(\DOMDocument $dom, $file)
 530      {
 531          foreach ($dom->documentElement->childNodes as $node) {
 532              if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) {
 533                  continue;
 534              }
 535   
 536              // can it be handled by an extension?
 537              if (!$this->container->hasExtension($node->namespaceURI)) {
 538                  $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions()));
 539                  throw new InvalidArgumentException(sprintf(
 540                      'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s',
 541                      $node->tagName,
 542                      $file,
 543                      $node->namespaceURI,
 544                      $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'
 545                  ));
 546              }
 547          }
 548      }
 549   
 550      /**
 551       * Loads from an extension.
 552       *
 553       * @param \DOMDocument $xml
 554       */
 555      private function loadFromExtensions(\DOMDocument $xml)
 556      {
 557          foreach ($xml->documentElement->childNodes as $node) {
 558              if (!$node instanceof \DOMElement || $node->namespaceURI === self::NS) {
 559                  continue;
 560              }
 561   
 562              $values = static::convertDomElementToArray($node);
 563              if (!is_array($values)) {
 564                  $values = array();
 565              }
 566   
 567              $this->container->loadFromExtension($node->namespaceURI, $values);
 568          }
 569      }
 570   
 571      /**
 572       * Converts a \DomElement object to a PHP array.
 573       *
 574       * The following rules applies during the conversion:
 575       *
 576       *  * Each tag is converted to a key value or an array
 577       *    if there is more than one "value"
 578       *
 579       *  * The content of a tag is set under a "value" key (<foo>bar</foo>)
 580       *    if the tag also has some nested tags
 581       *
 582       *  * The attributes are converted to keys (<foo foo="bar"/>)
 583       *
 584       *  * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>)
 585       *
 586       * @param \DomElement $element A \DomElement instance
 587       *
 588       * @return array A PHP array
 589       */
 590      public static function convertDomElementToArray(\DOMElement $element)
 591      {
 592          return XmlUtils::convertDomElementToArray($element);
 593      }
 594  }
 595