Problem/Motivation

As discussed in #3228623: DX: Creating lazy services is too difficult/obscure/bespoke/brittle the DX for our current lazy service proxies is poor. We tried using symfony lazy services in #3396928: Deprecate Drupal ProxyBuilder in favor of Symfony lazy services however that was not possible due to:

Sadly SF uses eval for the proxy classes which at least I can't see working with our serialized container solution

However we can now use service closures to lazy-load services which are supported by our serialized container.

Steps to reproduce

Proposed resolution

Replacy services tagged 'lazy' with service closures.

Prefer using autowiring and the #[AutowireServiceClosure] attribute over the !service_closure yaml command where possible.

https://symfony.com/doc/current/service_container/autowiring.html#genera...

This requires changing the constructor signature and adding a new method to invoke the closure like so:

Before:

public function __construct(
  protected CronInterface $cron,
) {}

Service definition:

services:
  my_service:
    class: Foo\Bar
    arguments: ['@cron']

After:

public function __construct(
  protected \Closure $cronClosure,
) {}


protected function getCron(): CronInterface {
  return ($this->cronClosure)();
}

Service definition:

services:
  my_service:
    class: Foo\Bar
    arguments: [!service_closure '@cron']

Autowiring approach

Before:

public function __construct(
  protected CronInterface $cron,
) {}

Service definition:

services:
  my_service:
    class: Foo\Bar
    autowire: true

After:

public function __construct(
#[AutowireServiceClosure('cron')]
  protected \Closure $cronClosure,
) {}


protected function getCron(): CronInterface {
  return ($this->cronClosure)();
}

Service definition:

services:
  my_service:
    class: Foo\Bar
    autowire: true

Remaining tasks

Current proxies under core/lib/Drupal/Core/ProxyClass:

├── Batch
│   └── BatchStorage.php
├── Config
│   └── ConfigInstaller.php
├── Cron.php
├── Extension
│   └── ModuleInstaller.php
├── File
│   └── MimeType
│       ├── ExtensionMimeTypeGuesser.php
│       └── MimeTypeGuesser.php
├── Lock
│   ├── DatabaseLockBackend.php
│   └── PersistentDatabaseLockBackend.php
├── Menu
│   └── MenuActiveTrail.php
├── PageCache
│   └── ChainResponsePolicy.php
├── ParamConverter
│   ├── AdminPathConfigEntityConverter.php
│   └── MenuLinkPluginConverter.php
├── Render
│   └── BareHtmlPageRenderer.php
└── Routing
    ├── MatcherDumper.php
    └── RouteBuilder.php

Proxies in core modules:

core/modules/language/src/ProxyClass/LanguageConverter.php
core/modules/node/src/ProxyClass/ParamConverter/NodePreviewConverter.php
core/modules/views_ui/src/ProxyClass/ParamConverter/ViewUIConverter.php

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Comments

godotislate created an issue. See original summary.

berdir’s picture

I created a sister issue for this one to start using service closures in places that are not yet using lazy service proxies: #3540386: [meta] Explore using more service closures to break deep dependency chains and load fewer services

berdir’s picture

Quite a few of them are param converters. This is also something that came up in my issue. I need to have a closer look at how that actually works and if we can't find a more performant solution for them where they are managed (e.g. a service id collector instead of a service collector, or maybe we can add generic support for a service closure collector? This would also be useful for 1685492) so we don't need to push that complexity into every instance.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

longwave’s picture

ParamConverters should be solved by #3436295: ParamConverterManager lazy services are broken and should use a service locator. Service locators are lazy by default, they only construct a service when it is retrieved.