Skip to content

[Debug] Support @final on methods #21465

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

Closed
wants to merge 2 commits into from
Closed

Conversation

GuilhemN
Copy link
Contributor

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #21461 (comment)
License MIT
Doc PR

This adds support for @final on methods:

class Foo {
    /**
     * @final since version 2.0.
     */
    public function bar()
    {
    }
}

ping @nicolas-grekas

if ($parent) {
foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
try {
$prototype = $method->getPrototype();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is getPrototype needed? (I'm not very familiar with it :) )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discovered it today, it is fetching the parent method, which is what we need, we could do that manually but we would have to support several edge cases (for instance a method of the parent of the parent extended).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GuilhemN Very cool find. I've needed something similar in the past.

Copy link
Member

@nicolas-grekas nicolas-grekas Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice finding.
Then I'm wondering if we need it at all :)
Parent classes are always loaded before their children. This means each class should only look at methods that have $method->class === $class (let's use properties as much as possible, performance is critical here).
By populating the self::$finalMethods props, then children should have everything to decide quickly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas we would have to support the edge case I described above and maybe others i didn't think about, we would also have to parse every method doc blocks which is bad for perfs, imo it is better this way.

This means each class should only look at methods that have $method->class === $class

It looks like I forgot this check...

Copy link
Member

@nicolas-grekas nicolas-grekas Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parsing the docblock (doing an strpos preflight really) should be faster than trying getPrototype, then fall in the catch block, which involves instanciating a ReflectionException behind the scene - especially since this situation is going to happen quite often (since many methods don't have a "protototype")

Copy link
Contributor Author

@GuilhemN GuilhemN Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it and the code is clearer too so 👍

@nicolas-grekas nicolas-grekas added this to the 3.3 milestone Jan 31, 2017

if ($parent && isset(self::$finalMethods[$parent][$method->name])) {
// Fetch the real class declaring the parent method
$prototype = $method->getPrototype();
Copy link
Member

@nicolas-grekas nicolas-grekas Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you don't need this call if you compute the message when populating self::$finalMethods[$name][$method->name]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍

}

$doc = $method->getDocComment();
// No @final annotation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code says it :)

self::$finalMethods[$name] = $parent && isset(self::$finalMethods[$parent]) ? self::$finalMethods[$parent] : array();

foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
// It's not the declaring class
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ueless to me :)

@nicolas-grekas nicolas-grekas changed the title Support @final on methods [Debug] Support @final on methods Jan 31, 2017

$xError = array(
'type' => E_USER_DEPRECATED,
'message' => 'The Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod() method is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod.',
Copy link
Member

@nicolas-grekas nicolas-grekas Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be possible to turn this into an @expectedDeprecation annotation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I just followed the rest of the file, I guess it was done to not add the legacy group.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed!

@@ -173,6 +174,32 @@ public function loadClass($class)
@trigger_error(sprintf('The %s class is considered final%s. It may change without further notice as of its next major version. You should not extend it from %s.', $parent, self::$final[$parent], $name), E_USER_DEPRECATED);
}

// Not an interface nor a trait
if (class_exists($name, false)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should also apply to @final on the class above, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but I think we should wait for this to be merged before doing it, to avoid conflicts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact it's not a big deal, I can just rebase this PR if we do it in another PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@fabpot
Copy link
Member

fabpot commented Feb 12, 2017

Thank you @GuilhemN.

@fabpot fabpot closed this Feb 12, 2017
fabpot added a commit that referenced this pull request Feb 12, 2017
This PR was squashed before being merged into the 3.3-dev branch (closes #21465).

Discussion
----------

[Debug] Support `@final` on methods

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #21461 (comment)
| License       | MIT
| Doc PR        |

This adds support for `@final` on methods:
```php
class Foo {
    /**
     * @Final since version 2.0.
     */
    public function bar()
    {
    }
}
```

ping @nicolas-grekas

Commits
-------

1b0a6b6 [Debug] Support  on methods
@GuilhemN GuilhemN deleted the DEBUGMETHODS branch February 13, 2017 15:35
nicolas-grekas added a commit that referenced this pull request Mar 27, 2017
This PR was merged into the 3.3-dev branch.

Discussion
----------

[HttpFoundation] Fix bad merge

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

Bad merge, code had been removed in #21465

Commits
-------

af1eed9 [HttpFoundation] Fix bad merge
@fabpot fabpot mentioned this pull request May 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants