diff --git a/src/Type/Php/ArraySpliceFunctionReturnTypeExtension.php b/src/Type/Php/ArraySpliceFunctionReturnTypeExtension.php index bf4c3abc6b..85def351d2 100644 --- a/src/Type/Php/ArraySpliceFunctionReturnTypeExtension.php +++ b/src/Type/Php/ArraySpliceFunctionReturnTypeExtension.php @@ -4,14 +4,22 @@ use PhpParser\Node\Expr\FuncCall; use PHPStan\Analyser\Scope; +use PHPStan\Php\PhpVersion; use PHPStan\Reflection\FunctionReflection; -use PHPStan\Type\ArrayType; +use PHPStan\TrinaryLogic; use PHPStan\Type\DynamicFunctionReturnTypeExtension; +use PHPStan\Type\NeverType; +use PHPStan\Type\NullType; use PHPStan\Type\Type; +use function count; final class ArraySpliceFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension { + public function __construct(private PhpVersion $phpVersion) + { + } + public function isFunctionSupported(FunctionReflection $functionReflection): bool { return $functionReflection->getName() === 'array_splice'; @@ -23,13 +31,20 @@ public function getTypeFromFunctionCall( Scope $scope, ): ?Type { - if (!isset($functionCall->getArgs()[0])) { + $args = $functionCall->getArgs(); + if (count($args) < 2) { return null; } - $arrayArg = $scope->getType($functionCall->getArgs()[0]->value); + $arrayType = $scope->getType($args[0]->value); + if ($arrayType->isArray()->no()) { + return $this->phpVersion->arrayFunctionsReturnNullWithNonArray() ? new NullType() : new NeverType(); + } + + $offsetType = $scope->getType($args[1]->value); + $lengthType = isset($args[2]) ? $scope->getType($args[2]->value) : new NullType(); - return new ArrayType($arrayArg->getIterableKeyType(), $arrayArg->getIterableValueType()); + return $arrayType->sliceArray($offsetType, $lengthType, TrinaryLogic::createNo()); } } diff --git a/tests/PHPStan/Analyser/nsrt/bug-5017.php b/tests/PHPStan/Analyser/nsrt/bug-5017.php index fa1abc5b46..9c9a3d3abf 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-5017.php +++ b/tests/PHPStan/Analyser/nsrt/bug-5017.php @@ -15,7 +15,7 @@ public function doFoo() assertType('non-empty-array<0|1|2|3|4, 0|1|2|3|4>', $items); $batch = array_splice($items, 0, 2); assertType('array<0|1|2|3|4, 0|1|2|3|4>', $items); - assertType('array<0|1|2|3|4, 0|1|2|3|4>', $batch); + assertType('non-empty-array<0|1|2|3|4, 0|1|2|3|4>', $batch); } } @@ -28,7 +28,7 @@ public function doBar($items) assertType('non-empty-array', $items); $batch = array_splice($items, 0, 2); assertType('array', $items); - assertType('array', $batch); + assertType('non-empty-array', $batch); } } @@ -38,7 +38,7 @@ public function doBar2() assertType('array{0, 1, 2, 3, 4}', $items); $batch = array_splice($items, 0, 2); assertType('array<0|1|2|3|4, 0|1|2|3|4>', $items); - assertType('array<0|1|2|3|4, 0|1|2|3|4>', $batch); + assertType('array{0, 1}', $batch); } /**