4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\DependencyInjection\Loader;
14 use Symfony\Component\DependencyInjection\Alias;
15 use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
16 use Symfony\Component\DependencyInjection\Argument\BoundArgument;
17 use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
18 use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
19 use Symfony\Component\DependencyInjection\ChildDefinition;
20 use Symfony\Component\DependencyInjection\ContainerBuilder;
21 use Symfony\Component\DependencyInjection\ContainerInterface;
22 use Symfony\Component\DependencyInjection\Definition;
23 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
24 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
25 use Symfony\Component\DependencyInjection\Reference;
26 use Symfony\Component\ExpressionLanguage\Expression;
27 use Symfony\Component\Yaml\Exception\ParseException;
28 use Symfony\Component\Yaml\Parser as YamlParser;
29 use Symfony\Component\Yaml\Tag\TaggedValue;
30 use Symfony\Component\Yaml\Yaml;
33 * YamlFileLoader loads YAML files service definitions.
35 * @author Fabien Potencier <fabien@symfony.com>
37 class YamlFileLoader extends FileLoader
39 private static $serviceKeywords = array(
44 'synthetic' => 'synthetic',
47 'abstract' => 'abstract',
48 'deprecated' => 'deprecated',
49 'factory' => 'factory',
51 'arguments' => 'arguments',
52 'properties' => 'properties',
53 'configurator' => 'configurator',
56 'decorates' => 'decorates',
57 'decoration_inner_name' => 'decoration_inner_name',
58 'decoration_priority' => 'decoration_priority',
59 'autowire' => 'autowire',
60 'autowiring_types' => 'autowiring_types',
61 'autoconfigure' => 'autoconfigure',
65 private static $prototypeKeywords = array(
66 'resource' => 'resource',
67 'namespace' => 'namespace',
68 'exclude' => 'exclude',
73 'abstract' => 'abstract',
74 'deprecated' => 'deprecated',
75 'factory' => 'factory',
76 'arguments' => 'arguments',
77 'properties' => 'properties',
78 'configurator' => 'configurator',
81 'autowire' => 'autowire',
82 'autoconfigure' => 'autoconfigure',
86 private static $instanceofKeywords = array(
90 'properties' => 'properties',
91 'configurator' => 'configurator',
94 'autowire' => 'autowire',
97 private static $defaultsKeywords = array(
100 'autowire' => 'autowire',
101 'autoconfigure' => 'autoconfigure',
107 private $anonymousServicesCount;
108 private $anonymousServicesSuffix;
113 public function load($resource, $type = null)
115 $path = $this->locator->locate($resource);
117 $content = $this->loadFile($path);
119 $this->container->fileExists($path);
122 if (null === $content) {
127 $this->parseImports($content, $path);
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));
135 foreach ($content['parameters'] as $key => $value) {
136 $this->container->setParameter($key, $this->resolveServices($value, $path, true));
141 $this->loadFromExtensions($content);
144 $this->anonymousServicesCount = 0;
145 $this->anonymousServicesSuffix = '~'.ContainerBuilder::hash($path);
146 $this->setCurrentDir(\dirname($path));
148 $this->parseDefinitions($content, $path);
150 $this->instanceof = array();
157 public function supports($resource, $type = null)
159 if (!\is_string($resource)) {
163 if (null === $type && \in_array(pathinfo($resource, PATHINFO_EXTENSION), array('yaml', 'yml'), true)) {
167 return \in_array($type, array('yaml', 'yml'), true);
171 * Parses all imports.
173 * @param array $content
174 * @param string $file
176 private function parseImports(array $content, $file)
178 if (!isset($content['imports'])) {
182 if (!\is_array($content['imports'])) {
183 throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $file));
186 $defaultDirectory = \dirname($file);
187 foreach ($content['imports'] as $import) {
188 if (!\is_array($import)) {
189 $import = array('resource' => $import);
191 if (!isset($import['resource'])) {
192 throw new InvalidArgumentException(sprintf('An import should provide a resource in %s. Check your YAML syntax.', $file));
195 $this->setCurrentDir($defaultDirectory);
196 $this->import($import['resource'], isset($import['type']) ? $import['type'] : null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
201 * Parses definitions.
203 * @param array $content
204 * @param string $file
206 private function parseDefinitions(array $content, $file)
208 if (!isset($content['services'])) {
212 if (!\is_array($content['services'])) {
213 throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
216 if (array_key_exists('_instanceof', $content['services'])) {
217 $instanceof = $content['services']['_instanceof'];
218 unset($content['services']['_instanceof']);
220 if (!\is_array($instanceof)) {
221 throw new InvalidArgumentException(sprintf('Service "_instanceof" key must be an array, "%s" given in "%s".', \gettype($instanceof), $file));
223 $this->instanceof = array();
224 $this->isLoadingInstanceof = true;
225 foreach ($instanceof as $id => $service) {
226 if (!$service || !\is_array($service)) {
227 throw new InvalidArgumentException(sprintf('Type definition "%s" must be a non-empty array within "_instanceof" in %s. Check your YAML syntax.', $id, $file));
229 if (\is_string($service) && 0 === strpos($service, '@')) {
230 throw new InvalidArgumentException(sprintf('Type definition "%s" cannot be an alias within "_instanceof" in %s. Check your YAML syntax.', $id, $file));
232 $this->parseDefinition($id, $service, $file, array());
236 $this->isLoadingInstanceof = false;
237 $defaults = $this->parseDefaults($content, $file);
238 foreach ($content['services'] as $id => $service) {
239 $this->parseDefinition($id, $service, $file, $defaults);
244 * @param array $content
245 * @param string $file
249 * @throws InvalidArgumentException
251 private function parseDefaults(array &$content, $file)
253 if (!array_key_exists('_defaults', $content['services'])) {
256 $defaults = $content['services']['_defaults'];
257 unset($content['services']['_defaults']);
259 if (!\is_array($defaults)) {
260 throw new InvalidArgumentException(sprintf('Service "_defaults" key must be an array, "%s" given in "%s".', \gettype($defaults), $file));
263 foreach ($defaults as $key => $default) {
264 if (!isset(self::$defaultsKeywords[$key])) {
265 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)));
269 if (isset($defaults['tags'])) {
270 if (!\is_array($tags = $defaults['tags'])) {
271 throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in %s. Check your YAML syntax.', $file));
274 foreach ($tags as $tag) {
275 if (!\is_array($tag)) {
276 $tag = array('name' => $tag);
279 if (!isset($tag['name'])) {
280 throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in %s.', $file));
282 $name = $tag['name'];
285 if (!\is_string($name) || '' === $name) {
286 throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in %s.', $file));
289 foreach ($tag as $attribute => $value) {
290 if (!is_scalar($value) && null !== $value) {
291 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));
297 if (isset($defaults['bind'])) {
298 if (!\is_array($defaults['bind'])) {
299 throw new InvalidArgumentException(sprintf('Parameter "bind" in "_defaults" must be an array in %s. Check your YAML syntax.', $file));
302 $defaults['bind'] = array_map(function ($v) { return new BoundArgument($v); }, $this->resolveServices($defaults['bind'], $file));
309 * @param array $service
313 private function isUsingShortSyntax(array $service)
315 foreach ($service as $key => $value) {
316 if (\is_string($key) && ('' === $key || '$' !== $key[0])) {
325 * Parses a definition.
328 * @param array|string $service
329 * @param string $file
330 * @param array $defaults
332 * @throws InvalidArgumentException When tags are invalid
334 private function parseDefinition($id, $service, $file, array $defaults)
336 if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) {
337 @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);
339 if (\is_string($service) && 0 === strpos($service, '@')) {
340 $this->container->setAlias($id, $alias = new Alias(substr($service, 1)));
341 if (isset($defaults['public'])) {
342 $alias->setPublic($defaults['public']);
348 if (\is_array($service) && $this->isUsingShortSyntax($service)) {
349 $service = array('arguments' => $service);
352 if (null === $service) {
356 if (!\is_array($service)) {
357 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));
360 $this->checkDefinition($id, $service, $file);
362 if (isset($service['alias'])) {
363 $this->container->setAlias($id, $alias = new Alias($service['alias']));
364 if (array_key_exists('public', $service)) {
365 $alias->setPublic($service['public']);
366 } elseif (isset($defaults['public'])) {
367 $alias->setPublic($defaults['public']);
370 foreach ($service as $key => $value) {
371 if (!\in_array($key, array('alias', 'public'))) {
372 @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);
379 if ($this->isLoadingInstanceof) {
380 $definition = new ChildDefinition('');
381 } elseif (isset($service['parent'])) {
382 if (!empty($this->instanceof)) {
383 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));
386 foreach ($defaults as $k => $v) {
388 // since tags are never inherited from parents, there is no confusion
389 // thus we can safely add them as defaults to ChildDefinition
393 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));
395 if (!isset($service[$k])) {
396 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));
400 $definition = new ChildDefinition($service['parent']);
402 $definition = new Definition();
404 if (isset($defaults['public'])) {
405 $definition->setPublic($defaults['public']);
407 if (isset($defaults['autowire'])) {
408 $definition->setAutowired($defaults['autowire']);
410 if (isset($defaults['autoconfigure'])) {
411 $definition->setAutoconfigured($defaults['autoconfigure']);
414 $definition->setChanges(array());
417 if (isset($service['class'])) {
418 $definition->setClass($service['class']);
421 if (isset($service['shared'])) {
422 $definition->setShared($service['shared']);
425 if (isset($service['synthetic'])) {
426 $definition->setSynthetic($service['synthetic']);
429 if (isset($service['lazy'])) {
430 $definition->setLazy($service['lazy']);
433 if (isset($service['public'])) {
434 $definition->setPublic($service['public']);
437 if (isset($service['abstract'])) {
438 $definition->setAbstract($service['abstract']);
441 if (array_key_exists('deprecated', $service)) {
442 $definition->setDeprecated(true, $service['deprecated']);
445 if (isset($service['factory'])) {
446 $definition->setFactory($this->parseCallable($service['factory'], 'factory', $id, $file));
449 if (isset($service['file'])) {
450 $definition->setFile($service['file']);
453 if (isset($service['arguments'])) {
454 $definition->setArguments($this->resolveServices($service['arguments'], $file));
457 if (isset($service['properties'])) {
458 $definition->setProperties($this->resolveServices($service['properties'], $file));
461 if (isset($service['configurator'])) {
462 $definition->setConfigurator($this->parseCallable($service['configurator'], 'configurator', $id, $file));
465 if (isset($service['calls'])) {
466 if (!\is_array($service['calls'])) {
467 throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
470 foreach ($service['calls'] as $call) {
471 if (isset($call['method'])) {
472 $method = $call['method'];
473 $args = isset($call['arguments']) ? $this->resolveServices($call['arguments'], $file) : array();
476 $args = isset($call[1]) ? $this->resolveServices($call[1], $file) : array();
479 if (!\is_array($args)) {
480 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));
482 $definition->addMethodCall($method, $args);
486 $tags = isset($service['tags']) ? $service['tags'] : array();
487 if (!\is_array($tags)) {
488 throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
491 if (isset($defaults['tags'])) {
492 $tags = array_merge($tags, $defaults['tags']);
495 foreach ($tags as $tag) {
496 if (!\is_array($tag)) {
497 $tag = array('name' => $tag);
500 if (!isset($tag['name'])) {
501 throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
503 $name = $tag['name'];
506 if (!\is_string($name) || '' === $name) {
507 throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file));
510 foreach ($tag as $attribute => $value) {
511 if (!is_scalar($value) && null !== $value) {
512 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));
516 $definition->addTag($name, $tag);
519 if (isset($service['decorates'])) {
520 if ('' !== $service['decorates'] && '@' === $service['decorates'][0]) {
521 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)));
524 $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null;
525 $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0;
526 $definition->setDecoratedService($service['decorates'], $renameId, $priority);
529 if (isset($service['autowire'])) {
530 $definition->setAutowired($service['autowire']);
533 if (isset($service['autowiring_types'])) {
534 if (\is_string($service['autowiring_types'])) {
535 $definition->addAutowiringType($service['autowiring_types']);
537 if (!\is_array($service['autowiring_types'])) {
538 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));
541 foreach ($service['autowiring_types'] as $autowiringType) {
542 if (!\is_string($autowiringType)) {
543 throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));
546 $definition->addAutowiringType($autowiringType);
551 if (isset($defaults['bind']) || isset($service['bind'])) {
552 // deep clone, to avoid multiple process of the same instance in the passes
553 $bindings = isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : array();
555 if (isset($service['bind'])) {
556 if (!\is_array($service['bind'])) {
557 throw new InvalidArgumentException(sprintf('Parameter "bind" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
560 $bindings = array_merge($bindings, $this->resolveServices($service['bind'], $file));
563 $definition->setBindings($bindings);
566 if (isset($service['autoconfigure'])) {
567 if (!$definition instanceof ChildDefinition) {
568 $definition->setAutoconfigured($service['autoconfigure']);
569 } elseif ($service['autoconfigure']) {
570 throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting "autoconfigure: false" for the service.', $id));
574 if (array_key_exists('namespace', $service) && !array_key_exists('resource', $service)) {
575 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));
578 if (array_key_exists('resource', $service)) {
579 if (!\is_string($service['resource'])) {
580 throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));
582 $exclude = isset($service['exclude']) ? $service['exclude'] : null;
583 $namespace = isset($service['namespace']) ? $service['namespace'] : $id;
584 $this->registerClasses($definition, $namespace, $service['resource'], $exclude);
586 $this->setDefinition($id, $definition);
593 * @param string|array $callable A callable
594 * @param string $parameter A parameter (e.g. 'factory' or 'configurator')
595 * @param string $id A service identifier
596 * @param string $file A parsed file
598 * @throws InvalidArgumentException When errors are occuried
600 * @return string|array A parsed callable
602 private function parseCallable($callable, $parameter, $id, $file)
604 if (\is_string($callable)) {
605 if ('' !== $callable && '@' === $callable[0]) {
606 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)));
609 if (false !== strpos($callable, ':') && false === strpos($callable, '::')) {
610 $parts = explode(':', $callable);
612 return array($this->resolveServices('@'.$parts[0], $file), $parts[1]);
618 if (\is_array($callable)) {
619 if (isset($callable[0]) && isset($callable[1])) {
620 return array($this->resolveServices($callable[0], $file), $callable[1]);
623 if ('factory' === $parameter && isset($callable[1]) && null === $callable[0]) {
627 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));
630 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));
636 * @param string $file
638 * @return array The file content
640 * @throws InvalidArgumentException when the given file is not a local file or when it does not exist
642 protected function loadFile($file)
644 if (!class_exists('Symfony\Component\Yaml\Parser')) {
645 throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.');
648 if (!stream_is_local($file)) {
649 throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file));
652 if (!file_exists($file)) {
653 throw new InvalidArgumentException(sprintf('The file "%s" does not exist.', $file));
656 if (null === $this->yamlParser) {
657 $this->yamlParser = new YamlParser();
660 $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($file, &$prevErrorHandler) {
661 $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$file.'"$0', $message) : $message;
663 return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false;
667 $configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
668 } catch (ParseException $e) {
669 throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e);
671 restore_error_handler();
674 return $this->validate($configuration, $file);
678 * Validates a YAML file.
680 * @param mixed $content
681 * @param string $file
685 * @throws InvalidArgumentException When service file is not valid
687 private function validate($content, $file)
689 if (null === $content) {
693 if (!\is_array($content)) {
694 throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file));
697 foreach ($content as $namespace => $data) {
698 if (\in_array($namespace, array('imports', 'parameters', 'services'))) {
702 if (!$this->container->hasExtension($namespace)) {
703 $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions()));
704 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'));
714 * @param mixed $value
715 * @param string $file
716 * @param bool $isParameter
718 * @return array|string|Reference|ArgumentInterface
720 private function resolveServices($value, $file, $isParameter = false)
722 if ($value instanceof TaggedValue) {
723 $argument = $value->getValue();
724 if ('iterator' === $value->getTag()) {
725 if (!\is_array($argument)) {
726 throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts sequences in "%s".', $file));
728 $argument = $this->resolveServices($argument, $file, $isParameter);
730 return new IteratorArgument($argument);
731 } catch (InvalidArgumentException $e) {
732 throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file));
735 if ('tagged' === $value->getTag()) {
736 if (!\is_string($argument) || !$argument) {
737 throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts non empty string in "%s".', $file));
740 return new TaggedIteratorArgument($argument);
742 if ('service' === $value->getTag()) {
744 throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file));
747 $isLoadingInstanceof = $this->isLoadingInstanceof;
748 $this->isLoadingInstanceof = false;
749 $instanceof = $this->instanceof;
750 $this->instanceof = array();
752 $id = sprintf('%d_%s', ++$this->anonymousServicesCount, preg_replace('/^.*\\\\/', '', isset($argument['class']) ? $argument['class'] : '').$this->anonymousServicesSuffix);
753 $this->parseDefinition($id, $argument, $file, array());
755 if (!$this->container->hasDefinition($id)) {
756 throw new InvalidArgumentException(sprintf('Creating an alias using the tag "!service" is not allowed in "%s".', $file));
759 $this->container->getDefinition($id)->setPublic(false);
761 $this->isLoadingInstanceof = $isLoadingInstanceof;
762 $this->instanceof = $instanceof;
764 return new Reference($id);
767 throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag()));
770 if (\is_array($value)) {
771 foreach ($value as $k => $v) {
772 $value[$k] = $this->resolveServices($v, $file, $isParameter);
774 } elseif (\is_string($value) && 0 === strpos($value, '@=')) {
775 if (!class_exists(Expression::class)) {
776 throw new \LogicException(sprintf('The "@=" expression syntax cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'));
779 return new Expression(substr($value, 2));
780 } elseif (\is_string($value) && 0 === strpos($value, '@')) {
781 if (0 === strpos($value, '@@')) {
782 $value = substr($value, 1);
783 $invalidBehavior = null;
784 } elseif (0 === strpos($value, '@!')) {
785 $value = substr($value, 2);
786 $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
787 } elseif (0 === strpos($value, '@?')) {
788 $value = substr($value, 2);
789 $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
791 $value = substr($value, 1);
792 $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
795 if ('=' === substr($value, -1)) {
796 @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);
797 $value = substr($value, 0, -1);
800 if (null !== $invalidBehavior) {
801 $value = new Reference($value, $invalidBehavior);
809 * Loads from Extensions.
811 private function loadFromExtensions(array $content)
813 foreach ($content as $namespace => $values) {
814 if (\in_array($namespace, array('imports', 'parameters', 'services'))) {
818 if (!\is_array($values) && null !== $values) {
822 $this->container->loadFromExtension($namespace, $values);
827 * Checks the keywords used to define a service.
829 * @param string $id The service name
830 * @param array $definition The service definition to check
831 * @param string $file The loaded YAML file
833 private function checkDefinition($id, array $definition, $file)
835 if ($throw = $this->isLoadingInstanceof) {
836 $keywords = self::$instanceofKeywords;
837 } elseif ($throw = (isset($definition['resource']) || isset($definition['namespace']))) {
838 $keywords = self::$prototypeKeywords;
840 $keywords = self::$serviceKeywords;
843 foreach ($definition as $key => $value) {
844 if (!isset($keywords[$key])) {
846 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)));
849 @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);