Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
The Symfony 3 container has the ability to autowire services in the container. Service autowiring is already supported by Drupal's container, but lacks test coverage and documentation.
Proposed resolution
Add test coverage for service autowiring, and document how to use this feature.
Remaining tasks
Add test coverageAdd documentation to core- Review & commit
- Update online service docs
User interface changes
None
API changes
None
Data model changes
None
Release notes snippet
Add documentation and test coverage for service
Comment | File | Size | Author |
---|---|---|---|
#70 | interdiff.3021803.66-70.txt | 778 bytes | longwave |
#70 | 3021803-70.patch | 6.2 KB | longwave |
#66 | 3021803-9.x-66.patch | 6.19 KB | alexpott |
Comments
Comment #2
alexpottComment #3
cilefen CreditAttribution: cilefen as a volunteer commented👍 This is my favorite feature of SF.
Comment #4
alexpottHere's it working - needs a couple of container fixes to be more like Symfony.
Comment #5
alexpottCreated a meta.
Comment #6
alexpott@alexpott run the tests you add before uploading the patch - especially after last minute changes.
Comment #7
alexpottNeed a comment here to explain a bit. Also we probably are not using AutowireRequiredMethodsPass yet so can remove that. ResolveClassPass is what supports services declared like
Drupal\autowire_test\TestInjection: ~
Comment #9
BerdirShould we profile this?
Some of the current passes are already very slow and this seems like something that could be quite expensive as well. Nobody cares much about that in Symfony world, but container rebuild is 50% + of the installation time of a large install profile...
Comment #10
alexpott@Berdir I think atm it would have very little impact because nothing would have autowire enabled. So I'm not sure how to get a good picture of what might happen - but yeah you are right we should think about performance and some sort of valid profiling.
Comment #11
dawehnerReading the patch does this mean someone could write a contrib module providing the same functionality?
Here is just a quick doc entry.
Comment #12
JParkinson1991 CreditAttribution: JParkinson1991 commentedIs it a requirement to add the compiler passes provided by symfony in the
\Drupal\Core\CoreServiceProvider
class?If im not mistaken i think they are added/enabled by default.
Im pretty sure the container builder uses the default compiler which on instantiation takes the default pass config containing all the symfony provided compiler passes configured by default.
See:
Also a dump of
$container->getCompilerPassConfig()
in the core service provider shows the autowire passes etc included.Does this patch not need just to contain the yaml parsing tweaks?
Comment #13
alexpott@JParkinson1991 good point!
So we're not changing the passes that are done already so I would argue that we don't need to profile here.
Comment #15
weaverryan CreditAttribution: weaverryan commentedHi o/
I'm from the Symfony world, so apologies if I violate any etiquette unintentionally!
I'm playing with this currently, and it seems like it mostly works. The big blocker (and it *is* big) is not exactly to autowiring, but the nice new features like
_defaults
and service-auto registration don't work, because the YamlFileLoader seems like it's getting increasingly out of date. So, autowiring works, but it's not possible use _defaults in a module's services file to default *all* your services to autowire. And it's also not possible for a module to auto-register a bunch of services from one or more directories. This limits autowiring's usefulness.The other big piece is that core will need to add aliases so that *their* classes can be autowired. That's because autowiring works by reading the type-hint and then looking in the container for a service with that id. So, for example, to enable the config.factory service to be autowired, core could do this:
That will introduce a new "alias" to config.factory, which matches the type-hint that should be used.
So not a lot of work needs to be done to allow end-users to take advantage of the autowiring features, but some work *does* need to be done.
Cheers!
Comment #16
weaverryan CreditAttribution: weaverryan commentedSorry, to clarify, about the service aliases.
In Symfony 3, there is still the "magic" autowiring that works by trying to match 1 service with the specific type. That is deprecated, and users could choose to turn it off entirely: https://github.com/symfony/symfony/pull/26278/files
By adding aliases, core would be enabling users to use the new, non-deprecated way.
Cheers!
Comment #17
cilefen CreditAttribution: cilefen as a volunteer commented@weaverryan Thank you for those details.
Comment #18
AaronBaumanWhy do we need both "autowired" and "autowire"?
in these patches:
around line 300:
Comment #19
AaronBaumanSwitching to "autowire" passes my tests locally.
In other words, aside from minor changes in YamlFileLoader, autowire is already supported, per #2611816: Update to symfony 2.8
Functionally, all we need is a test, and support for service definitions as strings.
Comment #20
larowlannit []
should we use ::class here?
Comment #21
larowlan@weaverryan thanks for your session at Drupalcon, I added #3049524: Support _defaults in services.yml files for improved autowiring for adding
_defaults
andresources
supportI also added #3049525: Enable service autowiring by adding interface aliases to core service definitions for the alias support (Symfony 4).
Tagged all three with
Service autowiring
Comment #22
AaronBaumanupdated patch with larowlan's suggestions from #20
re #21: also check out parent Plan with links to a few more symfony-related updates #3021900: Bring new features from Symfony 3/4/5/6 into our container
Comment #24
AaronBaumanUnrelated test harness failure.
grrrr, simpletest!
Comment #25
joachim CreditAttribution: joachim as a volunteer commentedFrom the Symfony docs:
> The autowiring system looks for a service whose id exactly matches the type-hint: so App\Util\Rot13Transformer.
Doesn't that mean that autowired services can't be swapped for a different implementation, since the typehint is a class rather than an interface?
Comment #26
AaronBaumanThis was my first thought too, but aliases solve this problem:
So, here's how i *think* it would work in Drupal context:
* canonical service names do not change
* add aliases in services.yml to link interfaces with service names
* existing ServiceProvider mechanism (and implementations) of replacing services at run-time remains the same: we can still fetch the definition using the existing service names
slootjes's github has an example of aliasing a core drupal service using interface:
Comment #27
AaronBaumanAnd just to reiterate: Drupal's container already supports autowiring and aliases.
The patch in this thread is only adding some test coverage and docs for the existing feature.
Comment #28
BerdirI think one problem is that we have have a few types of services that share the same interface, we don't have one logger, we have many different logger services, each logging to a certain channel. And we have many different cache "bins". So unless I'm missing something, if you implement one of those then you can't use autowiring? Or we would need to add a unique interface for e.g. each cache bin.
Comment #29
kim.pepperNo, it uses parameter name and matches the service alias. e.g. $entity_type.manager => entity_type.manager
Comment #30
tim.plunkettExcept that it can't figure out EntityTypeManager correctly by default. Nor $database => Connection...
For example, here's what's needed on top of this patch to get LB to work:
That's not a reason not to do this, but it is something to be aware of and to document.
And to be ready for the additions needed in core.services.yml
Comment #31
Chi CreditAttribution: Chi for DXPR commentedSymfony documentation suggests using interface aliases to deal with this.
https://symfony.com/doc/current/service_container/autowiring.html#dealin...
Though we could simply not to use
autowire
for such cases.Imho, The benefits of autowiring and autoconfiguring for Drupal core are unclear. Too much magic just to spare a few lines in configuration files. We need to support this to align container implementation with upstream project, but we don't have to enable it for core services.
Comment #32
AaronBaumanThis is some good discussion: should we move to another thread?
The patch so far is ostensibly just test coverage for existing autowire support.
There's a planning thread here: https://www.drupal.org/project/drupal/issues/3021900
And a task to update core service aliases here: https://www.drupal.org/project/drupal/issues/3049525
If we want to continue discussion here, the IS should be updated
Comment #33
tim.plunkettExcept that if you try to use autowiring right now, you won't be able to compile your container. Because you need this change:
Also, enabling a new feature of core without discussing how we're going to manage the follow-ups feels irresponsible. I'm not trying to hold up this issue, I think autowiring is great.
Tagging for some FM input here.
Comment #34
alexpott@tim.plunkett is there something you're missing from #3021900: Bring new features from Symfony 3/4/5/6 into our container? - when I originally embarked on this I thought doing it in small steps would make it easier.
Putting my framework manager hat on I really want to get to the point where we can point at the Symfony docs for the 3.x container (or 4.x or 5.x whatever version we're on) as an example of how to do things.
Comment #35
tim.plunkettI think the _only_ thing I'm missing is how to go about adding the aliases to core.services.yml when there are ambiguities. Say I want to rewrite LB to use autowiring, as above, I'd also need to change core.services.yml. If that's fine, then I think we're good here! I just don't want to get into another situation where we have the _ability_ to do something without all the committers being on board when core devs actually try to do that thing.
Comment #38
Peter MajmeskuAutowiring is already working (https://www.drupal.org/docs/drupal-apis/services-and-dependency-injectio...), but what is not working, is the ability to autowire services without a cache rebuild. Like it's provided by plain symfony. Are you talking about this?
Comment #39
rodrigoaguileraI think Drupal requires a cache rebuild for any changes related to services.
The main points are already stated on #15. No _defaults and no aliases.
Inspired by a talk @weaverryan gave at Drupalcon I created a small module to alleviate those two points.
https://gitlab.com/upstreamable/drupal-autoservices
Comment #41
alexpott#30 looks good to me in terms of enabling autowiring on things like the database service. The only thing I'd change to that is to make the alias private so it's not in the container once it is built and everything wired up.
Comment #42
louis-cuny CreditAttribution: louis-cuny at Aïon Solutions commentedIn a team we tried to work with autowired services in d8
We got very frequently a very annoying problem
If user1 change a service definition or I'm not sure what on a service. Use2 take code changes with git pull and drush cr (and everything else) is broken
Because drush caches are somehow exploding
If someone want more explanation I could try to write done a how to reproduce.
But I just think like #39 pointed out, the service injection caching in drupal is not "autowire" functionality compatible
Comment #43
joachim CreditAttribution: joachim as a volunteer commentedI wonder if that is related to the problem I found that Drupal doesn't handle a module being moved[*}:
1. drush cr
2. move a contrib module's folder
3. drush cr
4. BOOM
I reported it as a Drush bug https://github.com/drush-ops/drush/issues/4661, but the maintainer there says it's to do with drupal_rebuild().
The problem is basically that the {cache_container} table is not emptied early enough.
[*] This is a fairly recent new problem, I think -- Drush used to be fine about modules being moved if you did a rebuild.
Comment #44
alexpottLet's fix drupal_rebuild() - that thing should not be touching the container until it is rebuilt. There's a issue for this already - see #2160091: drupal_rebuild() rebuilds container twice, since drupal_flush_all_caches() also rebuilds it
Comment #45
alexpottRerolled the patch because #22 no longer applies. Changed autowiring to autowired because autowired is in our dictionary already.
Comment #47
longwaveGoing to go ahead and mark this RTBC to try and get a framework manager review. This is a simple patch that starts to allow us to use autowiring, once this is committed we can explore in other issues what happens if we start adding interface aliases to core.services.yml and actually enabling autowiring across core.
Personally I would love to see the day where plugins can be autowired and we don't need to manually write create() methods any more, alongside PHP 8 constructor promotion this would make our code much more concise. Autowiring services to start with would seem to be a step towards that.
Comment #48
larowlanthere's some phpcs issues here
Addressing the FM review
In #35 alexpott is in favour of this so I think the only thing holding this up is the comment from @tim.plunkett in #36
I think to resolve that we need a policy documented somewhere, so there's no ambiguity.
Comment #49
longwaveYamlFileLoader ignores our phpcs conventions as it is a copy of Symfony code, so that actually looks correct to me for Symfony style. Agree that core.api.php needs rewrapping though.
What do we need to do to get a policy for interface aliases in place? Can we do it here or do we need a separate [policy, no patch] issue? To me I don't see any issue with adding interface aliases to core.services.yml where appropriate, nobody is forced to use them but it opens up the possibility of using autowiring. It might be worth trying an experimental patch where we autowire as much of core as possible in one go, just to see how many aliases we would need to add?
Comment #50
larowlan> It might be worth trying an experimental patch where we autowire as much of core as possible in one go, just to see how many aliases we would need to add
I think that would be a good idea
> What do we need to do to get a policy for interface aliases in place
I'll put it on the next committer meeting agenda
Having the example from the LB one above is probably a good start for discussion
Comment #51
alexpottFixing the docs - using a proper @link - which according to the docs is allowed to be longer than 80 chars - see https://www.drupal.org/docs/develop/standards/api-documentation-and-comm...
I think this patch is worth it even if we don't have an alias policy. Being able to autowire your own services in a module is useful - yes it will be even better when core services are autowirable but baby steps...
Comment #52
longwaveAgree that we could put this in without an alias policy, the code won't change whatever policy we adopt.
I already started on a full core autowiring patch, will try to finish that at some point.
Comment #53
webchickIt would really help in evaluating the impact of this change to have a draft change record of what the before/after code might look like for a given module.
Comment #54
longwaveAdded a draft CR with an example autowired service from core.services.yml: https://www.drupal.org/node/3218156
Comment #55
bradjones1Feedback on the change record (I was going to take a stab at this but you beat me to it!)
It seems to me this is either overstated or ambiguous. Ambiguous in so far this example adds as much boilerplate as it removes. As a developer myself who loves Symfony's autowiring compared to the status quo in Drupal, autowiring the container sounds great. But if I have to add aliases for services I don't manage (core) then it's unclear to me from the CR:
1) where I should add these aliases - in my own module or hacking core?
2) what the benefit is, if you must add the boilerplate?
I think this sets the groundwork for really cool stuff in contrib and core, but most of that coolness is postponed until core adds aliases for the short service names that are ubiquitous in the Drupal ecosystem, since we have no real convention for Symfony service autoconfiguration as well.
Overstated in so far as, it seems right now this is really mostly helpful for services that are solely or mostly dependent on services "I" define in custom land. Then I am responsible for handling the aliasing and can choose to take advantage of this if it actually saves time.
Thoughts on the above?
Comment #56
alexpott@bradjones1 the eventual plan would be to make core entirely autowired too and remove all of the clunky service names from the arguments section. The follow-up to add aliases for core exists already - see #3049525: Enable service autowiring by adding interface aliases to core service definitions
[EDIT: removed an aside that's not helpful and for clarity]
Comment #57
longwave@bradjones1 Please feel free to update the CR as you see fit. I think I was a bit over eager for the initial version but as linked above I was hoping that #3049525: Enable service autowiring by adding interface aliases to core service definitions would follow shortly after this issue and then core services would be autowirable and remove quite a bit of boilerplate. I think if that issue does get in then it would be worth using the same CR for both issues, rather than developers having to read about autowiring and then learning separately they can actually use it for core services.
Comment #58
bradjones1Thanks to you both; I updated the change record draft.
Comment #59
effulgentsia CreditAttribution: effulgentsia at Acquia commentedI think at least in theory, services can be set to NULL independently of autowiring. Therefore, can we pull this out into a separate issue and add a unit test for it in
Drupal\Tests\Core\DependencyInjection\YamlFileLoaderTest
? The corresponding Symfony commit is https://github.com/symfony/symfony/commit/28a1b5ac4779a5cac48b716e150b67..., which was all the way back in 3.3.Comment #60
effulgentsia CreditAttribution: effulgentsia at Acquia commented#33 added the "Needs framework manager review" tag in part because the patch at that time included changes needed to
YamlFileLoader
. However, that has since been committed separately in #2828099: Service container aliases do not work.#59 references the only remaining change in the #51 patch to
YamlFileLoader
, but per that comment, I think we can do that separately as well.Meanwhile, #41 states the opinion that we should make the aliases private. If we follow that opinion, then we don't even need to wait for #59's separate issue, as demonstrated in the patch attached here.
At which point, this issue just becomes about documenting and adding tests for what is already possible. I support both doing that in this issue, and also doing the proposed follow-up of #3049525: Enable service autowiring by adding interface aliases to core service definitions, so removing the "Needs framework manager review" tag.
Comment #61
bradjones1Nits:
s/explicit/explicitly
I believe Symfony always uses "autowire" without the hyphen?
s/interface autowired/autowired interface
It's probably worth adding a note here as to why these are marked private?
Comment #62
longwaveNW for #61, also removing "needs change record" as we have one.
Comment #63
effulgentsia CreditAttribution: effulgentsia at Acquia commentedAddresses #61 and does a little bit more docs tweaking as well.
Comment #64
longwaveI did wonder about typehint vs type hint vs type-hint but we use all three interchangeably so I think this is OK.
Nits fixed, this only adds documentation and a test, and there is further proof that we can autowire parts of core going on over at #3049525: Enable service autowiring by adding interface aliases to core service definitions so for me this is RTBC.
Comment #65
mondrakeis it worth addig return typehints?
why not assertInstanceOf()?
Comment #66
alexpott@mondrake / #65 good points - fixed them up.
Comment #67
alexpottAdded #3218968: Support NULL services in the container for #59
Comment #68
longwave#65 addressed, back to RTBC.
Comment #69
mondrakeSuperpick: this could use a
: void
return typehint.Comment #70
longwaveFixed #69, leaving as RTBC.
Comment #71
alexpottI really hope that we don't start adding : void to test all methods.
Comment #72
bradjones1I did a little more tweaking of the proposed CR given some issue grooming I'm doing tonight: https://www.drupal.org/node/3218156/revisions/view/12318678/12332962
Comment #73
effulgentsia CreditAttribution: effulgentsia at Acquia commentedAdding issue credit to reviewers.
Comment #75
effulgentsia CreditAttribution: effulgentsia at Acquia commentedEven though I co-authored parts of this patch, this patch is small, my changes were trivial, the patch has received plenty of review, and it sat in RTBC for plenty of time for people to kick it back if they wanted to.
Therefore, pushed to 9.3.x. Thanks, everyone!