Problem/Motivation
Spin-off from #3231670: Suppress PSR-7 deprecation messages via Guzzle and #3161889: [META] Symfony 6 compatibility.
Symfony's DebugClassLoader added the capability to issue a deprecation message for return type hints.
On the surface this would be useful, however the implementation is very loose.
A deprecation message is issued when any method is overridden that has @return documentation, and there isn't an equivalent return type hint. I assume Symfony has decided to add return type hints to everything, but Drupal hasn't taken any such decision, so this would result in thousands of bogus deprecation messages for contrib as soon as Drupal runs on Symfony 5.4.
Additionally, we are already seeing bogus deprecation messages for Guzzle - PSR-7 interfaces have @return without return type hints, and Guzzle correctly implements those interfaces, but Symfony says that PSR-7 will add return type hints in its next major version, which is inaccurate - there's no new major release of PSR-7 at all (https://github.com/php-fig/http-message).
Steps to reproduce
Proposed resolution
1. Subclass and switch this off.
Remaining tasks
1. Open a follow-up to re-implement this, but with some kind of flag based on additional annotations (@return_type_hint or something?)
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #16 | 3232131-16.patch | 4.09 KB | daffie |
Comments
Comment #2
longwaveShould this be resolved upstream if Symfony is issuing deprecations for third party code, when Drupal is not involved at all?
Comment #3
catchComment #4
catch@longwave I think we should open an upstream issue, but we should probably decide what we want to ask them.
Also think we could subclass anyway even if things are fixed upstream, so that we can backport this to 9.x if possible.
Maybe something like this for an API:
Comment #5
xjmDoes it mean PSR-7 as in the FIG standard, or Guzzle's
psr7component which is weirdly its PSR-17 implementation in the 2.0 branch? See #3220220: Update guzzlehttp/psr7 to 2.1.0.Comment #6
xjmThere are return typehints in (e.g.) https://github.com/guzzle/psr7/blob/master/src/Request.php.
Comment #7
catchThe specific message that prompted this issue is:
(test run here: https://www.drupal.org/pift-ci-job/2174449)
Guzzle is implementing https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php - it has @type return but no return type hints.
The Stream class in Guzzle's master branch: https://github.com/guzzle/psr7/blob/master/src/Stream.php - has some return type hints but not for ::getMetadata()
Symfony is assuming that the next major version of anything that gets subclassed with have return type hints if there's are @return docs, which is only true for Symfony (or may not even be true there if they don't get to everything) and not everything else that runs through the classloader.
Comment #8
alexpottI think we might need to set SYMFONY_DEPRECATIONS_HELPER to something other than the default. Here's the docs for the env var.
See https://symfony.com/doc/current/components/phpunit_bridge.html#internal-...
Comment #9
alexpottApparently we can set
SYMFONY_PATCH_TYPE_DECLARATIONS=deprecations=0Looking at the docs:
So that'll stop deprecations from being triggered and we can leave SYMFONY_DEPRECATIONS_HELPER alone.
Comment #10
longwave#3231693: [Symfony 6] Add type hints to Drupal\Component\Annotation\Doctrine\SimpleAnnotationReader::getClassAnnotations(), ::getMethodAnnotations(), ::getPropertyAnnotations(), ::getClassAnnotation(), ::getMethodAnnotation() and ::getPropertyAnnotation() is another case where DebugClassLoader is reporting issues in third-party code (Doctrine) that it has no jurisdiction over.
Comment #11
xjmEven with the workaround in #9, this does seem worth reporting upstream as a weird and buggy default behavior.
Comment #12
alexpottI reached out to the Symfony community to discuss this issue. Here's the conversation:
alexpott Yesterday at 11:28 AM
@nicolasgrekas we’ve been working on identifying things we need to fix in Drupal for SF6 compatibility and we’ve run into new return type deprecations being triggered by the awesome new work you’ve done on the DebugClassloader - see https://www.drupal.org/project/drupal/issues/3232131#comment-14216127. Is there something we’re doing wrong? Maybe we’ve not got SYMFONY_DEPRECATIONS_HELPER set right but afaics all the return type deprecations are triggered from here https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/ErrorH... - so if we want to trigger them for our own code but not for vendor it’ll be tricky.
wouterj 23 hours ago
We're about to document this functionality. (see https://github.com/symfony/symfony-docs/pull/15802 )
The script is meant to trigger deprecations for any class: Let's push the community towards better typed systems.
We will also provide a utility to automatically add the correct return types to the project's methods (will be part of 5.4-dev within a couple days). This makes removing the deprecations effortless.
:tada:
wouterj 23 hours ago
Looking at the error, a slightly outdated 5.4-dev is used. The new error message is something along the lines of:
Method "Psr\Http\Message\StreamInterface::getMetadata()" might add "mixed" as a native return type declaration in the future. Do the same in child class "GuzzleHttp\Psr7\Stream" now to avoid errors or add an explicit @return annotation to suppress this message.
wouterj 23 hours ago
Sorry, last message (that I removed) was wrong. It should indeed trigger for Guzzle as well (but it is triggered as an "indirect" deprecation - meaning it's not a deprecation in your project).
Again, let's push the community towards better typing :slightly_smiling_face:
wouterj 23 hours ago
See https://symfony.com/doc/current/components/phpunit_bridge#direct-and-ind... for a bit more information about direct and indirect deprecations
alexpott 23 hours ago
@wouterj thanks for looking at this. I’m looking forward to the docs. We’ve got by with the default value for SYMFONY_DEPRECATIONS_HELPER since we added deprecation testing. When we’ve hit an indirect deprecation in our dependencies we’ve worked with the dependency to address it. The problem with the return type ones is that’ll be just too many to take that approach. (edited)
alexpott 23 hours ago
I think these return type deprecations are a different class than a real deprecation. There’s quite a bit of assumption that there will be a version of https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php that adds real return types. Also mixing them with real deprecations makes it harder to focus on stuff that you know will definitely happen.
wouterj 23 hours ago
Hmm, I see your point. But at the same time, it'll be required to allow any OSS package to move towards typing. Adding a return type is a hard BC break (as it immediately forces your users to add them as well).
This deprecation is warning you that you might experience this hard BC break at any moment. You can avoid it by adding the correct return type now, or you can explicitly say "I know what I'm doing" by adding the @phpdoc.
The patch-type-declarations script that will be introduced is able to automatically do both actions for you. So in the end, it's a matter of making the decision and running the script.
wouterj 23 hours ago
There’s quite a bit of assumption that there will be a version of https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php that adds real return types
Which isn't that unreasonable, given https://github.com/php-fig/log/compare/2.0.0...3.0.0 which caused Symfony to not support the latest Log PSR anymore :wink:
And that's all this deprecation is doing: warning you that this might happen, and letting you know that you can take action now to avoid that. (edited)
wouterj 23 hours ago
Anyway, I'm happy to hear opinions of others here :slightly_smiling_face:
alexpott 22 hours ago
I’m not sure - looking at the code in DebugClassloader - that it’ll make patches for vendor code. (which seems reasonable)
nicolasgrekas 22 hours ago
it will :slightly_smiling_face:
wouterj 21 hours ago
It'll trigger deprecations, but it won't patch the code (which is reasonable, as it's vendor code).
Package maintainers can use the tool on their packages to fix the deprecations.
:+1:
wouterj 21 hours ago
Alternatively, you can use SYMFONY_PATCH_TYPE_DECLARATIONS=deprecations=0. But let's focus on adding types (and explicit opt-outs) in the PHP community :slightly_smiling_face:
wouterj 13 hours ago
I see you know recommend the latter. That's really the unsafe way.
Doing SYMFONY_DEPRECATIONS_HELPER='max[direct]=0&max[total]= is much much much safer.
Alternatively, you can also create a baseline for all the dependency deprecations: https://symfony.com/doc/current/components/phpunit_bridge.html#baseline-...
But the best scenario is to just create an issue about this in the dependency that trigger the deprecation. Especially with Guzzle I'm quite confident that a quick decision and fix can be made (edited)
nicolasgrekas 33 minutes ago
Making patches to vendors is definitely the way to go. I've already done so for deps of Symfony (aka Doctrine repos libs) (edited)
Comment #13
alexpottThe docs for the new feature are now online - https://symfony.com/doc/5.4/setup/upgrade_major.html#upgrading-to-symfon...
Comment #14
longwaveShould we just add deprecation skips for upstream packages that are not our responsibility? While Symfony has good intentions here, there is no guarantee that the return types declared in docblocks are 100% correct, especially in third-party code that isn't even planning on adding return types any time soon.
Comment #15
wouterjFor what it is worth: We have been publishing more information about typing in Symfony 6 and how we envision preparing the PHP OSS community for more typing and Symfony 6 compatibility: https://symfony.com/blog/preparing-your-apps-and-bundles-for-symfony-6
We have also been submitting PRs to various OSS libraries that caused issues (more specifically, I checked the projects I'm working on and submitted PRs to the packages causing type deprecations). These were all merged pretty quickly, among them is the one mentioned as the cause for this issue: https://github.com/guzzle/psr7/pull/444
Comment #16
daffie commentedThe patch for suppression deprecation warnings from Symfony 5.4 for deprecations that are from non Symfony packages.
If we want to do a release D9.4 with Symfony then this is needed in D9.4 too.
The code is from #3161889-188: [META] Symfony 6 compatibility from @longwave.
Comment #17
catchThis looks great. Makes sense to commit it to 9.4.x as well as 10.0.x to keep things in sync if nothing else.
Comment #18
larowlanAdding issue credits
Comment #20
larowlanCommitted 36f09c0 and pushed to 9.4.x. Thanks!