Skip to content

Commit 368fc06

Browse files
authored
Merge pull request #827 from kukulich/enum
PHP 8.1: Enums
2 parents 580b81d + 67c673c commit 368fc06

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3687
-61
lines changed

infection.bootstrap.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@
22

33
declare(strict_types=1);
44

5+
require __DIR__ . '/stubs/UnitEnum.php';
6+
require __DIR__ . '/stubs/BackedEnum.php';
7+
8+
require __DIR__ . '/stubs/ReflectionEnum.php';
9+
require __DIR__ . '/stubs/ReflectionEnumUnitCase.php';
10+
require __DIR__ . '/stubs/ReflectionEnumBackedCase.php';
511
require __DIR__ . '/stubs/ReflectionIntersectionType.php';

phpstan.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ parameters:
66
- src
77

88
bootstrapFiles:
9+
- stubs/enum_exists.php
10+
- stubs/UnitEnum.php
11+
- stubs/BackedEnum.php
12+
- stubs/ReflectionEnum.php
13+
- stubs/ReflectionEnumUnitCase.php
14+
- stubs/ReflectionEnumBackedCase.php
915
- stubs/ReflectionIntersectionType.php
1016

1117
# Needs new PHPStan version with newer stubs

psalm.bootstrap.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@
22

33
declare(strict_types=1);
44

5+
require __DIR__ . '/stubs/UnitEnum.php';
6+
require __DIR__ . '/stubs/BackedEnum.php';
7+
8+
require __DIR__ . '/stubs/ReflectionEnum.php';
9+
require __DIR__ . '/stubs/ReflectionEnumUnitCase.php';
10+
require __DIR__ . '/stubs/ReflectionEnumBackedCase.php';
511
require __DIR__ . '/stubs/ReflectionIntersectionType.php';

src/NodeCompiler/CompilerContext.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Roave\BetterReflection\Reflection\ReflectionClass;
88
use Roave\BetterReflection\Reflection\ReflectionClassConstant;
99
use Roave\BetterReflection\Reflection\ReflectionConstant;
10+
use Roave\BetterReflection\Reflection\ReflectionEnumCase;
1011
use Roave\BetterReflection\Reflection\ReflectionFunction;
1112
use Roave\BetterReflection\Reflection\ReflectionFunctionAbstract;
1213
use Roave\BetterReflection\Reflection\ReflectionMethod;
@@ -21,7 +22,7 @@ class CompilerContext
2122
{
2223
public function __construct(
2324
private Reflector $reflector,
24-
private ReflectionClass|ReflectionProperty|ReflectionClassConstant|ReflectionMethod|ReflectionFunction|ReflectionParameter|ReflectionConstant $contextReflection,
25+
private ReflectionClass|ReflectionProperty|ReflectionClassConstant|ReflectionEnumCase|ReflectionMethod|ReflectionFunction|ReflectionParameter|ReflectionConstant $contextReflection,
2526
) {
2627
}
2728

@@ -66,6 +67,10 @@ public function getClass(): ?ReflectionClass
6667
return $this->contextReflection->getDeclaringClass();
6768
}
6869

70+
if ($this->contextReflection instanceof ReflectionEnumCase) {
71+
return $this->contextReflection->getDeclaringClass();
72+
}
73+
6974
return $this->contextReflection->getImplementingClass();
7075
}
7176

src/Reflection/Adapter/ReflectionClass.php

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use Roave\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute;
1313
use Roave\BetterReflection\Reflection\ReflectionClass as BetterReflectionClass;
1414
use Roave\BetterReflection\Reflection\ReflectionClassConstant as BetterReflectionClassConstant;
15+
use Roave\BetterReflection\Reflection\ReflectionEnum as BetterReflectionEnum;
16+
use Roave\BetterReflection\Reflection\ReflectionEnumCase as BetterReflectionEnumCase;
1517
use Roave\BetterReflection\Reflection\ReflectionMethod as BetterReflectionMethod;
1618
use Roave\BetterReflection\Reflection\ReflectionProperty as BetterReflectionProperty;
1719
use Roave\BetterReflection\Util\FileHelper;
@@ -20,13 +22,14 @@
2022
use function array_filter;
2123
use function array_map;
2224
use function array_values;
25+
use function constant;
2326
use function func_num_args;
2427
use function sprintf;
2528
use function strtolower;
2629

2730
final class ReflectionClass extends CoreReflectionClass
2831
{
29-
public function __construct(private BetterReflectionClass $betterReflectionClass)
32+
public function __construct(private BetterReflectionClass|BetterReflectionEnum $betterReflectionClass)
3033
{
3134
unset($this->name);
3235
}
@@ -154,24 +157,48 @@ public function getProperties(?int $filter = null): array
154157

155158
public function hasConstant(string $name): bool
156159
{
160+
if ($this->betterReflectionClass instanceof BetterReflectionEnum && $this->betterReflectionClass->hasCase($name)) {
161+
return true;
162+
}
163+
157164
return $this->betterReflectionClass->hasConstant($name);
158165
}
159166

160167
/**
161-
* @return array<string, scalar|array<scalar>|null>
168+
* @return array<string, mixed|null>
162169
*/
163170
public function getConstants(?int $filter = null): array
164171
{
165-
return array_map(static fn (BetterReflectionClassConstant $betterConstant) => $betterConstant->getValue(), $this->filterBetterReflectionClassConstants($filter));
172+
return array_map(
173+
fn (BetterReflectionClassConstant|BetterReflectionEnumCase $betterConstantOrEnumCase): mixed => $this->getConstantValue($betterConstantOrEnumCase),
174+
$this->filterBetterReflectionClassConstants($filter),
175+
);
166176
}
167177

168178
public function getConstant(string $name): mixed
169179
{
180+
if ($this->betterReflectionClass instanceof BetterReflectionEnum && $this->betterReflectionClass->hasCase($name)) {
181+
return $this->getConstantValue($this->betterReflectionClass->getCase($name));
182+
}
183+
170184
return $this->betterReflectionClass->getConstant($name);
171185
}
172186

187+
private function getConstantValue(BetterReflectionClassConstant|BetterReflectionEnumCase $betterConstantOrEnumCase): mixed
188+
{
189+
if ($betterConstantOrEnumCase instanceof BetterReflectionEnumCase) {
190+
return constant(sprintf('%s::%s', $betterConstantOrEnumCase->getDeclaringClass()->getName(), $betterConstantOrEnumCase->getName()));
191+
}
192+
193+
return $betterConstantOrEnumCase->getValue();
194+
}
195+
173196
public function getReflectionConstant(string $name): ReflectionClassConstant|false
174197
{
198+
if ($this->betterReflectionClass instanceof BetterReflectionEnum && $this->betterReflectionClass->hasCase($name)) {
199+
return new ReflectionClassConstant($this->betterReflectionClass->getCase($name));
200+
}
201+
175202
$betterReflectionConstant = $this->betterReflectionClass->getReflectionConstant($name);
176203
if ($betterReflectionConstant === null) {
177204
return false;
@@ -185,11 +212,14 @@ public function getReflectionConstant(string $name): ReflectionClassConstant|fal
185212
*/
186213
public function getReflectionConstants(?int $filter = null): array
187214
{
188-
return array_values(array_map(static fn (BetterReflectionClassConstant $betterConstant): ReflectionClassConstant => new ReflectionClassConstant($betterConstant), $this->filterBetterReflectionClassConstants($filter)));
215+
return array_values(array_map(
216+
static fn (BetterReflectionClassConstant|BetterReflectionEnumCase $betterConstantOrEnum): ReflectionClassConstant => new ReflectionClassConstant($betterConstantOrEnum),
217+
$this->filterBetterReflectionClassConstants($filter),
218+
));
189219
}
190220

191221
/**
192-
* @return array<string, BetterReflectionClassConstant>
222+
* @return array<string, BetterReflectionClassConstant|BetterReflectionEnumCase>
193223
*/
194224
private function filterBetterReflectionClassConstants(?int $filter): array
195225
{
@@ -202,6 +232,16 @@ private function filterBetterReflectionClassConstants(?int $filter): array
202232
);
203233
}
204234

235+
if (
236+
$this->betterReflectionClass instanceof BetterReflectionEnum
237+
&& (
238+
$filter === null
239+
|| $filter & ReflectionClassConstant::IS_PUBLIC
240+
)
241+
) {
242+
$reflectionConstants += $this->betterReflectionClass->getCases();
243+
}
244+
205245
return $reflectionConstants;
206246
}
207247

src/Reflection/Adapter/ReflectionClassConstant.php

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
use ReflectionClassConstant as CoreReflectionClassConstant;
88
use Roave\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute;
99
use Roave\BetterReflection\Reflection\ReflectionClassConstant as BetterReflectionClassConstant;
10+
use Roave\BetterReflection\Reflection\ReflectionEnumCase as BetterReflectionEnumCase;
1011

1112
use function array_map;
1213

1314
final class ReflectionClassConstant extends CoreReflectionClassConstant
1415
{
15-
public function __construct(private BetterReflectionClassConstant $betterClassConstant)
16+
public function __construct(private BetterReflectionClassConstant|BetterReflectionEnumCase $betterClassConstantOrEnumCase)
1617
{
1718
}
1819

@@ -22,7 +23,7 @@ public function __construct(private BetterReflectionClassConstant $betterClassCo
2223
*/
2324
public function getName(): string
2425
{
25-
return $this->betterClassConstant->getName();
26+
return $this->betterClassConstantOrEnumCase->getName();
2627
}
2728

2829
/**
@@ -32,55 +33,71 @@ public function getName(): string
3233
*/
3334
public function getValue(): string|int|float|bool|array|null
3435
{
35-
return $this->betterClassConstant->getValue();
36+
return $this->betterClassConstantOrEnumCase->getValue();
3637
}
3738

3839
/**
3940
* Constant is public
4041
*/
4142
public function isPublic(): bool
4243
{
43-
return $this->betterClassConstant->isPublic();
44+
if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
45+
return true;
46+
}
47+
48+
return $this->betterClassConstantOrEnumCase->isPublic();
4449
}
4550

4651
/**
4752
* Constant is private
4853
*/
4954
public function isPrivate(): bool
5055
{
51-
return $this->betterClassConstant->isPrivate();
56+
if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
57+
return false;
58+
}
59+
60+
return $this->betterClassConstantOrEnumCase->isPrivate();
5261
}
5362

5463
/**
5564
* Constant is protected
5665
*/
5766
public function isProtected(): bool
5867
{
59-
return $this->betterClassConstant->isProtected();
68+
if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
69+
return false;
70+
}
71+
72+
return $this->betterClassConstantOrEnumCase->isProtected();
6073
}
6174

6275
/**
6376
* Returns a bitfield of the access modifiers for this constant
6477
*/
6578
public function getModifiers(): int
6679
{
67-
return $this->betterClassConstant->getModifiers();
80+
if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
81+
return CoreReflectionClassConstant::IS_PUBLIC;
82+
}
83+
84+
return $this->betterClassConstantOrEnumCase->getModifiers();
6885
}
6986

7087
/**
7188
* Get the declaring class
7289
*/
7390
public function getDeclaringClass(): ReflectionClass
7491
{
75-
return new ReflectionClass($this->betterClassConstant->getDeclaringClass());
92+
return new ReflectionClass($this->betterClassConstantOrEnumCase->getDeclaringClass());
7693
}
7794

7895
/**
7996
* Returns the doc comment for this constant
8097
*/
8198
public function getDocComment(): string|false
8299
{
83-
return $this->betterClassConstant->getDocComment() ?: false;
100+
return $this->betterClassConstantOrEnumCase->getDocComment() ?: false;
84101
}
85102

86103
/**
@@ -90,7 +107,7 @@ public function getDocComment(): string|false
90107
*/
91108
public function __toString(): string
92109
{
93-
return $this->betterClassConstant->__toString();
110+
return $this->betterClassConstantOrEnumCase->__toString();
94111
}
95112

96113
/**
@@ -101,23 +118,27 @@ public function __toString(): string
101118
public function getAttributes(?string $name = null, int $flags = 0): array
102119
{
103120
if ($name !== null && $flags & ReflectionAttribute::IS_INSTANCEOF) {
104-
$attributes = $this->betterClassConstant->getAttributesByInstance($name);
121+
$attributes = $this->betterClassConstantOrEnumCase->getAttributesByInstance($name);
105122
} elseif ($name !== null) {
106-
$attributes = $this->betterClassConstant->getAttributesByName($name);
123+
$attributes = $this->betterClassConstantOrEnumCase->getAttributesByName($name);
107124
} else {
108-
$attributes = $this->betterClassConstant->getAttributes();
125+
$attributes = $this->betterClassConstantOrEnumCase->getAttributes();
109126
}
110127

111128
return array_map(static fn (BetterReflectionAttribute $betterReflectionAttribute): ReflectionAttribute => new ReflectionAttribute($betterReflectionAttribute), $attributes);
112129
}
113130

114131
public function isFinal(): bool
115132
{
116-
return $this->betterClassConstant->isFinal();
133+
if ($this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase) {
134+
return true;
135+
}
136+
137+
return $this->betterClassConstantOrEnumCase->isFinal();
117138
}
118139

119140
public function isEnumCase(): bool
120141
{
121-
throw new Exception\NotImplemented('Not implemented');
142+
return $this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase;
122143
}
123144
}

0 commit comments

Comments
 (0)