Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ErrorHandler] trigger deprecation in DebugClassLoader when child class misses a return type #30323

Merged
merged 2 commits into from Aug 14, 2019

Conversation

fancyweb
Copy link
Contributor

@fancyweb fancyweb commented Feb 20, 2019

Q A
Branch? 4.4
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #30123
License MIT
Doc PR TODO

I wanted to push something to show the advancement and get feedback.

I pushed two versions : one with dedicated functions for code clarity (DebugClassLoader.php) and one withtout (DebugClassLoader___.php). It would be nice if some people with Blackfire could compare the performances.

So let's be clear, we are never gonna be able to cover all cases! We can however cover the vast majority.

Current non covered cases and problems :

  • We assume that if there is more than 2 returned types, we cannot do anything. Even if it could technically be possible.
  • We assume that any returned type that doesn't fit our "returnable" types list is a class. We don't check at all if this class actualy exists.
  • We don't handle spaces in types. The types stop at the first space.
  • That means we don't handle (yet) the callable type with spaces (cf DebugClassLoader doesn't support Amphp typed callables #29969)
  • Vendor code extending other vendor core triggers the deprecations 😕

@stof
Copy link
Member

stof commented Feb 20, 2019

* Vendor code extending other vendor core triggers the deprecations confused

That's totally fine IMO. We already do that. We should maybe only whitelist things from same vendor (or maybe same namespace) as we do in some other places.

@fancyweb
Copy link
Contributor Author

Yes we already do that for all deprecations but they are rarely triggered by vendor code unlike these new ones. For example, on the homepage of the Symfony demo, there is 3 deprecations atm. With the "return types" deprecations, there are 34.

@stof
Copy link
Member

stof commented Feb 21, 2019

The thing is, our deprecation reporting tool is meant to tell you when you are ready to migrate to the new major version safely (when there is no deprecation anymore). If some vendor you use rely on the deprecated stuff, you cannot migrate.

Other deprecations are also regularly coming from vendors, and you need to know about them too.

@fancyweb
Copy link
Contributor Author

I'm fine with that. I just wanted to highlight the fact that if this feature ends up being merged, people are gonna find A LOT MORE of vendor deprecations than what they are used to.

@nicolas-grekas nicolas-grekas added this to the next milestone Feb 21, 2019
@fancyweb fancyweb force-pushed the handle-return-types branch 3 times, most recently from e791814 to 924141c Compare March 6, 2019 13:59
@fancyweb
Copy link
Contributor Author

fancyweb commented Mar 6, 2019

Just updated my code to handle the cases from the official PHPDoc types pages https://docs.phpdoc.org/guides/types.html

@stof
Copy link
Member

stof commented Apr 29, 2019

Triggering a deprecation based on the fact that the interface has @return is wrong. This phpdoc does not imply that the next version will switch to using an actual return type. That would trigger far too many useless deprecations.

@nicolas-grekas
Copy link
Member

Triggering a deprecation based on the fact that the interface has @return is wrong

wrong is too strong. I agree with the risk you describe. What we're seeking for here is a way to warn ppl they should add return type hints before we add some ourselves.

Let's do as in #30987: keep the deprecation only for Symfony\*. I would also suggest triggering it on PHP 7.2 only so that ppl can actually do something about the notices.

@fancyweb
Copy link
Contributor Author

@nicolas-grekas Why PHP 7.2? Don't you mean the lowest PHP version supported by each returnable type ? eg : 7.1 for void, 7.0 for string, etc.

@nicolas-grekas
Copy link
Member

Because only PHP7.2 has the require type variance that allows adding the return type hint without generating a fatal error when the base interface doesn't.

@derrabus
Copy link
Member

Because only PHP7.2 has the require type variance that allows adding the return type hint without generating a fatal error when the base interface doesn't.

PHP 7.1 already allows you to do that. https://3v4l.org/5lN5J

@fancyweb
Copy link
Contributor Author

fancyweb commented Jun 26, 2019

That's what I thought too.

BTW, the help I need here would be to compare both proposed implementations (dedicated functions vs nested conditins) with Blackfire to check the performance impact (since I don't have a subscription myself).

Also TODO :

  • add the check on Symfony\* and the PHP version

@fancyweb
Copy link
Contributor Author

Just rebased + took last comments into account.

The deprecations are triggered for any classes that extends / implements a Symfony class / interface and if the PHP version is >= 7.1. 👀 But isn't this runtime check useless since the minimum required PHP version by the component is currently 7.2.9?

The failed tests caused by those new deprecations can partially help us to add strict return types in the code base.

@derrabus
Copy link
Member

derrabus commented Aug 13, 2019

This means bundles should be deprecation free before the bump.

Exactly. And that means the bundles must have adopted the return types added with Symfony 5, because they're going to break with the bump otherwise. A bundle creator can add return types and at the same time remain compatible with Symfony 4.4 (example). So the path to get the bundle deprecation-free is to add the necessary return types.

@derrabus
Copy link
Member

If the IDE copies the docblock instead of generating an {@inheritdoc}, that's where the bug is.

We actually can find this pattern in the Symfony codebase as well. I've fixed the Twig bridge as an example: #33145.

fabpot added a commit that referenced this pull request Aug 13, 2019
…tdoc (derrabus)

This PR was merged into the 3.4 branch.

Discussion
----------

[TwigBridge] Replaced plain doc block copies with inheritdoc

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | preparation for #30323
| License       | MIT
| Doc PR        | N/A

Commits
-------

e50d3bc [TwigBridge] Replaced plain doc block copies with inheritdoc.
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Aug 13, 2019

that means the bundles must have adopted the return types added with Symfony 5

That's only for packages that rely on some Symfony type. If a dep depends on Symfony, we can assume they don't allow v5 in their composer.json file. Nothing will break until this is relaxed, because v5 will just not be installable yet. How do you know? By trying and seeing composer refuse to upgrade to v5, composer why for the details. The next question is: when does one reach this point in the migration process? Only one possible answer: when you're deprecation-free, because it's pointless to try v5 when you're not yet. But then, the dep that blocks us doesn't use return-types yet! Because if it were, v5 would be allowed for sure! You see the chicken and egg situation?

Now, let's take the group of deps that don't rely on Symfony. Basically, they don't care with what we're doing. So they won't do anything that we could ask them to do, because that'd be added maintainance burden for them, or just because getting a message is hard, provided we have the reach. And that's fair. I don't believe adding an opt-in configuration would work for any dep past the one we have direct influence over.

But I can bet on the fact that deps do want to use types, especially if they already use docblock annotations. I'd bet on this because return-types help maintaining a codebase.

So: yes, there will be docblocks that won't be accurate. But that'll be caught immediately, by just looking at the failure a real return type will trigger. And btw, ppl don't need us to add real return types right now. They're already allowed to do so.

We can help by showing which types would be needed where.
This is exactly what the strategy in this PR provides.

It's true that this won't report annotations that duplicate a parent @return. But that's not an issue if we ship the very next tool we're going to need for the Symfony codebase: one that turns @return into return type declarations.

This PR is creating this forward path, which, I believe, solves all issues mentionned above and creates the smoother process I can think of.

@nicolas-grekas
Copy link
Member

PR is green BTW :)

@Tobion
Copy link
Member

Tobion commented Aug 13, 2019

My example a direct userland implementation of a Symfony interface that will 100% break on upgrade. How's that case undetectable?

Because you're forgetting about third party bundles that do need an upgrade path too: we need to provide them a way to be deprecation-free with 4.4. Annotations are the way.

I think ignoring all user-land classes that have auto-generated phpdocs based on the parent makes this new feature useless in alot of cases. Such code exists alot, probably because that is the default behavior of phpstorm. I don't think we can realistically ignore this.
So I don't think return phpdcocs are the right solution to opting-out of these warnings. Can't we simply ignore all missing return types in vendor libraries? At the end this feature is for end-users and missing return types are irrelevant in vendors. Hm thinking about this, if people split their apps in bundles, then their vendors are not irrelevant. Maybe we can do a combination of both? return phpdocs in vendors are ok for opting out but they are not enough for non-vendor classes?

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Aug 14, 2019

return phpdocs in vendors are ok for opting out but they are not enough for non-vendor classes?

I think that's a good plan. Because there is no simple way to decide vendor vs non-vendor at runtime, I think we're going to have to ask ppl to declare their namespace when they're at this step in the migration process.

This means the 1st step would be this PR, exactly as is now. Then, we would ask ppl to run a command / their tests again, but this time with an env var that would give their App\ prefix.
This could either just report the missing spots, or patch directly the source.

We're going to need this tooling too, to migrate this very codebase. Let's see what the next PRs will provide on this path.

@nicolas-grekas
Copy link
Member

I'm now merging to unlock progress on the topic. Let's keep the discussion open, we'll continue fine-tuning the upgrade path until stable is out.

@nicolas-grekas
Copy link
Member

Thank you @fancyweb.

@nicolas-grekas nicolas-grekas merged commit aa338c8 into symfony:4.4 Aug 14, 2019
nicolas-grekas added a commit that referenced this pull request Aug 14, 2019
… when child class misses a return type (fancyweb, nicolas-grekas)

This PR was merged into the 4.4 branch.

Discussion
----------

[ErrorHandler] trigger deprecation in DebugClassLoader when child class misses a return type

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #30123
| License       | MIT
| Doc PR        | TODO

I wanted to push something to show the advancement and get feedback.

I pushed two versions : one with dedicated functions for code clarity (DebugClassLoader.php) and one withtout (DebugClassLoader___.php). It would be nice if some people with Blackfire could compare the performances.

So let's be clear, we are never gonna be able to cover all cases! We can however cover the vast majority.

Current non covered cases and problems :
- We assume that if there is more than 2 returned types, we cannot do anything. Even if it could technically be possible.
- We assume that any returned type that doesn't fit our "returnable" types list is a class. We don't check at all if this class actualy exists.
- We don't handle spaces in types. The types stop at the first space.
- That means we don't handle (yet) the callable type with spaces (cf #29969)
- Vendor code extending other vendor core triggers the deprecations 😕

Commits
-------

aa338c8 Import return annotations from vendors
10fc13e [ErrorHandler] Handle return types in DebugClassLoader
@fancyweb fancyweb deleted the handle-return-types branch August 14, 2019 13:42
@fancyweb
Copy link
Contributor Author

Thank you @nicolas-grekas 😅 😁

@mdlutz24
Copy link
Contributor

This is pretty exciting, and we have been playing around with running Drupal on the 4.4-dev branch to see how to manage this.

We are seeing a few complaints where the Symfony interface @return is ?object, and our implementation uses {@inheritdoc}. Since object is not a php return type, we can't type hint it. I suppose we could quiet the deprecation by adding an @return ?object of our own, but our coding standards don't like it. What's the expected best practice here?

@derrabus
Copy link
Member

Since object is not a php return type

It is since php 7.2 🤓

@nicolas-grekas
Copy link
Member

@mdlutz24 nice to see early interest from Drupal :) @return object|null could be the way to go, you might want to update your CS rules to account for this if that makes sense. Since this PR is merged, I invite you to continue discussing on #33235, which seems the most appropriate to me. We're at a quite early stage on the topic and things are open for changes.

@mdlutz24
Copy link
Contributor

@derrabus you know that's true and that might be enough, actually. The branch where we actually have to fix this will end up with 7.2 or 7.3 as a minimum, though my test container is 7.1 at the moment.

@mdlutz24
Copy link
Contributor

@nicolas-grekas it's very early for us too, but as we get closer to branching Drupal 9, we've been trying to be more proactive and involved in SF discussion and development, and running regular tests against symfony -dev instead of waiting for releases. We are definitely interested in helping fine tune this, and hopefully using it or a technique like it ourselves to prep our contrib modules for eventual return type hinting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Help Wanted
Nicolas'
Development

Successfully merging this pull request may close these issues.

None yet

9 participants