19
19
use PHPStan \Type \TypeCombinator ;
20
20
use PHPStan \Type \TypeUtils ;
21
21
use function array_map ;
22
+ use function array_reduce ;
22
23
use function array_slice ;
23
24
use function count ;
24
25
@@ -32,7 +33,8 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
32
33
33
34
public function getTypeFromFunctionCall (FunctionReflection $ functionReflection , FuncCall $ functionCall , Scope $ scope ): ?Type
34
35
{
35
- if (count ($ functionCall ->getArgs ()) < 2 ) {
36
+ $ numArgs = count ($ functionCall ->getArgs ());
37
+ if ($ numArgs < 2 ) {
36
38
return null ;
37
39
}
38
40
@@ -54,10 +56,58 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
54
56
)->getReturnType ();
55
57
} elseif ($ callableIsNull ) {
56
58
$ arrayBuilder = ConstantArrayTypeBuilder::createEmpty ();
59
+ $ argTypes = [];
60
+ $ areAllSameSize = true ;
61
+ $ expectedSize = null ;
57
62
foreach (array_slice ($ functionCall ->getArgs (), 1 ) as $ index => $ arg ) {
63
+ $ argTypes [$ index ] = $ argType = $ scope ->getType ($ arg ->value );
64
+ if (!$ areAllSameSize || $ numArgs === 2 ) {
65
+ continue ;
66
+ }
67
+
68
+ $ arraySizes = $ argType ->getArraySize ()->getConstantScalarValues ();
69
+ if ($ arraySizes === []) {
70
+ $ areAllSameSize = false ;
71
+ continue ;
72
+ }
73
+
74
+ foreach ($ arraySizes as $ size ) {
75
+ $ expectedSize ??= $ size ;
76
+ if ($ expectedSize === $ size ) {
77
+ continue ;
78
+ }
79
+
80
+ $ areAllSameSize = false ;
81
+ continue 2 ;
82
+ }
83
+ }
84
+
85
+ if (!$ areAllSameSize ) {
86
+ $ firstArr = $ functionCall ->getArgs ()[1 ]->value ;
87
+ $ identities = [];
88
+ foreach (array_slice ($ functionCall ->getArgs (), 2 ) as $ arg ) {
89
+ $ identities [] = new Node \Expr \BinaryOp \Identical ($ firstArr , $ arg ->value );
90
+ }
91
+
92
+ $ and = array_reduce (
93
+ $ identities ,
94
+ static fn (Node \Expr $ a , Node \Expr $ b ) => new Node \Expr \BinaryOp \BooleanAnd ($ a , $ b ),
95
+ new Node \Expr \ConstFetch (new Node \Name ('true ' )),
96
+ );
97
+ $ areAllSameSize = $ scope ->getType ($ and )->isTrue ()->yes ();
98
+ }
99
+
100
+ $ addNull = !$ areAllSameSize ;
101
+
102
+ foreach ($ argTypes as $ index => $ argType ) {
103
+ $ offsetValueType = $ argType ->getIterableValueType ();
104
+ if ($ addNull ) {
105
+ $ offsetValueType = TypeCombinator::addNull ($ offsetValueType );
106
+ }
107
+
58
108
$ arrayBuilder ->setOffsetValueType (
59
109
new ConstantIntegerType ($ index ),
60
- $ scope -> getType ( $ arg -> value )-> getIterableValueType () ,
110
+ $ offsetValueType ,
61
111
);
62
112
}
63
113
$ valueType = $ arrayBuilder ->getArray ();
0 commit comments