diff --git a/dictionaries/CallMap_83_delta.php b/dictionaries/CallMap_83_delta.php index 8a4a76077b8..f3168593ff9 100644 --- a/dictionaries/CallMap_83_delta.php +++ b/dictionaries/CallMap_83_delta.php @@ -18,6 +18,7 @@ return [ 'added' => [ 'json_validate' => ['bool', 'json'=>'string', 'depth='=>'positive-int', 'flags='=>'int'], + 'Random\\Randomizer::getBytesFromString' => ['non-empty-string', 'string' => 'non-empty-string', 'length' => 'int<1, max>'], ], 'changed' => [ diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php index e2a2cc5a14b..ebf3cb975d7 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php @@ -387,6 +387,14 @@ public static function parse( if (preg_match('/^[4578]\.\d(\.\d+)?$/', $since)) { $since_parts = explode('.', $since); + $info->since_php_major_version = (int)$since_parts[0]; + $info->since_php_minor_version = (int)$since_parts[1]; + } + } elseif (isset($parsed_docblock->tags['php-from'])) { + $since = trim(reset($parsed_docblock->tags['php-from'])); + if (preg_match('/^[4578]\.\d(\.\d+)?$/', $since)) { + $since_parts = explode('.', $since); + $info->since_php_major_version = (int)$since_parts[0]; $info->since_php_minor_version = (int)$since_parts[1]; } diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index f04a90072d0..30aa196d7c6 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -461,9 +461,9 @@ public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = fal } if ($docblock_info) { - if ($docblock_info->since_php_major_version && !$this->aliases->namespace) { + if ($docblock_info->since_php_major_version) { $analysis_major_php_version = $this->codebase->getMajorAnalysisPhpVersion(); - $analysis_minor_php_version = $this->codebase->getMajorAnalysisPhpVersion(); + $analysis_minor_php_version = $this->codebase->getMinorAnalysisPhpVersion(); if ($docblock_info->since_php_major_version > $analysis_major_php_version) { return false; } diff --git a/stubs/extensions/random.phpstub b/stubs/extensions/random.phpstub index 27135afc376..83e591fb2fa 100644 --- a/stubs/extensions/random.phpstub +++ b/stubs/extensions/random.phpstub @@ -90,7 +90,7 @@ namespace Random /** * @template TValue * @param array $array - * @return list + * @return ($array is non-empty-array ? non-empty-list : list) */ public function shuffleArray(array $array): array {} @@ -103,6 +103,15 @@ namespace Random */ public function pickArrayKeys(array $array, int $num): array {} + /** + * @since 8.3 + * + * @param non-empty-string $string + * @param positive-int $length + * @return non-empty-string + */ + public function getBytesFromString(string $string, int $length): string {} + public function __serialize(): array {} public function __unserialize(array $data): void {} diff --git a/tests/CoreStubsTest.php b/tests/CoreStubsTest.php index b0189897090..d4c909841d6 100644 --- a/tests/CoreStubsTest.php +++ b/tests/CoreStubsTest.php @@ -424,10 +424,44 @@ function takesList(array $list): void {} $globBrace = glob('abc', GLOB_BRACE); PHP, ]; + yield 'randomizer' => [ + 'code' => <<<'PHP' + */ + $arr = []; + $res = $r->shuffleArray($arr); + /** @psalm-check-type-exact $res = list */; + + /** @var non-empty-array */ + $arr = []; + $res = $r->shuffleArray($arr); + /** @psalm-check-type-exact $res = non-empty-list */; + + $str = '123'; + $res = $r->getBytesFromString($str, 3); + /** @psalm-check-type-exact $res = non-empty-string */; + PHP, + 'assertions' => [], + 'ignored_issues' => [], + 'php_version' => '8.3', + ]; } public function providerInvalidCodeParse(): iterable { + yield 'randomizer 8.2' => [ + 'code' => <<<'PHP' + getBytesFromString($str, 3); + PHP, + 'error_message' => 'InvalidAssignment', + 'error_levels' => [], + 'php_version' => '8.2', + ]; yield 'json_decode invalid depth' => [ 'code' => '