[PHP-DEV] [RFC] On the need of a `is_int_string` ?

2024-08-15 Thread Vincent Langlet
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


Re: [PHP-DEV] [RFC] On the need of a `is_int_string` ?

2024-08-23 Thread Vincent Langlet
I found a simpler implementation later which rely on array_keys
```
fn is_int_string(string $s): bool => \is_int(array_keys([$s => null])[0]);
```

I considered that `is_int_string` was better in the same namespace than
`is_object`, `is_array`, `is_int`, `is_numeric`, ... but maybe there was
something better
 than `int_string` to describe this category of string since english is not
good (integish ? integable ? integerable ?).
But indeed it could be interesting to relate this method to the array
namespace...

Anyway, this topic does not seems to interest lot of developer so far ^^'

Le ven. 16 août 2024 à 01:04, Rob Landers  a écrit :

> 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
>


[PHP-DEV] strrchr with needle with multiple characters

2024-09-02 Thread Vincent Langlet
I was today old when I discovered the big difference between strchr and
strrchr in the way they handle needle with multiple characters.

It's explained in the doc https://www.php.net/manual/en/function.strrchr.php
- If needle contains more than one character, only the first is used. This
behavior is different from that of strstr()
.

You can see an example https://3v4l.org/7j5ab

I feel like this behavior has no benefit (if I want to look for the first
character, I can pass `substr($needle, 0, 1)` instead) and is just
error-prone.

Is there a reason for such behavior ? Would it be easy to change it (I have
no knowledge at all in C and in the existing PHP code) ?


Re: [PHP-DEV] Re: [RFC] [Discussion] [VOTE] Rounding Integers as int

2024-03-17 Thread Vincent Langlet
Hi,

IMHO, the more meaningful cases of the RFC are missing :

round(float, precision: >= 0): int|float // only cast to float for int overflow
ceil(float): int|float // only cast to float for int overflow
floor(float): int|float // only cast to float for int overflow

Calling ceil or floor on integer is meaningless, because it will return the
same value.
The RFC should be "Prefer int as return value when possible".

Le dim. 17 mars 2024 à 15:00, Bob Weinand  a écrit :

> On 17.3.2024 13:23:04, Marc Bennewitz wrote:
> > Hello internals,
> >
> > I have opened the vote for the "Rounding Integers as int" RFC:
> > https://wiki.php.net/rfc/integer-rounding
> >
> > Do to Easter weekend the vote will run for two weeks and two days
> > until Tue the 2nd of April 2024.
> >
> > Best regards,
> >
> > Marc Bennewitz
>
> Hey Marc,
>
> I've voted no; it should be just changed without any force_float
> parameter. Just always return int when possible (and the input was int).
> If users wish to have the old behaviour, they should just explicitly
> cast via (float).
>
> The effective BC break of that would be quite small if some things which
> return float today now would return int. I cannot imagine many cases
> where this would actually be unwanted. And as said, explicit (float)
> casts are always possible.
>
> I also dislike force_float, as it cannot just be added to a function in
> any code which shall be backwards compatible to 8.3 and older. It would
> just emit Uncaught Error: Unknown named parameter $force_float.
>
> Bob
>


[PHP-DEV] Follow up of DateTimeException RFC

2024-06-12 Thread Vincent Langlet
Hi,

There was an RFC about more appropriate date exceptions
https://wiki.php.net/rfc/datetime-exceptions
implemented in
https://github.com/php/php-src/commit/66a1a911f1d6cd4b89c0bb5577fa77f1d6a2cb96

Looking at PHP example, it updated method return types like
- Datetime::modify
- DatetimeImmutable::modify
- DateInterval::createFromDateString

https://3v4l.org/iSPL9

These methods doesn't seem to return false anymore, but the PHP stub wasn't
updated so the reflection class still consider false as a valid return type
https://3v4l.org/IZXbp.

Is there still case where such method can return FALSE or should we update
the signature of these methods (and maybe some others) ?

Thanks.


[PHP-DEV] RFC: Stop to automatically cast numeric-string to int when using them as array-key

2021-12-29 Thread Vincent Langlet
Hi,

I recently discovered that an array was automatically casting
numeric-string keys to int if it was possible. For instance, the following
array:

$a = ['01' => '01', '10' => '10'];

Is not an array with the key '01' and '10' but instead consider the second
key as an int.

This has some implications like the fact that
- array_flip(array_flip($a)) !== $a
- array_search('10', $a) is an int when array_search('01', $a) is still a
string. Someone using strict types and passing the result to a function
expecting a string could end with an unexpected crash.

I've created an issue about this https://github.com/php/php-src/issues/7845
but it was recommended to me to send a mail to this mailing list instead.

I don't think this behavior should be considered as "normal" and would like
to propose to change this for PHP 9, as it's a BC-break. To me it can and
be considered as a follow-up of
https://wiki.php.net/rfc/string_to_number_comparison and
ttps://wiki.php.net/rfc/saner-numeric-strings
. This is still a "concept"
since I never code with C and know nothing about the PHP implementation and
if this change would be possible. Any help is welcome then.

Thanks


Re: [PHP-DEV] RFC: Stop to automatically cast numeric-string to int when using them as array-key

2021-12-29 Thread Vincent Langlet
> On 29/12/2021 16:58, Lynn wrote:
> > While I'd love for this inconsistency to go away, I also know that this
> is
> > most likely such a big change that it causes months of work and broken
> code
> > because it relies on this behavior. It wouldn't surprise me if this
> > singular change would cause more work than all previous BC breaks
> combined.
>
>
> I was about to say the same thing - this is one of those ingrained
> behaviours that is very hard to analyse use of without running the code,
> so only someone with an extraordinarily good test suite would detect
> that their code was affected before it went live.
>
>
I encounter this issue with an array of zipcode as key, and let's say some
delay as int:
['1' => 2, '2' => 5, ..., '1' => 4, ... ];
If I want all the zipcode with a delay superior to 3, combining array_keys
and array_filter will give me an array of string and int, instead of an
array of only string ; like I would have expected.

Maybe a way to reduce the BC-break would be to say that
- The type of the key won't change anymore i.e. array_search(0, ['10' =>
0]) will be '10' and not 10.
- $array[10] will return $array[10] ?? $array['10']
- $array['10'] will return $array['10'] ?? $array[10]

This would avoid the issue with $products[$_GET['id']] VS
$products[(int)$_GET['id']] and still be a first step in the improvement of
this situation.


Re: [PHP-DEV] RFC: Stop to automatically cast numeric-string to int when using them as array-key

2021-12-30 Thread Vincent Langlet
> It'd not be just a BC break, it'd be an absolutely massive BC break that
> has a potential to break a ton of code and would be extremely hard to
> detect. This is not something that should be changed in an advanced
> version of the language.
>

Would you still consider this as a massive BC break if
- The type of the key don't change anymore i.e. array_search(0, ['10' =>
0]) will be '10' and not 10.
- $array[10] still return $array[10] ?? $array['10']
- $array['10'] still return $array['10'] ?? $array[10]

so it still cast the value when accessing to an array key, but when using
array_keys/array_search/array_flip/... it won't be cast anymore.


Re: [PHP-DEV] RFC: Stop to automatically cast numeric-string to int when using them as array-key

2022-01-07 Thread Vincent Langlet
> On 6 January 2022 07:29:58 GMT, James Titcumb  wrote:
> >Just thinking out loud, being able to identify an array type might be a
> way
> >forward here (using "syntax" already in use by static analysis tools in
> >docblocks, for familiarity), e.g.
> >
> >private array $property;
> >
> >This might "turn off" the automatic type juggling behaviour, for example.
> >If the existing strict_types=1 mode is on, this might fail:
> >
> >$this->property[3] = "hi";
> >
> >If strict_types=0 or not declared, the key would be cast to a string.
>
>
> Yes, something similar occurred to me earlier, and I honestly think that
> some new types like this are the only way this can practically be solved.
>
>
So a solution, which doesn't require generics, would be to introduce
something new like a `dict` as a way to create an `array`.
`{}` could be used instead of `[]`.

$dict = {'1' => '1', 'key' => 'value'};
\is_array($dict); // true


[PHP-DEV] Using less generic exceptions for dates

2022-11-21 Thread Vincent Langlet
Hi,

When using json_encode or json_decode with the `JSON_THROW_ON_ERROR` flag,
`JsonException` might be thrown.

When using `new DateTime('foo')`, a generic `Exception` is thrown.
Incidentally, I wonder why it's not an `InvalidArgumentException` (but that
could be another debate).

But my main point is that I think it would be useful to use a specific
exception
```
class DateException extends Exception {}
```
- It allows a specific treatment when catching exceptions
- It allows a specific analysis when using static analysis tools like Psalm
or PHPStan.

In a general way, I would say that PHP class/method should always use
scoped Exception instead of generics ones.

I know nothing about how php is implemented but I would say introducing
DateException shouldn't be too hard and it's BC. What do you think ?