Skip to content

Commit 77d15c2

Browse files
authored
Merge pull request #2 from eboreum/1.1.0
ClosureFormatter: Added return type to produced output
2 parents ae53195 + e63104a commit 77d15c2

File tree

2 files changed

+104
-44
lines changed

2 files changed

+104
-44
lines changed

src/Formatter/Object_/ClosureFormatter.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,22 @@ public function format(CasterInterface $caster, object $object): ?string
8787
$arguments[] = $argument;
8888
}
8989

90-
return sprintf(
90+
$reflectionReturnType = $reflectionFunction->getReturnType();
91+
92+
$return = sprintf(
9193
'%s(%s)',
9294
Caster::makeNormalizedClassName(new \ReflectionObject($object)),
9395
implode(', ', $arguments)
9496
);
97+
98+
if ($reflectionReturnType) {
99+
$return .= sprintf(
100+
': %s',
101+
(string)$reflectionReturnType,
102+
);
103+
}
104+
105+
return $return;
95106
}
96107

97108
/**

tests/tests/Test/Unit/Formatter/Object_/ClosureFormatterTest.php

Lines changed: 92 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
namespace Test\Unit\Eboreum\Caster\Formatter\Object_;
66

7+
use ArrayIterator;
78
use Eboreum\Caster\Caster;
89
use Eboreum\Caster\Formatter\Object_\ClosureFormatter;
10+
use Iterator;
911
use PHPUnit\Framework\TestCase;
12+
use Traversable;
1013

1114
class ClosureFormatterTest extends TestCase
1215
{
@@ -25,7 +28,7 @@ public function testFormatReturnsNullWhenObjectIsNotQualified(): void
2528
/**
2629
* @dataProvider dataProvider_testFormatWorks
2730
*/
28-
public function testFormatWorks(string $message, \Closure $closure, string $expectedArguments): void
31+
public function testFormatWorks(string $message, string $expected, \Closure $closure): void
2932
{
3033
$caster = Caster::create();
3134
$closureFormatter = new ClosureFormatter();
@@ -34,95 +37,141 @@ public function testFormatWorks(string $message, \Closure $closure, string $expe
3437
$formatted = $closureFormatter->format($caster, $closure);
3538
$this->assertIsString($formatted);
3639
assert(is_string($formatted)); // Make phpstan happy
37-
$this->assertMatchesRegularExpression(
38-
sprintf(
39-
implode('', [
40-
'/',
41-
'^',
42-
'\\\\Closure\(%s\)',
43-
'$',
44-
'/',
45-
]),
46-
preg_quote($expectedArguments, '/'),
47-
),
48-
$formatted,
49-
$message,
50-
);
40+
$this->assertSame($expected, $formatted, $message);
5141
}
5242

5343
/**
54-
* @return array<int, array{string, \Closure, string}>
44+
* @return array<int, array{string, string, \Closure}>
5545
*/
5646
public function dataProvider_testFormatWorks(): array
5747
{
5848
return [
5949
[
60-
'\Closure with no arguments.',
50+
'\Closure with no arguments and no return type.',
51+
'\\Closure()',
6152
static function () {},
62-
'',
6353
],
6454
[
65-
'\Closure with 1 argument. No default value.',
55+
'\Closure with 1 argument. No default value. No return type.',
56+
'\\Closure(int $a)',
6657
static function (int $a) {},
67-
'int $a',
6858
],
6959
[
70-
'\Closure with 1 argument. With default value.',
60+
'\Closure with 1 argument. With default value. No return type.',
61+
'\\Closure(int $a = 42)',
7162
static function (int $a = 42) {},
72-
'int $a = 42',
7363
],
7464
[
75-
'\Closure with 1 argument. With default value being a global constant.',
65+
'\Closure with 1 argument. With default value being a global constant. No return type.',
66+
'\\Closure(int $a = PHP_INT_MAX)',
7667
static function (int $a = \PHP_INT_MAX) {},
77-
'int $a = PHP_INT_MAX',
7868
],
7969
[
80-
'\Closure with 1 argument. With default value being a constant with a `self` reference.',
70+
implode('', [
71+
'\Closure with 1 argument. With default value being a constant with a `self` reference. No return',
72+
' type.',
73+
]),
74+
'\\Closure(int $a = self::A_CONSTANT)',
8175
static function (int $a = self::A_CONSTANT) {},
82-
'int $a = self::A_CONSTANT',
8376
],
8477
[
85-
'\Closure with 1 argument. With default value being a constant with a class name reference.',
86-
static function (int $a = ClosureFormatterTest::A_CONSTANT) {},
78+
implode('', [
79+
'\Closure with 1 argument. With default value being a constant with a class name reference. No',
80+
' return type.',
81+
]),
8782
sprintf(
88-
'int $a = \\%s::A_CONSTANT',
83+
'\\Closure(int $a = \\%s::A_CONSTANT)',
8984
ClosureFormatterTest::class,
9085
),
86+
static function (int $a = ClosureFormatterTest::A_CONSTANT) {},
9187
],
9288
[
93-
'\Closure with 3 arguments. No default values.',
89+
'\Closure with 3 arguments. No default values. No return type.',
90+
'\\Closure(int $a, string $b, bool $c)',
9491
static function (int $a, string $b, bool $c) {},
95-
'int $a, string $b, bool $c',
9692
],
9793
[
98-
'\Closure with 3 arguments. With 3 default values.',
94+
'\Closure with 3 arguments. With 3 default values. No return type.',
95+
'\\Closure(int $a = 42, string $b = "foo", bool $c = true)',
9996
static function (int $a = 42, string $b = 'foo', bool $c = true) {},
100-
'int $a = 42, string $b = "foo", bool $c = true',
10197
],
10298
[
103-
'\Closure with 1 typed variadic argument.',
99+
'\Closure with 1 typed variadic argument. No return type.',
100+
'\\Closure(int ...$a)',
104101
static function (int ...$a) {},
105-
'int ...$a',
106102
],
107103
[
108-
'\Closure with 1 typed variadic argument being nullable.',
104+
'\Closure with 1 typed variadic argument being nullable. No return type.',
105+
'\\Closure(?int ...$a)',
109106
static function (?int ...$a) {},
110-
'?int ...$a',
111107
],
112108
[
113-
'\Closure with 1 typed argument passed by reference.',
109+
'\Closure with 1 typed argument passed by reference. No return type.',
110+
'\\Closure(int &$a)',
114111
static function (int &$a) {},
115-
'int &$a',
116112
],
117113
[
118-
'\Closure with 1 typed argument passed by reference being nullable.',
114+
'\Closure with 1 typed argument passed by reference being nullable. No return type.',
115+
'\\Closure(?int &$a)',
119116
static function (?int &$a) {},
120-
'?int &$a',
117+
],
118+
[
119+
'\Closure with no arguments Return type "int".',
120+
'\\Closure(): int',
121+
static function (): int {
122+
return 1; // phpstan love
123+
},
124+
],
125+
[
126+
'\Closure with no arguments Return type "static".',
127+
'\\Closure(): static',
128+
function (): static {
129+
return $this; // phpstan love
130+
},
131+
],
132+
[
133+
'\Closure with no arguments Return type "?int".',
134+
'\\Closure(): ?int',
135+
static function (): ?int {
136+
return rand(0, 1) === 1 ? 1 : null; // phpstan love
137+
},
138+
],
139+
[
140+
'\Closure with no arguments Return type "int|null" (union). Must get shorted to "?int".',
141+
'\\Closure(): ?int',
142+
static function (): int|null {
143+
return rand(0, 1) === 1 ? 1 : null; // phpstan love
144+
},
145+
],
146+
[
147+
'\Closure with no arguments Return type "int|float|string". Must get normalized to "string|int|float".',
148+
'\\Closure(): string|int|float',
149+
static function (): int|float|string {
150+
// phpstan love
151+
152+
switch (rand(0, 2)) {
153+
case 0:
154+
return 3.14;
155+
case 1:
156+
return 42;
157+
}
158+
159+
return 'foo';
160+
},
161+
],
162+
[
163+
'\Closure with no arguments Return type "Traversable&Iterator" (intersection).',
164+
'\\Closure(): Traversable&Iterator',
165+
static function (): Traversable&Iterator {
166+
return new ArrayIterator([]); // phpstan love
167+
},
121168
],
122169
[
123170
'The big one.',
124-
static function ($a, &$b, int $c, bool $d, \stdClass $e, array $f = ['lala'], ?string ...$z) {},
125-
'$a, &$b, int $c, bool $d, \\stdClass $e, array $f = [0 => "lala"], ?string ...$z',
171+
'\\Closure($a, &$b, int $c, bool $d, \\stdClass $e, array $f = [0 => "lala"], ?string ...$z): int',
172+
static function ($a, &$b, int $c, bool $d, \stdClass $e, array $f = ['lala'], ?string ...$z): int {
173+
return 1; // phpstan love
174+
},
126175
],
127176
];
128177
}

0 commit comments

Comments
 (0)