|
6 | 6 |
|
7 | 7 | use PhpParser\Node\Expr\StaticCall; |
8 | 8 | use PHPStan\Analyser\Scope; |
| 9 | +use PHPStan\PhpDoc\TypeNodeResolver; |
9 | 10 | use PHPStan\Reflection\MethodReflection; |
10 | | -use PHPStan\Type\ArrayType; |
11 | | -use PHPStan\Type\BooleanType; |
| 11 | +use PHPStan\Reflection\ReflectionProvider; |
12 | 12 | use PHPStan\Type\Constant\ConstantArrayType; |
13 | | -use PHPStan\Type\Constant\ConstantBooleanType; |
14 | | -use PHPStan\Type\Constant\ConstantStringType; |
15 | 13 | use PHPStan\Type\DynamicStaticMethodReturnTypeExtension; |
16 | | -use PHPStan\Type\IntegerType; |
| 14 | +use PHPStan\Type\ErrorType; |
17 | 15 | use PHPStan\Type\NullType; |
18 | | -use PHPStan\Type\StringType; |
19 | 16 | use PHPStan\Type\Type; |
20 | 17 | use PHPStan\Type\TypeCombinator; |
21 | 18 |
|
22 | 19 | use function count; |
23 | | -use function array_values; |
24 | 20 |
|
25 | 21 | final class WPCliGetConfigDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension { |
26 | 22 |
|
| 23 | + /** @var ReflectionProvider */ |
| 24 | + private $reflectionProvider; |
| 25 | + |
| 26 | + /** @var TypeNodeResolver */ |
| 27 | + private $typeNodeResolver; |
| 28 | + |
| 29 | + /** @var ConstantArrayType|null */ |
| 30 | + private $globalConfigType = null; |
| 31 | + |
| 32 | + public function __construct( |
| 33 | + ReflectionProvider $reflectionProvider, |
| 34 | + TypeNodeResolver $typeNodeResolver |
| 35 | + ) { |
| 36 | + $this->reflectionProvider = $reflectionProvider; |
| 37 | + $this->typeNodeResolver = $typeNodeResolver; |
| 38 | + } |
| 39 | + |
27 | 40 | public function getClass(): string { |
28 | 41 | return 'WP_CLI'; |
29 | 42 | } |
@@ -51,88 +64,57 @@ public function getTypeFromStaticMethodCall( |
51 | 64 |
|
52 | 65 | $constantStrings = $keyType->getConstantStrings(); |
53 | 66 | if ( count( $constantStrings ) > 0 ) { |
54 | | - $types = []; |
55 | | - $configMap = $this->getConfigMap(); |
| 67 | + $types = []; |
| 68 | + $globalConfigType = $this->getGlobalConfigArrayType(); |
56 | 69 |
|
57 | 70 | foreach ( $constantStrings as $constantString ) { |
58 | | - $key = $constantString->getValue(); |
59 | | - if ( isset( $configMap[ $key ] ) ) { |
60 | | - $types[] = $configMap[ $key ]; |
61 | | - } else { |
| 71 | + $valueType = $globalConfigType->getOffsetValueType( $constantString ); |
| 72 | + if ( $valueType instanceof ErrorType ) { |
62 | 73 | $types[] = new NullType(); |
| 74 | + } else { |
| 75 | + $types[] = $valueType; |
63 | 76 | } |
64 | 77 | } |
65 | 78 |
|
66 | 79 | if ( count( $types ) > 0 ) { |
67 | 80 | $returnType = TypeCombinator::union( ...$types ); |
68 | 81 | if ( $keyType->isNull()->maybe() ) { |
69 | | - $returnType = TypeCombinator::union( $returnType, $this->getGlobalConfigArrayType() ); |
| 82 | + $returnType = TypeCombinator::union( $returnType, $globalConfigType ); |
70 | 83 | } |
71 | 84 | return $returnType; |
72 | 85 | } |
73 | 86 | } |
74 | 87 |
|
75 | 88 | // Fallback for non-constant string or unknown types |
76 | | - $fallback = TypeCombinator::addNull( $this->getFallbackValueType() ); |
| 89 | + $fallback = TypeCombinator::addNull( $this->getGlobalConfigArrayType()->getItemType() ); |
77 | 90 | if ( $keyType->isNull()->maybe() ) { |
78 | 91 | return TypeCombinator::union( $fallback, $this->getGlobalConfigArrayType() ); |
79 | 92 | } |
80 | 93 |
|
81 | 94 | return $fallback; |
82 | 95 | } |
83 | 96 |
|
84 | | - /** |
85 | | - * @return array<string, Type> |
86 | | - */ |
87 | | - private function getConfigMap(): array { |
88 | | - $stringType = new StringType(); |
89 | | - $stringOrNull = TypeCombinator::addNull( $stringType ); |
90 | | - $stringList = new ArrayType( new IntegerType(), $stringType ); |
91 | | - $boolType = new BooleanType(); |
92 | | - $trueOrStringList = TypeCombinator::union( new ConstantBooleanType( true ), $stringList ); |
93 | | - $stringOrTrue = TypeCombinator::union( $stringType, new ConstantBooleanType( true ) ); |
94 | | - $stringOrFalse = TypeCombinator::union( $stringType, new ConstantBooleanType( false ) ); |
95 | | - |
96 | | - return [ |
97 | | - 'path' => $stringOrNull, |
98 | | - 'ssh' => $stringOrNull, |
99 | | - 'ssh-args' => $stringList, |
100 | | - 'http' => $stringOrNull, |
101 | | - 'url' => $stringOrNull, |
102 | | - 'user' => $stringOrNull, |
103 | | - 'skip-plugins' => $trueOrStringList, |
104 | | - 'skip-themes' => $trueOrStringList, |
105 | | - 'skip-packages' => $boolType, |
106 | | - 'require' => $stringList, |
107 | | - 'exec' => $stringList, |
108 | | - 'context' => $stringType, |
109 | | - 'debug' => $stringOrTrue, |
110 | | - 'prompt' => $stringOrFalse, |
111 | | - 'quiet' => $boolType, |
112 | | - 'apache_modules' => $stringList, |
113 | | - 'assume-https' => $boolType, |
114 | | - 'color' => TypeCombinator::union( $stringType, $boolType ), |
115 | | - 'disabled_commands' => $stringList, |
116 | | - 'locale' => $stringType, |
117 | | - 'allow-root' => $boolType, |
118 | | - 'alias' => $stringType, |
119 | | - ]; |
120 | | - } |
| 97 | + private function getGlobalConfigArrayType(): ConstantArrayType { |
| 98 | + if ( null === $this->globalConfigType ) { |
| 99 | + $classReflection = $this->reflectionProvider->getClass( 'WP_CLI' ); |
| 100 | + $typeAliases = $classReflection->getTypeAliases(); |
| 101 | + $globalConfig = $typeAliases['GlobalConfig'] ?? null; |
121 | 102 |
|
122 | | - private function getGlobalConfigArrayType(): Type { |
123 | | - $keyTypes = []; |
124 | | - $valueTypes = []; |
| 103 | + if ( null === $globalConfig ) { |
| 104 | + throw new \PHPStan\ShouldNotHappenException( 'GlobalConfig type alias not found on WP_CLI class.' ); |
| 105 | + } |
125 | 106 |
|
126 | | - foreach ( $this->getConfigMap() as $key => $type ) { |
127 | | - $keyTypes[] = new ConstantStringType( $key ); |
128 | | - $valueTypes[] = $type; |
129 | | - } |
| 107 | + /** @phpstan-ignore phpstanApi.method */ |
| 108 | + $resolved = $globalConfig->resolve( $this->typeNodeResolver ); |
| 109 | + $constantArrays = $resolved->getConstantArrays(); |
130 | 110 |
|
131 | | - return new ConstantArrayType( $keyTypes, $valueTypes ); |
132 | | - } |
| 111 | + if ( count( $constantArrays ) === 0 ) { |
| 112 | + throw new \PHPStan\ShouldNotHappenException( 'GlobalConfig type alias on WP_CLI must resolve to a ConstantArrayType.' ); |
| 113 | + } |
| 114 | + |
| 115 | + $this->globalConfigType = $constantArrays[0]; |
| 116 | + } |
133 | 117 |
|
134 | | - private function getFallbackValueType(): Type { |
135 | | - $types = array_values( $this->getConfigMap() ); |
136 | | - return TypeCombinator::union( ...$types ); |
| 118 | + return $this->globalConfigType; |
137 | 119 | } |
138 | 120 | } |
0 commit comments