Skip to content

Commit 452a803

Browse files
committed
refactoring & fix getValues return type
1 parent be25773 commit 452a803

19 files changed

+406
-140
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"phpstan/phpstan": "^0.12"
1111
},
1212
"require-dev": {
13-
"phpunit/phpunit": "^7.5"
13+
"phpunit/phpunit": "^7.5.20"
1414
},
1515
"autoload": {
1616
"psr-4": {

extension.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ services:
33
tags:
44
- phpstan.broker.methodsClassReflectionExtension
55

6-
- class: MabeEnumPHPStan\EnumGetValueDynamicReturnTypeExtension
6+
- class: MabeEnumPHPStan\EnumDynamicReturnTypeExtension
77
tags:
88
- phpstan.broker.dynamicMethodReturnTypeExtension
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MabeEnumPHPStan;
6+
7+
use MabeEnum\Enum;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Reflection\MethodReflection;
11+
use PHPStan\Reflection\ParametersAcceptorSelector;
12+
use PHPStan\ShouldNotHappenException;
13+
use PHPStan\Type\ArrayType;
14+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
15+
use PHPStan\Type\IntegerType;
16+
use PHPStan\Type\Type;
17+
use PHPStan\Type\TypeCombinator;
18+
19+
class EnumDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
20+
{
21+
/**
22+
* Buffer of known return types of Enum::getValues()
23+
* @var Type[]
24+
* @phpstan-var array<class-string<Enum>, Type>
25+
*/
26+
private $enumValuesTypeBuffer = [];
27+
28+
public function getClass(): string
29+
{
30+
return Enum::class;
31+
}
32+
33+
public function isMethodSupported(MethodReflection $methodReflection): bool
34+
{
35+
$supportedMethods = ['getvalue'];
36+
if (method_exists(Enum::class, 'getValues')) {
37+
array_push($supportedMethods, 'getvalues');
38+
}
39+
40+
return in_array(strtolower($methodReflection->getName()), $supportedMethods, true);
41+
}
42+
43+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
44+
{
45+
$enumType = $scope->getType($methodCall->var);
46+
$methodName = $methodReflection->getName();
47+
$methodClasses = $enumType->getReferencedClasses();
48+
if (count($methodClasses) !== 1) {
49+
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
50+
}
51+
52+
$enumeration = $methodClasses[0];
53+
54+
switch (strtolower($methodName)) {
55+
case 'getvalue':
56+
return $this->getEnumValuesType($enumeration, $scope);
57+
case 'getvalues':
58+
return new ArrayType(
59+
new IntegerType(),
60+
$this->getEnumValuesType($enumeration, $scope)
61+
);
62+
default:
63+
throw new ShouldNotHappenException("Method {$methodName} is not supported");
64+
}
65+
}
66+
67+
/**
68+
* Returns union type of all values of an enumeration
69+
* @phpstan-param class-string<Enum> $enumClass
70+
*/
71+
private function getEnumValuesType(string $enumeration, Scope $scope): Type
72+
{
73+
if (isset($this->enumValuesTypeBuffer[$enumeration])) {
74+
return $this->enumValuesTypeBuffer[$enumeration];
75+
}
76+
77+
$values = array_values($enumeration::getConstants());
78+
$types = array_map(function ($value) use ($scope): Type {
79+
return $scope->getTypeFromValue($value);
80+
}, $values);
81+
82+
return $this->enumValuesTypeBuffer[$enumeration] = TypeCombinator::union(...$types);
83+
}
84+
}

src/EnumGetValueDynamicReturnTypeExtension.php

Lines changed: 0 additions & 70 deletions
This file was deleted.

tests/Assets/AllTypeEnum.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MabeEnumPHPStanTest\Assets;
6+
7+
use MabeEnum\Enum;
8+
9+
class AllTypeEnum extends Enum
10+
{
11+
const NIL = null;
12+
const BOOL = true;
13+
const INT = 1;
14+
const FLOAT = 1.1;
15+
const STR = 'str';
16+
const ARR = [null, true, 1, 1.1, 'str', []];
17+
}

tests/Assets/ArrayTypeEnum.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MabeEnumPHPStanTest\Assets;
6+
7+
use MabeEnum\Enum;
8+
9+
class ArrayTypeEnum extends Enum
10+
{
11+
const ARRAY = [[]];
12+
}

tests/Assets/BoolTypeEnum.php

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 MabeEnumPHPStanTest\Assets;
6+
7+
use MabeEnum\Enum;
8+
9+
class BoolTypeEnum extends Enum
10+
{
11+
const BOOL_TRUE = true;
12+
const BOOL_FALSE = false;
13+
}

tests/Assets/DocCommentEnum.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MabeEnumPHPStanTest\Assets;
6+
7+
use MabeEnum\Enum;
8+
9+
class DocCommentEnum extends Enum
10+
{
11+
/**
12+
* With doc block
13+
*
14+
* @var string
15+
*/
16+
const WITH_DOC_BLOCK = 'with doc block';
17+
18+
const WITHOUT_DOC_BLOCK = 'without doc block';
19+
}

tests/Assets/FloatTypeEnum.php

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 MabeEnumPHPStanTest\Assets;
6+
7+
use MabeEnum\Enum;
8+
9+
class FloatTypeEnum extends Enum
10+
{
11+
const FLOAT11 = 1.1;
12+
const FLOAT12 = 1.2;
13+
}

tests/Assets/IntTypeEnum.php

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 MabeEnumPHPStanTest\Assets;
6+
7+
use MabeEnum\Enum;
8+
9+
class IntTypeEnum extends Enum
10+
{
11+
const INT0 = 0;
12+
const INT1 = 1;
13+
}

tests/Assets/NullTypeEnum.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MabeEnumPHPStanTest\Assets;
6+
7+
use MabeEnum\Enum;
8+
9+
class NullTypeEnum extends Enum
10+
{
11+
const NULL = null;
12+
}

tests/Assets/StrTypeEnum.php

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 MabeEnumPHPStanTest\Assets;
6+
7+
use MabeEnum\Enum;
8+
9+
class StrTypeEnum extends Enum
10+
{
11+
const STR1 = 'str1';
12+
const STR2 = 'str2';
13+
}

tests/Assets/StrEnum.php renamed to tests/Assets/VisibilityEnum.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use MabeEnum\Enum;
88

9-
class StrEnum extends Enum
9+
class VisibilityEnum extends Enum
1010
{
1111
/**
1212
* String const without visibility declaration
@@ -29,6 +29,4 @@ class StrEnum extends Enum
2929
* Public string const
3030
*/
3131
public const PUBLIC_STR = 'public str';
32-
33-
public const NO_DOC_BLOCK = 'no doc block';
3432
}

0 commit comments

Comments
 (0)