-
-
Notifications
You must be signed in to change notification settings - Fork 371
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a26bfd9
commit 3a7b2f3
Showing
9 changed files
with
293 additions
and
2 deletions.
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
...tests/CodingStyle/Rector/FuncCall/AddNamedArgumentsRector/AddNamedArgumentsRectorTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Rector\Tests\CodingStyle\Rector\FuncCall\AddNamedArgumentsRector; | ||
|
||
use Rector\CodingStyle\Rector\FuncCall\AddNamedArgumentsRector; | ||
use Rector\Testing\PHPUnit\AbstractRectorTestCase; | ||
|
||
final class AddNamedArgumentsRectorTest extends AbstractRectorTestCase | ||
{ | ||
/** | ||
* @dataProvider provideCases() | ||
*/ | ||
public function test(string $filePath): void | ||
{ | ||
$this->doTestFile($filePath); | ||
} | ||
|
||
public static function provideCases(): iterable | ||
{ | ||
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); | ||
} | ||
|
||
public function provideConfigFilePath(): string | ||
{ | ||
return __DIR__ . '/config/rector.php'; | ||
} | ||
|
||
protected function getRectorClass(): string | ||
{ | ||
return AddNamedArgumentsRector::class; | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
rules-tests/CodingStyle/Rector/FuncCall/AddNamedArgumentsRector/Fixture/construct.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
new DateTimeImmutable('now'); | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
new DateTimeImmutable(datetime: 'now'); | ||
|
||
?> |
11 changes: 11 additions & 0 deletions
11
rules-tests/CodingStyle/Rector/FuncCall/AddNamedArgumentsRector/Fixture/function.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
str_contains('foo', 'bar'); | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
str_contains(haystack: 'foo', needle: 'bar'); | ||
|
||
?> |
11 changes: 11 additions & 0 deletions
11
rules-tests/CodingStyle/Rector/FuncCall/AddNamedArgumentsRector/Fixture/method.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
(new DateTimeImmutable())->format('Y-m-d'); | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
(new DateTimeImmutable())->format(format: 'Y-m-d'); | ||
|
||
?> |
6 changes: 6 additions & 0 deletions
6
...s-tests/CodingStyle/Rector/FuncCall/AddNamedArgumentsRector/Fixture/skip_function.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?php | ||
|
||
str_contains(haystack: 'foo', needle: 'bar'); | ||
|
||
?> | ||
|
11 changes: 11 additions & 0 deletions
11
...s-tests/CodingStyle/Rector/FuncCall/AddNamedArgumentsRector/Fixture/static_method.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
DateTime::createFromFormat('Y-m-d', '2001-01-01'); | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
DateTime::createFromFormat(format: 'Y-m-d', datetime: '2001-01-01'); | ||
|
||
?> |
9 changes: 9 additions & 0 deletions
9
rules-tests/CodingStyle/Rector/FuncCall/AddNamedArgumentsRector/config/rector.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use Rector\CodingStyle\Rector\FuncCall\AddNamedArgumentsRector; | ||
use Rector\Config\RectorConfig; | ||
|
||
return RectorConfig::configure() | ||
->withRules([AddNamedArgumentsRector::class]); |
194 changes: 194 additions & 0 deletions
194
rules/CodingStyle/Rector/FuncCall/AddNamedArgumentsRector.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Rector\CodingStyle\Rector\FuncCall; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Expr\FuncCall; | ||
use PhpParser\Node\Expr\MethodCall; | ||
use PhpParser\Node\Expr\New_; | ||
use PhpParser\Node\Expr\StaticCall; | ||
use PhpParser\Node\Identifier; | ||
use PhpParser\Node\Name; | ||
use PHPStan\Reflection\ParameterReflectionWithPhpDocs; | ||
use PHPStan\Reflection\ReflectionProvider; | ||
use Rector\NodeTypeResolver\Node\AttributeKey; | ||
use Rector\Rector\AbstractRector; | ||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; | ||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
use const PHP_VERSION_ID; | ||
|
||
final class AddNamedArgumentsRector extends AbstractRector | ||
{ | ||
public function __construct(private readonly ReflectionProvider $reflectionProvider) | ||
{ | ||
} | ||
|
||
public function getRuleDefinition(): RuleDefinition | ||
{ | ||
return new RuleDefinition('Convert all arguments to named arguments', [ | ||
new CodeSample('$user->setPassword("123456");', '$user->changePassword(password: "123456");'), | ||
]); | ||
} | ||
|
||
public function getNodeTypes(): array | ||
{ | ||
return [FuncCall::class, StaticCall::class, MethodCall::class, New_::class]; | ||
} | ||
|
||
public function refactor(Node $node): ?Node | ||
{ | ||
if (PHP_VERSION_ID < 80000) { | ||
return null; | ||
} | ||
|
||
$parameters = $this->getParameters($node); | ||
$this->addNamesToArgs($node, $parameters); | ||
|
||
return $node; | ||
} | ||
|
||
/** | ||
* @param Node $node | ||
* @return ParameterReflectionWithPhpDocs[] | ||
*/ | ||
private function getParameters(Node $node): array | ||
{ | ||
$parameters = []; | ||
|
||
if ($node instanceof New_) { | ||
$parameters = $this->getConstructorArgs($node); | ||
} elseif ($node instanceof MethodCall) { | ||
$parameters = $this->getMethodArgs($node); | ||
} elseif ($node instanceof StaticCall) { | ||
$parameters = $this->getStaticMethodArgs($node); | ||
} elseif ($node instanceof FuncCall) { | ||
$parameters = $this->getFuncArgs($node); | ||
} | ||
|
||
return $parameters; | ||
} | ||
|
||
/** | ||
* @return ParameterReflectionWithPhpDocs[] | ||
*/ | ||
private function getStaticMethodArgs(StaticCall $node): array | ||
{ | ||
$namespaceAnswerer = $node->getAttribute(AttributeKey::SCOPE); | ||
|
||
if (! $node->class instanceof Name) { | ||
return []; | ||
} | ||
|
||
$className = $this->getName($node->class); | ||
if (! $this->reflectionProvider->hasClass($className)) { | ||
return []; | ||
} | ||
|
||
$classReflection = $this->reflectionProvider->getClass($className); | ||
if (! $classReflection->hasMethod($node->name->toString())) { | ||
return []; | ||
} | ||
|
||
$methodReflection = $classReflection->getMethod($node->name->toString(), $namespaceAnswerer); | ||
|
||
return $methodReflection->getOnlyVariant()->getParameters(); | ||
} | ||
|
||
/** | ||
* @return ParameterReflectionWithPhpDocs[] | ||
*/ | ||
private function getMethodArgs(MethodCall $node): array | ||
{ | ||
$namespaceAnswerer = $node->getAttribute(AttributeKey::SCOPE); | ||
|
||
$callerType = $this->nodeTypeResolver->getType($node->var); | ||
if (! $callerType->hasMethod($node->name->toString())) { | ||
return []; | ||
} | ||
|
||
$methodReflection = $callerType->getMethod($node->name->toString(), $namespaceAnswerer); | ||
|
||
return $methodReflection->getOnlyVariant()->getParameters(); | ||
} | ||
|
||
private function resolveCalledName(Node $node): ?string | ||
{ | ||
if ($node instanceof FuncCall && $node->name instanceof Name) { | ||
return (string) $node->name; | ||
} | ||
|
||
if ($node instanceof MethodCall && $node->name instanceof Identifier) { | ||
return (string) $node->name; | ||
} | ||
|
||
if ($node instanceof StaticCall && $node->name instanceof Identifier) { | ||
return (string) $node->name; | ||
} | ||
|
||
if ($node instanceof New_ && $node->class instanceof Name) { | ||
return (string) $node->class; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* @return ParameterReflectionWithPhpDocs[] | ||
*/ | ||
private function getConstructorArgs(New_ $node): array | ||
{ | ||
$calledName = $this->resolveCalledName($node); | ||
if ($calledName === null) { | ||
return []; | ||
} | ||
|
||
if (! $this->reflectionProvider->hasClass($calledName)) { | ||
return []; | ||
} | ||
$classReflection = $this->reflectionProvider->getClass($calledName); | ||
|
||
if (! $classReflection->hasConstructor()) { | ||
return []; | ||
} | ||
|
||
$constructorReflection = $classReflection->getConstructor(); | ||
|
||
return $constructorReflection->getOnlyVariant()->getParameters(); | ||
} | ||
|
||
/** | ||
* @return ParameterReflectionWithPhpDocs[] | ||
*/ | ||
private function getFuncArgs(FuncCall $node): array | ||
{ | ||
$namespaceAnswerer = $node->getAttribute(AttributeKey::SCOPE); | ||
|
||
$calledName = $this->resolveCalledName($node); | ||
if ($calledName === null) { | ||
return []; | ||
} | ||
|
||
if (! $this->reflectionProvider->hasFunction(new Name($calledName), $namespaceAnswerer)) { | ||
return []; | ||
} | ||
$reflection = $this->reflectionProvider->getFunction(new Name($calledName), $namespaceAnswerer); | ||
|
||
return $reflection->getOnlyVariant()->getParameters(); | ||
} | ||
|
||
/** | ||
* @param ParameterReflectionWithPhpDocs[] $parameters | ||
*/ | ||
private function addNamesToArgs(Node $node, array $parameters): void | ||
{ | ||
/** @var FuncCall|StaticCall|MethodCall|New_ $node */ | ||
foreach ($node->args as $index => $arg) { | ||
if (! isset($parameters[$index])) { | ||
return; | ||
} | ||
$arg->name = new Identifier($parameters[$index]->getName()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters