-
-
Notifications
You must be signed in to change notification settings - Fork 400
Add ClosureFromCallableToFirstClassCallableRector
rule
#7071
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 ClosureFromCallableToFirstClassCallableRector
rule
#7071
Conversation
rules/Php81/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php
Outdated
Show resolved
Hide resolved
ee6d875
to
0a4cbea
Compare
config/set/php81.php
Outdated
@@ -23,6 +24,7 @@ | |||
SpatieEnumClassToEnumRector::class, | |||
SpatieEnumMethodCallToEnumConstRector::class, | |||
NullToStrictStringFuncCallArgRector::class, | |||
ClosureFromCallableToFirstClassCallableRector::class, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to use CodingStyle namespace for this, and no need to register to config set, as it mostly by preference, use of first class callable mostly cause error on unit tests and we skip that over time on projects
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to use CodingStyle namespace for this, and no need to register to config set
No problem! It should be a pretty safe refactor for PHP 8.1. There are a lot of upgrade rules that I could consider too as more opinionated towards coding style, so I'm a little bit lost here. To me, Rector promotes you to use new language features by applying them to your entire codebase.
use of first class callable mostly cause error on unit tests and we skip that over time on projects
How does that happen? As far as I understand, both are closures, both are bindable, and I can't think of a case when this rule could break something:
If you are aware of a case when this could be a problem, could you share a snippet so I can try to be sure that it doesn't do anything unexpected on specific Closure::fromCallable
usages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unit test may compare its arguments over its value, [DateTime', 'creatFromInterface]
which is an array, vs ...
which an variadic place holder.
.../FuncCall/ClosureFromCallableToFirstClassCallableRector/Fixture/class_callable_array.php.inc
Outdated
Show resolved
Hide resolved
I've not removed the rule from the 8.1 ruleset as I'm not sure about in which level of CodingStyle it should be. To me, it's a very safe rule 🙂 Where would you place it exactly? |
rules/Php81/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php
Outdated
Show resolved
Hide resolved
ClosureFromCallableToFirstClassCallableRector
rule, fixture…ClosureFromCallableToFirstClassCallableRector
rule
|
||
if ($arg->value instanceof Node\Scalar\String_) { | ||
return new Node\Expr\FuncCall( | ||
new Node\Name($this->prefixRootNamespace($arg->value->value)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FullyQualified node can be used instead of manual prefix check
@@ -0,0 +1,23 @@ | |||
<?php | |||
|
|||
use SomeNamespace\Foo; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use SomeNamespace\Foo; | |
namespace Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this help testing relative namespaces?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless required by rule, fixture namespace should psr4 per defined in composer.json
----- | ||
<?php | ||
|
||
use SomeNamespace\Foo; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use SomeNamespace\Foo; | |
namespace Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture; |
@@ -0,0 +1,21 @@ | |||
<?php | |||
|
|||
namespace Foo; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace Foo; | |
namespace Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture; |
----- | ||
<?php | ||
|
||
namespace Foo; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace Foo; | |
namespace Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture; |
|
||
namespace Foo; | ||
|
||
\trim(...); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is fine without \
prefix, you should can use combination of:
$this->getName($funcCall)
to verify only short name.- use of :
$funcReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node);
and verify if that instanceof NativeFunctionReflection
if (! $funcReflection instanceof \PHPStan\Reflection\Native\NativeFunctionReflection\NativeFunctionReflection) {
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also add test needs check when function defined inside the namespace:
namespace Foo;
function bar()
{
}
Closure::fromCallable('bar');
which should be changed without prefix:
namespace Foo;
function bar()
{
}
-Closure::fromCallable('bar');
+bar(...);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example is not valid: https://3v4l.org/NTdvJ#vnull
AFAIK, strings referring to functions can't be relative and must be FQDN: https://3v4l.org/DFr8s#vnull
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I think the native trim is still valid without \\
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding to this comment:
this is fine without
\
prefix, you should can use combination of:
$this->getName($funcCall)
to verify only short name.- use of :
$funcReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node);
and verify if that instanceof
NativeFunctionReflection
if (! $funcReflection instanceof \PHPStan\Reflection\Native\NativeFunctionReflection\NativeFunctionReflection) { }
I've grinded my gears and I don't find a way to get what you want. I guess that the ' $node' I should pass to that resolver is the FuncCall
that I'm creating and returning.
In that case, this example
namespace Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture {
function trim()
{}
\Closure::fromCallable('Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture\trim');
\Closure::fromCallable('trim');
}
becomes this one:
namespace Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture {
function trim()
{}
\Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture\trim(...);
trim(...);
}
while it should be
namespace Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture {
function trim()
{}
\Rector\Tests\Php81\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector\Fixture\trim(...);
\trim(...);
}
The new node I'm creating, when evaluated by that resolver, is not aware that it will be inside a namespace.
I'm personally not bothered by having unnecessary \
in some cases, and I know about a lot of cases that create nodes with an unnecessary FQDN prefixed with the root \
. That's why I rely on a formatting tool.
Maybe it's something doable and I'm just not seeing it. If there's a way, I would love to learn about it 🙏🏻
I've taken a look at all your comments @samsonasik, please let me know if I can improve it in any other way. Thanks for having a look! <3 |
Ok, just unregister from config set, I still don't want it to exists by php set for now, user can test try in the wild :) |
I will move it to the
|
Yep, coding style is better imo :) |
b49d20a
to
ae9a5de
Compare
I hoped it was a problem only on my machine, but it replicates on the CI too 😰 In the PHP 8.1 namespace the tests were green, and in the process of moving it to the new namespace, the tests are failing like the rule is not being applied. I can't xdebug it launching the whole test suite. Funny enough, if I run the |
That usually happen when there is typo or invalid character case due to file name vs class name vs folder name due to copy paste and replace |
use Rector\Config\RectorConfig; | ||
|
||
return RectorConfig::configure() | ||
->withRules([ClosureFromCallableToFirstClassCallableRector::class]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You seems need to add ->withPhpVersion()
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooooof, I've just literally found it right now! thank you anyways 😄
ae9a5de
to
8511042
Compare
Okay, let's give it a try, thank you @devnix |
Closes rectorphp/rector#9266