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

Add NoUnneededFinalMethodFixer #2972

Closed
wants to merge 7 commits into from

Conversation

Slamdunk
Copy link
Contributor

@Slamdunk Slamdunk commented Aug 9, 2017

 final class Foo {
-    final public function foo() {}
-    final protected function bar() {}
-    final private function baz() {}
+    public function foo() {}
+    protected function bar() {}
+    private function baz() {}
 }

@keradus
Copy link
Member

keradus commented Aug 9, 2017

Not sure about the rule itself.
Same way, one could say that public in public function is unneeded

*/
public function isCandidate(Tokens $tokens)
{
return $tokens->isAnyTokenKindsFound([T_CLASS]) && $tokens->isAnyTokenKindsFound([T_FINAL]);
Copy link
Member

@julienfalque julienfalque Aug 10, 2017

Choose a reason for hiding this comment

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

You can replace these isAnyTokenKindsFound() calls with a single isAllTokenKindsFound() call.

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, thank you

@Slamdunk
Copy link
Contributor Author

Not sure about the rule itself.
Same way, one could say that public in public function is unneeded

@keradus you are right; my reasoning is:

  1. I want a standard, be it with or without final; having final classes with half final methods and half non-final methods is nonsense

  2. What standard should we pursue?

Well, visibility needs to be explicit because in this example:

class A {
    protected function foo() {}
}
class B extends A {
    function foo() {}
}

PHP has strictly rules about the result, but the developer could be confused by what should be the outcome (hit: B::foo() becomes public)

On the other side, final keyword in a final class has no interaction with possible parent classes and as far as I can see there is no misunderstanding of what the outcome is, that is the same with or without final.

So my choise is to raise verbosity for visibility, and to reduce verbosity for final

@keradus
Copy link
Member

keradus commented Aug 10, 2017

why raising verbosity in one place and reducing in another? this looks not consistent

@Slamdunk
Copy link
Contributor Author

Because as I said in one context it helps, in the other it doesn't.

BTW I can make it configurable if you prefer.

@keradus
Copy link
Member

keradus commented Aug 10, 2017

I agree that some enforced standard to have about this is nice.
While final methods in final class could look superfluous, they follow defensive approach:
when having final class + final methods, if one drop final from class, then he need to decide which methods he want to open as well. comparing to final only on class level - droping one final would open every method.

@Slamdunk
Copy link
Contributor Author

This is a good point.

In our business this can't happen because we adopt two harsh custom fixers:

  1. All non-abstract classes MUST be final
  2. All abstract classes MUST have public methods marked ad final

To reduce inheritance nightmares and increase template method pattern.

I told you this not advertise something, but to drop some thoughts about the topic.
We would never benefit from a fixer that marks methods as final in a final class.

@keradus
Copy link
Member

keradus commented Aug 10, 2017

FYI: #1389 ;)

If you go extra defensive like that, it's cool and not needed for you indeed.
But when one don't follow that approach and has only some of the class marked as final, he would benefit from it.

@Slamdunk
Copy link
Contributor Author

Knew #1389 👍 but never used because it requires an annotation to be written; I use PHP-CS-Fixer in CI to get rid of lazy developers 😸

It seems we all understood the topic, but what about the solution?
Is it ok for you to make it configurable, with default behaviour as adding the final keyword?
If yes, @julienfalque please help me to find a better fixer name, you words master 🙏

@keradus
Copy link
Member

keradus commented Aug 10, 2017

To be honest, not sure... yet ;)
I do see the value of the fixer, I find your reasoning valid in your case for sure.
On the other side, I want to promote only good, generic rules. That's the reason why we don't have option to remove public before function.
Not claiming this rule in current form is bad, don't take it like that. I just need to think a bit more about it ;)

Other opinions are welcome of course ;)

@julienfalque
Copy link
Member

julienfalque commented Aug 11, 2017

IMO this PR can be merged as is. I don't see the benefits of a defensive approach with final keyword on each method when the class itself is final. I would not make the fixer configurable, and its name looks good to me :)

@keradus
Copy link
Member

keradus commented Aug 18, 2017

btw, if you would add it to .php_cs.dist, what will happen ?


$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if ($prevToken->isGivenKind(T_FINAL)) {
$this->fixClass($tokens, $classOpen, $classClose);
Copy link
Member

@keradus keradus Aug 18, 2017

Choose a reason for hiding this comment

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

you basically traverse whole tokens of single class twice - one to find it's end, one to fix it's content.
not needed, you may determine it's end while fixing it.
pass only $classOpen here,
then, in fixClass, if you see nested {, it's some method body opening, you skip whole block.
if you find }, it's close } of class. voila, you just found a $classClose, no need to pass it to fixClass method, no need to determine it in applyFix, and you could return it via fixClass so you could still do the jump $index = $classClose

Copy link
Contributor Author

Choose a reason for hiding this comment

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

New logic applied.

FYI, this code was copy-pasted from ProtectedToPrivateFixer, and the algorithm is a @SpacePossum optimization: Slamdunk#1

After this, we should refactor other fixers too.

@Slamdunk
Copy link
Contributor Author

btw, if you would add it to .php_cs.dist, what will happen ?

Nothing because the only final method here is this one, and is legitime:

https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/63661f3add3609e90e4ab8115113e189ae547bb4/src/AbstractFixer.php#L66

@keradus
Copy link
Member

keradus commented Aug 21, 2017

please add it to .php_cs.dist file

@Slamdunk
Copy link
Contributor Author

Already done

@keradus
Copy link
Member

keradus commented Aug 21, 2017

then, my other concert is still in place ;)

@Slamdunk
Copy link
Contributor Author

I'm working 🏃

*/
private function fixClass(Tokens $tokens, $classOpenIndex, $end, $isFinalClass)
{
for ($index = $classOpenIndex + 1; $index < $end; ++$index) {
Copy link
Member

Choose a reason for hiding this comment

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

end is never reached.
then, when you have open for class, end for file is weird ;)

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 end is never reached, but without it we have to use a while and I dislike loops without a declared end, it is far easier to end in an infinite loop.
I'll change the name in the next commit.

Copy link
Member

Choose a reason for hiding this comment

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

if $end === $tokens->count(), no need to pass duplicated data

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

*
* @return int
*/
private function fixClass(Tokens $tokens, $classOpenIndex, $end, $isFinalClass)
Copy link
Member

@keradus keradus Aug 21, 2017

Choose a reason for hiding this comment

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

if class is final we could just jump over whole class, right? no need for iterating it manually, jumping over single methods and so

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. I just tried to write less code, but it seems is less readable.

'<?php final class Foo { public function foo() {} } abstract class Bar { final public function bar() {} }',
'<?php final class Foo { final public function foo() {} } abstract class Bar { final public function bar() {} }',
],

Copy link
Member

Choose a reason for hiding this comment

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

Is this empty line intended?

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 find it usseful to separate to-be-changed cases from dont-touch ones

@julienfalque julienfalque added this to the 2.5.0 milestone Aug 21, 2017
@keradus keradus added the RTM Ready To Merge label Aug 21, 2017
@SpacePossum
Copy link
Contributor

@keradus can you look at the failing utest? https://travis-ci.org/FriendsOfPHP/PHP-CS-Fixer/jobs/266908788#L757 (not sure who to ping)

This PR looks good to me :)
I would however like to see a utest to be added with multiple candidates in one test/file (like <?php class A {} class B{}). Pretty sure the fixer will work correctly, but the added test will prevent regression.

@keradus
Copy link
Member

keradus commented Aug 21, 2017

you already approved a fix for that failure #2989

@keradus
Copy link
Member

keradus commented Aug 21, 2017

Thank you @Slamdunk.

keradus added a commit that referenced this pull request Aug 21, 2017
This PR was squashed before being merged into the 2.5-dev branch (closes #2972).

Discussion
----------

Add NoUnneededFinalMethodFixer

```diff
 final class Foo {
-    final public function foo() {}
-    final protected function bar() {}
-    final private function baz() {}
+    public function foo() {}
+    protected function bar() {}
+    private function baz() {}
 }
```

Commits
-------

731acea Add NoUnneededFinalMethodFixer
@keradus keradus removed the RTM Ready To Merge label Aug 21, 2017
@keradus keradus closed this Aug 21, 2017
@Slamdunk Slamdunk deleted the feature-no-final branch August 21, 2017 19:48
keradus added a commit that referenced this pull request Aug 22, 2017
This PR was submitted for the 2.4 branch but it was merged into the 2.2 branch instead (closes #2994).

Discussion
----------

ProtectedToPrivateFixer: minor optimization

Following #2972 (comment) I tried to make this fixer faster, but the [trait check](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.4/src/Fixer/ClassNotation/ProtectedToPrivateFixer.php#L135) doesn't let me skip getting the class close before the main loop.

Any other idea is welcome

Commits
-------

7233e84 ProtectedToPrivateFixer: minor optimization
39a57b6 Merge branch '2.2' into 2.4
f507e1a Merge branch '2.2' into 2.4
176e618 minor #2989 CiIntegrationTest - fix min supported PHP versions (keradus)
8ce604e CiIntegrationTest - fix min supported PHP versions
5d0d668 Merge branch '2.2' into 2.4
6b1a9b2 bug #2959 MethodArgumentSpaceFixer - Skip body of fixed function (greg0ire)
7a087d0 bug #2984 AlignMultilineCommentFixer - handle uni code (SpacePossum)
5df5258 Merge branch '2.2' into 2.4
4c9d570 Merge branch '2.2' into 2.4
ef2dca6 AlignMultilineCommentFixer - handle ucode
240d8da Merge branch '2.2' into 2.4
f9abae0 Merge branch '2.2' into 2.4
b1b52d9 minor #2968 AssertTokensTrait - don't use AccessibleObject (keradus)
1735850 AssertTokensTrait - don't use AccessibleObject
f087642 Merge branch '2.2' into 2.4
9fd0483 Skip body of fixed function
3fd0bda bug #2954 NoBreakCommentFixer - Disable case sensitivity (julienfalque)
5cea194 minor #2955 Travis - stop using old TASK_SCA residue (keradus)
b322632 Merge branch '2.2' into 2.4
d7290d8 Travis - stop using old TASK_SCA residue
fb19b7b Disable case sensitivity
f394201 minor #2952 BlankLineBeforeStatementFixer - Reference fixer instead of test class (localheinz)
26df6cf Fix: Reference fixer instead of test class
9495672 Merge branch '2.2'
99fb7e6 minor #2945 BlankLineBeforeStatementFixerTest - Fix covered class (julienfalque)
582025e Merge branch '2.2'
8313df2 minor #2924 Add missing Token deprecations (julienfalque)
88337c7 Add missing Token deprecations
e68ea74 Fix covered class
ea2041a Merge branch '2.2'
d751a88 Merge branch '2.2'
98bcb56 bumped version
d9127f2 Merge branch '2.2'
63661f3 prepared the 2.4.0 release
faa989d minor #2921 InvalidForEnvFixerConfigurationException - fix handling in tests on 2.4 line (keradus)
348e910 Merge branch '2.3'
cd1e6c4 prepared the 2.3.3 release
8124224 Merge branch '2.2' into 2.3
4f8c340 Merge branch '2.3'
e5aa3fb Merge branch '2.2' into 2.3
89c5e01 Merge branch '2.3'
1d79dff Merge branch '2.2' into 2.3
dd2b707 InvalidForEnvFixerConfigurationException - fix handling in tests on 2.4 line
4ed2920 Merge branch '2.3'
2feb1e9 Merge branch '2.2' into 2.3
7fe4e62 Merge branch '2.3'
544c5b7 Merge branch '2.2' into 2.3
b09cda1 feature #2825 Add PhpdocTypesOrderFixer (julienfalque, keradus)
2c0cdb5 Add PhpdocTypesOrderFixer
311e5d8 Merge branch '2.3'
e1066bb Merge branch '2.2' into 2.3
3e41a53 Merge branch '2.3'
c247d95 Merge branch '2.2' into 2.3
0496cd2 feature #2857 Add AlignMultilineCommentFixer (Slamdunk, keradus)
591ee63 Add AlignMultilineCommentFixer
9eba191 bug #2880 NoBreakCommentFixer - fix edge case (julienfalque)
69a4fc5 feature #2684 BracesFixer - new options for braces position after control structures and anonymous constructs (aidantwoods, keradus)
8c6d50c BracesFixer - new options for braces position after control structures and anonymous constructs
844bfa9 NoBreakCommentFixer - fix edge case
ff2236b bug #2900 VoidReturnFixer - handle functions containing anonymous functions/classes (bendavies, keradus)
c1f4952 Merge branch '2.3'
f005e0f Merge branch '2.2' into 2.3
89538af VoidReturnFixer - handle functions containing anonymous functions/classes
4941e8b VoidReturnFixer - add failing test
5212e8b bug #2902 Fix test classes constructor (julienfalque)
2998702 Fix test classes constructor
80ddad8 Merge branch '2.3'
0f67a86 Merge branch '2.2' into 2.3
99135fe Merge branch '2.3'
055d67b Merge branch '2.2' into 2.3
ea057db Merge branch '2.3'
051f28d Merge branch '2.2' into 2.3
4f76ee4 minor #2894 NonPrintableCharacterFixer - fix handling required PHP version on PHPUnit 4.x (keradus)
a3385aa feature #2384 Add BlankLineBeforeStatementFixer (localheinz, keradus, SpacePossum)
0ae03ef NonPrintableCharacterFixer - fix handling required PHP version on PHPUnit 4.x
c05a48a Merge branch '2.3'
594bea2 Merge branch '2.2' into 2.3
a5036e0 BlankLineBeforeControlStatementFixer - rename to BlankLineBeforeStatementFixer, update the fixer
dff72c6 Merge branch '2.3'
dc8eee8 Merge branch '2.2' into 2.3
eb021d9 BlankLineBeforeControlStatementFixer - adjust fixer to newest code requirements
b71a6da Add BlankLineBeforeControlStatementFixer
ac20c3b Merge branch '2.3'
7ceb53b Merge branch '2.2' into 2.3
711a831 feature #2701 NoExtraConsecutiveBlankLinesFixer - Add more configuration options related to switch statements (SpacePossum)
42abe56 Add more configuration options related to switch statements.
091a8e6 Merge branch '2.3'
a3f4677 Merge branch '2.2' into 2.3
aefcac2 Merge branch '2.3'
d2dc99d Merge branch '2.2' into 2.3
19d5207 feature #2815 NonPrintableCharacterFixer - Add option to replace with escape sequences (julienfalque, keradus)
cc3cf7e NonPrintableCharacterFixer - Add option to replace with escape sequences
5c28c97 feature #2667 Add NoBreakCommentFixer (julienfalque)
f7241f5 feature #2866 Add SingleLineCommentStyleFixer, deprecate HashToSlashCommentFixer (Slamdunk, keradus)
ecffd89 Add SingleLineCommentStyleFixer, deprecate HashToSlashCommentFixer
e17c116 Merge branch '2.3'
bc68ab9 Merge branch '2.2' into 2.3
57891bc Merge branch '2.3'
bfb0ae1 Merge branch '2.2' into 2.3
95aec52 Add NoBreakCommentFixer
ce2aceb feature #2822 Add NoNullPropertyInitializationFixer (ntzm, julienfalque, SpacePossum)
b3ddc0a minor #2877 Fix PHPMD report (julienfalque)
9cc9a7d Merge branch '2.3'
6670722 Merge branch '2.2' into 2.3
69ad77e Fix PHPMD report
77a9f0b NoNullPropertyInitializationFixer - Rename from NullPropertyDeclarationFixer, update description, fix \null handling
113f8e8 NullPropertyDeclarationFixer - Candidate check include class/trait, remove not needed var., do not remove comments.
3d98b67 NullPropertyDeclarationFixer - Support grouped properties and comments
1665be8 Implement null_property_declaration fixer
69b26d7 feature #2856 CastSpacesFixer - add space option (kubawerlos, keradus)
2ab2e2a CastSpacesFixer - add space option
1565733 Merge branch '2.3'
328a9d2 Merge branch '2.2' into 2.3
476147a Merge branch '2.3'
a984bbf minor #2870 Tokens - ensureWhitespaceAtIndex - Clear tokens before compare. (SpacePossum)
acc826c Clear tokens before compare.
db502a9 Merge branch '2.3'
30e3162 Merge branch '2.2' into 2.3
735d1e6 feature #2765 DoctrineAnnotationIndentationFixer - add option to indent mixed lines (julienfalque)
0b4e986 feature #2664 Add DoctrineAnnotationArrayAssignmentFixer (julienfalque)
ea5d449 feature #2440 MethodArgumentSpaceFixer - add ensure_fully_multiline option (greg0ire)
6877da0 MethodArgumentSpaceFixer - add ensure_fully_multiline option
b21630e Merge branch '2.3'
c2520fe Merge branch '2.2' into 2.3
d83d7ea Add option to indent mixed lines
db54a10 Add Doctrine Annotation array assignment fixer
6dc5756 minor #2773 Travis - use stages (keradus)
624f137 Travis - use stages
3383d54 Merge branch '2.3'
59f23be Merge branch '2.2' into 2.3
202d0b8 Merge branch '2.3'
026565a fix CS
24ee202 Merge branch '2.2' into 2.3
dd0f467 Merge branch '2.3'
671239e Merge branch '2.2' into 2.3
5d509fb Merge branch '2.3'
eee8390 Merge branch '2.2' into 2.3
7d570d0 Merge branch '2.3'
c5a4b2c Merge branch '2.2' into 2.3
f0f3109 Merge branch '2.3'
22f24c2 Merge branch '2.2' into 2.3
87d8acc minor #2818 Token become immutable, performance optimizations (keradus)
964586f Token become immutable, performance optimizations
dca89c0 feature #2649 PhpdocAlignFixer - make fixer configurable (ntzm)
ce3604a Merge branch '2.3'
4ca4aa5 Merge branch '2.2' into 2.3
500ebc8 Merge branch '2.3'
4805e38 Merge branch '2.2' into 2.3
2cfec26 minor #2823 Tokenizer - use future-compatible interface (keradus)
d9bef07 Tokenizer - use future-compatible interface
1b0056f Merge branch '2.3'
edb5009 Merge branch '2.2' into 2.3
91736ad Merge branch '2.3'
ae8f46b Merge branch '2.2' into 2.3
4649ba9 Merge branch '2.3'
b8d39c4 minor #2820 MagicConstantCasingFixer - Remove defined check (SpacePossum)
5202c3b Merge branch '2.2' into 2.3
a5e0b33 Remove defined check.
8f6fac8 Merge branch '2.3'
12d4ec7 Merge branch '2.2' into 2.3
c9ac542 Merge branch '2.3'
76c6546 Merge branch '2.2' into 2.3
a0b1248 Merge branch '2.3'
597745f prepared the 2.3.2 release
0d45f8b Merge branch '2.2' into 2.3
c615d54 minor #2801 ProjectCodeTest - Fix typo in deprecation message (SpacePossum)
1911e1b Merge branch '2.3'
888981c minor #2797 Update .gitattributes (SpacePossum)
03d9f1b Merge branch '2.2' into 2.3
3c28f35 minor #2794 Drop HHVM support (keradus, julienfalque)
9e8b6e8 Fix PHPMD command with unknown files
3c72ea6 Drop HHVM support
f71a5c4 Update ProjectCodeTest.php
ee9a0ae Update .gitattributes
1aae92d feature #2740 Add VoidReturnFixer (mrmark)
505459d Add VoidReturnFixer
61348f0 bumped version
b1d9b1b Merge branch '2.2'
cf9bc2c Merge branch '2.2'
d0f5e75 minor #2774 AssertTokens Trait (keradus)
32c7362 AssertTokens Trait
c9700cf Merge branch '2.2'
a1d52a5 Merge branch '2.2'
cbf1cb1 Merge branch '2.2'
ca0cde5 minor #2304 DX: use PHPMD (keradus)
561fc54 DX: use PHPMD
e98b85d Merge branch '2.2'
5b874a0 Merge branch '2.2'
c606b75 Merge branch '2.2'
dfb997b Merge branch '2.2'
00250d2 Merge branch '2.2'
d830c54 minor #2725 Use method chaining for configuration definitions (julienfalque)
e623eae Use method chaining for configuration definitions
37accf6 Merge branch '2.2'
6e9ea6d Merge branch '2.2'
bd14fb5 Merge branch '2.2'
e0021ef Merge branch '2.2'
b22b513 Merge branch '2.2'
1de1a99 Merge branch '2.2'
2929788 Merge branch '2.2'
085640a Make phpdoc_align configurable
0cd8f38 Merge branch '2.2'
f268941 Merge branch '2.2'
06c14be Merge branch '2.2'
1c41528 Merge branch '2.2'
cf98a1a Merge branch '2.2'
95f4f58 minor #2718 Remove old Symfony exception message expectation (julienfalque)
387a8c6 minor #2742 Code cleanup (GrahamCampbell, keradus)
77d80cc Code cleanup
382e54a Merge branch '2.2'
c794bcf Merge branch '2.2'
d5257f7 prepared the 2.3.1 release
bd8f080 Merge branch '2.2'
6d2f124 Merge branch '2.2'
5002ecd Remove old Symfony exception message expectation
ab8c613 prepared the 2.3.0 release
56417e3 Merge branch '2.2'
0f68656 minor #2672 Bump symfony/* deps (keradus)
ea6f970 Bump symfony/* deps
4411227 feature #2708 Add PhpUnitTestClassRequiresCoversFixer (keradus)
14ce819 Add PhpUnitTestClassRequiresCoversFixer
24a0bd6 Merge branch '2.2'
945b572 feature #2450 Add ListSyntaxFixer (SpacePossum)
f55e705 Add ListSyntaxFixer
312f748 minor #2568 Require PHP 5.6+ (keradus)
b1a3c43 Require PHP 5.6+
b6cd1aa Merge branch '2.2'
3804cd0 Merge branch '2.2'
733ba2f Merge branch '2.2'
0437677 Merge branch '2.2'
502f06c Merge branch '2.2'
8d3f905 Merge branch '2.2'
eb54981 Merge branch '2.2'
0a19b49 Merge branch '2.2'
c9c6217 Merge branch '2.2'
02339fe Merge branch '2.2'
cca97a0 Merge branch '2.2'
82bebae Merge branch '2.2'
3249d40 Merge branch '2.2'
1a031e9 Merge branch '2.2'
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.

4 participants