Skip to content

Commit 6e7dc59

Browse files
ainesophaurbrendt
andauthored
fix: nullable properties not seen as nullable by TypeReflector (#591)
Co-authored-by: Brent Roose <[email protected]>
1 parent b16f797 commit 6e7dc59

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

src/Tempest/Reflection/src/TypeReflector.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,13 @@
3939

4040
private string $cleanDefinition;
4141

42+
private bool $isNullable;
43+
4244
public function __construct(
4345
private PHPReflector|PHPReflectionType|string $reflector,
4446
) {
4547
$this->definition = $this->resolveDefinition($this->reflector);
48+
$this->isNullable = $this->resolveIsNullable($this->reflector);
4649
$this->cleanDefinition = str_replace('?', '', $this->definition);
4750
}
4851

@@ -62,7 +65,7 @@ public function equals(string|TypeReflector $type): bool
6265

6366
public function accepts(mixed $input): bool
6467
{
65-
if ($this->isNullable() && $input === null) {
68+
if ($this->isNullable && $input === null) {
6669
return true;
6770
}
6871

@@ -152,8 +155,7 @@ public function isIterable(): bool
152155

153156
public function isNullable(): bool
154157
{
155-
return str_contains($this->definition, '?')
156-
|| str_contains($this->definition, 'null');
158+
return $this->isNullable;
157159
}
158160

159161
/** @return self[] */
@@ -202,4 +204,24 @@ private function resolveDefinition(PHPReflector|PHPReflectionType|string $reflec
202204

203205
throw new Exception('Could not resolve type');
204206
}
207+
208+
private function resolveIsNullable(PHPReflectionType|PHPReflector|string $reflector): bool
209+
{
210+
if (is_string($reflector)) {
211+
return str_contains($this->definition, '?') || str_contains($this->definition, 'null');
212+
}
213+
214+
if (
215+
$reflector instanceof PHPReflectionParameter
216+
|| $reflector instanceof PHPReflectionProperty
217+
) {
218+
return $reflector->getType()->allowsNull();
219+
}
220+
221+
if ($reflector instanceof PHPReflectionType) {
222+
return $reflector->allowsNull();
223+
}
224+
225+
return false;
226+
}
205227
}

src/Tempest/Reflection/tests/ClassReflectorTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use ReflectionClass;
99
use Tempest\Reflection\ClassReflector;
1010
use Tempest\Reflection\Tests\Fixtures\TestClassA;
11+
use Tempest\Reflection\Tests\Fixtures\TestClassB;
1112

1213
/**
1314
* @internal
@@ -36,4 +37,10 @@ public function test_getting_short_name(): void
3637

3738
$this->assertSame($reflector->getShortName(), $reflection->getShortName());
3839
}
40+
41+
public function test_nullable_property_type(): void
42+
{
43+
$reflector = new ClassReflector(TestClassB::class);
44+
$this->assertTrue($reflector->getProperty('name')->isNullable());
45+
}
3946
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Reflection\Tests\Fixtures;
6+
7+
final class TestClassB
8+
{
9+
public function __construct(
10+
public ?string $name,
11+
) {
12+
}
13+
}

0 commit comments

Comments
 (0)