On 7 May 2022, at 22:11, Jordan LeDoux <jordan.led...@gmail.com> wrote:
> On Sat, May 7, 2022 at 1:38 AM Craig Francis <cr...@craigfrancis.co.uk 
> <mailto:cr...@craigfrancis.co.uk>> wrote:
> 
> Not what I'm going for... but anyway, to get an idea of your position, do you 
> think the string '15' should be coerced to a number, or should it fatal error 
> as well? e.g.
> 
> $my_number = round($request->get('my_number'));
> 
> 
> '15' is not an undefined value.
> 
> If a program uses the paradigm that null represents the equivalent of 
> something like `unset($var)`


Hi Jordan,

Some programers can do that, but it's not how everyone sees it.

NULL is not the same as `unset()`, it is a value in itself, and many 
programmers simply give no thought to the distinction between an Empty String 
and NULL. e.g. when using the noted frameworks, NULL is provided for user 
values, and developers will often treat it the same as an Empty String... if 
`$name == ''`, then show an error saying your name is required; if 
`strlen($name) > 200` then your name is too long, etc.



> What exactly would be the purpose of `?int` if this RFC was passed? To pass 
> the value as null instead of 0?


I'll talk about `int` below; but just so I can focus on the nullability part of 
this question, I'll use `?string`...

Yes, that's all I'm proposing.

For example `?string` would not change (it will continue to provide the NULL 
value to the function), whereas `string` would coerce NULL to an Empty String, 
e.g.

```
function accept_nullable_string(?string $my_string) {
  var_dump($my_string);
}

function accept_string(string $my_string) {
  var_dump($my_string);
}

accept_nullable_string(3);     // string(1) "3"
accept_nullable_string('2');   // string(1) "2"
accept_nullable_string(true);  // string(1) "1"
accept_nullable_string(false); // string(0) ""
accept_nullable_string(NULL);  // NULL

accept_string(3);     // string(1) "3"
accept_string('2');   // string(1) "2"
accept_string(true);  // string(1) "1"
accept_string(false); // string(0) ""
accept_string(NULL);  // Currently TypeError, change to string(0) ""
```


> That's it?


Yep, but the point is about avoiding the upgrade problem when internal 
functions will stop coercing NULL, and instead throw type errors (these are 
hard to find, and will break code in seemly random places).



> What about `int|float`? Which one would it be coerced to?


Good question,

Short answer; today, if you pass the string '4' to a function that uses 
`int|float`, then it receives `int(4)`:

```
function accept_number(int|float $my_int) {
  var_dump($my_int);
}

accept_number('4'); // int(4)
```

Longer answer...

First I'll note the documentation says (for string to integer conversion) "If 
the string is numeric or leading numeric then it will resolve to the 
corresponding integer value, otherwise it is converted to zero (0)."

https://www.php.net/manual/en/language.types.integer.php#language.types.integer.casting
 
<https://www.php.net/manual/en/language.types.integer.php#language.types.integer.casting>

But an Empty String is not coerced to `int(0)` for user defined functions, or 
arithmetic (e.g. `5 + ""` complains after PHP 7.1)... whereas, an empty string 
is converted to 0 when using `intval($var)`, and `(int) $var`.

While I think this can be a bit harsh, I can see how it might avoid some 
ambiguity/issues, e.g. `$offset = ''; $a = substr('abc', $offset)`, although I 
have seen `substr('abc', NULL, $length)` a few times.

If PHP was to throw an exception for NULL to integer coercion with internal 
functions, while I recognise this would still create a BC break (e.g. 
`round($nullable)`), I'd might be fine with it, because an Empty String to 
Integer is also rejected.

That said, and back to your question about `int|float`, because the string '4' 
is coerced to `int(4)`, I'd prefer NULL to be coerced to `int(0)`.



> This pretty radically changes how typing itself would work in existing user 
> programs.


I wouldn't say this is radical, considering it's just removing a single type 
error (I suspect it's rare for code to rely on NULL coercion triggering an 
exception)... in contrast, internal functions have always worked with null 
coercion, and works in other contexts like string concatenation (`'A' . 
$nullable`), == comparisons, the print()/echo() functions, etc.



> What I'm saying is that for such a radical BC break you need to provide some 
> compelling justification, and I'm concerned from your replies in this thread 
> and the content of the RFC that you don't actually understand the extent of a 
> BC-break you're proposing.


As noted previously, and the example I've added to the "Backward Incompatible 
Changes" section, I don't think this is a radical BC break, it's fairly small 
change designed to make NULL coercion work as documented, in a constant way 
(all contexts), and most importantly, avoid the upgrade problems for PHP 9.0 
(please keep in mind the lack of tooling to help with this, where the only safe 
and easy way to handle this would be to add NULL coalescing throughout the code 
base, which is not a good idea).

Craig


Reply via email to