Problem/Motivation
Using non-object services is deprecated as of Symfony 4.4. We create the app.root services as a string in our services file.
Proposed resolution
Replace the string services with container parameters.
Plan for 8.9.x
Commit this patch without the deprecations
Decide whether to convert usages of the services
Plan for 8.8.x
Decide whether to commit a patch adding the new parameters.
Remaining tasks
User interface changes
None
API changes
The app.root, app.root.factory, site.path and site.path.factory services are deprecated. There are new container parameters app.root and site.path that should be used instead.
Data model changes
None
Release notes snippet
The app.root and site.path services are deprecated. Use the app.root and site.path container parameters instead.
Comment | File | Size | Author |
---|---|---|---|
#91 | interdiff_89-91.txt | 443 bytes | vsujeetkumar |
#91 | 3074585-91.patch | 42.98 KB | vsujeetkumar |
#89 | interdiff_87-89.txt | 433 bytes | vsujeetkumar |
#89 | 3074585-89.patch | 42.41 KB | vsujeetkumar |
#87 | interdiff_85-87.txt | 1.86 KB | vsujeetkumar |
Comments
Comment #2
mikelutzLooking deeper, it's possible our service.yml implementation is fine, the deprecations I'm seeing might be coming from unit tests setting
$container->set('app.root', $this->root)
directly. I'm still looking into it.Comment #3
Gábor HojtsyThis is Symfony 5 compatibility.
Comment #4
Gábor HojtsyComment #5
alexpottThere might be some mileage in doing this in Drupal 8 because we're going to trigger these deprecations an awful lot. These are very very low level services for Drupal.
Some other thoughts:
app.root
probably should be should be a container parameter - determined during container build. If you move the code you should have to do a container rebuild. The rebuild can determine the new app.root.site.path
is determined by the request and should be part of that system - and so should be a service of some sort with the request stack injected. One massive problem we have is that site.path determines which modules can possibly by installed and where their code is and so this does things that the Symfony container is not really optimised for.\Drupal\Core\DrupalKernel::findSitePath
and\Drupal\Core\DrupalKernel::guessApplicationRoot
Comment #6
mikelutzLet's see what happens if we convert app.root to a container parameter.
I'm thinking that converting app.root and site.path might best be two separate issues.
Comment #7
mikelutzComment #9
mikelutzWhoops. :-)
Comment #10
alexpottI think we need to add the app.root to \Drupal\Core\DrupalKernel::getContainerCacheKey() to avoid the problems that @catch eludes to in #2328111-48: Replace most instances of the DRUPAL_ROOT constant with the app.root container parameter (thanks @chx for the archaeology).
Comment #11
Mile23app.root is generated by app.root.factory. The history is here: #2328111-102: Replace most instances of the DRUPAL_ROOT constant with the app.root container parameter
Re: #5:
The root path is actually determined by the front controller, which should inject it into the kernel.That's why it's a guess on the part of the kernel if it's not injected. See
DrupalKernel::__construct()
.In an ideal world, we'd deprecate
guessApplicationRoot()
, so that we require the root path to be injected, and it can be added to the container as a parameter during container build time. And the site path would be determined by a service provider that acts as a site router.But the scope here is make things work with Symfony 5, so maybe we don't live in that ideal world. :-)
Comment #13
Mile23So core.services.yml says this:
And then
AppRootFactory::get()
says this:...which is not an
SplString
.So if
SplString
satisfies our need for an object, then we should modifyAppRootFactory::get()
to return anSplString
.Comment #14
mikelutzInteresting. Neither locally, nor in any of my hosting environments do I have the SPL_Types php extension installed. Pecl errored out on the one attempt I made to install it locally. None of my goto online php testers support it either. I could go as far as trying to get it running in a container, but I suspect if I don't have it installed by default anywhere, then adding it in as a requirement is going to be problematic. I suspect it's not going to be available on testbot either.
Per the symfony documentation:
Comment #15
mikelutzThis should fix the last test in the previous patch iteration. I really feel like the parameter is the way to go, but
assuming this comes back green, we still need to address #10 and #11 and properly deprecate the app.root service, which I haven't yet done.Comment #16
mikelutzWell, that went the wrong way...
Comment #17
mikelutzOkay, injecting the app root into the container cache key, a couple basic tests, cleanup on the app.root service deprecation. I think this is ready for review now, depending on what breaks by changing the cache key or fixing the deprecations.
Comment #18
mikelutzUgh, that's not embarrassing at all, just pretend I never uploaded that one. Interdiff from #16
Comment #19
alexpott@mikelutz nice work this looks really good to me. Given that the container has different cache keys for different site roots I think we've got a good answer for the original concern as different site roots will use a different container.
Hmmm this raises an interesting concern - what happens if the containers can out-of-sync? Maybe we're doing this wrong. Maybe instead of adding it to the cache key we need to fail and point people at the rebuild process.
Comment #20
mikelutzAs in someone moves the site, gets a new container, something changes in the new container definition, and then they move it back and pulls the old container from the cache not realizing it needs a rebuild?
Comment #21
alexpott#20 yep that's a very clear expression of my concern.
Comment #22
mikelutzDo we need to bail and go to rebuild.php, or can we just catch it from the cached container definition before building the container and force a container rebuild if it has changed?
My main concern here is are we ever creating a temporary kernel with a guessed app.root somewhere I don't know about? If we did, then it could cause the cache to be continuously invalidated, Which I don't want to happen.
Comment #23
mikelutzRebase
Comment #24
alexpottI think going to rebuild.php is probably best. Automatically rebuilding could get a set in a very interesting state. Going to rebuild.php would at least allow someone to discover they are in this state.
Comment #25
mikelutzComment #26
mikelutzHere's an attempt at a Proof of Concept of another approach, switching them both to objects implementing __toString(). this would allow for minimal disruption, however, I've found a case in core where we use the value of those services as an array key, and php does not like using the new stringable objects as array keys. I casted the places I found in ExtensionDiscovery, but testbot might find more.
No interdiff, as it's pretty much start from scratch.
Comment #28
mikelutzI'm not sure this approach is going to be tenable, but let's see where it ends up.
Comment #29
mikelutzI'm blind...
Comment #30
mikelutzI can't win...
Comment #31
catchThis looks like a good minimal change to get rid of the deprecation to me.
Comment #32
mikelutzI also just proved that it fixes the APCu issue with the installer in SF4 #2976394-270: Allow Symfony 4.4 to be installed in Drupal 8
Comment #33
catchBumping priority again to critical, partly due to it fixing the test failure, partly because it'll be critical for Drupal 9 if not so much 8.
Comment #34
mikelutzComment #35
alexpottI think we should update the return documentation
Do we want to deprecate passing in a string $root and $site_path?
Need to update the return documentation
I'd be tempted to put this in core/lib/Drupal/Core/DependencyInjection as this exists for the purposes of the container. In and of itself I don't think we want usage of this case to spread.
Comment #36
mikelutzWRT #35-2, I think I'd rather just typehint the string in the constructor to automatically cast it, since we really want to be working with a string anyway.
Comment #37
alexpottMaybe the word object is now redundant so maybe StringableService - I mean in some ways both service and object are pretty meaningless but I think here the word service does have some meaning as this object is only mean to be used where we need a service on the container that represents a string.
+1 for the primitive type hints. It makes the code quite elegant.
Comment #38
mikelutzIt is a breaking change. In most cases, the object will simply be typecast to a string where needed. The big exception will be if the value happens to be used as an array key, in which case it will need to be explicitly cast.
Deprecating and replacing is probably the right way to go, but the practical considerations at this point (at least in terms of trying to deprecate in Drupal 8 and remove in Drupal 9) give me pause.
Switching to these objects from the existing strings might hard break a small percentage of code that uses these services.
Deprecating and replacing will require all code that uses these services to be updated.
There is also the time constraint of actually getting a patch written, RTBCed and committed by the 8.8 alpha deadline in two days.
So maybe we need to deprecate for removal in Drupal 10, sometime before 9.0.0 alpha. That solves the time constraint, and reduces the disruption, giving everyone much more time to update.
Comment #39
mikelutzHere's a first attempt at deprecate and replace via search and replace. I haven't yet really read through the resulting patch yet, I just wanted to throw it at testbot.
Comment #40
mikelutzWell, then there you go... We could probably avoid the factories and even the StringableService class and make custom new services that depend on the kernel, but that makes mocking/replacing them tougher.
Comment #41
mikelutzComment #42
andypostClosed old duplicate #2536012: The app.root and site.path services are invalid
Comment #43
larowlanFYI #3079810: core/help_topics directory does not work is adding another injection of app.root
Comment #44
Wim LeersThe issue summary does not explain the rationale for this rename?
🤔 It's odd to
app.root.factory
andapplication.root
. Weirdly inconsistent?Same remarks here.
Comment #45
neclimdulA lot of this is rehashes of the discussion on #2536012: The app.root and site.path services are invalid so linking it. The generic utility class over there makes a lot of sense as well instead of a one off. :shrug:
Comment #46
effulgentsia CreditAttribution: effulgentsia at Acquia commentedI'm wondering how to reconcile this issue with #3088246-20: [policy, no patch] How to handle Drupal 8.9.x deprecations (see also https://groups.drupal.org/node/535670). Is this something that's ok to punt to 9.1, or is it necessary to make 9.0 (and 8.9?) not invoke any Symfony 4.4 deprecations?
If it's necessary to make 9.0/8.9 not invoke Symfony 4.4 deprecations, is it reasonable to add the new services (and use them in core), but not yet mark the old ones as deprecated until 9.1?
Or, do we need to discuss making this (Critical) issue an exception to the policy in https://groups.drupal.org/node/535670?
Comment #47
catchWe haven't got a deprecation message for this listed in DeprecationListenerTrait::getSkippedDeprecations() so it seems like resolving it wasn't a blocker for moving to Symfony 4.4. Apart from the issue in #2536012: The app.root and site.path services are invalid which is a general bug, I'm not sure exactly what the issue is here so we definitely need an issue summary update.
I do think we should consider making exceptions for Symfony 4.4/5.0 compatibility issues for 8.9.x/9.0.x deprecations so that there's less change later on, just not clear whether this really is one or not.
Comment #48
BerdirWhen I run a test on D9 that expects a certain deprecation message then I get this output:
Took me a while to find them too but the problem is that they are dynamic, so we put it in \Drupal\Tests\Listeners\DeprecationListenerTrait::isDeprecationSkipped.
But I'm not sure if we should be doing that, as this suppresses it for all other string services too that custom/contrib code might add?
Comment #49
BerdirStarting with a reroll, I assume that this is a D9 issue now as there's no reason to fix this in D8 anymore, as we'll never update to symfony 4 there.
While rerolling, I did remove a large chunk (mostly comment) in \Drupal\KernelTests\Core\Theme\BaseThemeDefaultDeprecationTest that apparently isn't needed anymore to pass the test.
And of course a few changes were for code that is now removed in D9.
I also removed the skipped deprecation messages now and fixed a few new instances that use app.root. Still quite a bit of cleanup to do.
Comment #51
BerdirThat was an accidental leftover where I tested that the deprecation message is gone.
Comment #52
shrop CreditAttribution: shrop at Mediacurrent commentedComment #53
mikelutzIn. retrospec, those shouldn't have been dynamically skipped like that, although the plan was and is to get rid of all of these SF5 skipped deprecations early enough in the D9 cycle to surface those deprecations for contrib/custom code in plenty of time for D10.
Comment #54
alexpottSo here's one idea that builds on #3111008: Use native Symfony YamlLoader + Config and brings in the Symfony expression language.
However I have a few concerns about this - namely the additional complexity. I thought I'd leave it here in case it inspires any thoughts... I'm not a huge fan of StringableService - it seems way too parameter like for my taste.
This patch will fail at least a few tests but Drupal installs and at least 1 functional test passes.
Comment #56
alexpottSo #54 is possible :) - but as I said we're adding a tonne of complexity and at least the first get to each version of the expression needs work. It's cached afterwards in a static array cache - almost certainly possible to store the parsed expression in our db dumped container so that would save this work.
Comment #57
alexpottHere's a simpler solution using parameters again like #23 but importantly without the downsides. Because it sets the parameters on the compiled container dynamically prior to re-instantiating it.
Comment #58
alexpottIt seems there's an instance where we have a container without a kernel - interesting. But not the fault of this patch...
Comment #61
mikelutzI haven't reviewed the patch fully, but I wasn't a fan of stringable service either. At the time I was playing with it, it was an easy way to try to make the move in D8 quickly with minimal disruption. If we've moved on to fixing this in D9 as we should be at this point, a more robust and correct solution makes a lot more sense, as we have time to deprecate and fix the system properly like this.
Comment #62
alexpottSo delved into why
- see fails in #57 and the fix n #58. It's because we have extension objects stored in the FileCache and we use this when compiling the container. So right now in HEAD when compiling the container we fallback to DRUPAL_ROOT in
\Drupal\Core\Extension\Extension::__wakeup()
when it is called during initialising service providers as part of the very very early code in the container compilation. This occurs even before the container as the kernel service set! The funny thing is that we could cache these objects with the app root here because it is stored in the file cache and therefore cached by the absolute path. However we can't assume that all caches that store extension objects are keyed by something that represents the absolute path.Since the above is all happening in HEAD atm - I've changed change \Drupal\Core\Extension\Extension::__wakeup() in the patch to behave in the same way - ie. falling back to DRUPAL_ROOT at this point. I've also added docs so that we eventually try to remove \Drupal we'll be aware that we have to handle this in some way.
Comment #63
alexpottThe comment in the code in #62 is not my best. Trying again.
Comment #64
alexpottI've opened #3116858: Don't cache whole extension objects in FileCache to address the Extension object relying on DRUPAL_ROOT issue.
Comment #65
alexpottUpdating the issue summary.
Also I think we should consider backporting without the deprecations to 8.9.x. To that end we should reimplement the services to return the container parameter.
Comment #66
alexpottComment #67
mikelutz+1 to this approach, and the code looks good to me. I'll leave it to someone else to RTBC since I started down the parameter route in #23
Comment #68
heddnUsing the parameters makes sense to me. CR looks good. One small nit (can be fixed on commit):
"are dynamically provided" or something like that. Seems to be missing some words.
Comment #69
alexpottFixed the comment and went into a bit more detail why it's worth listed them there.
Comment #70
heddn+1 on the comment updates
Comment #71
catchComment #73
catchCommitted/pushed to 9.0.x, thanks!
Removing the deprecations for 8.9.x seems good, not 100% sure on the conversions.
Comment #74
catchMoving to needs work for the 8.9.x backport.
Comment #75
alexpottFilling out the 8.9.x and 8.8.x plans after discussing with @catch a bit.
I think we have two decisions to make:
Comment #76
andypostLooks it missing post update hook to cause rebuild of container
Comment #77
alexpott@andypost the container is keyed by \Drupal::VERSION - we don't need post updates to do that.
Comment #79
andypostIt still for backport, second option from #75 looks the most promissing
Comment #80
ravi.shankar CreditAttribution: ravi.shankar at OpenSense Labs commentedWorking on this.
Comment #81
ravi.shankar CreditAttribution: ravi.shankar at OpenSense Labs commentedHere I have added reroll of patch #69 for Drupal 8.9.x.
Comment #83
alexpottWe should not be deprecating these service in D8. None of this should change.
Comment #84
alexpottAlso I think that now we have 8.9.0 what we should backport has changed. I think we need to follow the plan for 8.8.x. I.e. I think we should consider adding the parameters there to make it easier for contrib to support 8.8.x -> 9.0.0.
We shouldn't change any existing services. All we need to ensure is that the container has the new parameters and they are correct.
Note given that the services still exist in D9 the other option is to close this issue. If you want to be be D8 and D9 compatible use the deprecated services.
Comment #85
ravi.shankar CreditAttribution: ravi.shankar at OpenSense Labs commentedHere I have made changes as suggested in comment #83.
We still need to address comment #84.
Comment #86
catchRetitling to make it clearer when scanning the issue queue that we're only discussing the backport here.
Technically it's too late to add the new services to 8.9.x, but we might want to make an exception and backport anyway.
Comment #87
vsujeetkumar CreditAttribution: vsujeetkumar at Srijan | A Material+ Company for Drupal India Association commentedFixing test, Please review.
Comment #89
vsujeetkumar CreditAttribution: vsujeetkumar at Srijan | A Material+ Company for Drupal India Association commentedFixed test, Please review.
Comment #91
vsujeetkumar CreditAttribution: vsujeetkumar at Srijan | A Material+ Company for Drupal India Association commentedFixed test, Please review.
Comment #92
longwaveIf we are to backport this now we can't make all these changes, we can only add the new services as in #84. But at this stage, perhaps this is just "won't fix" for 8.9.x and can be closed as fixed in D9?
Comment #93
catchAt this point I think we should just mark this fixed against 9.0.x - the window for supporting both 8.9.x and 9.1.x is already half gone since this was originally committed.
Comment #95
Wim LeersPublished the CR at https://www.drupal.org/node/3080612.
Comment #96
Wim LeersFYI: this is how you can be compatible with Drupal 8 and 9 while not triggering any Drupal 10 deprecation notices (i.e. be compatible with Drupal 8, 9 and 10):
Also added this to the change record.
(Developed in #3228162: Fix Drupal 9-only deprecation notices while retaining Drupal 8 compatibility.)