Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

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

YamlFileLoader.php

Zuletzt modifiziert: 01.05.2026, 11:27 - Dateigröße: 33.45 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\DependencyInjection\Alias;
 015  use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
 016  use Symfony\Component\DependencyInjection\Argument\BoundArgument;
 017  use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
 018  use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
 019  use Symfony\Component\DependencyInjection\ChildDefinition;
 020  use Symfony\Component\DependencyInjection\ContainerBuilder;
 021  use Symfony\Component\DependencyInjection\ContainerInterface;
 022  use Symfony\Component\DependencyInjection\Definition;
 023  use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 024  use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 025  use Symfony\Component\DependencyInjection\Reference;
 026  use Symfony\Component\ExpressionLanguage\Expression;
 027  use Symfony\Component\Yaml\Exception\ParseException;
 028  use Symfony\Component\Yaml\Parser as YamlParser;
 029  use Symfony\Component\Yaml\Tag\TaggedValue;
 030  use Symfony\Component\Yaml\Yaml;
 031   
 032  /**
 033   * YamlFileLoader loads YAML files service definitions.
 034   *
 035   * @author Fabien Potencier <fabien@symfony.com>
 036   */
 037  class YamlFileLoader extends FileLoader
 038  {
 039      private static $serviceKeywords = [
 040          'alias' => 'alias',
 041          'parent' => 'parent',
 042          'class' => 'class',
 043          'shared' => 'shared',
 044          'synthetic' => 'synthetic',
 045          'lazy' => 'lazy',
 046          'public' => 'public',
 047          'abstract' => 'abstract',
 048          'deprecated' => 'deprecated',
 049          'factory' => 'factory',
 050          'file' => 'file',
 051          'arguments' => 'arguments',
 052          'properties' => 'properties',
 053          'configurator' => 'configurator',
 054          'calls' => 'calls',
 055          'tags' => 'tags',
 056          'decorates' => 'decorates',
 057          'decoration_inner_name' => 'decoration_inner_name',
 058          'decoration_priority' => 'decoration_priority',
 059          'autowire' => 'autowire',
 060          'autowiring_types' => 'autowiring_types',
 061          'autoconfigure' => 'autoconfigure',
 062          'bind' => 'bind',
 063      ];
 064   
 065      private static $prototypeKeywords = [
 066          'resource' => 'resource',
 067          'namespace' => 'namespace',
 068          'exclude' => 'exclude',
 069          'parent' => 'parent',
 070          'shared' => 'shared',
 071          'lazy' => 'lazy',
 072          'public' => 'public',
 073          'abstract' => 'abstract',
 074          'deprecated' => 'deprecated',
 075          'factory' => 'factory',
 076          'arguments' => 'arguments',
 077          'properties' => 'properties',
 078          'configurator' => 'configurator',
 079          'calls' => 'calls',
 080          'tags' => 'tags',
 081          'autowire' => 'autowire',
 082          'autoconfigure' => 'autoconfigure',
 083          'bind' => 'bind',
 084      ];
 085   
 086      private static $instanceofKeywords = [
 087          'shared' => 'shared',
 088          'lazy' => 'lazy',
 089          'public' => 'public',
 090          'properties' => 'properties',
 091          'configurator' => 'configurator',
 092          'calls' => 'calls',
 093          'tags' => 'tags',
 094          'autowire' => 'autowire',
 095      ];
 096   
 097      private static $defaultsKeywords = [
 098          'public' => 'public',
 099          'tags' => 'tags',
 100          'autowire' => 'autowire',
 101          'autoconfigure' => 'autoconfigure',
 102          'bind' => 'bind',
 103      ];
 104   
 105      private $yamlParser;
 106   
 107      private $anonymousServicesCount;
 108      private $anonymousServicesSuffix;
 109   
 110      /**
 111       * {@inheritdoc}
 112       */
 113      public function load($resource, $type = null)
 114      {
 115          $path = $this->locator->locate($resource);
 116   
 117          $content = $this->loadFile($path);
 118   
 119          $this->container->fileExists($path);
 120   
 121          // empty file
 122          if (null === $content) {
 123              return;
 124          }
 125   
 126          // imports
 127          $this->parseImports($content, $path);
 128   
 129          // parameters
 130          if (isset($content['parameters'])) {
 131              if (!\is_array($content['parameters'])) {
 132                  throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in "%s". Check your YAML syntax.', $path));
 133              }
 134   
 135              foreach ($content['parameters'] as $key => $value) {
 136                  $this->container->setParameter($key, $this->resolveServices($value, $path, true));
 137              }
 138          }
 139   
 140          // extensions
 141          $this->loadFromExtensions($content);
 142   
 143          // services
 144          $this->anonymousServicesCount = 0;
 145          $this->anonymousServicesSuffix = '~'.ContainerBuilder::hash($path);
 146          $this->setCurrentDir(\dirname($path));
 147          try {
 148              $this->parseDefinitions($content, $path);
 149          } finally {
 150              $this->instanceof = [];
 151          }
 152      }
 153   
 154      /**
 155       * {@inheritdoc}
 156       */
 157      public function supports($resource, $type = null)
 158      {
 159          if (!\is_string($resource)) {
 160              return false;
 161          }
 162   
 163          if (null === $type && \in_array(pathinfo($resource, \PATHINFO_EXTENSION), ['yaml', 'yml'], true)) {
 164              return true;
 165          }
 166   
 167          return \in_array($type, ['yaml', 'yml'], true);
 168      }
 169   
 170      /**
 171       * Parses all imports.
 172       *
 173       * @param string $file
 174       */
 175      private function parseImports(array $content, $file)
 176      {
 177          if (!isset($content['imports'])) {
 178              return;
 179          }
 180   
 181          if (!\is_array($content['imports'])) {
 182              throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in "%s". Check your YAML syntax.', $file));
 183          }
 184   
 185          $defaultDirectory = \dirname($file);
 186          foreach ($content['imports'] as $import) {
 187              if (!\is_array($import)) {
 188                  $import = ['resource' => $import];
 189              }
 190              if (!isset($import['resource'])) {
 191                  throw new InvalidArgumentException(sprintf('An import should provide a resource in "%s". Check your YAML syntax.', $file));
 192              }
 193   
 194              $this->setCurrentDir($defaultDirectory);
 195              $this->import($import['resource'], isset($import['type']) ? $import['type'] : null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
 196          }
 197      }
 198   
 199      /**
 200       * Parses definitions.
 201       *
 202       * @param string $file
 203       */
 204      private function parseDefinitions(array $content, $file)
 205      {
 206          if (!isset($content['services'])) {
 207              return;
 208          }
 209   
 210          if (!\is_array($content['services'])) {
 211              throw new InvalidArgumentException(sprintf('The "services" key should contain an array in "%s". Check your YAML syntax.', $file));
 212          }
 213   
 214          if (\array_key_exists('_instanceof', $content['services'])) {
 215              $instanceof = $content['services']['_instanceof'];
 216              unset($content['services']['_instanceof']);
 217   
 218              if (!\is_array($instanceof)) {
 219                  throw new InvalidArgumentException(sprintf('Service "_instanceof" key must be an array, "%s" given in "%s".', \gettype($instanceof), $file));
 220              }
 221              $this->instanceof = [];
 222              $this->isLoadingInstanceof = true;
 223              foreach ($instanceof as $id => $service) {
 224                  if (!$service || !\is_array($service)) {
 225                      throw new InvalidArgumentException(sprintf('Type definition "%s" must be a non-empty array within "_instanceof" in "%s". Check your YAML syntax.', $id, $file));
 226                  }
 227                  if (\is_string($service) && 0 === strpos($service, '@')) {
 228                      throw new InvalidArgumentException(sprintf('Type definition "%s" cannot be an alias within "_instanceof" in "%s". Check your YAML syntax.', $id, $file));
 229                  }
 230                  $this->parseDefinition($id, $service, $file, []);
 231              }
 232          }
 233   
 234          $this->isLoadingInstanceof = false;
 235          $defaults = $this->parseDefaults($content, $file);
 236          foreach ($content['services'] as $id => $service) {
 237              $this->parseDefinition($id, $service, $file, $defaults);
 238          }
 239      }
 240   
 241      /**
 242       * @param string $file
 243       *
 244       * @return array
 245       *
 246       * @throws InvalidArgumentException
 247       */
 248      private function parseDefaults(array &$content, $file)
 249      {
 250          if (!\array_key_exists('_defaults', $content['services'])) {
 251              return [];
 252          }
 253          $defaults = $content['services']['_defaults'];
 254          unset($content['services']['_defaults']);
 255   
 256          if (!\is_array($defaults)) {
 257              throw new InvalidArgumentException(sprintf('Service "_defaults" key must be an array, "%s" given in "%s".', \gettype($defaults), $file));
 258          }
 259   
 260          foreach ($defaults as $key => $default) {
 261              if (!isset(self::$defaultsKeywords[$key])) {
 262                  throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', self::$defaultsKeywords)));
 263              }
 264          }
 265   
 266          if (isset($defaults['tags'])) {
 267              if (!\is_array($tags = $defaults['tags'])) {
 268                  throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file));
 269              }
 270   
 271              foreach ($tags as $tag) {
 272                  if (!\is_array($tag)) {
 273                      $tag = ['name' => $tag];
 274                  }
 275   
 276                  if (!isset($tag['name'])) {
 277                      throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in "%s".', $file));
 278                  }
 279                  $name = $tag['name'];
 280                  unset($tag['name']);
 281   
 282                  if (!\is_string($name) || '' === $name) {
 283                      throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in "%s".', $file));
 284                  }
 285   
 286                  foreach ($tag as $attribute => $value) {
 287                      if (!is_scalar($value) && null !== $value) {
 288                          throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, $attribute, $file));
 289                      }
 290                  }
 291              }
 292          }
 293   
 294          if (isset($defaults['bind'])) {
 295              if (!\is_array($defaults['bind'])) {
 296                  throw new InvalidArgumentException(sprintf('Parameter "bind" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file));
 297              }
 298   
 299              $defaults['bind'] = array_map(function ($v) { return new BoundArgument($v); }, $this->resolveServices($defaults['bind'], $file));
 300          }
 301   
 302          return $defaults;
 303      }
 304   
 305      /**
 306       * @return bool
 307       */
 308      private function isUsingShortSyntax(array $service)
 309      {
 310          foreach ($service as $key => $value) {
 311              if (\is_string($key) && ('' === $key || '$' !== $key[0])) {
 312                  return false;
 313              }
 314          }
 315   
 316          return true;
 317      }
 318   
 319      /**
 320       * Parses a definition.
 321       *
 322       * @param string       $id
 323       * @param array|string $service
 324       * @param string       $file
 325       *
 326       * @throws InvalidArgumentException When tags are invalid
 327       */
 328      private function parseDefinition($id, $service, $file, array $defaults)
 329      {
 330          if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) {
 331              @trigger_error(sprintf('Service names that start with an underscore are deprecated since Symfony 3.3 and will be reserved in 4.0. Rename the "%s" service or define it in XML instead.', $id), \E_USER_DEPRECATED);
 332          }
 333          if (\is_string($service) && 0 === strpos($service, '@')) {
 334              $this->container->setAlias($id, $alias = new Alias(substr($service, 1)));
 335              if (isset($defaults['public'])) {
 336                  $alias->setPublic($defaults['public']);
 337              }
 338   
 339              return;
 340          }
 341   
 342          if (\is_array($service) && $this->isUsingShortSyntax($service)) {
 343              $service = ['arguments' => $service];
 344          }
 345   
 346          if (null === $service) {
 347              $service = [];
 348          }
 349   
 350          if (!\is_array($service)) {
 351              throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but "%s" found for service "%s" in "%s". Check your YAML syntax.', \gettype($service), $id, $file));
 352          }
 353   
 354          $this->checkDefinition($id, $service, $file);
 355   
 356          if (isset($service['alias'])) {
 357              $this->container->setAlias($id, $alias = new Alias($service['alias']));
 358              if (isset($service['public'])) {
 359                  $alias->setPublic($service['public']);
 360              } elseif (isset($defaults['public'])) {
 361                  $alias->setPublic($defaults['public']);
 362              }
 363   
 364              foreach ($service as $key => $value) {
 365                  if (!\in_array($key, ['alias', 'public'])) {
 366                      @trigger_error(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public". The YamlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $key, $id, $file), \E_USER_DEPRECATED);
 367                  }
 368              }
 369   
 370              return;
 371          }
 372   
 373          if ($this->isLoadingInstanceof) {
 374              $definition = new ChildDefinition('');
 375          } elseif (isset($service['parent'])) {
 376              if (!empty($this->instanceof)) {
 377                  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.', $id));
 378              }
 379   
 380              foreach ($defaults as $k => $v) {
 381                  if ('tags' === $k) {
 382                      // since tags are never inherited from parents, there is no confusion
 383                      // thus we can safely add them as defaults to ChildDefinition
 384                      continue;
 385                  }
 386                  if ('bind' === $k) {
 387                      throw new InvalidArgumentException(sprintf('Attribute "bind" on service "%s" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file.', $id));
 388                  }
 389                  if (!isset($service[$k])) {
 390                      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, $id));
 391                  }
 392              }
 393   
 394              $definition = new ChildDefinition($service['parent']);
 395          } else {
 396              $definition = new Definition();
 397   
 398              if (isset($defaults['public'])) {
 399                  $definition->setPublic($defaults['public']);
 400              }
 401              if (isset($defaults['autowire'])) {
 402                  $definition->setAutowired($defaults['autowire']);
 403              }
 404              if (isset($defaults['autoconfigure'])) {
 405                  $definition->setAutoconfigured($defaults['autoconfigure']);
 406              }
 407   
 408              $definition->setChanges([]);
 409          }
 410   
 411          if (isset($service['class'])) {
 412              $definition->setClass($service['class']);
 413          }
 414   
 415          if (isset($service['shared'])) {
 416              $definition->setShared($service['shared']);
 417          }
 418   
 419          if (isset($service['synthetic'])) {
 420              $definition->setSynthetic($service['synthetic']);
 421          }
 422   
 423          if (isset($service['lazy'])) {
 424              $definition->setLazy($service['lazy']);
 425          }
 426   
 427          if (isset($service['public'])) {
 428              $definition->setPublic($service['public']);
 429          }
 430   
 431          if (isset($service['abstract'])) {
 432              $definition->setAbstract($service['abstract']);
 433          }
 434   
 435          if (\array_key_exists('deprecated', $service)) {
 436              $definition->setDeprecated(true, $service['deprecated']);
 437          }
 438   
 439          if (isset($service['factory'])) {
 440              $definition->setFactory($this->parseCallable($service['factory'], 'factory', $id, $file));
 441          }
 442   
 443          if (isset($service['file'])) {
 444              $definition->setFile($service['file']);
 445          }
 446   
 447          if (isset($service['arguments'])) {
 448              $definition->setArguments($this->resolveServices($service['arguments'], $file));
 449          }
 450   
 451          if (isset($service['properties'])) {
 452              $definition->setProperties($this->resolveServices($service['properties'], $file));
 453          }
 454   
 455          if (isset($service['configurator'])) {
 456              $definition->setConfigurator($this->parseCallable($service['configurator'], 'configurator', $id, $file));
 457          }
 458   
 459          if (isset($service['calls'])) {
 460              if (!\is_array($service['calls'])) {
 461                  throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
 462              }
 463   
 464              foreach ($service['calls'] as $call) {
 465                  if (isset($call['method'])) {
 466                      $method = $call['method'];
 467                      $args = isset($call['arguments']) ? $this->resolveServices($call['arguments'], $file) : [];
 468                  } else {
 469                      $method = $call[0];
 470                      $args = isset($call[1]) ? $this->resolveServices($call[1], $file) : [];
 471                  }
 472   
 473                  if (!\is_array($args)) {
 474                      throw new InvalidArgumentException(sprintf('The second parameter for function call "%s" must be an array of its arguments for service "%s" in "%s". Check your YAML syntax.', $method, $id, $file));
 475                  }
 476                  $definition->addMethodCall($method, $args);
 477              }
 478          }
 479   
 480          $tags = isset($service['tags']) ? $service['tags'] : [];
 481          if (!\is_array($tags)) {
 482              throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
 483          }
 484   
 485          if (isset($defaults['tags'])) {
 486              $tags = array_merge($tags, $defaults['tags']);
 487          }
 488   
 489          foreach ($tags as $tag) {
 490              if (!\is_array($tag)) {
 491                  $tag = ['name' => $tag];
 492              }
 493   
 494              if (!isset($tag['name'])) {
 495                  throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in "%s".', $id, $file));
 496              }
 497              $name = $tag['name'];
 498              unset($tag['name']);
 499   
 500              if (!\is_string($name) || '' === $name) {
 501                  throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file));
 502              }
 503   
 504              foreach ($tag as $attribute => $value) {
 505                  if (!is_scalar($value) && null !== $value) {
 506                      throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, $attribute, $file));
 507                  }
 508              }
 509   
 510              $definition->addTag($name, $tag);
 511          }
 512   
 513          if (isset($service['decorates'])) {
 514              if ('' !== $service['decorates'] && '@' === $service['decorates'][0]) {
 515                  throw new InvalidArgumentException(sprintf('The value of the "decorates" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['decorates'], substr($service['decorates'], 1)));
 516              }
 517   
 518              $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null;
 519              $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0;
 520              $definition->setDecoratedService($service['decorates'], $renameId, $priority);
 521          }
 522   
 523          if (isset($service['autowire'])) {
 524              $definition->setAutowired($service['autowire']);
 525          }
 526   
 527          if (isset($service['autowiring_types'])) {
 528              if (\is_string($service['autowiring_types'])) {
 529                  $definition->addAutowiringType($service['autowiring_types']);
 530              } else {
 531                  if (!\is_array($service['autowiring_types'])) {
 532                      throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
 533                  }
 534   
 535                  foreach ($service['autowiring_types'] as $autowiringType) {
 536                      if (!\is_string($autowiringType)) {
 537                          throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file));
 538                      }
 539   
 540                      $definition->addAutowiringType($autowiringType);
 541                  }
 542              }
 543          }
 544   
 545          if (isset($defaults['bind']) || isset($service['bind'])) {
 546              // deep clone, to avoid multiple process of the same instance in the passes
 547              $bindings = isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : [];
 548   
 549              if (isset($service['bind'])) {
 550                  if (!\is_array($service['bind'])) {
 551                      throw new InvalidArgumentException(sprintf('Parameter "bind" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
 552                  }
 553   
 554                  $bindings = array_merge($bindings, $this->resolveServices($service['bind'], $file));
 555              }
 556   
 557              $definition->setBindings($bindings);
 558          }
 559   
 560          if (isset($service['autoconfigure'])) {
 561              if (!$definition instanceof ChildDefinition) {
 562                  $definition->setAutoconfigured($service['autoconfigure']);
 563              } elseif ($service['autoconfigure']) {
 564                  throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting "autoconfigure: false" for the service.', $id));
 565              }
 566          }
 567   
 568          if (\array_key_exists('namespace', $service) && !\array_key_exists('resource', $service)) {
 569              throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in "%s". Check your YAML syntax.', $id, $file));
 570          }
 571   
 572          if (\array_key_exists('resource', $service)) {
 573              if (!\is_string($service['resource'])) {
 574                  throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file));
 575              }
 576              $exclude = isset($service['exclude']) ? $service['exclude'] : null;
 577              $namespace = isset($service['namespace']) ? $service['namespace'] : $id;
 578              $this->registerClasses($definition, $namespace, $service['resource'], $exclude);
 579          } else {
 580              $this->setDefinition($id, $definition);
 581          }
 582      }
 583   
 584      /**
 585       * Parses a callable.
 586       *
 587       * @param string|array $callable  A callable
 588       * @param string       $parameter A parameter (e.g. 'factory' or 'configurator')
 589       * @param string       $id        A service identifier
 590       * @param string       $file      A parsed file
 591       *
 592       * @throws InvalidArgumentException When errors occur
 593       *
 594       * @return string|array A parsed callable
 595       */
 596      private function parseCallable($callable, $parameter, $id, $file)
 597      {
 598          if (\is_string($callable)) {
 599              if ('' !== $callable && '@' === $callable[0]) {
 600                  throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $parameter, $id, $callable, substr($callable, 1)));
 601              }
 602   
 603              if (false !== strpos($callable, ':') && false === strpos($callable, '::')) {
 604                  $parts = explode(':', $callable);
 605   
 606                  return [$this->resolveServices('@'.$parts[0], $file), $parts[1]];
 607              }
 608   
 609              return $callable;
 610          }
 611   
 612          if (\is_array($callable)) {
 613              if (isset($callable[0]) && isset($callable[1])) {
 614                  return [$this->resolveServices($callable[0], $file), $callable[1]];
 615              }
 616   
 617              if ('factory' === $parameter && isset($callable[1]) && null === $callable[0]) {
 618                  return $callable;
 619              }
 620   
 621              throw new InvalidArgumentException(sprintf('Parameter "%s" must contain an array with two elements for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file));
 622          }
 623   
 624          throw new InvalidArgumentException(sprintf('Parameter "%s" must be a string or an array for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file));
 625      }
 626   
 627      /**
 628       * Loads a YAML file.
 629       *
 630       * @param string $file
 631       *
 632       * @return array The file content
 633       *
 634       * @throws InvalidArgumentException when the given file is not a local file or when it does not exist
 635       */
 636      protected function loadFile($file)
 637      {
 638          if (!class_exists('Symfony\Component\Yaml\Parser')) {
 639              throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.');
 640          }
 641   
 642          if (!stream_is_local($file)) {
 643              throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file));
 644          }
 645   
 646          if (!file_exists($file)) {
 647              throw new InvalidArgumentException(sprintf('The file "%s" does not exist.', $file));
 648          }
 649   
 650          if (null === $this->yamlParser) {
 651              $this->yamlParser = new YamlParser();
 652          }
 653   
 654          $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($file, &$prevErrorHandler) {
 655              $message = \E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$file.'"$0', $message) : $message;
 656   
 657              return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false;
 658          });
 659   
 660          try {
 661              $configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
 662          } catch (ParseException $e) {
 663              throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $file).$e->getMessage(), 0, $e);
 664          } finally {
 665              restore_error_handler();
 666          }
 667   
 668          return $this->validate($configuration, $file);
 669      }
 670   
 671      /**
 672       * Validates a YAML file.
 673       *
 674       * @param mixed  $content
 675       * @param string $file
 676       *
 677       * @return array
 678       *
 679       * @throws InvalidArgumentException When service file is not valid
 680       */
 681      private function validate($content, $file)
 682      {
 683          if (null === $content) {
 684              return $content;
 685          }
 686   
 687          if (!\is_array($content)) {
 688              throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file));
 689          }
 690   
 691          foreach ($content as $namespace => $data) {
 692              if (\in_array($namespace, ['imports', 'parameters', 'services'])) {
 693                  continue;
 694              }
 695   
 696              if (!$this->container->hasExtension($namespace)) {
 697                  $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions()));
 698                  throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $file, $namespace, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'));
 699              }
 700          }
 701   
 702          return $content;
 703      }
 704   
 705      /**
 706       * Resolves services.
 707       *
 708       * @param mixed  $value
 709       * @param string $file
 710       * @param bool   $isParameter
 711       *
 712       * @return array|string|Reference|ArgumentInterface
 713       */
 714      private function resolveServices($value, $file, $isParameter = false)
 715      {
 716          if ($value instanceof TaggedValue) {
 717              $argument = $value->getValue();
 718              if ('iterator' === $value->getTag()) {
 719                  if (!\is_array($argument)) {
 720                      throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts sequences in "%s".', $file));
 721                  }
 722                  $argument = $this->resolveServices($argument, $file, $isParameter);
 723                  try {
 724                      return new IteratorArgument($argument);
 725                  } catch (InvalidArgumentException $e) {
 726                      throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file));
 727                  }
 728              }
 729              if ('tagged' === $value->getTag()) {
 730                  if (!\is_string($argument) || !$argument) {
 731                      throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts non empty string in "%s".', $file));
 732                  }
 733   
 734                  return new TaggedIteratorArgument($argument);
 735              }
 736              if ('service' === $value->getTag()) {
 737                  if ($isParameter) {
 738                      throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file));
 739                  }
 740   
 741                  $isLoadingInstanceof = $this->isLoadingInstanceof;
 742                  $this->isLoadingInstanceof = false;
 743                  $instanceof = $this->instanceof;
 744                  $this->instanceof = [];
 745   
 746                  $id = sprintf('%d_%s', ++$this->anonymousServicesCount, preg_replace('/^.*\\\\/', '', isset($argument['class']) ? $argument['class'] : '').$this->anonymousServicesSuffix);
 747                  $this->parseDefinition($id, $argument, $file, []);
 748   
 749                  if (!$this->container->hasDefinition($id)) {
 750                      throw new InvalidArgumentException(sprintf('Creating an alias using the tag "!service" is not allowed in "%s".', $file));
 751                  }
 752   
 753                  $this->container->getDefinition($id)->setPublic(false);
 754   
 755                  $this->isLoadingInstanceof = $isLoadingInstanceof;
 756                  $this->instanceof = $instanceof;
 757   
 758                  return new Reference($id);
 759              }
 760   
 761              throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag()));
 762          }
 763   
 764          if (\is_array($value)) {
 765              foreach ($value as $k => $v) {
 766                  $value[$k] = $this->resolveServices($v, $file, $isParameter);
 767              }
 768          } elseif (\is_string($value) && 0 === strpos($value, '@=')) {
 769              if (!class_exists(Expression::class)) {
 770                  throw new \LogicException(sprintf('The "@=" expression syntax cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'));
 771              }
 772   
 773              return new Expression(substr($value, 2));
 774          } elseif (\is_string($value) && 0 === strpos($value, '@')) {
 775              if (0 === strpos($value, '@@')) {
 776                  $value = substr($value, 1);
 777                  $invalidBehavior = null;
 778              } elseif (0 === strpos($value, '@!')) {
 779                  $value = substr($value, 2);
 780                  $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
 781              } elseif (0 === strpos($value, '@?')) {
 782                  $value = substr($value, 2);
 783                  $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
 784              } else {
 785                  $value = substr($value, 1);
 786                  $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
 787              }
 788   
 789              if ('=' === substr($value, -1)) {
 790                  @trigger_error(sprintf('The "=" suffix that used to disable strict references in Symfony 2.x is deprecated since Symfony 3.3 and will be unsupported in 4.0. Remove it in "%s".', $value), \E_USER_DEPRECATED);
 791                  $value = substr($value, 0, -1);
 792              }
 793   
 794              if (null !== $invalidBehavior) {
 795                  $value = new Reference($value, $invalidBehavior);
 796              }
 797          }
 798   
 799          return $value;
 800      }
 801   
 802      /**
 803       * Loads from Extensions.
 804       */
 805      private function loadFromExtensions(array $content)
 806      {
 807          foreach ($content as $namespace => $values) {
 808              if (\in_array($namespace, ['imports', 'parameters', 'services'])) {
 809                  continue;
 810              }
 811   
 812              if (!\is_array($values) && null !== $values) {
 813                  $values = [];
 814              }
 815   
 816              $this->container->loadFromExtension($namespace, $values);
 817          }
 818      }
 819   
 820      /**
 821       * Checks the keywords used to define a service.
 822       *
 823       * @param string $id         The service name
 824       * @param array  $definition The service definition to check
 825       * @param string $file       The loaded YAML file
 826       */
 827      private function checkDefinition($id, array $definition, $file)
 828      {
 829          if ($throw = $this->isLoadingInstanceof) {
 830              $keywords = self::$instanceofKeywords;
 831          } elseif ($throw = (isset($definition['resource']) || isset($definition['namespace']))) {
 832              $keywords = self::$prototypeKeywords;
 833          } else {
 834              $keywords = self::$serviceKeywords;
 835          }
 836   
 837          foreach ($definition as $key => $value) {
 838              if (!isset($keywords[$key])) {
 839                  if ($throw) {
 840                      throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', $keywords)));
 841                  }
 842   
 843                  @trigger_error(sprintf('The configuration key "%s" is unsupported for service definition "%s" in "%s". Allowed configuration keys are "%s". The YamlFileLoader object will raise an exception instead in Symfony 4.0 when detecting an unsupported service configuration key.', $key, $id, $file, implode('", "', $keywords)), \E_USER_DEPRECATED);
 844              }
 845          }
 846      }
 847  }
 848