Commit 59a925f3 authored by catch's avatar catch
Browse files

Issue #3021898 by longwave, alexpott, AaronBauman, daffie, opdavies: Support...

Issue #3021898 by longwave, alexpott, AaronBauman, daffie, opdavies: Support _defaults key in service.yml files for public, tags and autowire settings

(cherry picked from commit 3d682d17)
parent c9989dbe
Loading
Loading
Loading
Loading
+115 −32
Original line number Diff line number Diff line
@@ -31,6 +31,11 @@
 */
class YamlFileLoader
{
    private const DEFAULTS_KEYWORDS = [
        'public' => 'public',
        'tags' => 'tags',
        'autowire' => 'autowire',
    ];

    /**
     * @var \Drupal\Core\DependencyInjection\ContainerBuilder $container
@@ -44,7 +49,6 @@ class YamlFileLoader
     */
    protected $fileCache;


    public function __construct(ContainerBuilder $container)
    {
        $this->container = $container;
@@ -121,28 +125,91 @@ private function parseDefinitions($content, $file)
            $basename = basename($file);
            [$provider, ] = explode('.', $basename, 2);
        }
        $defaults = $this->parseDefaults($content, $file);
        $defaults['tags'][] = [
            'name' => '_provider',
            'provider' => $provider
        ];
        foreach ($content['services'] as $id => $service) {
            if (is_array($service)) {
              $service['tags'][] = ['name' => '_provider', 'provider' => $provider];
            $this->parseDefinition($id, $service, $file, $defaults);
        }
    }

    /**
     * @param array  $content
     * @param string $file
     *
     * @return array
     *
     * @throws InvalidArgumentException
     */
    private function parseDefaults(array &$content, string $file): array
    {
        if (!\array_key_exists('_defaults', $content['services'])) {
            return [];
        }
        $defaults = $content['services']['_defaults'];
        unset($content['services']['_defaults']);

        if (!\is_array($defaults)) {
            throw new InvalidArgumentException(sprintf('Service "_defaults" key must be an array, "%s" given in "%s".', \gettype($defaults), $file));
        }

        foreach ($defaults as $key => $default) {
            if (!isset(self::DEFAULTS_KEYWORDS[$key])) {
                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::DEFAULTS_KEYWORDS)));
            }
        }

        if (isset($defaults['tags'])) {
            if (!\is_array($tags = $defaults['tags'])) {
                throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file));
            }

            foreach ($tags as $tag) {
                if (!\is_array($tag)) {
                    $tag = ['name' => $tag];
                }

                if (!isset($tag['name'])) {
                    throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in "%s".', $file));
                }
                $name = $tag['name'];
                unset($tag['name']);

                if (!\is_string($name) || '' === $name) {
                    throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in "%s".', $file));
                }

                foreach ($tag as $attribute => $value) {
                    if (!is_scalar($value) && null !== $value) {
                        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));
                    }
                }
            $this->parseDefinition($id, $service, $file);
            }
        }

        return $defaults;
    }

    /**
     * Parses a definition.
     *
     * @param string $id
     * @param array $service
     * @param string $file
     * @param array $defaults
     *
     * @throws InvalidArgumentException
     *   When tags are invalid.
     */
    private function parseDefinition($id, $service, $file)
    private function parseDefinition(string $id, $service, string $file, array $defaults)
    {
        if (is_string($service) && 0 === strpos($service, '@')) {
            $this->container->setAlias($id, substr($service, 1));
        if (\is_string($service) && 0 === strpos($service, '@')) {
            $this->container->setAlias($id, $alias = new Alias(substr($service, 1)));
            if (isset($defaults['public'])) {
                $alias->setPublic($defaults['public']);
            }

            return;
        }
@@ -156,10 +223,11 @@ private function parseDefinition($id, $service, $file)
        }

        if (isset($service['alias'])) {
            $alias = $this->container->setAlias($id, new Alias($service['alias']));

            if (array_key_exists('public', $service)) {
            $this->container->setAlias($id, $alias = new Alias($service['alias']));
            if (isset($service['public'])) {
                $alias->setPublic($service['public']);
            } elseif (isset($defaults['public'])) {
                $alias->setPublic($defaults['public']);
            }

            if (array_key_exists('deprecated', $service)) {
@@ -176,6 +244,18 @@ private function parseDefinition($id, $service, $file)
            $definition = new Definition();
        }

        // Drupal services are public by default.
        $definition->setPublic(true);

        if (isset($defaults['public'])) {
            $definition->setPublic($defaults['public']);
        }
        if (isset($defaults['autowire'])) {
            $definition->setAutowired($defaults['autowire']);
        }

        $definition->setChanges([]);

        if (isset($service['class'])) {
            $definition->setClass($service['class']);
        }
@@ -195,9 +275,6 @@ private function parseDefinition($id, $service, $file)
        if (isset($service['public'])) {
            $definition->setPublic($service['public']);
        }
        else {
            $definition->setPublic(true);
        }

        if (isset($service['abstract'])) {
            $definition->setAbstract($service['abstract']);
@@ -271,32 +348,38 @@ private function parseDefinition($id, $service, $file)
            }
        }

        if (isset($service['tags'])) {
            if (!is_array($service['tags'])) {
                throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
        $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));
        }

            foreach ($service['tags'] as $tag) {
                if (!is_array($tag)) {
                    throw new InvalidArgumentException(sprintf('A "tags" entry 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']);
        }

                if (!isset($tag['name'])) {
                    throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
        foreach ($tags as $tag) {
            if (!\is_array($tag)) {
                $tag = ['name' => $tag];
            }

            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));
            }

            foreach ($tag as $attribute => $value) {
                if (!is_scalar($value) && null !== $value) {
                        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));
                    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));
                }
            }

            $definition->addTag($name, $tag);
        }
        }

        if (isset($service['decorates'])) {
            $renameId = $service['decoration_inner_name'] ?? null;
+5 −0
Original line number Diff line number Diff line
name: 'Services _defaults test'
type: module
description: 'Support module for services _defaults'
package: Testing
version: VERSION
+29 −0
Original line number Diff line number Diff line
services:
  _defaults:
    autowire: true
    public: false
    tags:
      - 'foo.tag1'
      - { name: bar.tag2, test: 123 }
      - { name: bar.tag3, value: null }
  # Use an alias so the interface autowiring is tested.
  Drupal\services_defaults_test\TestInjectionInterface: '@Drupal\services_defaults_test\TestInjection'
  # A service that implements TestInjectionInterface.
  Drupal\services_defaults_test\TestInjection:
    public: true
  Drupal\services_defaults_test\TestInjection2:
    public: true
    tags:
      - 'zee.bang'
      - { name: bar.tag2, test: 321 }
  Drupal\services_defaults_test\TestService:
    public: true
  Drupal\services_defaults_test\TestPrivateService: ~
  'services_default_test.no_autowire':
    class: 'Drupal\services_defaults_test\TestService'
    autowire: false
    arguments: ['@services_default_test.no_autowire.arg', '@Drupal\services_defaults_test\TestInjection2']
    public: true
  'services_default_test.no_autowire.arg':
    class: 'Drupal\services_defaults_test\TestInjection'
    public: true
+9 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\services_defaults_test;

/**
 * A service that is injected via default autowiring.
 */
class TestInjection implements TestInjectionInterface {
}
+9 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\services_defaults_test;

/**
 * A service that is injected via default autowiring.
 */
class TestInjection2 {
}
Loading