mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-03 01:40:49 +00:00
[Arguments] Add the missing default value and type for the added arguments (#837)
This commit is contained in:
parent
327929f620
commit
3ff17513c4
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture;
|
||||
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeClass;
|
||||
|
||||
class ArgumentWithoutType extends SomeClass
|
||||
{
|
||||
public function withoutTypeOrDefaultValue($arguments = [])
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class ArgumentWithoutDefaultValue extends SomeClass
|
||||
{
|
||||
public function withoutTypeOrDefaultValue(array $arguments)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class ArgumentWithoutTypeAndDefaultValue extends SomeClass
|
||||
{
|
||||
public function withoutTypeOrDefaultValue($arguments)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture;
|
||||
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeClass;
|
||||
|
||||
class ArgumentWithoutType extends SomeClass
|
||||
{
|
||||
public function withoutTypeOrDefaultValue(array $arguments = [])
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class ArgumentWithoutDefaultValue extends SomeClass
|
||||
{
|
||||
public function withoutTypeOrDefaultValue(array $arguments = [])
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class ArgumentWithoutTypeAndDefaultValue extends SomeClass
|
||||
{
|
||||
public function withoutTypeOrDefaultValue(array $arguments = [])
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source;
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
use Rector\Arguments\NodeAnalyzer\ArgumentAddingScope;
|
||||
use Rector\Arguments\Rector\ClassMethod\ArgumentAdderRector;
|
||||
use Rector\Arguments\ValueObject\ArgumentAdder;
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeClass;
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeContainerBuilder;
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeParentClient;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
@ -45,6 +46,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
'array',
|
||||
ArgumentAddingScope::SCOPE_CLASS_METHOD
|
||||
),
|
||||
new ArgumentAdder(SomeClass::class, 'withoutTypeOrDefaultValue', 0, 'arguments', [], 'array'),
|
||||
]),
|
||||
]]);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Fixture;
|
||||
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeClassWithAnyDefaultValue;
|
||||
|
||||
class ParamWithoutDefaultValue extends SomeClassWithAnyDefaultValue
|
||||
{
|
||||
public function someMethod($argument)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ParamWithScalar extends SomeClassWithAnyDefaultValue
|
||||
{
|
||||
public function someMethod($argument = 1)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Fixture;
|
||||
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeClassWithAnyDefaultValue;
|
||||
|
||||
class ParamWithoutDefaultValue extends SomeClassWithAnyDefaultValue
|
||||
{
|
||||
public function someMethod($argument = [])
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ParamWithScalar extends SomeClassWithAnyDefaultValue
|
||||
{
|
||||
public function someMethod($argument = [])
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Fixture;
|
||||
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeClassWithAnyDefaultValue;
|
||||
|
||||
class ParamWithNull extends SomeClassWithAnyDefaultValue
|
||||
{
|
||||
public function paramWithNull($argument = null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Fixture;
|
||||
|
||||
use Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeClassWithAnyDefaultValue;
|
||||
|
||||
class ParamWithNull extends SomeClassWithAnyDefaultValue
|
||||
{
|
||||
public function paramWithNull($argument = [])
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source;
|
||||
|
||||
class SomeClassWithAnyDefaultValue
|
||||
{
|
||||
|
||||
}
|
|
@ -49,6 +49,20 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
'Symfony\Component\Yaml\Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE'
|
||||
),
|
||||
|
||||
new ReplaceArgumentDefaultValue(
|
||||
'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeClassWithAnyDefaultValue',
|
||||
'someMethod',
|
||||
0,
|
||||
ReplaceArgumentDefaultValue::ANY_VALUE_BEFORE,
|
||||
[]
|
||||
),
|
||||
new ReplaceArgumentDefaultValue(
|
||||
'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeClassWithAnyDefaultValue',
|
||||
'paramWithNull',
|
||||
0,
|
||||
null,
|
||||
[]
|
||||
),
|
||||
]),
|
||||
]]);
|
||||
};
|
||||
|
|
|
@ -7,11 +7,14 @@ namespace Rector\Arguments;
|
|||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface;
|
||||
use Rector\Arguments\ValueObject\ReplaceArgumentDefaultValue;
|
||||
use Rector\Core\PhpParser\Node\NodeFactory;
|
||||
use Rector\Core\PhpParser\Node\Value\ValueResolver;
|
||||
|
||||
|
@ -31,17 +34,60 @@ final class ArgumentDefaultValueReplacer
|
|||
if (! isset($node->params[$replaceArgumentDefaultValue->getPosition()])) {
|
||||
return null;
|
||||
}
|
||||
} elseif (isset($node->args[$replaceArgumentDefaultValue->getPosition()])) {
|
||||
$this->processArgs($node, $replaceArgumentDefaultValue);
|
||||
|
||||
return $this->processParams($node, $replaceArgumentDefaultValue);
|
||||
}
|
||||
|
||||
return $node;
|
||||
if (! isset($node->args[$replaceArgumentDefaultValue->getPosition()])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->processArgs($node, $replaceArgumentDefaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function isDefaultValueMatched(?Expr $expr, $value): bool
|
||||
{
|
||||
// allow any values before, also allow param without default value
|
||||
if ($value === ReplaceArgumentDefaultValue::ANY_VALUE_BEFORE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($expr === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->valueResolver->isValue($expr, $value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ValueResolver::isValue returns false when default value is `null`
|
||||
return $value === null && $this->valueResolver->isNull($expr);
|
||||
}
|
||||
|
||||
private function processParams(
|
||||
ClassMethod $classMethod,
|
||||
ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue
|
||||
): ?ClassMethod {
|
||||
$position = $replaceArgumentDefaultValue->getPosition();
|
||||
|
||||
if (! $this->isDefaultValueMatched(
|
||||
$classMethod->params[$position]->default,
|
||||
$replaceArgumentDefaultValue->getValueBefore()
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classMethod->params[$position]->default = $this->normalizeValue($replaceArgumentDefaultValue->getValueAfter());
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
private function processArgs(
|
||||
MethodCall | StaticCall | FuncCall $expr,
|
||||
ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue
|
||||
): void {
|
||||
): Expr {
|
||||
$position = $replaceArgumentDefaultValue->getPosition();
|
||||
|
||||
$argValue = $this->valueResolver->getValue($expr->args[$position]->value);
|
||||
|
@ -57,22 +103,30 @@ final class ArgumentDefaultValueReplacer
|
|||
$expr->args = $newArgs;
|
||||
}
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function normalizeValueToArgument($value): Arg
|
||||
{
|
||||
return new Arg($this->normalizeValue($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function normalizeValue($value): ClassConstFetch|Expr
|
||||
{
|
||||
// class constants → turn string to composite
|
||||
if (is_string($value) && \str_contains($value, '::')) {
|
||||
[$class, $constant] = explode('::', $value);
|
||||
$classConstFetch = $this->nodeFactory->createClassConstFetch($class, $constant);
|
||||
|
||||
return new Arg($classConstFetch);
|
||||
return $this->nodeFactory->createClassConstFetch($class, $constant);
|
||||
}
|
||||
|
||||
return new Arg(BuilderHelpers::normalizeValue($value));
|
||||
return BuilderHelpers::normalizeValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
43
rules/Arguments/NodeAnalyzer/ChangedArgumentsDetector.php
Normal file
43
rules/Arguments/NodeAnalyzer/ChangedArgumentsDetector.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Arguments\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Param;
|
||||
use Rector\Core\PhpParser\Node\Value\ValueResolver;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
final class ChangedArgumentsDetector
|
||||
{
|
||||
public function __construct(
|
||||
private ValueResolver $valueResolver,
|
||||
private NodeNameResolver $nodeNameResolver
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function isDefaultValueChanged(Param $param, $value): bool
|
||||
{
|
||||
if ($param->default === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! $this->valueResolver->isValue($param->default, $value);
|
||||
}
|
||||
|
||||
public function isTypeChanged(Param $param, ?string $type): bool
|
||||
{
|
||||
if ($param->type === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($type === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! $this->nodeNameResolver->isName($param->type, $type);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ use PhpParser\Node\Stmt\Class_;
|
|||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\Arguments\NodeAnalyzer\ArgumentAddingScope;
|
||||
use Rector\Arguments\NodeAnalyzer\ChangedArgumentsDetector;
|
||||
use Rector\Arguments\ValueObject\ArgumentAdder;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
|
@ -45,7 +46,8 @@ final class ArgumentAdderRector extends AbstractRector implements ConfigurableRe
|
|||
private bool $haveArgumentsChanged = false;
|
||||
|
||||
public function __construct(
|
||||
private ArgumentAddingScope $argumentAddingScope
|
||||
private ArgumentAddingScope $argumentAddingScope,
|
||||
private ChangedArgumentsDetector $changedArgumentsDetector
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -202,7 +204,22 @@ CODE_SAMPLE
|
|||
return false;
|
||||
}
|
||||
|
||||
return $this->isName($node->params[$position], $argumentName);
|
||||
$param = $node->params[$position];
|
||||
// argument added and name has been changed
|
||||
if (! $this->isName($param, $argumentName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// argument added and default has been changed
|
||||
if ($this->changedArgumentsDetector->isDefaultValueChanged(
|
||||
$param,
|
||||
$argumentAdder->getArgumentDefaultValue()
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// argument added and type has been changed
|
||||
return $this->changedArgumentsDetector->isTypeChanged($param, $argumentAdder->getArgumentType());
|
||||
}
|
||||
|
||||
if (isset($node->args[$position])) {
|
||||
|
|
|
@ -9,6 +9,11 @@ use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface;
|
|||
|
||||
final class ReplaceArgumentDefaultValue implements ReplaceArgumentDefaultValueInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const ANY_VALUE_BEFORE = '*ANY_VALUE_BEFORE*';
|
||||
|
||||
/**
|
||||
* @param mixed $valueBefore
|
||||
* @param mixed $valueAfter
|
||||
|
|
Loading…
Reference in New Issue
Block a user