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
Since Symfony 5.2, services in the container are fully private by default and the setPrivate()
method has been deprecated.
Steps to reproduce
Update symfony/dependency-injection to 5.2 and see the tests fail.
Proposed resolution
Force services to public if not otherwise specified at container build time.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Comment | File | Size | Author |
---|---|---|---|
#35 | 3187074-35-sf4.patch | 18.98 KB | daffie |
#35 | 3187074-35-sf5.patch | 24.68 KB | daffie |
#35 | interdiff-3187074-33-35.txt | 1.37 KB | daffie |
Comments
Comment #2
longwaveThe first patch just upgrades to symfony/dependency-injection:5.2 to find the failures.
The second patch has various fixes extracted from #3161889: [META] Symfony 6 compatibility to get the tests running, let's see how this fares on the full set.
Comment #3
longwaveComment #4
longwaveOne more fix for some kernel tests.
In the first patch in #2, run-tests.sh can't even boot without a fatal error:
Comment #6
longwaveFixed two more tests.
No idea what is up with InlineBlockPrivateFilesTest or InlineBlockTest.
Comment #8
Gábor HojtsyMy understanding is the existing code works fine with Symfony 5 other than deprecation notices, so this would be for Symfony 6 compatibility.
Comment #9
longwaveNo, it doesn't work at all - we get fatal errors before we even run any tests with the 5.2 dependency injection component, see the first patch in #2.
Comment #10
longwaveFigured out a fix for the inline block tests, however I'm not sure how we can notify contrib that they might need to add setPublic() calls to their service providers, or if we can do this in another way.
Comment #11
Gábor HojtsyRe #9 how is this considered backwards compatible within Symfony? Are we doing our own sauce too much again? Either way, restored the title and added a Symfony 5 tag too. Sorry for the noise.
Comment #12
longwaveSymfony services have been private by default since 3.4, but Drupal still uses public by default - we need public services because we directly access the container in lots of places such as plugin factory methods.
There has been this sort of halfway house BC layer in Symfony where services could be declared both public and private and that still worked for us, but that layer has now been removed in Symfony 5.2 and this is what we are running into.
Comment #13
alexpottI think we need a decent comment here.
Unrelated change - we should file an issue to fix all of core for this... as PHPStorm changes this by default.
Is this change necessary? I don't think it should be. I think we need some functionality in \Drupal\Core\DependencyInjection\ContainerBuilder::setDefinition() still.
Comment #14
longwaveRe #13.3 the problem I had here is that setDefinition() could assume public by default, but then what if a caller really wants to add a private service? How would setDefinition tell the difference between a definition that should be public-by-omission vs explicitly set private? The Definition object belongs to Symfony so we can't add any extra flags to detect this.
This is the same reason ContainerBuilderTest has to change, I'm not sure this is right either but I don't see what the alternative is.
Comment #15
andypostRe 14 I bet the
setPrivate(false)
is proper way to disallow private services, but not sugye that anyone using private services in contribComment #16
longwaveAddressed #13.1, #13.2 in this patch.
#13.3 is still outstanding as per #14 and I am not sure what to do.
#13.4 we not changed YamlFileLoaderTest here, which proves that private services remain private, shortened version:
Comment #17
longwaveWrong interdiff above, sorry - correct one is here.
Comment #18
longwaveRe #15 the
setPrivate(false)
used to work until SF 5.1 but nowsetPrivate()
is fully deprecated in SF 5.2:The code we had before in our
setDefinition()
implementation:This does nothing, because
setPrivate()
now just does this:ie.
setPrivate(false)
now just callssetPublic(true)
, which we already checked in the if statement.Comment #19
Gábor HojtsyLooks good, even though we would need and should commit a composer-update-less version.
We only need this due to the composer update to prove green pass right? Otherwise does not look related.
Comment #20
alexpottI think this is the best we can do. We need a really good CR that states if you're using ServiceProviderInterface or ServiceModifierInterface to register a new definition then in order to preserve BC you need to make the service public. If you're only usages are via the container then leaving it as private is a good thing.
At some point we are going to need to bite the biscuit and use auto-wiring and use Symfony's Service Subscribers & Locators to handle \Drupal and all the service location we do in core. It's gonna be a tonne of work.
+1 #19 I don't think the skips should be part of this patch - that can be addressed by #3187857: [Symfony 6] Service deprecation messages require more fields. There's no point adding skips for things that are not addressed.
Don't we need to keep this in but skip the Sf5 / 6 deprecation?
Comment #21
Gábor HojtsyComment #22
longwaveHad a thought that I will try out in a minute: by default Definitions are both public and private by default in SF4, which is why we need that explicit
setPrivate(FALSE)
. But we could detect this default and warn the caller via a deprecation that in Drupal 10 they will explictly need to callsetPublic(TRUE)
if they want a public service; if they make this change it would remove the deprecation assetPublic()
does thesetPrivate(FALSE)
part for us.Comment #23
longwaveSo I tried the idea in #22 and added a deprecation that is triggered only when isPublic() and isPrivate() are both true, which as far as I can see only happens with definitions that haven't explicitly been declared either way in SF4 only.
The attached two patches are the same underlying set of changes, the SF4 patch stays on SF4 and includes a legacy test for the new deprecation, the SF5 patch upgrades to SF5 but doesn't include the legacy test as the new deprecation is never triggered (and in the D10 cycle we would remove this anyway).
There is now a failure in OptimizedPhpArrayDumperTest that I don't understand and also
setDefinition()
is still being called without public/private being set properly from somewhere, but leaving this for a bit and will look at it fresh another day unless someone else gets there first!Comment #25
daffie CreditAttribution: daffie commentedFixed the test failures for Symfony 4. The test failures for Symfony 5 are not related to what we are trying to fix in this issue.
Comment #27
daffie CreditAttribution: daffie commentedSince this blocks Drupal 10 it should be marked critical.
Comment #28
longwaveWhy is this failing in the SF5 patch? Ideally I think both patches should be green here.
Comment #29
daffie CreditAttribution: daffie commentedYou are right @longwave, and in such a world I would be an expert in working with composer. Only I am not, therefore the patch is failing for Sympony 5. Sorry.
Comment #30
Gábor HojtsyReparenting to the Symfony 5 meta.
Comment #31
daffie CreditAttribution: daffie commentedThe patch for Symfony 5 is the same as the one for Symfony 4, only with the
symfony/dependency-injection
set to ^5.2 and with the deprecation message test forsetDefinition()
removed. In Symfony 5 a service is either public of private, not both.For the persons who worked on making it difficult to install Drupal with the wrong dependencies, congratulations it works.
Comment #32
longwave@daffie Thank you so much for working on this. This is looking great now, just one minor improvement:
Here I think we could move
->setPublic(TRUE)
to the helper method?Comment #33
daffie CreditAttribution: daffie commented@longwave: Thank you for your review!
For 32: Fixed.
Comment #34
longwaveThanks! Good news that this is green now on both Symfony 4 and 5. Sorry for not picking this up before, but I think as per #19/#20 we need to remove the deprecation skips from the Symfony 4 patch that is to be committed? Once that is done this is RTBC as far as I can see.
Comment #35
daffie CreditAttribution: daffie commentedFor #34: Fixed.
Comment #36
longwaveI think the SF5 patch will fail now due to the new deprecations, but assuming the SF4 patch is green that one is good to go.
Comment #39
catchAgreed this seems like the best option for now.
Committed 91a4336 and pushed to 9.2.x. Thanks!