Problem/Motivation
I was looking into creating a lazy service, since I knew this was possible in the Symfony DI component. There isn't much documentation on this in Drupal (see #2514582: Document lazy services and fix script doxygen) though it would be easy to think this works the same as in Symfony, which would be incorrect.
Lazy services were first introduced in #1973618: DIC: Lazy instantiation of service dependencies (ProxyManager for "proxy services"), in which the Symfony approach of a compiler pass was passed over in favor of a custom ProxyBuilder and ProxyDumper. The reasoning in the IS was:
This is the same approach as Symfony documents, except instead of using the Ocramius ProxyManager library that Symfony recommends, we use a custom lightweight implementation, which is faster (e.g., calls the proxied object's methods directly rather than via reflection) but not as complete (e.g., doesn't proxy property access, only method calls, and doesn't proxy magic methods). We think that's a worthwhile trade-off, since Drupal services don't use public properties, magic methods, etc.: the whole point of a service is to be defined by an interface.
This approach was later found to be incompatible with contrib modules, as the execution order of operations in building and dumping he DI container meant contrib interfaces were not discoverable. #2408371: Proxies of module interfaces don't work introduced the requirement to manually create the proxy service, using a script shipped with core.
There are a number of issues, such as #2532126: [meta] Follow-up for #2408371: Fix various issues with proxy services, #2539494: Provide a nice error message when trying to run the generate proxy class script on uninstalled/missing modules, the above-linked documentation todo, and #2513336: Consider checking checksum of interfaces during container building for proxy services which is probably most significant, having to do with the generated proxies going stale.
The vast majority of this work (and isuses) were done/logged 6+ years ago. From a DX perspective, I've personally concluded that the benefit of creating a lazy-instantiated service in Drupal is more trouble/danger than it's worth.
Steps to reproduce
Proposed resolution
Drupal's DI container differs from the upstream Symfony component in that it is serialized and dumped into the database. That introduces some significant challenges, but I'm optimistic that recent improvements in Symfony, paired with efforts in Drupal to come back closer to feature parity with upstream, might mean we can back out of the very Drupal-ized lazy service pattern. This would help site owners/developers, allow us to delete a lot of code from Drupal core, and make this less of a DX WTF.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Issue fork drupal-3228623
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
Comment #2
bradjones1Comment #3
bradjones1Comment #4
bradjones1Comment #5
bradjones1Comment #6
wim leersIIRC this was critical for certain aspects of the Drupal bootstrap process to become sufficiently fast in the majority of cases.
So the work that gets done here almost certainly will need to provide profiling data if it changes lazy services, and certainly if it removes them altogether.
That being said: +1 to this being poor DX right now. It's painful to make service lazy.
Comment #11
longwaveSymfony 6.2 supports lazy services out of the box: https://symfony.com/doc/current/service_container/lazy_services.html
We should figure out how this works and see if we can swap our custom implementation to using Symfony's.
Comment #12
catchI think that was already attempted in #3396928: Deprecate Drupal ProxyBuilder in favor of Symfony lazy services .
Comment #13
ghost of drupal pastYou want ServiceClosureArgument
Your problem is our YAML loader AFAIK doesn't support it
You want to add a notation like the already existing @? probably @> because it's reminiscent of fn () => and probably upstream it too -- Symfony has its own way to do it using some godforsaken YAML notation uglier than sin and waaaaay too long, too.
Comment #14
bradjones1Re: ##13, we're using this pattern at #3272093: Cache bin names should be set from service tags, not the service name though as mentioned, the Drupal container doesn't support it directly. I disagree that the Symfony syntax is too ugly to use, but service closure is a good candidate here to remove all this even uglier boilerplate.
Comment #15
longwave#3450516: Add support for !service_closure custom tag in YamlFileLoader ?
I'm also (slowly) working on removing/replacing our lazy proxy implementations in #3432820: [meta] Remove some lazy proxy services, given that at least two of the ones I've investigated so far are broken in that they aren't really lazy at all.
Comment #16
bradjones1Does this mean we could deprecate the existing lazy proxy creation script and require any new core lazy services be injected with a service closure or something else less boilerplate?
Comment #17
ghost of drupal pastYeah,
arguments: [!service_closure '@example_service_1']is exactly the godforsaken ugly syntax I mentioned. Nah. We could and should do better:arguments: ['@>example_service_1']and be done.Comment #18
dpiWe already support the nice thing,
\Symfony\Component\DependencyInjection\Attribute\AutowireServiceClosure. AFAICT.Symfony Docs
!service_closureshould be avoided in new code.Comment #19
dpiA worthwhile cause <3
Comment #20
kim.pepperNote #3450516: Add support for !service_closure custom tag in YamlFileLoader went in recently.
Comment #21
catchComment #22
kim.pepperI created #3483996: Remove lazy declaration and proxy class for cron and use service closure instead to look at the service closure approach. It might be a big change to do in one go, so we could split it into smaller tasks.
Comment #23
ghost of drupal pastI found the !service_closure syntax so ugly I put aside my disdain for all things upstream and added the shorthand to Symfony https://github.com/symfony/symfony/commit/ba299601bf460a5888a511d2c6da16...
I'd recommend this to be applied to the YamlFileLoader in core, it's really little code.
Comment #24
longwave#23 oh that's a nice little improvement, yes we should add that to Drupal too!
Comment #25
kim.pepperCreated an issue for #23 #3498038: Support service closure shorthand syntax in YamlFileLoader