[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Loader/ -> XmlFileLoader.php (source)

   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\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\SimpleXMLElement;
  22  use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  23  use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  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      /**
  33       * {@inheritdoc}
  34       */
  35      public function load($resource, $type = null)
  36      {
  37          $path = $this->locator->locate($resource);
  38  
  39          $xml = $this->parseFile($path);
  40          $xml->registerXPathNamespace('container', 'http://symfony.com/schema/dic/services');
  41  
  42          $this->container->addResource(new FileResource($path));
  43  
  44          // anonymous services
  45          $this->processAnonymousServices($xml, $path);
  46  
  47          // imports
  48          $this->parseImports($xml, $path);
  49  
  50          // parameters
  51          $this->parseParameters($xml);
  52  
  53          // extensions
  54          $this->loadFromExtensions($xml);
  55  
  56          // services
  57          $this->parseDefinitions($xml, $path);
  58      }
  59  
  60      /**
  61       * {@inheritdoc}
  62       */
  63      public function supports($resource, $type = null)
  64      {
  65          return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
  66      }
  67  
  68      /**
  69       * Parses parameters.
  70       *
  71       * @param SimpleXMLElement $xml
  72       */
  73      private function parseParameters(SimpleXMLElement $xml)
  74      {
  75          if (!$xml->parameters) {
  76              return;
  77          }
  78  
  79          $this->container->getParameterBag()->add($xml->parameters->getArgumentsAsPhp('parameter'));
  80      }
  81  
  82      /**
  83       * Parses imports.
  84       *
  85       * @param SimpleXMLElement $xml
  86       * @param string           $file
  87       */
  88      private function parseImports(SimpleXMLElement $xml, $file)
  89      {
  90          if (false === $imports = $xml->xpath('//container:imports/container:import')) {
  91              return;
  92          }
  93  
  94          $defaultDirectory = dirname($file);
  95          foreach ($imports as $import) {
  96              $this->setCurrentDir($defaultDirectory);
  97              $this->import((string) $import['resource'], null, (bool) $import->getAttributeAsPhp('ignore-errors'), $file);
  98          }
  99      }
 100  
 101      /**
 102       * Parses multiple definitions.
 103       *
 104       * @param SimpleXMLElement $xml
 105       * @param string           $file
 106       */
 107      private function parseDefinitions(SimpleXMLElement $xml, $file)
 108      {
 109          if (false === $services = $xml->xpath('//container:services/container:service')) {
 110              return;
 111          }
 112  
 113          foreach ($services as $service) {
 114              $this->parseDefinition((string) $service['id'], $service, $file);
 115          }
 116      }
 117  
 118      /**
 119       * Parses an individual Definition.
 120       *
 121       * @param string           $id
 122       * @param SimpleXMLElement $service
 123       * @param string           $file
 124       */
 125      private function parseDefinition($id, $service, $file)
 126      {
 127          if ((string) $service['alias']) {
 128              $public = true;
 129              if (isset($service['public'])) {
 130                  $public = $service->getAttributeAsPhp('public');
 131              }
 132              $this->container->setAlias($id, new Alias((string) $service['alias'], $public));
 133  
 134              return;
 135          }
 136  
 137          if (isset($service['parent'])) {
 138              $definition = new DefinitionDecorator((string) $service['parent']);
 139          } else {
 140              $definition = new Definition();
 141          }
 142  
 143          foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'synchronized', 'lazy', 'abstract') as $key) {
 144              if (isset($service[$key])) {
 145                  $method = 'set'.str_replace('-', '', $key);
 146                  $definition->$method((string) $service->getAttributeAsPhp($key));
 147              }
 148          }
 149  
 150          if ($service->file) {
 151              $definition->setFile((string) $service->file);
 152          }
 153  
 154          $definition->setArguments($service->getArgumentsAsPhp('argument'));
 155          $definition->setProperties($service->getArgumentsAsPhp('property'));
 156  
 157          if (isset($service->configurator)) {
 158              if (isset($service->configurator['function'])) {
 159                  $definition->setConfigurator((string) $service->configurator['function']);
 160              } else {
 161                  if (isset($service->configurator['service'])) {
 162                      $class = new Reference((string) $service->configurator['service'], ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false);
 163                  } else {
 164                      $class = (string) $service->configurator['class'];
 165                  }
 166  
 167                  $definition->setConfigurator(array($class, (string) $service->configurator['method']));
 168              }
 169          }
 170  
 171          foreach ($service->call as $call) {
 172              $definition->addMethodCall((string) $call['method'], $call->getArgumentsAsPhp('argument'));
 173          }
 174  
 175          foreach ($service->tag as $tag) {
 176              $parameters = array();
 177              foreach ($tag->attributes() as $name => $value) {
 178                  if ('name' === $name) {
 179                      continue;
 180                  }
 181  
 182                  if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) {
 183                      $parameters[$normalizedName] = SimpleXMLElement::phpize($value);
 184                  }
 185                  // keep not normalized key for BC too
 186                  $parameters[$name] = SimpleXMLElement::phpize($value);
 187              }
 188  
 189              if ('' === (string) $tag['name']) {
 190                  throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file));
 191              }
 192  
 193              $definition->addTag((string) $tag['name'], $parameters);
 194          }
 195  
 196          $this->container->setDefinition($id, $definition);
 197      }
 198  
 199      /**
 200       * Parses a XML file.
 201       *
 202       * @param string $file Path to a file
 203       *
 204       * @return SimpleXMLElement
 205       *
 206       * @throws InvalidArgumentException When loading of XML file returns error
 207       */
 208      protected function parseFile($file)
 209      {
 210          try {
 211              $dom = XmlUtils::loadFile($file, array($this, 'validateSchema'));
 212          } catch (\InvalidArgumentException $e) {
 213              throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e);
 214          }
 215  
 216          $this->validateExtensions($dom, $file);
 217  
 218          return simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement');
 219      }
 220  
 221      /**
 222       * Processes anonymous services.
 223       *
 224       * @param SimpleXMLElement $xml
 225       * @param string           $file
 226       */
 227      private function processAnonymousServices(SimpleXMLElement $xml, $file)
 228      {
 229          $definitions = array();
 230          $count = 0;
 231  
 232          // anonymous services as arguments/properties
 233          if (false !== $nodes = $xml->xpath('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) {
 234              foreach ($nodes as $node) {
 235                  // give it a unique name
 236                  $node['id'] = sprintf('%s_%d', md5($file), ++$count);
 237  
 238                  $definitions[(string) $node['id']] = array($node->service, $file, false);
 239                  $node->service['id'] = (string) $node['id'];
 240  
 241                  // anonymous services are always private
 242                  $node->service['public'] = false;
 243              }
 244          }
 245  
 246          // anonymous services "in the wild"
 247          if (false !== $nodes = $xml->xpath('//container:services/container:service[not(@id)]')) {
 248              foreach ($nodes as $node) {
 249                  // give it a unique name
 250                  $node['id'] = sprintf('%s_%d', md5($file), ++$count);
 251  
 252                  $definitions[(string) $node['id']] = array($node, $file, true);
 253                  $node->service['id'] = (string) $node['id'];
 254              }
 255          }
 256  
 257          // resolve definitions
 258          krsort($definitions);
 259          foreach ($definitions as $id => $def) {
 260              $this->parseDefinition($id, $def[0], $def[1]);
 261  
 262              $oNode = dom_import_simplexml($def[0]);
 263              if (true === $def[2]) {
 264                  $nNode = new \DOMElement('_services');
 265                  $oNode->parentNode->replaceChild($nNode, $oNode);
 266                  $nNode->setAttribute('id', $id);
 267              } else {
 268                  $oNode->parentNode->removeChild($oNode);
 269              }
 270          }
 271      }
 272  
 273      /**
 274       * Validates a documents XML schema.
 275       *
 276       * @param \DOMDocument $dom
 277       *
 278       * @return bool
 279       *
 280       * @throws RuntimeException When extension references a non-existent XSD file
 281       */
 282      public function validateSchema(\DOMDocument $dom)
 283      {
 284          $schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
 285  
 286          if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) {
 287              $items = preg_split('/\s+/', $element);
 288              for ($i = 0, $nb = count($items); $i < $nb; $i += 2) {
 289                  if (!$this->container->hasExtension($items[$i])) {
 290                      continue;
 291                  }
 292  
 293                  if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) {
 294                      $path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]);
 295  
 296                      if (!is_file($path)) {
 297                          throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', get_class($extension), $path));
 298                      }
 299  
 300                      $schemaLocations[$items[$i]] = $path;
 301                  }
 302              }
 303          }
 304  
 305          $tmpfiles = array();
 306          $imports = '';
 307          foreach ($schemaLocations as $namespace => $location) {
 308              $parts = explode('/', $location);
 309              if (0 === stripos($location, 'phar://')) {
 310                  $tmpfile = tempnam(sys_get_temp_dir(), 'sf2');
 311                  if ($tmpfile) {
 312                      copy($location, $tmpfile);
 313                      $tmpfiles[] = $tmpfile;
 314                      $parts = explode('/', str_replace('\\', '/', $tmpfile));
 315                  }
 316              }
 317              $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
 318              $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts));
 319  
 320              $imports .= sprintf('  <xsd:import namespace="%s" schemaLocation="%s" />'."\n", $namespace, $location);
 321          }
 322  
 323          $source = <<<EOF
 324  <?xml version="1.0" encoding="utf-8" ?>
 325  <xsd:schema xmlns="http://symfony.com/schema"
 326      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 327      targetNamespace="http://symfony.com/schema"
 328      elementFormDefault="qualified">
 329  
 330      <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
 331  $imports
 332  </xsd:schema>
 333  EOF
 334          ;
 335  
 336          $valid = @$dom->schemaValidateSource($source);
 337  
 338          foreach ($tmpfiles as $tmpfile) {
 339              @unlink($tmpfile);
 340          }
 341  
 342          return $valid;
 343      }
 344  
 345      /**
 346       * Validates an extension.
 347       *
 348       * @param \DOMDocument $dom
 349       * @param string       $file
 350       *
 351       * @throws InvalidArgumentException When no extension is found corresponding to a tag
 352       */
 353      private function validateExtensions(\DOMDocument $dom, $file)
 354      {
 355          foreach ($dom->documentElement->childNodes as $node) {
 356              if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) {
 357                  continue;
 358              }
 359  
 360              // can it be handled by an extension?
 361              if (!$this->container->hasExtension($node->namespaceURI)) {
 362                  $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions()));
 363                  throw new InvalidArgumentException(sprintf(
 364                      'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s',
 365                      $node->tagName,
 366                      $file,
 367                      $node->namespaceURI,
 368                      $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'
 369                  ));
 370              }
 371          }
 372      }
 373  
 374      /**
 375       * Loads from an extension.
 376       *
 377       * @param SimpleXMLElement $xml
 378       */
 379      private function loadFromExtensions(SimpleXMLElement $xml)
 380      {
 381          foreach (dom_import_simplexml($xml)->childNodes as $node) {
 382              if (!$node instanceof \DOMElement || $node->namespaceURI === 'http://symfony.com/schema/dic/services') {
 383                  continue;
 384              }
 385  
 386              $values = static::convertDomElementToArray($node);
 387              if (!is_array($values)) {
 388                  $values = array();
 389              }
 390  
 391              $this->container->loadFromExtension($node->namespaceURI, $values);
 392          }
 393      }
 394  
 395      /**
 396       * Converts a \DomElement object to a PHP array.
 397       *
 398       * The following rules applies during the conversion:
 399       *
 400       *  * Each tag is converted to a key value or an array
 401       *    if there is more than one "value"
 402       *
 403       *  * The content of a tag is set under a "value" key (<foo>bar</foo>)
 404       *    if the tag also has some nested tags
 405       *
 406       *  * The attributes are converted to keys (<foo foo="bar"/>)
 407       *
 408       *  * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>)
 409       *
 410       * @param \DomElement $element A \DomElement instance
 411       *
 412       * @return array A PHP array
 413       */
 414      public static function convertDomElementToArray(\DomElement $element)
 415      {
 416          return XmlUtils::convertDomElementToArray($element);
 417      }
 418  }


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1