1919use PHPStan \Type \TypeCombinator ;
2020use PHPStan \Type \TypeUtils ;
2121use function array_map ;
22+ use function array_reduce ;
2223use function array_slice ;
2324use function count ;
2425
@@ -32,7 +33,8 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
3233
3334 public function getTypeFromFunctionCall (FunctionReflection $ functionReflection , FuncCall $ functionCall , Scope $ scope ): ?Type
3435 {
35- if (count ($ functionCall ->getArgs ()) < 2 ) {
36+ $ numArgs = count ($ functionCall ->getArgs ());
37+ if ($ numArgs < 2 ) {
3638 return null ;
3739 }
3840
@@ -54,10 +56,58 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5456 )->getReturnType ();
5557 } elseif ($ callableIsNull ) {
5658 $ arrayBuilder = ConstantArrayTypeBuilder::createEmpty ();
59+ $ argTypes = [];
60+ $ areAllSameSize = true ;
61+ $ expectedSize = null ;
5762 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+
58108 $ arrayBuilder ->setOffsetValueType (
59109 new ConstantIntegerType ($ index ),
60- $ scope -> getType ( $ arg -> value )-> getIterableValueType () ,
110+ $ offsetValueType ,
61111 );
62112 }
63113 $ valueType = $ arrayBuilder ->getArray ();
0 commit comments