Skip to content

Commit a1dd70e

Browse files
committed
feat: add CallLike::getArg() method
This method returns the named argument that matches the given `$name`, or the positional (unnamed) argument that exists at the given `$position`, otherwise, returns `null` for first-class callables or if no match is found.
1 parent 7fc3bcf commit a1dd70e

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

lib/PhpParser/Node/Expr/CallLike.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,29 @@ public function getArgs(): array {
3232
assert(!$this->isFirstClassCallable());
3333
return $this->getRawArgs();
3434
}
35+
36+
/**
37+
* Retrieves a specific argument from the raw arguments.
38+
*
39+
* Returns the named argument that matches the given `$name`, or the
40+
* positional (unnamed) argument that exists at the given `$position`,
41+
* otherwise, returns `null` for first-class callables or if no match is found.
42+
*/
43+
public function getArg(string $name, int $position): ?Arg {
44+
if ($this->isFirstClassCallable()) {
45+
return null;
46+
}
47+
foreach ($this->getRawArgs() as $i => $arg) {
48+
if ($arg->unpack) {
49+
continue;
50+
}
51+
if (
52+
($arg->name !== null && $arg->name->toString() === $name)
53+
|| ($arg->name === null && $i === $position)
54+
) {
55+
return $arg;
56+
}
57+
}
58+
return null;
59+
}
3560
}

test/PhpParser/Node/Expr/CallableLikeTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PhpParser\Node\Expr;
44

55
use PhpParser\Node\Arg;
6+
use PhpParser\Node\Identifier;
67
use PhpParser\Node\Name;
78
use PhpParser\Node\Scalar\Int_;
89
use PhpParser\Node\VariadicPlaceholder;
@@ -18,6 +19,13 @@ public function testIsFirstClassCallable(CallLike $node, bool $isFirstClassCalla
1819
}
1920
}
2021

22+
/**
23+
* @dataProvider provideTestGetArg
24+
*/
25+
public function testGetArg(CallLike $node, ?Arg $expected): void {
26+
$this->assertSame($expected, $node->getArg('bar', 1));
27+
}
28+
2129
public static function provideTestIsFirstClassCallable() {
2230
$normalArgs = [new Arg(new Int_(1))];
2331
$callableArgs = [new VariadicPlaceholder()];
@@ -35,4 +43,56 @@ public static function provideTestIsFirstClassCallable() {
3543
[new NullsafeMethodCall(new Variable('this'), 'test', $callableArgs), true],
3644
];
3745
}
46+
47+
public static function provideTestGetArg() {
48+
$foo = new Arg(new Int_(1));
49+
$namedFoo = new Arg(new Int_(1), false, false, [], new Identifier('foo'));
50+
$bar = new Arg(new Int_(2));
51+
$namedBar = new Arg(new Int_(2), false, false, [], new Identifier('bar'));
52+
$unpack = new Arg(new Int_(3), false, true);
53+
$callableArgs = [new VariadicPlaceholder()];
54+
return [
55+
[new FuncCall(new Name('test'), [$foo]), null],
56+
[new FuncCall(new Name('test'), [$namedFoo]), null],
57+
[new FuncCall(new Name('test'), [$foo, $bar]), $bar],
58+
[new FuncCall(new Name('test'), [$namedBar]), $namedBar],
59+
[new FuncCall(new Name('test'), [$namedFoo, $namedBar]), $namedBar],
60+
[new FuncCall(new Name('test'), [$namedBar, $namedFoo]), $namedBar],
61+
[new FuncCall(new Name('test'), [$namedFoo, $unpack]), null],
62+
[new FuncCall(new Name('test'), $callableArgs), null],
63+
[new MethodCall(new Variable('this'), 'test', [$foo]), null],
64+
[new MethodCall(new Variable('this'), 'test', [$namedFoo]), null],
65+
[new MethodCall(new Variable('this'), 'test', [$foo, $bar]), $bar],
66+
[new MethodCall(new Variable('this'), 'test', [$namedBar]), $namedBar],
67+
[new MethodCall(new Variable('this'), 'test', [$namedFoo, $namedBar]), $namedBar],
68+
[new MethodCall(new Variable('this'), 'test', [$namedBar, $namedFoo]), $namedBar],
69+
[new MethodCall(new Variable('this'), 'test', [$namedFoo, $unpack]), null],
70+
[new MethodCall(new Variable('this'), 'test', $callableArgs), null],
71+
[new StaticCall(new Name('Test'), 'test', [$foo]), null],
72+
[new StaticCall(new Name('Test'), 'test', [$namedFoo]), null],
73+
[new StaticCall(new Name('Test'), 'test', [$foo, $bar]), $bar],
74+
[new StaticCall(new Name('Test'), 'test', [$namedBar]), $namedBar],
75+
[new StaticCall(new Name('Test'), 'test', [$namedFoo, $namedBar]), $namedBar],
76+
[new StaticCall(new Name('Test'), 'test', [$namedBar, $namedFoo]), $namedBar],
77+
[new StaticCall(new Name('Test'), 'test', [$namedFoo, $unpack]), null],
78+
[new StaticCall(new Name('Test'), 'test', $callableArgs), null],
79+
[new New_(new Name('test'), [$foo]), null],
80+
[new New_(new Name('test'), [$namedFoo]), null],
81+
[new New_(new Name('test'), [$foo, $bar]), $bar],
82+
[new New_(new Name('test'), [$namedBar]), $namedBar],
83+
[new New_(new Name('test'), [$namedFoo, $namedBar]), $namedBar],
84+
[new New_(new Name('test'), [$namedBar, $namedFoo]), $namedBar],
85+
[new New_(new Name('test'), [$namedFoo, $unpack]), null],
86+
[new NullsafeMethodCall(new Variable('this'), 'test', [$foo]), null],
87+
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedFoo]), null],
88+
[new NullsafeMethodCall(new Variable('this'), 'test', [$foo, $bar]), $bar],
89+
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedBar]), $namedBar],
90+
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedFoo, $namedBar]), $namedBar],
91+
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedBar, $namedFoo]), $namedBar],
92+
[new NullsafeMethodCall(new Variable('this'), 'test', [$namedFoo, $unpack]), null],
93+
// This is not legal code, but accepted by the parser.
94+
[new New_(new Name('Test'), $callableArgs), null],
95+
[new NullsafeMethodCall(new Variable('this'), 'test', $callableArgs), null],
96+
];
97+
}
3898
}

0 commit comments

Comments
 (0)