diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index 3c1034df2db..f4258e56cab 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -44,11 +44,15 @@ use function array_merge; use function array_values; use function count; +use function filter_var; use function in_array; +use function is_numeric; use function is_string; use function preg_match; +use function trim; use const PHP_INT_MAX; +use const FILTER_VALIDATE_INT; /** * @internal @@ -237,6 +241,33 @@ public static function analyze( return true; } + /** + * @param string|int $literal_array_key + * @return false|int + */ + public static function getLiteralArrayKeyInt( + $literal_array_key + ) { + if (!is_numeric($literal_array_key)) { + return false; + } + + // PHP 8 values with whitespace after number are counted as numeric + // and filter_var treats them as such too + // ensures that '15 ' will stay '15 ' + if (trim($literal_array_key) !== $literal_array_key) { + return false; + } + + // '+5' will pass the filter_var check but won't be changed in keys + if ($literal_array_key[0] === '+') { + return false; + } + + // e.g. 015 is numeric but won't be typecast as it's not a valid int + return filter_var($literal_array_key, FILTER_VALIDATE_INT); + } + private static function analyzeArrayItem( StatementsAnalyzer $statements_analyzer, Context $context, diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index f79cc3cfdc8..30558a837fc 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -7,6 +7,7 @@ use Psalm\Codebase; use Psalm\Exception\TypeParseTreeException; use Psalm\Internal\Analyzer\ProjectAnalyzer; +use Psalm\Internal\Analyzer\Statements\Expression\ArrayAnalyzer; use Psalm\Internal\Type\ParseTree\CallableParamTree; use Psalm\Internal\Type\ParseTree\CallableTree; use Psalm\Internal\Type\ParseTree\CallableWithReturnTypeTree; @@ -86,7 +87,6 @@ use function defined; use function end; use function explode; -use function filter_var; use function get_class; use function in_array; use function is_int; @@ -100,9 +100,6 @@ use function strtolower; use function strtr; use function substr; -use function trim; - -use const FILTER_VALIDATE_INT; /** * @psalm-suppress InaccessibleProperty Allowed during construction @@ -669,11 +666,8 @@ private static function getTypeFromGenericTree( } foreach ($generic_params[0]->getAtomicTypes() as $key => $atomic_type) { - // PHP 8 values with whitespace after number are counted as numeric - // and filter_var treats them as such too if ($atomic_type instanceof TLiteralString - && ($string_to_int = filter_var($atomic_type->value, FILTER_VALIDATE_INT)) !== false - && trim($atomic_type->value) === $atomic_type->value + && ($string_to_int = ArrayAnalyzer::getLiteralArrayKeyInt($atomic_type->value)) !== false ) { $builder = $generic_params[0]->getBuilder(); $builder->removeType($key);