66use PHPStan \Analyser \Scope ;
77use PHPStan \DependencyInjection \AutowiredService ;
88use PHPStan \Reflection \FunctionReflection ;
9+ use PHPStan \ShouldNotHappenException ;
910use PHPStan \Type \DynamicFunctionReturnTypeExtension ;
1011use PHPStan \Type \NullType ;
1112use PHPStan \Type \Type ;
@@ -19,7 +20,12 @@ final class ArrayFirstLastDynamicReturnTypeExtension implements DynamicFunctionR
1920
2021 public function isFunctionSupported (FunctionReflection $ functionReflection ): bool
2122 {
22- return in_array ($ functionReflection ->getName (), ['array_first ' , 'array_last ' ], true );
23+ return in_array ($ functionReflection ->getName (), [
24+ 'array_key_first ' ,
25+ 'array_key_last ' ,
26+ 'array_first ' ,
27+ 'array_last ' ,
28+ ], true );
2329 }
2430
2531 public function getTypeFromFunctionCall (FunctionReflection $ functionReflection , FuncCall $ functionCall , Scope $ scope ): ?Type
@@ -37,13 +43,24 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
3743 return new NullType ();
3844 }
3945
40- $ valueType = $ argType ->getIterableValueType ();
46+ switch ($ functionReflection ->getName ()) {
47+ case 'array_key_first ' :
48+ case 'array_key_last ' :
49+ $ resultType = $ argType ->getIterableKeyType ();
50+ break ;
51+ case 'array_first ' :
52+ case 'array_last ' :
53+ $ resultType = $ argType ->getIterableValueType ();
54+ break ;
55+ default :
56+ throw new ShouldNotHappenException ();
57+ }
4158
4259 if ($ iterableAtLeastOnce ->yes ()) {
43- return $ valueType ;
60+ return $ resultType ;
4461 }
4562
46- return TypeCombinator::union ($ valueType , new NullType ());
63+ return TypeCombinator::union ($ resultType , new NullType ());
4764 }
4865
4966}
0 commit comments