First, thanks Rowan (same to you Kamil), I do appreciate your thoughts on this...
On 9 Nov 2023, at 20:01, Rowan Tommins <rowan.collins@gmail.com> wrote:
> On 09/11/2023 14:58, Craig Francis wrote:
>> We might as well make the PHP 9 upgrade as hard as possible, just to force a little bit of `strict_types=1` on everyone.
>
>
> Just to be clear, strict_types has nothing to do with this;
strict_types=1 enables strict type checking, which adds fatal type errors instead of coercion (all fine, all good); but for everyone not using strict_types=1, there is a fatal type error when NULL is passed to a function argument, while all other type coercions (e.g. string '5' to int) work?
> changing it does not allow you to pass nulls to typed parameters, and never did: https://3v4l.org/atT0B
Yep, specifically for user defined function parameters, but NULL coercion works with string concatenation, == comparisons, arithmetics, sprintf, print, echo, array keys?
That said, with the original RFC:
https://wiki.php.net/rfc/scalar_type_hints_v5
> "The only exception to this is the handling of NULL: in order to be consistent with our existing type declarations for classes, callables and arrays, NULL is not accepted by default"
I never understood why NULL was considered a complex value like a class/callable/array, when it's more like a simple bool/int/float/string, where NULL can be coerced (and NULL is documented as being coercible in all other contexts, like concatenation).
Also...
> “it should be possible for existing userland libraries to add scalar type declarations without breaking compatibility”
But that's not the case; if you add types to a legacy projects functions, fatal type errors happen instead of accepting NULL and coercing it as needed (it's why I never bothered adding types to the legacy project I still look after).
> Nor has strict_types=0 ever been aligned to the loosest coercion rules used in other contexts; for instance, an empty string was never an acceptable input for an integer parameter, even in versions where it was an acceptable operand for addition: https://3v4l.org/khD32
Fair, but that makes a little bit more sense to me (although I'd assume an empty string would be coerced to 0).
> As for your previous example:
>
> > redirect('/thank-you/?ref=' . urlencode($ref));
>
> If $ref isn't set, any of these might be the correct URL: "/thank-you/", "/thank-you/?ref=", "/thank-you/?ref=default", ... The language can only guess one of those.
NULL has always been coerced to an empty string with urlencode(), it happens in a lot of projects, and everyone seemed to be fine with it?
> A similar example I've come across is in manually escaped SQL (yes, I know, use parameters instead...):
>
> $sql = "Insert Into blah ( whatever ) Values ( '" . sql_escape($someVar) . "' )";
>
> Nine times out of ten, if the PHP variable is null, you want an SQL null, not ''; but if the [imaginary] sql_escape function doesn't reject nulls, you may not notice the bug until you've ended up with garbage in your DB.
Fortunately I only have to maintain 1 legacy project, and I've only had to make 29 edits so far, but every single one involved me adding strval(), it's certainly not making the code better, and I know there are still more to find, but I'll have to wait for customers to trip them.
Craig