Problem/Motivation

Symfony allows you to specify _defaults key in your services.yml file. This allows you to specify defaults for all of your services. See https://symfony.com/doc/current/service_container.html

In Symfony it often looks like

# config/services.yaml
services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
        public: false       # Allows optimizing the container by removing unused services; this also means
                            # fetching services directly from the container via $container->get() won't work.
                            # The best practice is to be explicit about your dependencies anyway.

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/*'
        exclude: '../src/{Entity,Migrations,Tests,Kernel.php}'

    # ...

Whilst autoconfigure is not relevant yet it would be nice for a module to be able to declare all of its services as public: false (if for example it wants to prevent the service locator pattern - yes this is hard is Drupal because plugin managers and events don't work with them but maybe in the future) and autowire will be supported by #3021803: Document and add tests for service autowiring.

Proposed resolution

Implement this by copying code from YamlFileLoader once #3021803: Document and add tests for service autowiring has landed.

Remaining tasks

User interface changes

None

API changes

@todo

Data model changes

None

Release notes snippet

@todo

Issue fork drupal-3021898

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

alexpott created an issue. See original summary.

alexpott’s picture

alexpott’s picture

Status: Postponed » Needs review
StatusFileSize
new15.44 KB
alexpott’s picture

Status: Needs review » Postponed
opdavies’s picture

I've given the patch a quick test and it seems to work well.

It's allowed me to change my drupalversary.services.yml from:

services:
  cache.drupalversary:
    class: Drupal\Core\Cache\CacheBackendInterface
    arguments:
      - drupalversary
    factory: cache_factory:get
    tags:
      - name: cache.bin

  Drupal\drupalversary\Controller\UserController:
    autowire: true

  Drupal\drupalversary\Service\AccountRetriever:
    autowire: true

  Drupal\drupalversary\Service\CreatedDateParser:
    autowire: true

to:

services:
  _defaults:
    autowire: true

  cache.drupalversary:
    class: Drupal\Core\Cache\CacheBackendInterface
    arguments:
      - drupalversary
    factory: cache_factory:get
    tags:
      - name: cache.bin

  Drupal\drupalversary\Controller\UserController: ~
  Drupal\drupalversary\Service\AccountRetriever: ~
  Drupal\drupalversary\Service\CreatedDateParser: ~

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

aaronbauman’s picture

Status: Postponed » Needs review
StatusFileSize
new16.1 KB
new10.36 KB

Autowiring is already supported, as of #2611816: Update to symfony 2.8, so I don't think this needs to be postponed on #3021803: Document and add tests for service autowiring since this thread is about support for _defaults, not autowiring specifically.

This patch is a re-roll of #3 and includes test coverage for the new _defaults: autowiring, tags, and public.

There are still some TODOs in the code, not sure what to do with those.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

longwave’s picture

Issue tags: +Service autowiring

Tagging as this looks like a good next target if #3021803: Document and add tests for service autowiring gets in.

longwave’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
@@ -42,6 +42,14 @@ class YamlFileLoader
+// @todo I think remove these.
+//        'autoconfigure' => 'autoconfigure',
+//        'bind' => 'bind',

I think it's OK to allow both these, autoconfiguration would be nice in the future (e.g. not having to tag event subscriber services manually) as would argument binding (so we can autowire container parameters). We might as well move towards allowing everything Symfony allows unless there is good reason not to.

alexpott’s picture

@longwave I very much agree but I wasn't sure if we actually supported these things yet and therefore whether allowing them to be defaulted was a kind of over promise.

longwave’s picture

Title: Support _defaults key in service.yml files » Support _defaults key in service.yml files for public, tags and autowire settings

I guess we could add tests for both binding and autoconfiguration, as both are handled by Symfony compiler passes, and with a bit of support here should hopefully just work. Autoconfiguration would also need us to support _instanceof though - maybe we should leave these to a followup?

longwave’s picture

StatusFileSize
new16.45 KB

Rerolled with some minor changes to make tests pass following #3187074: [Symfony 5] Services are private by default

Still need to remove the bind and autoconfigure parts, and Symfony's YamlFileLoader has also moved on since the original patch - I think we should bring any new additions here in line with latest Symfony.

longwave’s picture

StatusFileSize
new16.89 KB
new447 bytes

Make cspell happy.

longwave’s picture

Status: Needs work » Needs review

Copied the relevant parts of parseDefaults() and parseDefinition() directly from Symfony 4.4 so we are closer to upstream. We can do more, but that's out of scope of this issue and may have some BC concerns (Symfony does a lot of additional validation that we don't currently do, for example).

longwave’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

NW for MR feedback.

longwave’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests

Added unit tests for all exceptions touched here in YamlFileLoader.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

alexpott’s picture

I think this looks great - I've re-created the branch against 10.0.x so it can be tested against Symfony 6. It'd be great to make some progress here to bring our container more into line with Symfony's upstream.

alexpott’s picture

StatusFileSize
new20.4 KB

Note that the 10.0.x branch also applies to 9.4.x so I'm going to trigger a run against Symfony 4 there...

alexpott’s picture

So this is fun... I've fixed the MR for Drupal 10. Some of the code in the upstream YamlFileLoader has moved around because of how \Symfony\Component\DependencyInjection\ChildDefinition has changed (it no longer calls $this->setPrivate(false); in its constructor). This means that now we need separate changes for 9.4.x and 10.0.x. The patch in #25 is the correct change for 9.4.x and the latest in the MR is (hopefully) the correct change for 10.0.x.

Here's the diff of the 2 approaches:

diff --git a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
index bd68229750..0c35209135 100644
--- a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
+++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
@@ -242,19 +242,20 @@ private function parseDefinition(string $id, $service, string $file, array $defa
             $definition = new ChildDefinition($service['parent']);
         } else {
             $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']);
-            }
+        // Drupal services are public by default.
+        $definition->setPublic(true);
 
-            $definition->setChanges([]);
+        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']);
         }

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

daffie’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
    @@ -121,12 +125,71 @@ private function parseDefinitions($content, $file)
    +                if (!\is_string($name) || '' === $name) {
    
    @@ -271,31 +347,37 @@ private function parseDefinition($id, $service, $file)
    +            if (!\is_string($name) || '' === $name) {
    

    I am missing testing for when $name is set and is not a string.

  2. +++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
    @@ -121,12 +125,71 @@ private function parseDefinitions($content, $file)
    +                    if (!is_scalar($value) && null !== $value) {
    
    @@ -271,31 +347,37 @@ private function parseDefinition($id, $service, $file)
    +                if (!is_scalar($value) && null !== $value) {
    

    Missing testing for the $value is null.

  3. +++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
    @@ -174,6 +242,17 @@ private function parseDefinition($id, $service, $file)
    +            // Drupal services are public by default.
    +            $definition->setPublic(true);
    
    @@ -195,9 +274,6 @@ private function parseDefinition($id, $service, $file)
    -            $definition->setPublic(true);
    

    With this change will, when the $service['parent'] is set, not be set public by default. Is that what we want? And will this be a BC break? Not sure, just asking the question.

  4. +++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
    @@ -121,12 +125,71 @@ private function parseDefinitions($content, $file)
    +    /**
    +     * @param array  $content
    +     * @param string $file
    +     *
    +     * @return array
    +     *
    +     * @throws InvalidArgumentException
    +     */
    +    private function parseDefaults(array &$content, string $file): array
    

    Can we improve the docblock? It is not very informing now.

  5. Again just asking the question: Can the added InvalidArgumentExceptions to the method YamlFileLoader::parseDefinition() result is a BC break?
  6. +++ b/core/modules/system/tests/modules/services_defaults_test/services_defaults_test.services.yml
    @@ -0,0 +1,16 @@
    +    autowire: true
    

    Can we add a test to see if overriding the _default autowire is true works.

  7. +++ b/core/tests/Drupal/KernelTests/Core/DependencyInjection/ServicesDefaultsTest.php
    @@ -0,0 +1,62 @@
    +    $this->assertSame(
    +      $testServiceDefinition->getTags(),
    +      $testInjection1Definition->getTags());
    

    Nitpick: Can we put this on a single line or move the last ");" to the next line.

  8. +++ b/core/tests/Drupal/KernelTests/Core/DependencyInjection/ServicesDefaultsTest.php
    @@ -0,0 +1,62 @@
    +    // Ensure overridden tag works.
    +    $this->assertTrue($testInjection2Definition->hasTag('zee.bang'));
    

    Can we add testing that the _default tags are still there too and are not lost.

  9. +++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
    @@ -31,6 +31,11 @@
    +    private const DEFAULTS_KEYWORDS = [
    

    Needs a docblock.

  10. Does this issue need a CR?
alexpott’s picture

Status: Needs work » Needs review
Issue tags: +Needs change record

Thanks for the review @daffie

  1. Fixed
  2. Fixed
  3. In 9.x parent services are automatically public - this is no longer the case in 10.0.x (different Symfony versions) - see #26 for how this difference caused problems.
  4. It's a private method and a copy from \Symfony\Component\DependencyInjection\Loader\YamlFileLoader::parseDefaults - SF6 has even less docs. Imo docs are not really work it here.
  5. I don';t think so. We currently don't support defaults at all. This is new functionality.
  6. Fixed
  7. Fixed
  8. Fixed
  9. Again this is a copy from Symfony - there's no docs there and this is private - I'd rather not come up with our own documentation.
  10. Yeah it definitely does - just to announce the new capability.
daffie’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs change record

All code changes look good to me.
All my points have been addressed.
Testing has been added.
I have created a CR.
For me it is RTBC.

alexpott’s picture

StatusFileSize
new21.76 KB

Here's a patch for 9.5.x which has @daffie's review fixes merged on top of #25. Because of the differences upstream 10.0.x and 9.5.x need different patches - see #26 for why.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 31: 3021898-9.5.x-31.patch, failed testing. View results

daffie’s picture

Status: Needs work » Reviewed & tested by the community

Testbot is happy again. Back to RTBC.

  • catch committed 59a925f on 10.0.x
    Issue #3021898 by longwave, alexpott, AaronBauman, daffie, opdavies:...
  • catch committed 3d682d1 on 10.1.x
    Issue #3021898 by longwave, alexpott, AaronBauman, daffie, opdavies:...
  • catch committed 1a4ef7f on 9.5.x
    Issue #3021898 by longwave, alexpott, AaronBauman, daffie, opdavies:...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed/cherry-picked the respective patches to 10.1.x/10.0.x and 9.5.x, thanks!

Since this isn't disruptive or an API change etc., I don't think it needs a release note, just the CR seems fine. If we start changing core's service definitions, we should probably do one then.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

donquixote’s picture

74dbb97f - Remove support for autoconfigure and bind for now.

Do we have a follow-up issue to re-add the "bind"?
I think it would be quite useful to have :)

EDIT: Yes, this is the follow-up: #3108565: Update the Yaml file Loader functionality to simplify service configuration