Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. YamlFileLoader.php

function YamlFileLoader::parseDefinition

Same name in this branch
  1. 11.1.x core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php \Drupal\Core\DependencyInjection\YamlFileLoader::parseDefinition()

Throws

InvalidArgumentException When tags are invalid

2 calls to YamlFileLoader::parseDefinition()
YamlFileLoader::parseDefinitions in vendor/symfony/dependency-injection/Loader/YamlFileLoader.php
YamlFileLoader::resolveServices in vendor/symfony/dependency-injection/Loader/YamlFileLoader.php

File

vendor/symfony/dependency-injection/Loader/YamlFileLoader.php, line 340

Class

YamlFileLoader
YamlFileLoader loads YAML files service definitions.

Namespace

Symfony\Component\DependencyInjection\Loader

Code

private function parseDefinition(string $id, array|string|null $service, string $file, array $defaults, bool $return = false, bool $trackBindings = true) : Definition|Alias|null {
    if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) {
        throw new InvalidArgumentException(\sprintf('Service names that start with an underscore are reserved. Rename the "%s" service or define it in XML instead.', $id));
    }
    if (\is_string($service) && str_starts_with($service, '@')) {
        $alias = new Alias(substr($service, 1));
        if (isset($defaults['public'])) {
            $alias->setPublic($defaults['public']);
        }
        return $return ? $alias : $this->container
            ->setAlias($id, $alias);
    }
    if (\is_array($service) && $this->isUsingShortSyntax($service)) {
        $service = [
            'arguments' => $service,
        ];
    }
    if (!\is_array($service ??= [])) {
        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.', get_debug_type($service), $id, $file));
    }
    if (isset($service['stack'])) {
        if (!\is_array($service['stack'])) {
            throw new InvalidArgumentException(\sprintf('A stack must be an array of definitions, "%s" given for service "%s" in "%s". Check your YAML syntax.', get_debug_type($service), $id, $file));
        }
        $stack = [];
        foreach ($service['stack'] as $k => $frame) {
            if (\is_array($frame) && 1 === \count($frame) && !isset(self::SERVICE_KEYWORDS[key($frame)])) {
                $frame = [
                    'class' => key($frame),
                    'arguments' => current($frame),
                ];
            }
            if (\is_array($frame) && isset($frame['stack'])) {
                throw new InvalidArgumentException(\sprintf('Service stack "%s" cannot contain another stack in "%s".', $id, $file));
            }
            $definition = $this->parseDefinition($id . '" at index "' . $k, $frame, $file, $defaults, true);
            if ($definition instanceof Definition) {
                $definition->setInstanceofConditionals($this->instanceof);
            }
            $stack[$k] = $definition;
        }
        if ($diff = array_diff(array_keys($service), [
            'stack',
            'public',
            'deprecated',
        ])) {
            throw new InvalidArgumentException(\sprintf('Invalid attribute "%s"; supported ones are "public" and "deprecated" for service "%s" in "%s". Check your YAML syntax.', implode('", "', $diff), $id, $file));
        }
        $service = [
            'parent' => '',
            'arguments' => $stack,
            'tags' => [
                'container.stack',
            ],
            'public' => $service['public'] ?? null,
            'deprecated' => $service['deprecated'] ?? null,
        ];
    }
    $definition = isset($service[0]) && $service[0] instanceof Definition ? array_shift($service) : null;
    $return = null === $definition ? $return : true;
    if (isset($service['from_callable'])) {
        foreach ([
            'alias',
            'parent',
            'synthetic',
            'factory',
            'file',
            'arguments',
            'properties',
            'configurator',
            'calls',
        ] as $key) {
            if (isset($service['factory'])) {
                throw new InvalidArgumentException(\sprintf('The configuration key "%s" is unsupported for the service "%s" when using "from_callable" in "%s".', $key, $id, $file));
            }
        }
        if ('Closure' !== ($service['class'] ??= 'Closure')) {
            $service['lazy'] = true;
        }
        $service['factory'] = [
            'Closure',
            'fromCallable',
        ];
        $service['arguments'] = [
            $service['from_callable'],
        ];
        unset($service['from_callable']);
    }
    $this->checkDefinition($id, $service, $file);
    if (isset($service['alias'])) {
        $alias = new Alias($service['alias']);
        if (isset($service['public'])) {
            $alias->setPublic($service['public']);
        }
        elseif (isset($defaults['public'])) {
            $alias->setPublic($defaults['public']);
        }
        foreach ($service as $key => $value) {
            if (!\in_array($key, [
                'alias',
                'public',
                'deprecated',
            ])) {
                throw new InvalidArgumentException(\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", "public" and "deprecated".', $key, $id, $file));
            }
            if ('deprecated' === $key) {
                $deprecation = \is_array($value) ? $value : [
                    'message' => $value,
                ];
                if (!isset($deprecation['package'])) {
                    throw new InvalidArgumentException(\sprintf('Missing attribute "package" of the "deprecated" option in "%s".', $file));
                }
                if (!isset($deprecation['version'])) {
                    throw new InvalidArgumentException(\sprintf('Missing attribute "version" of the "deprecated" option in "%s".', $file));
                }
                $alias->setDeprecated($deprecation['package'], $deprecation['version'], $deprecation['message'] ?? '');
            }
        }
        return $return ? $alias : $this->container
            ->setAlias($id, $alias);
    }
    $changes = [];
    if (null !== $definition) {
        $changes = $definition->getChanges();
    }
    elseif ($this->isLoadingInstanceof) {
        $definition = new ChildDefinition('');
    }
    elseif (isset($service['parent'])) {
        if ('' !== $service['parent'] && '@' === $service['parent'][0]) {
            throw new InvalidArgumentException(\sprintf('The value of the "parent" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['parent'], substr($service['parent'], 1)));
        }
        $definition = new ChildDefinition($service['parent']);
    }
    else {
        $definition = new Definition();
    }
    if (isset($defaults['public'])) {
        $definition->setPublic($defaults['public']);
    }
    if (isset($defaults['autowire'])) {
        $definition->setAutowired($defaults['autowire']);
    }
    if (isset($defaults['autoconfigure'])) {
        $definition->setAutoconfigured($defaults['autoconfigure']);
    }
    $definition->setChanges($changes);
    if (isset($service['class'])) {
        $definition->setClass($service['class']);
    }
    if (isset($service['shared'])) {
        $definition->setShared($service['shared']);
    }
    if (isset($service['synthetic'])) {
        $definition->setSynthetic($service['synthetic']);
    }
    if (isset($service['lazy'])) {
        $definition->setLazy((bool) $service['lazy']);
        if (\is_string($service['lazy'])) {
            $definition->addTag('proxy', [
                'interface' => $service['lazy'],
            ]);
        }
    }
    if (isset($service['public'])) {
        $definition->setPublic($service['public']);
    }
    if (isset($service['abstract'])) {
        $definition->setAbstract($service['abstract']);
    }
    if (isset($service['deprecated'])) {
        $deprecation = \is_array($service['deprecated']) ? $service['deprecated'] : [
            'message' => $service['deprecated'],
        ];
        if (!isset($deprecation['package'])) {
            throw new InvalidArgumentException(\sprintf('Missing attribute "package" of the "deprecated" option in "%s".', $file));
        }
        if (!isset($deprecation['version'])) {
            throw new InvalidArgumentException(\sprintf('Missing attribute "version" of the "deprecated" option in "%s".', $file));
        }
        $definition->setDeprecated($deprecation['package'], $deprecation['version'], $deprecation['message'] ?? '');
    }
    if (isset($service['factory'])) {
        $definition->setFactory($this->parseCallable($service['factory'], 'factory', $id, $file));
    }
    if (isset($service['constructor'])) {
        if (null !== $definition->getFactory()) {
            throw new LogicException(\sprintf('The "%s" service cannot declare a factory as well as a constructor.', $id));
        }
        $definition->setFactory([
            null,
            $service['constructor'],
        ]);
    }
    if (isset($service['file'])) {
        $definition->setFile($service['file']);
    }
    if (isset($service['arguments'])) {
        $definition->setArguments($this->resolveServices($service['arguments'], $file));
    }
    if (isset($service['properties'])) {
        $definition->setProperties($this->resolveServices($service['properties'], $file));
    }
    if (isset($service['configurator'])) {
        $definition->setConfigurator($this->parseCallable($service['configurator'], 'configurator', $id, $file));
    }
    if (isset($service['calls'])) {
        if (!\is_array($service['calls'])) {
            throw new InvalidArgumentException(\sprintf('Parameter "calls" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
        }
        foreach ($service['calls'] as $k => $call) {
            if (!\is_array($call) && (!\is_string($k) || !$call instanceof TaggedValue)) {
                throw new InvalidArgumentException(\sprintf('Invalid method call for service "%s": expected map or array, "%s" given in "%s".', $id, $call instanceof TaggedValue ? '!' . $call->getTag() : get_debug_type($call), $file));
            }
            if (\is_string($k)) {
                throw new InvalidArgumentException(\sprintf('Invalid method call for service "%s", did you forget a leading dash before "%s: ..." in "%s"?', $id, $k, $file));
            }
            if (isset($call['method']) && \is_string($call['method'])) {
                $method = $call['method'];
                $args = $call['arguments'] ?? [];
                $returnsClone = $call['returns_clone'] ?? false;
            }
            else {
                if (1 === \count($call) && \is_string(key($call))) {
                    $method = key($call);
                    $args = $call[$method];
                    if ($args instanceof TaggedValue) {
                        if ('returns_clone' !== $args->getTag()) {
                            throw new InvalidArgumentException(\sprintf('Unsupported tag "!%s", did you mean "!returns_clone" for service "%s" in "%s"?', $args->getTag(), $id, $file));
                        }
                        $returnsClone = true;
                        $args = $args->getValue();
                    }
                    else {
                        $returnsClone = false;
                    }
                }
                elseif (empty($call[0])) {
                    throw new InvalidArgumentException(\sprintf('Invalid call for service "%s": the method must be defined as the first index of an array or as the only key of a map in "%s".', $id, $file));
                }
                else {
                    $method = $call[0];
                    $args = $call[1] ?? [];
                    $returnsClone = $call[2] ?? false;
                }
            }
            if (!\is_array($args)) {
                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));
            }
            $args = $this->resolveServices($args, $file);
            $definition->addMethodCall($method, $args, $returnsClone);
        }
    }
    $tags = $service['tags'] ?? [];
    if (!\is_array($tags)) {
        throw new InvalidArgumentException(\sprintf('Parameter "tags" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
    }
    if (isset($defaults['tags'])) {
        $tags = array_merge($tags, $defaults['tags']);
    }
    foreach ($tags as $tag) {
        if (!\is_array($tag)) {
            $tag = [
                'name' => $tag,
            ];
        }
        if (1 === \count($tag) && \is_array(current($tag))) {
            $name = key($tag);
            $tag = current($tag);
        }
        else {
            if (!isset($tag['name'])) {
                throw new InvalidArgumentException(\sprintf('A "tags" entry is missing a "name" key for service "%s" in "%s".', $id, $file));
            }
            $name = $tag['name'];
            unset($tag['name']);
        }
        if (!\is_string($name) || '' === $name) {
            throw new InvalidArgumentException(\sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file));
        }
        $this->validateAttributes(\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, '%s', $file), $tag);
        $definition->addTag($name, $tag);
    }
    if (null !== ($decorates = $service['decorates'] ?? null)) {
        if ('' !== $decorates && '@' === $decorates[0]) {
            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($decorates, 1)));
        }
        $decorationOnInvalid = \array_key_exists('decoration_on_invalid', $service) ? $service['decoration_on_invalid'] : 'exception';
        if ('exception' === $decorationOnInvalid) {
            $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
        }
        elseif ('ignore' === $decorationOnInvalid) {
            $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
        }
        elseif (null === $decorationOnInvalid) {
            $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
        }
        elseif ('null' === $decorationOnInvalid) {
            throw new InvalidArgumentException(\sprintf('Invalid value "%s" for attribute "decoration_on_invalid" on service "%s". Did you mean null (without quotes) in "%s"?', $decorationOnInvalid, $id, $file));
        }
        else {
            throw new InvalidArgumentException(\sprintf('Invalid value "%s" for attribute "decoration_on_invalid" on service "%s". Did you mean "exception", "ignore" or null in "%s"?', $decorationOnInvalid, $id, $file));
        }
        $renameId = $service['decoration_inner_name'] ?? null;
        $priority = $service['decoration_priority'] ?? 0;
        $definition->setDecoratedService($decorates, $renameId, $priority, $invalidBehavior);
    }
    if (isset($service['autowire'])) {
        $definition->setAutowired($service['autowire']);
    }
    if (isset($defaults['bind']) || isset($service['bind'])) {
        // deep clone, to avoid multiple process of the same instance in the passes
        $bindings = $definition->getBindings();
        $bindings += isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : [];
        if (isset($service['bind'])) {
            if (!\is_array($service['bind'])) {
                throw new InvalidArgumentException(\sprintf('Parameter "bind" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file));
            }
            $bindings = array_merge($bindings, $this->resolveServices($service['bind'], $file));
            $bindingType = $this->isLoadingInstanceof ? BoundArgument::INSTANCEOF_BINDING : BoundArgument::SERVICE_BINDING;
            foreach ($bindings as $argument => $value) {
                if (!$value instanceof BoundArgument) {
                    $bindings[$argument] = new BoundArgument($value, $trackBindings, $bindingType, $file);
                }
            }
        }
        $definition->setBindings($bindings);
    }
    if (isset($service['autoconfigure'])) {
        $definition->setAutoconfigured($service['autoconfigure']);
    }
    if (\array_key_exists('namespace', $service) && !\array_key_exists('resource', $service)) {
        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));
    }
    if ($return) {
        if (\array_key_exists('resource', $service)) {
            throw new InvalidArgumentException(\sprintf('Invalid "resource" attribute found for service "%s" in "%s". Check your YAML syntax.', $id, $file));
        }
        return $definition;
    }
    if (\array_key_exists('resource', $service)) {
        if (!\is_string($service['resource'])) {
            throw new InvalidArgumentException(\sprintf('A "resource" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file));
        }
        $exclude = $service['exclude'] ?? null;
        $namespace = $service['namespace'] ?? $id;
        $this->registerClasses($definition, $namespace, $service['resource'], $exclude, $file);
    }
    else {
        $this->setDefinition($id, $definition);
    }
    return null;
}
RSS feed
Powered by Drupal