From 8ae82c4b4df18c85c9992f4436dbd474c38fad71 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 2 Oct 2025 09:43:03 +0200 Subject: [PATCH 1/4] array_rand() requires a non-empty-array as of php8 --- .../functionMap_php80delta_bleedingEdge.php | 4 ++++ .../CallToFunctionParametersRuleTest.php | 23 +++++++++++++++++++ .../Rules/Functions/data/array-rand.php | 9 ++++++++ 3 files changed, 36 insertions(+) create mode 100644 tests/PHPStan/Rules/Functions/data/array-rand.php diff --git a/resources/functionMap_php80delta_bleedingEdge.php b/resources/functionMap_php80delta_bleedingEdge.php index 92f41e9db0..882165d7a2 100644 --- a/resources/functionMap_php80delta_bleedingEdge.php +++ b/resources/functionMap_php80delta_bleedingEdge.php @@ -2,7 +2,11 @@ return [ 'new' => [ + 'array_rand' => ['int|string|array|array', 'input'=>'non-empty-array', 'num_req'=>'int'], + 'array_rand\'1' => ['int|string', 'input'=>'non-empty-array'], ], 'old' => [ + 'array_rand' => ['int|string|array|array', 'input'=>'array', 'num_req'=>'int'], + 'array_rand\'1' => ['int|string', 'input'=>'array'], ], ]; diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 1753a05e0e..c809761454 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -2368,4 +2368,27 @@ public function testBug13556(): void $this->analyse([__DIR__ . '/data/bug-13556.php'], []); } + #[RequiresPhp('>= 8.0')] + public function testArrayRand(): void + { + $this->analyse([__DIR__ . '/data/array-rand.php'], [ + [ + 'Parameter #1 $input of function array_rand expects non-empty-array, array{} given.', + 7, + 'array{} is empty.' + ], + [ + 'Parameter #1 $input of function array_rand expects non-empty-array, array{} given.', + 8, + 'array{} is empty.' + ] + ]); + } + + #[RequiresPhp('< 8.0')] + public function testArrayRandPhp7(): void + { + $this->analyse([__DIR__ . '/data/array-rand.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/array-rand.php b/tests/PHPStan/Rules/Functions/data/array-rand.php new file mode 100644 index 0000000000..c577f7f030 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/array-rand.php @@ -0,0 +1,9 @@ + Date: Thu, 2 Oct 2025 09:50:51 +0200 Subject: [PATCH 2/4] requires non-empty num --- resources/functionMap_php80delta_bleedingEdge.php | 4 ++-- .../Functions/CallToFunctionParametersRuleTest.php | 12 ++++++++++++ tests/PHPStan/Rules/Functions/data/array-rand.php | 8 ++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/resources/functionMap_php80delta_bleedingEdge.php b/resources/functionMap_php80delta_bleedingEdge.php index 882165d7a2..8ada7d1e87 100644 --- a/resources/functionMap_php80delta_bleedingEdge.php +++ b/resources/functionMap_php80delta_bleedingEdge.php @@ -2,11 +2,11 @@ return [ 'new' => [ - 'array_rand' => ['int|string|array|array', 'input'=>'non-empty-array', 'num_req'=>'int'], + 'array_rand' => ['int|string|array|array', 'input'=>'non-empty-array', 'num_req'=>'positive-int'], 'array_rand\'1' => ['int|string', 'input'=>'non-empty-array'], ], 'old' => [ - 'array_rand' => ['int|string|array|array', 'input'=>'array', 'num_req'=>'int'], + 'array_rand' => ['int|string|array|array', 'input'=>'array', 'num_req'=>'positive-int'], 'array_rand\'1' => ['int|string', 'input'=>'array'], ], ]; diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index c809761454..4d24ea1e55 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -2381,6 +2381,18 @@ public function testArrayRand(): void 'Parameter #1 $input of function array_rand expects non-empty-array, array{} given.', 8, 'array{} is empty.' + ], + [ + 'Parameter #2 $num_req of function array_rand expects int<1, max>, int given.', + 8, + ], + [ + 'Parameter #2 $num_req of function array_rand expects int<1, max>, -5 given.', + 13, + ], + [ + 'Parameter #2 $num_req of function array_rand expects int<1, max>, 0 given.', + 14, ] ]); } diff --git a/tests/PHPStan/Rules/Functions/data/array-rand.php b/tests/PHPStan/Rules/Functions/data/array-rand.php index c577f7f030..92fa5a808c 100644 --- a/tests/PHPStan/Rules/Functions/data/array-rand.php +++ b/tests/PHPStan/Rules/Functions/data/array-rand.php @@ -7,3 +7,11 @@ function doFoo(int $i) { $x = array_rand($arr); $y = array_rand($arr, $i); } + +/** @param non-empty-array $arr */ +function doBar(array $arr) { + $y = array_rand($arr, -5); + $y = array_rand($arr, 0); + $y = array_rand($arr, 1); + $y = array_rand($arr); +} From 5856e00de349d61b3bfe54e094a33888bdbde73a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 2 Oct 2025 09:52:21 +0200 Subject: [PATCH 3/4] Update functionMap_php80delta_bleedingEdge.php --- resources/functionMap_php80delta_bleedingEdge.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/functionMap_php80delta_bleedingEdge.php b/resources/functionMap_php80delta_bleedingEdge.php index 8ada7d1e87..701777c4d1 100644 --- a/resources/functionMap_php80delta_bleedingEdge.php +++ b/resources/functionMap_php80delta_bleedingEdge.php @@ -6,7 +6,7 @@ 'array_rand\'1' => ['int|string', 'input'=>'non-empty-array'], ], 'old' => [ - 'array_rand' => ['int|string|array|array', 'input'=>'array', 'num_req'=>'positive-int'], + 'array_rand' => ['int|string|array|array', 'input'=>'array', 'num_req'=>'int'], 'array_rand\'1' => ['int|string', 'input'=>'array'], ], ]; From 785b3cce18be19cad1081652828f8708b122ed8c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 2 Oct 2025 10:09:18 +0200 Subject: [PATCH 4/4] cover analog \Random\Randomizer::pickArrayKeys() method --- resources/functionMap_php82delta.php | 1 + .../CallToFunctionParametersRuleTest.php | 10 ++++----- .../data/{array-rand.php => array_rand.php} | 0 .../Rules/Methods/CallMethodsRuleTest.php | 21 +++++++++++++++++++ .../PHPStan/Rules/Methods/data/randomizer.php | 8 +++++++ 5 files changed, 35 insertions(+), 5 deletions(-) rename tests/PHPStan/Rules/Functions/data/{array-rand.php => array_rand.php} (100%) create mode 100644 tests/PHPStan/Rules/Methods/data/randomizer.php diff --git a/resources/functionMap_php82delta.php b/resources/functionMap_php82delta.php index 6054b7a9ce..033e6e716b 100644 --- a/resources/functionMap_php82delta.php +++ b/resources/functionMap_php82delta.php @@ -24,6 +24,7 @@ 'iterator_count' => ['0|positive-int', 'iterator'=>'iterable'], 'iterator_to_array' => ['array', 'iterator'=>'iterable', 'use_keys='=>'bool'], 'str_split' => ['list', 'str'=>'string', 'split_length='=>'positive-int'], + 'Random\Randomizer::pickArrayKeys' => ['non-empty-array', 'array'=>'non-empty-array', 'num'=>'positive-int'], ], 'old' => [ diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 4d24ea1e55..c8deff26b8 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -2371,16 +2371,16 @@ public function testBug13556(): void #[RequiresPhp('>= 8.0')] public function testArrayRand(): void { - $this->analyse([__DIR__ . '/data/array-rand.php'], [ + $this->analyse([__DIR__ . '/data/array_rand.php'], [ [ 'Parameter #1 $input of function array_rand expects non-empty-array, array{} given.', 7, - 'array{} is empty.' + 'array{} is empty.', ], [ 'Parameter #1 $input of function array_rand expects non-empty-array, array{} given.', 8, - 'array{} is empty.' + 'array{} is empty.', ], [ 'Parameter #2 $num_req of function array_rand expects int<1, max>, int given.', @@ -2393,14 +2393,14 @@ public function testArrayRand(): void [ 'Parameter #2 $num_req of function array_rand expects int<1, max>, 0 given.', 14, - ] + ], ]); } #[RequiresPhp('< 8.0')] public function testArrayRandPhp7(): void { - $this->analyse([__DIR__ . '/data/array-rand.php'], []); + $this->analyse([__DIR__ . '/data/array_rand.php'], []); } } diff --git a/tests/PHPStan/Rules/Functions/data/array-rand.php b/tests/PHPStan/Rules/Functions/data/array_rand.php similarity index 100% rename from tests/PHPStan/Rules/Functions/data/array-rand.php rename to tests/PHPStan/Rules/Functions/data/array_rand.php diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 354b11b027..d81000ce49 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -3675,4 +3675,25 @@ public function testBug13511(): void $this->analyse([__DIR__ . '/data/bug-13511.php'], []); } + #[RequiresPhp('>= 8.2')] + public function testRandomizer(): void + { + $this->checkThisOnly = false; + $this->checkNullables = true; + $this->checkUnionTypes = true; + $this->checkExplicitMixed = true; + + $this->analyse([__DIR__ . '/data/randomizer.php'], [ + [ + 'Parameter #2 $num of method Random\Randomizer::pickArrayKeys() expects int<1, max>, 0 given.', + 7, + ], + [ + 'Parameter #1 $array of method Random\Randomizer::pickArrayKeys() expects non-empty-array, array{} given.', + 8, + 'array{} is empty.', + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/randomizer.php b/tests/PHPStan/Rules/Methods/data/randomizer.php new file mode 100644 index 0000000000..d124dd8785 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/randomizer.php @@ -0,0 +1,8 @@ +pickArrayKeys(['a', 'b'], 10)); +var_dump($r->pickArrayKeys(['a'], 0)); +var_dump($r->pickArrayKeys([], 1));