From 4ec35406d626a3c762ab49b40a4b2190f266012a Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Fri, 18 Apr 2025 21:21:13 +0200 Subject: [PATCH] Improve `ConstantArrayType::sliceArray()` with non constant integer args --- src/Type/Constant/ConstantArrayType.php | 19 +++++++++++++++++-- tests/PHPStan/Analyser/nsrt/array-slice.php | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 76fdf42a5d..de02feec05 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -939,8 +939,23 @@ public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $pre return $this; } - $offset = $offsetType instanceof ConstantIntegerType ? $offsetType->getValue() : 0; - $length = $lengthType instanceof ConstantIntegerType ? $lengthType->getValue() : $keyTypesCount; + $offset = $offsetType instanceof ConstantIntegerType ? $offsetType->getValue() : null; + + if ($lengthType instanceof ConstantIntegerType) { + $length = $lengthType->getValue(); + } elseif ($lengthType->isNull()->yes()) { + $length = $keyTypesCount; + } else { + $length = null; + } + + if ($offset === null || $length === null) { + $builder = ConstantArrayTypeBuilder::createFromConstantArray($this); + $builder->degradeToGeneralArray(); + + return $builder->getArray() + ->sliceArray($offsetType, $lengthType, $preserveKeys); + } if ($length < 0) { // Negative lengths prevent access to the most right n elements diff --git a/tests/PHPStan/Analyser/nsrt/array-slice.php b/tests/PHPStan/Analyser/nsrt/array-slice.php index 847f535df1..c4402770b5 100644 --- a/tests/PHPStan/Analyser/nsrt/array-slice.php +++ b/tests/PHPStan/Analyser/nsrt/array-slice.php @@ -43,6 +43,7 @@ public function constantArrays(array $arr): void /** @var array{17: 'foo', b: 'bar', 19: 'baz'} $arr */ assertType('array{b: \'bar\', 0: \'baz\'}', array_slice($arr, 1, 2)); assertType('array{b: \'bar\', 19: \'baz\'}', array_slice($arr, 1, 2, true)); + assertType('array<17|19|\'b\', \'bar\'|\'baz\'|\'foo\'>', array_slice($arr, rand(0, 1) ? 0 : 1, rand(0, 1) ? 0 : 1)); /** @var array{17: 'foo', 19: 'bar', 21: 'baz'}|array{foo: 17, bar: 19, baz: 21} $arr */ assertType('array{\'bar\', \'baz\'}|array{bar: 19, baz: 21}', array_slice($arr, 1, 2));