Skip to content

Commit ae92215

Browse files
committed
Fix missing named and unnamed parameters in PHP 7
1 parent ad1ad91 commit ae92215

File tree

3 files changed

+99
-17
lines changed

3 files changed

+99
-17
lines changed

src/main/php/lang/reflection/Routine.class.php

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
use Error, ReflectionException, ReflectionUnionType, ReflectionIntersectionType;
44
use lang\{Reflection, Type, TypeUnion};
55

6-
/** Base class for methods and constructors */
6+
/**
7+
* Base class for methods and constructors
8+
*
9+
* @test lang.reflection.unittest.ArgumentPassingTest
10+
*/
711
abstract class Routine extends Member {
812

913
/** @return [:var] */
@@ -90,23 +94,26 @@ public function parameters(): Parameters {
9094

9195
/** Support named arguments for PHP 7.X */
9296
public static function pass($reflect, $args) {
93-
if (is_string(key($args))) {
94-
$pass= [];
95-
foreach ($reflect->getParameters() as $param) {
96-
if (isset($args[$param->name])) {
97-
$pass[]= $args[$param->name];
98-
} else if ($param->isOptional()) {
99-
$pass[]= $param->getDefaultValue();
100-
} else {
101-
throw new ReflectionException('Missing parameter $'.$param->name);
102-
}
97+
$pass= [];
98+
foreach ($reflect->getParameters() as $i => $param) {
99+
if (isset($args[$param->name])) {
100+
$pass[]= $args[$param->name];
103101
unset($args[$param->name]);
102+
} else if (isset($args[$i])) {
103+
$pass[]= $args[$i];
104+
unset($args[$i]);
105+
} else if ($param->isOptional()) {
106+
$pass[]= $param->getDefaultValue();
107+
} else {
108+
throw new ReflectionException('Missing parameter $'.$param->name);
104109
}
105-
if ($args) {
106-
throw new Error('Unknown named parameter $'.key($args));
107-
}
108-
return $pass;
109110
}
110-
return $args;
111+
112+
// Check for excess named parameters
113+
if ($args && is_string($excess= key($args))) {
114+
throw new Error('Unknown named parameter $'.$excess);
115+
}
116+
117+
return $pass;
111118
}
112119
}

src/test/php/lang/reflection/unittest/AnnotationTest.class.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ public function instantiation() {
273273
Assert::instance(Annotated::class, $t->annotation(Annotated::class)->newInstance());
274274
}
275275

276-
#[Test, Values(['#[Parameterized(1, 2)]', '#[Parameterized(a: 1, b: 2)]', '#[Parameterized(b: 2, a: 1)]'])]
276+
#[Test, Values(['#[Parameterized(1, 2)]', '#[Parameterized(1, b: 2)]', '#[Parameterized(a: 1, b: 2)]', '#[Parameterized(b: 2, a: 1)]'])]
277277
public function parameterized_instantiation($declaration) {
278278
$t= $this->declare('{}', $declaration);
279279
Assert::equals(new Parameterized(1, 2), $t->annotation(Parameterized::class)->newInstance());
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php namespace lang\reflection\unittest;
2+
3+
use ReflectionFunction;
4+
use lang\Error;
5+
use lang\reflection\Routine;
6+
use test\{Assert, Expect, Test};
7+
8+
class ArgumentPassingTest {
9+
10+
#[Test]
11+
public function pass_ordered() {
12+
$f= new ReflectionFunction(fn($a, $b) => null);
13+
Assert::equals([1, 2], Routine::pass($f, [1, 2]));
14+
}
15+
16+
#[Test, Expect(class: Error::class, message: 'Missing parameter $a')]
17+
public function missing() {
18+
$f= new ReflectionFunction(fn($a, $b) => null);
19+
Routine::pass($f, []);
20+
}
21+
22+
#[Test, Expect(class: Error::class, message: 'Missing parameter $b')]
23+
public function missing_ordered() {
24+
$f= new ReflectionFunction(fn($a, $b) => null);
25+
Routine::pass($f, [1]);
26+
}
27+
28+
#[Test]
29+
public function pass_named() {
30+
$f= new ReflectionFunction(fn($a, $b) => null);
31+
Assert::equals([1, 2], Routine::pass($f, ['a' => 1, 'b' => 2]));
32+
}
33+
34+
#[Test]
35+
public function pass_named_out_of_order() {
36+
$f= new ReflectionFunction(fn($a, $b) => null);
37+
Assert::equals([1, 2], Routine::pass($f, ['b' => 2, 'a' => 1]));
38+
}
39+
40+
#[Test, Expect(class: Error::class, message: 'Missing parameter $b')]
41+
public function missing_named() {
42+
$f= new ReflectionFunction(fn($a, $b) => null);
43+
Routine::pass($f, ['a' => 1]);
44+
}
45+
46+
#[Test, Expect(class: Error::class, message: 'Unknown named parameter $unknown')]
47+
public function unknown_named() {
48+
$f= new ReflectionFunction(fn($a, $b) => null);
49+
Routine::pass($f, ['a' => 1, 'b' => 2, 'unknown' => null]);
50+
}
51+
52+
#[Test]
53+
public function pass_named_and_ordered() {
54+
$f= new ReflectionFunction(fn($a, $b) => null);
55+
Assert::equals([1, 2], Routine::pass($f, [1, 'b' => 2]));
56+
}
57+
58+
#[Test]
59+
public function pass_too_many() {
60+
$f= new ReflectionFunction(fn($a, $b) => null);
61+
Assert::equals([1, 2], Routine::pass($f, [1, 2, 3]));
62+
}
63+
64+
#[Test]
65+
public function pass_optional() {
66+
$f= new ReflectionFunction(fn($a, $b= 0) => null);
67+
Assert::equals([1, 2], Routine::pass($f, [1, 2]));
68+
}
69+
70+
#[Test]
71+
public function pass_without_optional() {
72+
$f= new ReflectionFunction(fn($a, $b= 0) => null);
73+
Assert::equals([1, 0], Routine::pass($f, [1]));
74+
}
75+
}

0 commit comments

Comments
 (0)