Add RemoveExtraParametersRector

This commit is contained in:
Tomas Votruba 2018-12-15 10:30:25 +01:00
parent cac89cbb9a
commit 8d783a68bd
11 changed files with 320 additions and 6 deletions

3
abc.php Normal file
View File

@ -0,0 +1,3 @@
<?php
strlen('asdf', 1);

View File

@ -8,3 +8,4 @@ services:
Rector\Php\Rector\FuncCall\CountOnNullRector: ~
Rector\Php\Rector\FunctionLike\ParamScalarTypehintRector: ~
Rector\Php\Rector\FunctionLike\ReturnScalarTypehintRector: ~
Rector\Php\Rector\FuncCall\RemoveExtraParametersRector: ~

View File

@ -22,13 +22,12 @@ final class NullCoalescingOperatorRector extends AbstractRector
$array = [];
$array['user_id'] = $array['user_id'] ?? 'value';
CODE_SAMPLE
,
,
<<<'CODE_SAMPLE'
$array = [];
$array['user_id'] ??= 'value';
CODE_SAMPLE
)
),
]);
}

View File

@ -0,0 +1,142 @@
<?php declare(strict_types=1);
namespace Rector\Php\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Parser;
use Rector\NodeTypeResolver\Node\Attribute;
use Rector\PhpParser\Node\BetterNodeFinder;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
use ReflectionFunction;
use ReflectionFunctionAbstract;
use ReflectionMethod;
/**
* @see https://www.reddit.com/r/PHP/comments/a1ie7g/is_there_a_linter_for_argumentcounterror_for_php/
* @see http://php.net/manual/en/class.argumentcounterror.php
*/
final class RemoveExtraParametersRector extends AbstractRector
{
/**
* @var BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @var Parser
*/
private $parser;
public function __construct(BetterNodeFinder $betterNodeFinder, Parser $parser)
{
$this->betterNodeFinder = $betterNodeFinder;
$this->parser = $parser;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Remove extra parameters', [
new CodeSample('strlen("asdf", 1);', 'strlen("asdf");'),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FuncCall::class, MethodCall::class, StaticCall::class];
}
/**
* @param FuncCall|MethodCall|StaticCall $node
*/
public function refactor(Node $node): ?Node
{
if (count($node->args) === 0) {
return null;
}
$reflectionFunctionLike = $this->resolveReflectionFunctionLike($node);
// can be any number of arguments → nothing to limit here
if ($this->isVariadic($reflectionFunctionLike, $node)) {
return null;
}
if ($reflectionFunctionLike->getNumberOfParameters() >= count($node->args)) {
return null;
}
for ($i = $reflectionFunctionLike->getNumberOfParameters(); $i < count($node->args); $i++) {
unset($node->args[$i]);
}
// re-index for printer
$node->args = array_values($node->args);
return $node;
}
private function resolveReflectionFunctionLike(Node $node): ReflectionFunctionAbstract
{
if ($node instanceof FuncCall) {
return new ReflectionFunction($this->getName($node));
}
$className = $node->getAttribute(Attribute::CLASS_NAME);
return new ReflectionMethod($className, $this->getName($node));
}
/**
* @param FuncCall|MethodCall|StaticCall $node
*/
private function isVariadic(ReflectionFunctionAbstract $reflectionFunctionAbstract): bool
{
// detects from ... in parameters
if ($reflectionFunctionAbstract->isVariadic()) {
return true;
}
// external function/method
$nodes = $this->parser->parse(file_get_contents($reflectionFunctionAbstract->getFileName()));
$removeFunctionLikeNode = $this->betterNodeFinder->find($nodes, function (Node $node) use ($reflectionFunctionAbstract) {
if ($reflectionFunctionAbstract instanceof ReflectionFunction) {
if ($node instanceof Function_) {
if ($this->isName($node, $reflectionFunctionAbstract->getName())) {
return true;
}
}
} else {
if ($node instanceof ClassMethod) {
if ($this->isName($node, $reflectionFunctionAbstract->getName())) {
return true;
}
}
}
return null;
});
// detect from "func_*_arg(s)" calls in the body
return (bool) $this->betterNodeFinder->findFirst($removeFunctionLikeNode, function (Node $node) {
if (! $node instanceof FuncCall) {
return null;
}
if ($this->isNames($node, ['func_get_args', 'func_get_arg', 'func_num_args'])) {
return true;
}
return null;
});
}
}

View File

@ -9,9 +9,7 @@ final class NullCoalescingOperatorRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([
__DIR__ . '/Fixture/fixture.php.inc'
]);
$this->doTestFiles([__DIR__ . '/Fixture/fixture.php.inc']);
}
protected function getRectorClass(): string

View File

@ -0,0 +1,51 @@
<?php
function removeExtraParams()
{
strlen('asdf', 1);
strlen('asdf');
functionWithParameters(1);
functionWithParameters(1, 2);
functionWithParameters(1, 2, 3);
functionWithVariadics(1);
functionWithVariadics(1, 2);
}
function functionWithParameters($a, $b = 5)
{
}
function functionWithVariadics(...$variadic)
{
$argumemnts = $variadic;
}
?>
-----
<?php
function removeExtraParams()
{
strlen('asdf');
strlen('asdf');
functionWithParameters(1);
functionWithParameters(1, 2);
functionWithParameters(1, 2);
functionWithVariadics(1);
functionWithVariadics(1, 2);
}
function functionWithParameters($a, $b = 5)
{
}
function functionWithVariadics(...$variadic)
{
$argumemnts = $variadic;
}
?>

View File

@ -0,0 +1,19 @@
<?php
namespace Rector\Php\Tests\Rector\FuncCall\RemoveExtraParametersRector\Fixture;
final class FuncGetAll
{
public function run()
{
$this->perform();
$this->perform(1);
$this->perform(1, 2);
}
function perform()
{
$argumemnts = func_get_args();
var_dump($argumemnts);
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace Rector\Php\Tests\Rector\FuncCall\RemoveExtraParametersRector\Fixture;
final class MethodExtraArgument
{
public function run()
{
$this->perform(1);
$this->perform(1, 2);
}
private function perform($value)
{
}
}
?>
-----
<?php
namespace Rector\Php\Tests\Rector\FuncCall\RemoveExtraParametersRector\Fixture;
final class MethodExtraArgument
{
public function run()
{
$this->perform(1);
$this->perform(1);
}
private function perform($value)
{
}
}
?>

View File

@ -0,0 +1,37 @@
<?php
namespace Rector\Php\Tests\Rector\FuncCall\RemoveExtraParametersRector\Fixture;
final class StaticCalls
{
public static function run()
{
self::perform(1);
self::perform(1, 2);
}
private static function perform($value)
{
}
}
?>
-----
<?php
namespace Rector\Php\Tests\Rector\FuncCall\RemoveExtraParametersRector\Fixture;
final class StaticCalls
{
public static function run()
{
self::perform(1);
self::perform(1);
}
private static function perform($value)
{
}
}
?>

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
namespace Rector\Php\Tests\Rector\FuncCall\RemoveExtraParametersRector;
use Rector\Php\Rector\FuncCall\RemoveExtraParametersRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class RemoveExtraParametersRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([
// __DIR__ . '/Fixture/fixture.php.inc',
__DIR__ . '/Fixture/func_get_all.php.inc',
// __DIR__ . '/Fixture/methods.php.inc',
// __DIR__ . '/Fixture/static_calls.php.inc',
]);
}
protected function getRectorClass(): string
{
return RemoveExtraParametersRector::class;
}
}

View File

@ -0,0 +1,2 @@
services:
Rector\Php\Rector\FuncCall\RemoveExtraParametersRector: ~