11
11
use PhpParser \Node \Expr \FuncCall ;
12
12
use PHPStan \Analyser \Scope ;
13
13
use PHPStan \Reflection \FunctionReflection ;
14
+ use PHPStan \Type \Accessory \NonEmptyArrayType ;
15
+ use PHPStan \Type \ArrayType ;
14
16
use PHPStan \Type \Constant \ConstantArrayType ;
17
+ use PHPStan \Type \Constant \ConstantArrayTypeBuilder ;
15
18
use PHPStan \Type \StringType ;
16
19
use PHPStan \Type \Type ;
17
20
use PHPStan \Type \TypeCombinator ;
@@ -31,20 +34,49 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
31
34
public function getTypeFromFunctionCall (FunctionReflection $ functionReflection , FuncCall $ functionCall , Scope $ scope ): ?Type
32
35
{
33
36
$ args = $ functionCall ->getArgs ();
34
- if ($ args === []) {
37
+
38
+ if (count ($ args ) < 2 ) {
35
39
return null ;
36
40
}
37
41
38
- $ type = $ scope ->getType ($ args [0 ]->value );
42
+ $ pairsType = $ scope ->getType ($ args [0 ]->value );
43
+ $ attsType = $ scope ->getType ($ args [1 ]->value );
44
+
45
+ if ($ attsType ->isIterableAtLeastOnce ()->no ()) {
46
+ return $ pairsType ;
47
+ }
48
+
49
+ if (! $ this ->hasConstantArrays ($ pairsType )) {
50
+ return $ this ->resolveTypeForNonConstantPairs ($ pairsType );
51
+ }
39
52
40
- if (count ( $ type -> getConstantArrays ()) === 0 ) {
41
- return $ type ;
53
+ if (! $ this -> hasConstantArrays ( $ attsType ) ) {
54
+ return $ this -> resolveTypeForNonConstantAtts ( $ pairsType ) ;
42
55
}
43
56
44
- $ returnType = [];
45
- foreach ($ type ->getConstantArrays () as $ constantArray ) {
46
- // shortcode_atts values are coming from the defined defaults or from the actual string shortcode attributes
47
- $ returnType [] = new ConstantArrayType (
57
+ return $ this ->resolveTypeForConstantAtts ($ pairsType , $ attsType );
58
+ }
59
+
60
+ protected function resolveTypeForNonConstantPairs (Type $ pairsType ): Type
61
+ {
62
+ $ keyType = $ pairsType ->getIterableKeyType ();
63
+ $ valueType = TypeCombinator::union (
64
+ $ pairsType ->getIterableValueType (),
65
+ new StringType ()
66
+ );
67
+ $ arrayType = new ArrayType ($ keyType , $ valueType );
68
+
69
+ return $ pairsType ->isIterableAtLeastOnce ()->yes ()
70
+ ? TypeCombinator::intersect ($ arrayType , new NonEmptyArrayType ())
71
+ : $ arrayType ;
72
+ }
73
+
74
+ protected function resolveTypeForNonConstantAtts (Type $ pairsType ): Type
75
+ {
76
+ $ types = [];
77
+
78
+ foreach ($ pairsType ->getConstantArrays () as $ constantArray ) {
79
+ $ types [] = new ConstantArrayType (
48
80
$ constantArray ->getKeyTypes (),
49
81
array_map (
50
82
static function (Type $ valueType ): Type {
@@ -55,6 +87,52 @@ static function (Type $valueType): Type {
55
87
);
56
88
}
57
89
58
- return TypeCombinator::union (...$ returnType );
90
+ return TypeCombinator::union (...$ types );
91
+ }
92
+
93
+ protected function resolveTypeForConstantAtts (Type $ pairsType , Type $ attsType ): Type
94
+ {
95
+ $ types = [];
96
+
97
+ foreach ($ pairsType ->getConstantArrays () as $ constantPairsArray ) {
98
+ foreach ($ attsType ->getConstantArrays () as $ constantAttsArray ) {
99
+ $ types [] = $ this ->mergeArrays ($ constantPairsArray , $ constantAttsArray );
100
+ }
101
+ }
102
+
103
+ return TypeCombinator::union (...$ types );
104
+ }
105
+
106
+ protected function mergeArrays (ConstantArrayType $ pairsArray , ConstantArrayType $ attsArray ): Type
107
+ {
108
+ if (count ($ attsArray ->getKeyTypes ()) === 0 ) {
109
+ return $ pairsArray ;
110
+ }
111
+
112
+ $ builder = ConstantArrayTypeBuilder::createFromConstantArray ($ pairsArray );
113
+
114
+ foreach ($ pairsArray ->getKeyTypes () as $ keyType ) {
115
+ $ hasOffsetValueType = $ attsArray ->hasOffsetValueType ($ keyType );
116
+
117
+ if ($ hasOffsetValueType ->no ()) {
118
+ continue ;
119
+ }
120
+
121
+ $ valueType = $ hasOffsetValueType ->yes ()
122
+ ? $ attsArray ->getOffsetValueType ($ keyType )
123
+ : TypeCombinator::union (
124
+ $ pairsArray ->getOffsetValueType ($ keyType ),
125
+ $ attsArray ->getOffsetValueType ($ keyType )
126
+ );
127
+
128
+ $ builder ->setOffsetValueType ($ keyType , $ valueType );
129
+ }
130
+
131
+ return $ builder ->getArray ();
132
+ }
133
+
134
+ protected function hasConstantArrays (Type $ type ): bool
135
+ {
136
+ return count ($ type ->getConstantArrays ()) > 0 ;
59
137
}
60
138
}
0 commit comments