On Thu, Aug 15, 2024, at 17:42, Vincent Langlet wrote:
> Hi,
> 
> When string is used as an array key, it's sometimes casted to an int.
> As explained in https://www.php.net/manual/en/language.types.array.php:
> "Strings containing valid decimal ints, unless the number is preceded by a + 
> sign, will be cast to the int type. E.g the key "8" will actually be stored 
> under 8. On the other 08 will not be cast as it isn't a valid decimal 
> integer."
> 
> This behavior cause some issues, especially for static analysis. As an 
> example https://phpstan.org/r/5a387113-de45-4bef-89af-b6c52adc5f69
> vs real life https://3v4l.org/pDkoB
> 
> Currently most of static analysis rely on one/many native php functions to 
> describe types.
> PHPStan/Psalm supports a `numeric-string` thanks to the `is_numeric` method.
> 
> I don't think there is a native function to know if the key will be casted to 
> an int. The implementation would be something similar (but certainly better 
> and in C) to 
> ```
> function is_int_string(string $s): bool
> {
> if (!is_numeric($s)) {
> return false;
> } 
> 
> $a[$s] = $s;
> 
> return array_keys($a) !== array_values($a);
> }
> ```
> 
> Which gives:
> is_numeric('08') => true
> ctype_digit('08') => true
> is_int_string('08') => false
> 
> is_numeric('8') => true
> ctype_digit('8') => true
> is_int_string('8') => true
> 
> is_numeric('+8') => true
> ctype_digit('+8') => false
> is_int_string('+8') => false
> 
> is_numeric('8.4') => true
> ctype_digit('8.4') => false
> is_int_string('8.4') => false
> 
> Such method would allow to easily introduce a `int-string` type in static 
> analysis and the opposite, a `non-int-string` one (cf 
> https://github.com/phpstan/phpstan/issues/10239#issuecomment-1837571316).
> 
> WDYT about adding a `is_int_string` method then ?
> 
> Thanks

Hello,

At the risk of bikeshedding, it would probably be better to define it in the 
`array_*` space, maybe something like `array_key_is_string(string $key): bool`?

As for your function definition, it can be simplified a bit:

return (($s[0] ?? '') > 0 || (($s[0] ?? '') === '-' && ($s[1] ?? '') > 0)) && 
is_numeric($s);

I believe that covers all the cases that I could think of:

01, -01, +01, 1, 1.2, -1, -1.2, ~1, ~01

— Rob

Reply via email to