Skip to content

feat: Introduce StaticPrivateMethodFixer #4557

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

Merged
merged 32 commits into from
Apr 20, 2025

Conversation

Slamdunk
Copy link
Contributor

@Slamdunk Slamdunk commented Sep 19, 2019

References:

  1. https://twitter.com/BackEndTea/status/1174225152034385920 (/cc @BackEndTea)
  2. Methods\StaticRule ergebnis/phpstan-rules#133
 class Foo
 {
     public function bar()
     {
-        return $this->baz();
+        return self::baz();
     }
     
-    private function baz()
+    private static function baz()
     {
         return 1;
     }
 }
  • Must not contain $this references
  • Must not contain non-static closures see feat: Introduce StaticPrivateMethodFixer #4557 (comment) updated to: Must not contain closures
  • Must not contain debug_backtrace() calls
  • Must be marked as risky, no dynamic reference handled such as $this->{$method}() or $$var inside the method
  • Must run after ProtectedToPrivateFixer
  • Must run before StaticLambdaFixer

@Ocramius
Copy link

Ocramius commented Sep 19, 2019

Restrictions:

  • Must not contain non-static closures
  • Must not contain debug_backtrace() usage
  • Must not contain $this
  • Must not contain $$var
  • must not be declared by interface

@Slamdunk
Copy link
Contributor Author

Must not contain debug_backtrace() usage

Why? How could a code calling debug_backtrace() be affected by this change?
I mean affected runtime; the output would obviously be different, but I would not consider this as an issue.

Must not contain $$var

This could only affects $$var referencing to $this or global variables AFAICS, we'll point this out in the Risky signature but not handle them

@Ocramius
Copy link

Why? How could a code calling debug_backtrace() be affected by this change?

debbug_backtrace()[1]['object'] stops working

@dkarlovi
Copy link
Contributor

Must not contain non-static closures
Must run before StaticLambdaFixer

Wouldn't running after static lambda relax the first requirement?

@BackEndTea
Copy link
Contributor

Wouldn't running after static lambda relax the first requirement?

These two kinda have circular dependency, idk if php-cs-fixer can handle that atm.

@Slamdunk
Copy link
Contributor Author

Must not contain non-static closures
Must run before StaticLambdaFixer

Wouldn't running after static lambda relax the first requirement?

Heh, that's tricky, it should run before to relax methods containing non-static closures, but also after to fix non-static closures using private methods now declared static.
As pointed out by @BackEndTea here's a circular dependency.

@Ocramius
Copy link

You can solve the dependency by running these recursively, which would require changing the tool architecture.

Your exit condition being "nothing got fixed", or "nesting level limit reached".

@dkarlovi
Copy link
Contributor

I mean, it's the same effect as running it twice, I guess. It's a bit annoying if you have an unstable rule combo like this: you run CS and commit, your CI runs it again and fails because it changed something too.

@Slamdunk
Copy link
Contributor Author

@keradus always pushed for avoiding such architectures for clear reasons. I think I'll propose the first production-ready version of the Fixer to completely ignore private method containing any closure

@Slamdunk
Copy link
Contributor Author

must not be declared by interface

@Ocramius you killed me 😢 this practically makes the Fixer impossible to be implemented without context awareness

@dkarlovi
Copy link
Contributor

download

@BackEndTea
Copy link
Contributor

must not be declared by interface

Interfaces can't declare private methods right?

@Slamdunk
Copy link
Contributor Author

Interfaces can't declare private methods right?

No, but the resulting new method can be present in the interface, and such creating a conflict

@BackEndTea
Copy link
Contributor

I don't understand, could you provide an example

@Slamdunk
Copy link
Contributor Author

Slamdunk commented Sep 19, 2019

I thought this https://3v4l.org/G6vS6 could be possible but turns out it can't, so either @Ocramius knows something we don't or he was wrong; and it would be really weird for him to be wrong 😛

@Ocramius
Copy link

Maybe annoying, but still a good amount of improvement 👍

@Ocramius
Copy link

@Slamdunk sorry, I missed the limitation to private methods in my thougt process 😛

@dkarlovi
Copy link
Contributor

TBH I really assumed you actually could put private methods in interfaces, because... you know, PHP.

TIL you can't.

*/
public function isCandidate(Tokens $tokens)
{
return $tokens->isAllTokenKindsFound([T_CLASS, T_PRIVATE]);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the fixer can also be applied inside of Traits

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mmm, I think not

// file Foo.php
trait Foo
{
    private function bar() {}
}
// file Baz.php
class Baz
{
    use Foo;
    
    public function wut()
    {
        return $this->bar();
    }
}

I cannot fix Foo::bar because I would break Baz::wut behavior; well, not entirely break as php still run without issue, but a static analyzer would complain

Copy link
Contributor Author

@Slamdunk Slamdunk Sep 20, 2019

Choose a reason for hiding this comment

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

I'm sorry I just read #4557 (comment): yes we can fix Traits too pointing the possible side effects into the risky description.

But honestly the two scenarios are much differently spread imho:

  1. A class that uses a private method of a trait: ok, bad but common, we can't use this fixer for Trait as we would (logically) break the code
  2. A trait that uses a private method of a class: WUT? Are you kidding me? Yeah it's feasible, but if you write crappy code it's not our fault

I feel safe to fix classes and not fix traits, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like a good idea.

I don't think it will be a common problem, but it is one of those nasty edge cases.

@BackEndTea
Copy link
Contributor

One more possible edge case that (may) not be relevant yet. How about $this etc in php 7.4 arrow functions

@Slamdunk
Copy link
Contributor Author

One more possible edge case that (may) not be relevant yet. How about $this etc in php 7.4 arrow functions

I was planning to pinpoint it in an issue like #4382 as I haven't a clear idea on the topic as of yet, nor on the way this library intend to support the new code structure

@Slamdunk Slamdunk changed the title [WIP] Add StaticPrivateMethodFixer Add StaticPrivateMethodFixer Sep 20, 2019
@Slamdunk Slamdunk force-pushed the static_private_method branch from 435d2a8 to beb77cc Compare November 4, 2019 11:18
@Slamdunk
Copy link
Contributor Author

@SpacePossum may we tag this for 2.17?

@Slamdunk Slamdunk force-pushed the static_private_method branch from 8c2f8b0 to 8681bac Compare January 21, 2020 06:58
@Wirone Wirone force-pushed the static_private_method branch from 30b441c to 64fc4e8 Compare November 13, 2024 09:00
@Wirone Wirone removed the status/rebase required PR is outdated and required synchronisation with main branch label Nov 13, 2024
@coveralls
Copy link

coveralls commented Mar 11, 2025

Coverage Status

coverage: 94.895% (+0.01%) from 94.882%
when pulling df2bc41 on Slamdunk:static_private_method
into 395ce85 on PHP-CS-Fixer:master.

@keradus keradus enabled auto-merge (squash) April 20, 2025 21:54
@keradus keradus merged commit fb625af into PHP-CS-Fixer:master Apr 20, 2025
30 checks passed
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.

9 participants