Skip to content

Commit e70037e

Browse files
committed
Fix signatures for functions inside annotations when using PHP 7
1 parent 70af1ed commit e70037e

File tree

3 files changed

+69
-5
lines changed

3 files changed

+69
-5
lines changed

ChangeLog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ XP Reflection ChangeLog
33

44
## ?.?.? / ????-??-??
55

6+
* Fixed parameter default values, by-reference and variadic markers,
7+
and parameter and return types being swallowed for functions inside
8+
annotations when using PHP 7
9+
(@thekid)
10+
611
## 2.13.4 / 2023-06-25
712

813
* Fixed parsing global imports and grouped imports containing aliases,

src/main/php/lang/meta/FromSyntaxTree.class.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,20 @@ private function parse($code, $resolver) {
8383

8484
$params= '';
8585
foreach ($signature->parameters as $param) {
86-
$params.= ', $'.$param->name;
86+
$params.=
87+
', '.
88+
($param->type ? $param->type->literal() : '').
89+
($param->variadic ? '...' : '').
90+
($param->reference ? ' &$': ' $').
91+
$param->name.
92+
($param->default ? '='.$param->default->expression : '')
93+
;
8794
}
95+
$return= $signature->returns ? ':'.$signature->returns->literal() : '';
8896
if (0 === strncmp($code, ' throw ', 7)) {
89-
return new Code('function('.substr($params, 2).') {'.$code.'; }');
97+
return new Code('function('.substr($params, 2).')'.$return.' {'.$code.'; }');
9098
} else {
91-
return new Code('function('.substr($params, 2).') { return'.$code.'; }');
99+
return new Code('function('.substr($params, 2).')'.$return.' { return'.$code.'; }');
92100
}
93101
});
94102

@@ -114,9 +122,17 @@ private function parse($code, $resolver) {
114122

115123
$params= '';
116124
foreach ($signature->parameters as $param) {
117-
$params.= ', $'.$param->name;
125+
$params.=
126+
', '.
127+
($param->type ? $param->type->literal() : '').
128+
($param->variadic ? '...' : '').
129+
($param->reference ? ' &$': ' $').
130+
$param->name.
131+
($param->default ? '='.$param->default->expression : '')
132+
;
118133
}
119-
return new Code('function('.substr($params, 2).')'.$code.' }');
134+
$return= $signature->returns ? ':'.$signature->returns->literal() : '';
135+
return new Code('function('.substr($params, 2).')'.$return.$code.' }');
120136
};
121137

122138
// Function expressions and function expressions used as statement

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php namespace lang\reflection\unittest;
22

3+
use ReflectionFunction;
34
use lang\reflection\{Annotation, CannotInstantiate, InvocationFailed};
45
use lang\{IllegalStateException, Reflection, XPClass};
56
use test\{Assert, Expect, Test, Values};
@@ -170,6 +171,48 @@ public function with_array_lambda() {
170171
Assert::equals(6100, $f[0]());
171172
}
172173

174+
#[Test, Values(['function(\$param= null) { }', 'fn(\$param= null) => null'])]
175+
public function with_optional_param($function) {
176+
$t= $this->declare('{}', '#[Annotated(eval: "'.$function.'")]');
177+
$f= $t->annotation(Annotated::class)->argument(0);
178+
179+
Assert::true((new ReflectionFunction($f))->getParameters()[0]->isOptional());
180+
}
181+
182+
#[Test, Values(['function(... \$param) { }', 'fn(... \$param) => null'])]
183+
public function with_variadic_param($function) {
184+
$t= $this->declare('{}', '#[Annotated(eval: "'.$function.'")]');
185+
$f= $t->annotation(Annotated::class)->argument(0);
186+
187+
Assert::true((new ReflectionFunction($f))->getParameters()[0]->isVariadic());
188+
}
189+
190+
#[Test, Values(['function(&\$param) { }', 'fn(&\$param) => null'])]
191+
public function with_reference_param($function) {
192+
$t= $this->declare('{}', '#[Annotated(eval: "'.$function.'")]');
193+
$f= $t->annotation(Annotated::class)->argument(0);
194+
195+
Assert::true((new ReflectionFunction($f))->getParameters()[0]->isPassedByReference());
196+
}
197+
198+
#[Test, Values(['function(int \$param) { }', 'fn(int \$param) => null'])]
199+
public function with_param_type($function) {
200+
$t= $this->declare('{}', '#[Annotated(eval: "'.$function.'")]');
201+
$f= $t->annotation(Annotated::class)->argument(0);
202+
203+
$type= (new ReflectionFunction($f))->getParameters()[0]->getType();
204+
Assert::equals('int', PHP_VERSION_ID >= 70100 ? $type->getName() : (string)$type);
205+
}
206+
207+
#[Test, Values(['function(): int { return 6100; }', 'fn(): int => 6100'])]
208+
public function with_return_type($function) {
209+
$t= $this->declare('{}', '#[Annotated(eval: "'.$function.'")]');
210+
$f= $t->annotation(Annotated::class)->argument(0);
211+
212+
$type= (new ReflectionFunction($f))->getReturnType();
213+
Assert::equals('int', PHP_VERSION_ID >= 70100 ? $type->getName() : (string)$type);
214+
}
215+
173216
#[Test]
174217
public function multiple() {
175218
$t= $this->declare('{}', '#[Annotated, Enumeration([])]');

0 commit comments

Comments
 (0)