Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class ByPass2ndArgValue
public function run()
{
$containerBuilder = new SomeMultiArg();
$containerBuilder->run(1);
$containerBuilder->thirdArgument(1);
}
}

Expand All @@ -26,7 +26,7 @@ class ByPass2ndArgValue
public function run()
{
$containerBuilder = new SomeMultiArg();
$containerBuilder->run(1, 2, 4);
$containerBuilder->thirdArgument(1, 2, 6);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture;

use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeMultiArg;

class NamedArguments
{
public function run()
{
$containerBuilder = new SomeMultiArg();
$containerBuilder->firstArgument(b: 1);
$containerBuilder->firstArgument(c: 1);
$containerBuilder->firstArgument(b: 1, c: 2);
$containerBuilder->firstArgument(c: 1, b: 2);
$containerBuilder->secondArgument(a: 1);
$containerBuilder->secondArgument(c: 1);
$containerBuilder->secondArgument(a: 1, c: 2);
$containerBuilder->secondArgument(c: 1, a: 2);
$containerBuilder->secondArgument(1, c: 2);
$containerBuilder->thirdArgument(a: 1);
$containerBuilder->thirdArgument(b: 1);
$containerBuilder->thirdArgument(a: 1, b: 2);
$containerBuilder->thirdArgument(b: 1, a: 2);
$containerBuilder->thirdArgument(1, b: 2);
}
}

?>
-----
<?php

namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture;

use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeMultiArg;

class NamedArguments
{
public function run()
{
$containerBuilder = new SomeMultiArg();
$containerBuilder->firstArgument(b: 1, a: 4);
$containerBuilder->firstArgument(c: 1, a: 4);
$containerBuilder->firstArgument(b: 1, c: 2, a: 4);
$containerBuilder->firstArgument(c: 1, b: 2, a: 4);
$containerBuilder->secondArgument(a: 1, b: 5);
$containerBuilder->secondArgument(c: 1, b: 5);
$containerBuilder->secondArgument(a: 1, c: 2, b: 5);
$containerBuilder->secondArgument(c: 1, a: 2, b: 5);
$containerBuilder->secondArgument(1, c: 2, b: 5);
$containerBuilder->thirdArgument(a: 1, c: 6);
$containerBuilder->thirdArgument(b: 1, c: 6);
$containerBuilder->thirdArgument(a: 1, b: 2, c: 6);
$containerBuilder->thirdArgument(b: 1, a: 2, c: 6);
$containerBuilder->thirdArgument(1, b: 2, c: 6);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture;

use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeMultiArg;

class SkipNamedArguments
{
public function run()
{
$containerBuilder = new SomeMultiArg();
$containerBuilder->firstArgument(a: 7);
$containerBuilder->firstArgument(1, b: 1);
$containerBuilder->secondArgument(a: 7, b: 8);
$containerBuilder->secondArgument(b: 7, a: 8);
$containerBuilder->secondArgument(1, 2, c: 8);
$containerBuilder->thirdArgument(c: 8);
$containerBuilder->thirdArgument(b: 7, c: 8, a: 9);
$containerBuilder->thirdArgument(1, b: 8, c: 9);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture;

use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeMultiArg;

class SkipStaticNamedArguments extends SomeMultiArg
{
public function firstArgument($a = 1, $b = 2, $c = 3)
{
parent::firstArgument(a: 7);
parent::firstArgument(1, b: 1);
}

public function secondArgument($a = 1, $b = 2, $c = 3)
{
parent::secondArgument(a: 7, b: 8);
parent::secondArgument(b: 7, a: 8);
parent::secondArgument(1, 2, c: 8);
}

public function thirdArgument($a = 1, $b = 2, $c = 3)
{
parent::thirdArgument(c: 8);
parent::thirdArgument(b: 7, c: 8, a: 9);
parent::thirdArgument(1, b: 8, c: 9);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture;

use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeMultiArg;

class StaticNamedArguments extends SomeMultiArg
{
public function firstArgument($a = 1, $b = 2, $c = 3)
{
parent::firstArgument(b: 1);
parent::firstArgument(c: 1);
parent::firstArgument(b: 1, c: 2);
parent::firstArgument(c: 1, b: 2);
}

public function secondArgument($a = 1, $b = 2, $c = 3)
{
parent::secondArgument(a: 1);
parent::secondArgument(c: 1);
parent::secondArgument(a: 1, c: 2);
parent::secondArgument(c: 1, a: 2);
parent::secondArgument(1, c: 2);
}

public function thirdArgument($a = 1, $b = 2, $c = 3)
{
parent::thirdArgument(a: 1);
parent::thirdArgument(b: 1);
parent::thirdArgument(a: 1, b: 2);
parent::thirdArgument(b: 1, a: 2);
parent::thirdArgument(1, b: 2);
}
}

?>
-----
<?php

namespace Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Fixture;

use Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\Source\SomeMultiArg;

class StaticNamedArguments extends SomeMultiArg
{
public function firstArgument($a = 1, $b = 2, $c = 3)
{
parent::firstArgument(b: 1, a: $a);
parent::firstArgument(c: 1, a: $a);
parent::firstArgument(b: 1, c: 2, a: $a);
parent::firstArgument(c: 1, b: 2, a: $a);
}

public function secondArgument($a = 1, $b = 2, $c = 3)
{
parent::secondArgument(a: 1, b: $b);
parent::secondArgument(c: 1, b: $b);
parent::secondArgument(a: 1, c: 2, b: $b);
parent::secondArgument(c: 1, a: 2, b: $b);
parent::secondArgument(1, c: 2, b: $b);
}

public function thirdArgument($a = 1, $b = 2, $c = 3)
{
parent::thirdArgument(a: 1, c: $c);
parent::thirdArgument(b: 1, c: $c);
parent::thirdArgument(a: 1, b: 2, c: $c);
parent::thirdArgument(b: 1, a: 2, c: $c);
parent::thirdArgument(1, b: 2, c: $c);
}
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@

class SomeMultiArg
{
public function run($a = 1, $b = 2, $c = 3)
public function firstArgument($a = 1, $b = 2, $c = 3)
{
}

public function secondArgument($a = 1, $b = 2, $c = 3)
{
}

public function thirdArgument($a = 1, $b = 2, $c = 3)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
ArgumentAddingScope::SCOPE_CLASS_METHOD
),
new ArgumentAdder(SomeClass::class, 'withoutTypeOrDefaultValue', 0, 'arguments', [], $arrayType),
new ArgumentAdder(SomeMultiArg::class, 'run', 2, 'c', 4),
new ArgumentAdder(SomeMultiArg::class, 'firstArgument', 0, 'a', 4),
new ArgumentAdder(SomeMultiArg::class, 'secondArgument', 1, 'b', 5),
new ArgumentAdder(SomeMultiArg::class, 'thirdArgument', 2, 'c', 6),
new ArgumentAdder(SomeClass::class, 'someMethod', 0, 'default', 1),
new ArgumentAdderWithoutDefaultValue(WithoutDefaultValue::class, 'someMethod', 0, 'foo', $arrayType),
]);
Expand Down
46 changes: 38 additions & 8 deletions rules/Arguments/Rector/ClassMethod/ArgumentAdderRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
Expand All @@ -24,6 +25,7 @@
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Enum\ObjectReference;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeAnalyzer\ArgsAnalyzer;
use Rector\PhpParser\AstResolver;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\Rector\AbstractRector;
Expand All @@ -48,7 +50,8 @@ public function __construct(
private readonly ArgumentAddingScope $argumentAddingScope,
private readonly ChangedArgumentsDetector $changedArgumentsDetector,
private readonly AstResolver $astResolver,
private readonly StaticTypeMapper $staticTypeMapper
private readonly StaticTypeMapper $staticTypeMapper,
private readonly ArgsAnalyzer $argsAnalyzer
) {
}

Expand Down Expand Up @@ -181,13 +184,21 @@ private function processMethodCall(

$defaultValue = $argumentAdder->getArgumentDefaultValue();
$arg = new Arg(BuilderHelpers::normalizeValue($defaultValue));
if (isset($methodCall->args[$position])) {
return;

// if there are named argyments, we just add it at the end as a new named argument
if ($this->argsAnalyzer->hasNamedArg($methodCall->getArgs())) {
$argumentName = $argumentAdder->getArgumentName();
if ($argumentName === null) {
throw new ShouldNotHappenException();
}

$arg->name = new Identifier($argumentName);
} else {
$this->fillGapBetweenWithDefaultValue($methodCall, $position);
}

$this->fillGapBetweenWithDefaultValue($methodCall, $position);
$methodCall->args[] = $arg;

$methodCall->args[$position] = $arg;
$this->hasChanged = true;
}

Expand Down Expand Up @@ -249,7 +260,20 @@ private function shouldSkipParameter(
return $this->changedArgumentsDetector->isTypeChanged($param, $argumentAdder->getArgumentType());
}

if (isset($node->args[$position])) {
$arguments = $node->getArgs();
$firstNamedArgumentPosition = $this->argsAnalyzer->resolveFirstNamedArgPosition($arguments);
// If named arguments exist
if ($firstNamedArgumentPosition !== null) {
// Check if the parameter we're trying to add is before the first named argument
if ($position < $firstNamedArgumentPosition) {
return true; //if that is the case, the parameter already exists, skip
}

// Check if the parameter we're trying to add is already present as a named argument
if ($this->argsAnalyzer->resolveArgPosition($arguments, $argumentName, -1) !== -1) {
return true; // if it exists as a named argument, skip
}
} elseif (isset($node->args[$position])) {
return true;
}

Expand Down Expand Up @@ -333,9 +357,15 @@ private function processStaticCall(
return;
}

$this->fillGapBetweenWithDefaultValue($staticCall, $position);
// if there are named arguments, we just add it at the end as a new named argument
$arg = new Arg(new Variable($argumentName));
if ($this->argsAnalyzer->hasNamedArg($staticCall->getArgs())) {
$arg->name = new Identifier($argumentName);
} else {
$this->fillGapBetweenWithDefaultValue($staticCall, $position);
}

$staticCall->args[$position] = new Arg(new Variable($argumentName));
$staticCall->args[] = $arg;
$this->hasChanged = true;
}

Expand Down
17 changes: 17 additions & 0 deletions src/NodeAnalyzer/ArgsAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,21 @@ public function resolveArgPosition(array $args, string $name, int $defaultPositi

return $defaultPosition;
}

/**
* @param Arg[] $args
*/
public function resolveFirstNamedArgPosition(array $args): ?int
{
$position = 0;
foreach ($args as $arg) {
if ($arg->name instanceof Identifier) {
return $position;
}

++$position;
}

return null;
}
}