diff --git a/src/Type/Php/ArrayKeyFirstLastTypeSpecifyingExtension.php b/src/Type/Php/ArrayKeyFirstLastTypeSpecifyingExtension.php new file mode 100644 index 00000000000..dd191f61e5c --- /dev/null +++ b/src/Type/Php/ArrayKeyFirstLastTypeSpecifyingExtension.php @@ -0,0 +1,59 @@ +getName()), ['array_key_first', 'array_key_last']) + && $context->true(); + } + + public function specifyTypes( + FunctionReflection $functionReflection, + FuncCall $node, + Scope $scope, + TypeSpecifierContext $context, + ): SpecifiedTypes + { + $arrayArg = $node->getArgs()[0]->value ?? null; + if ($arrayArg === null) { + return new SpecifiedTypes(); + } + + return $this->typeSpecifier->create( + $arrayArg, + new NonEmptyArrayType(), + $context, + $scope, + ); + } + + public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void + { + $this->typeSpecifier = $typeSpecifier; + } + +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-14081.php b/tests/PHPStan/Analyser/nsrt/bug-14081.php new file mode 100644 index 00000000000..33589cae28f --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-14081.php @@ -0,0 +1,31 @@ + $array */ +function first(array $array): mixed +{ + if (($key = array_key_first($array))) { + assertType('int<1, max>', $key); + assertType('non-empty-list', $array); + assertType('string', $array[$key]); + return $array[$key]; + } + return null; +} + +/** @param list $array */ +function last(array $array): mixed +{ + if (($key = array_key_last($array))) { + assertType('int<1, max>', $key); + assertType('non-empty-list', $array); + assertType('string', $array[$key]); + return $array[$key]; + } + return null; +}