Skip to content

Commit 25ec5eb

Browse files
committed
ReflectionClass stub with lazy object methods
1 parent 1d63c05 commit 25ec5eb

10 files changed

+202
-14
lines changed

conf/config.neon

+5-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ parameters:
9898
- stdClass
9999
stubFiles:
100100
- ../stubs/ReflectionAttribute.stub
101-
- ../stubs/ReflectionClass.stub
102101
- ../stubs/ReflectionClassConstant.stub
103102
- ../stubs/ReflectionFunctionAbstract.stub
104103
- ../stubs/ReflectionMethod.stub
@@ -418,6 +417,11 @@ services:
418417
tags:
419418
- phpstan.stubFilesExtension
420419

420+
-
421+
class: PHPStan\PhpDoc\ReflectionClassStubFilesExtension
422+
tags:
423+
- phpstan.stubFilesExtension
424+
421425
-
422426
class: PHPStan\PhpDoc\ReflectionEnumStubFilesExtension
423427
tags:

src/Php/PhpVersion.php

+5
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,11 @@ public function supportsAsymmetricVisibility(): bool
362362
return $this->versionId >= 80400;
363363
}
364364

365+
public function supportsLazyObjects(): bool
366+
{
367+
return $this->versionId >= 80400;
368+
}
369+
365370
public function hasDateTimeExceptions(): bool
366371
{
367372
return $this->versionId >= 80300;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDoc;
4+
5+
use PHPStan\Php\PhpVersion;
6+
7+
final class ReflectionClassStubFilesExtension implements StubFilesExtension
8+
{
9+
10+
public function __construct(private PhpVersion $phpVersion)
11+
{
12+
}
13+
14+
public function getFiles(): array
15+
{
16+
if (!$this->phpVersion->supportsLazyObjects()) {
17+
return [
18+
__DIR__ . '/../../stubs/ReflectionClass.stub',
19+
];
20+
}
21+
22+
return [
23+
__DIR__ . '/../../stubs/ReflectionClassWithLazyObjects.stub',
24+
];
25+
}
26+
27+
}

src/PhpDoc/ReflectionEnumStubFilesExtension.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ public function getFiles(): array
1717
return [];
1818
}
1919

20-
return [__DIR__ . '/../../stubs/ReflectionEnum.stub'];
20+
if (!$this->phpVersion->supportsLazyObjects()) {
21+
return [__DIR__ . '/../../stubs/ReflectionEnum.stub'];
22+
}
23+
24+
return [__DIR__ . '/../../stubs/ReflectionEnumWithLazyObjects.stub'];
2125
}
2226

2327
}
+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
/**
4+
* @template T of object
5+
*/
6+
class ReflectionClass
7+
{
8+
9+
/**
10+
* @readonly
11+
* @var class-string<T>
12+
*/
13+
public $name;
14+
15+
/**
16+
* @param T|class-string<T> $argument
17+
* @throws ReflectionException
18+
*/
19+
public function __construct($argument) {}
20+
21+
/**
22+
* @return class-string<T>
23+
*/
24+
public function getName() : string;
25+
26+
/**
27+
* @param mixed ...$args
28+
*
29+
* @return T
30+
*/
31+
public function newInstance(...$args) {}
32+
33+
/**
34+
* @param array<int|string, mixed> $args
35+
*
36+
* @return T
37+
*/
38+
public function newInstanceArgs(array $args) {}
39+
40+
/**
41+
* @return T
42+
*/
43+
public function newInstanceWithoutConstructor();
44+
45+
/**
46+
* @return list<ReflectionAttribute<object>>
47+
*/
48+
public function getAttributes(?string $name = null, int $flags = 0)
49+
{
50+
}
51+
52+
/**
53+
* @param callable(T): void $initializer
54+
* @return T
55+
*/
56+
public function newLazyGhost(callable $initializer, int $options = 0): object
57+
{
58+
}
59+
60+
/**
61+
* @param callable(T): T $factory
62+
* @return T
63+
*/
64+
public function newLazyProxy(callable $factory, int $options = 0): object
65+
{
66+
}
67+
68+
/**
69+
* @param T $object
70+
* @param callable(T): void $initializer
71+
*/
72+
public function resetAsLazyGhost(object $object, callable $initializer, int $options = 0): void
73+
{
74+
}
75+
76+
/**
77+
* @param T $object
78+
* @param callable(T): T $factory
79+
*/
80+
public function resetAsLazyProxy(object $object, callable $factory, int $options = 0): void
81+
{
82+
}
83+
84+
/**
85+
* @param T $object
86+
* @return T
87+
*/
88+
public function initializeLazyObject(object $object): object
89+
{
90+
}
91+
92+
/**
93+
* @param T $object
94+
* @return T
95+
*/
96+
public function markLazyObjectAsInitialized(object $object): object
97+
{
98+
}
99+
100+
/**
101+
* @param T $object
102+
*/
103+
public function getLazyInitializer(object $object): ?callable
104+
{
105+
}
106+
107+
/**
108+
* @param T $object
109+
*/
110+
public function isUninitializedLazyObject(object $object): bool
111+
{
112+
}
113+
}

stubs/ReflectionEnum.stub

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ReflectionEnum extends ReflectionClass
1919
public function getCase(string $name): ReflectionEnumUnitCase {}
2020

2121
/**
22-
* @phpstan-assert-if-true self<BackedEnum> $this
22+
* @phpstan-assert-if-true self<T&BackedEnum> $this
2323
* @phpstan-assert-if-true !null $this->getBackingType()
2424
*/
2525
public function isBacked(): bool {}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/**
4+
* @template T of UnitEnum
5+
* @extends ReflectionClass<T>
6+
*/
7+
class ReflectionEnum extends ReflectionClass
8+
{
9+
10+
/**
11+
* @return (T is BackedEnum ? ReflectionEnumBackedCase[] : ReflectionEnumUnitCase[])
12+
*/
13+
public function getCases(): array {}
14+
15+
/**
16+
* @return (T is BackedEnum ? ReflectionEnumBackedCase : ReflectionEnumUnitCase)
17+
* @throws ReflectionException
18+
*/
19+
public function getCase(string $name): ReflectionEnumUnitCase {}
20+
21+
/**
22+
* @phpstan-assert-if-true self<T&BackedEnum> $this
23+
* @phpstan-assert-if-true !null $this->getBackingType()
24+
*/
25+
public function isBacked(): bool {}
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php // lint < 8.4
2+
3+
namespace EnumReflection;
4+
5+
use ReflectionEnum;
6+
use UnitEnum;
7+
use function PHPStan\Testing\assertType;
8+
9+
/** @param class-string<UnitEnum> $class */
10+
function testNarrowGetNameTypeAfterIsBacked(string $class) {
11+
$r = new ReflectionEnum($class);
12+
assertType('class-string<UnitEnum>', $r->getName());
13+
if ($r->isBacked()) {
14+
assertType('class-string<BackedEnum>', $r->getName());
15+
}
16+
}

tests/PHPStan/Analyser/nsrt/enum-reflection.php

-9
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,3 @@ public function doFoo(): void
4141
}
4242

4343
}
44-
45-
/** @param class-string<UnitEnum> $class */
46-
function testNarrowGetNameTypeAfterIsBacked(string $class) {
47-
$r = new ReflectionEnum($class);
48-
assertType('class-string<UnitEnum>', $r->getName());
49-
if ($r->isBacked()) {
50-
assertType('class-string<BackedEnum>', $r->getName());
51-
}
52-
}

tests/PHPStan/Type/Generic/GenericObjectTypeTest.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Traversable;
2727
use function array_map;
2828
use function sprintf;
29+
use const PHP_VERSION_ID;
2930

3031
class GenericObjectTypeTest extends PHPStanTestCase
3132
{
@@ -134,7 +135,7 @@ public function dataIsSuperTypeOf(): array
134135
new GenericObjectType(ReflectionClass::class, [
135136
new ObjectType(stdClass::class),
136137
]),
137-
TrinaryLogic::createYes(),
138+
PHP_VERSION_ID >= 80400 ? TrinaryLogic::createNo() : TrinaryLogic::createYes(),
138139
],
139140
[
140141
new GenericObjectType(ReflectionClass::class, [
@@ -143,7 +144,7 @@ public function dataIsSuperTypeOf(): array
143144
new GenericObjectType(ReflectionClass::class, [
144145
new ObjectWithoutClassType(),
145146
]),
146-
TrinaryLogic::createMaybe(),
147+
PHP_VERSION_ID >= 80400 ? TrinaryLogic::createNo() : TrinaryLogic::createMaybe(),
147148
],
148149
[
149150
new GenericObjectType(ReflectionClass::class, [

0 commit comments

Comments
 (0)