Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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: 02.04.2025, 15:03 - 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