Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:117790 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 46059 invoked from network); 26 May 2022 10:38:12 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 26 May 2022 10:38:12 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id A93B5180210 for ; Thu, 26 May 2022 05:20:56 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 26 May 2022 05:20:56 -0700 (PDT) Received: by mail-wr1-f44.google.com with SMTP id l30so1924031wrb.8 for ; Thu, 26 May 2022 05:20:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=craigfrancis.co.uk; s=default; h=mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=88uHNZyd/39gHn0bJRRpRbyKrH4xmnjOQUWRxqU+ja0=; b=ScHGJ/9do1+aulr+jHDQmrk0rNre4vslc7kfMQ0rqZ9o1wSjs8Hbq9jijjyQlm84NU q3+p6bRktYN7s0xezjdUfp1+juOJYEUSKG6audpQasWf6aVZ3I1ClMKldemCgfWKR2/u naU72rY5LFDaxhYZnjsJk+OTebH6cbrxawm1o= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=88uHNZyd/39gHn0bJRRpRbyKrH4xmnjOQUWRxqU+ja0=; b=yGG2aZ9CZrv1zOHAWoCHXn/r5ZFj5fuUKlOfY/T3fBN2Yhue4a6TkjA7KCOPByx4Kl WF90y1XtOebkR/HbQDScVRXVm/RmYwdb4eOH4pCDf7rgpLO6pM+U71F6zDmsRsv8FoqS HnxAd52KcJxVEChLWQPpAd4rUoanGdUxhjDDiO1dLDkwjj4Ji+vQRKPDx+HSji/GYWLe UxYhMSDlgT1bSFmTIeWJvmDlGGb+ZSAWuSA9PtRdapt4H0w2czLh51rVyHWVOaUMZBKM tAPdAJ+7f6ly3lnjVn0L3WTOj67gsCaONi5AhwtkWGCmxNaYGoGvFPNzBEXxn215DSdN nc3g== X-Gm-Message-State: AOAM531A/UcIxBp8Je8etEA0ycT+t3HU9OvnWK5AzLHSpC2Us+eSwCc0 SIKslJy11vQULgJJjSJxjenRyO8hLSJjPQ== X-Google-Smtp-Source: ABdhPJx2jhNe9bXf8nODgkxupdaUOotfVpgbJdYUlM+oGYIwgsq/WeJ54WPgIWf6bmG9yC2TF3Kd0A== X-Received: by 2002:adf:8bc2:0:b0:20e:7a89:277 with SMTP id w2-20020adf8bc2000000b0020e7a890277mr25782993wra.58.1653567654734; Thu, 26 May 2022 05:20:54 -0700 (PDT) Received: from smtpclient.apple ([94.173.138.98]) by smtp.gmail.com with ESMTPSA id h2-20020a7bc922000000b003942a244ecbsm1824298wml.16.2022.05.26.05.20.53 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 26 May 2022 05:20:54 -0700 (PDT) Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3696.80.82.1.1\)) In-Reply-To: <1180af01-080f-ee0a-3159-74bf7e0a8aea@gmail.com> Date: Thu, 26 May 2022 13:20:53 +0100 Cc: internals@lists.php.net Content-Transfer-Encoding: quoted-printable Message-ID: <73F563E2-7C31-4B5E-A6B9-AE1BD05ADD1C@craigfrancis.co.uk> References: <1755E8B5-229B-47B2-BBAF-B5E014F5473D@craigfrancis.co.uk> <1180af01-080f-ee0a-3159-74bf7e0a8aea@gmail.com> To: Rowan Tommins X-Mailer: Apple Mail (2.3696.80.82.1.1) Subject: Re: [PHP-DEV] NULL Coercion Consistency From: craig@craigfrancis.co.uk (Craig Francis) On 24 May 2022, at 09:47, Rowan Tommins wrote: > On 23/05/2022 19:45, Craig Francis wrote: >=20 >> For those against my RFC, can you take a quick look at this patch for = Laravel: >>=20 >> = https://github.com/laravel/framework/pull/36262/files#diff-15b0a3e2eb2d683= 222d19dfacc04c616a3db4e3d3b3517e96e196ccbf838f59eR118 >>=20 >> If passing NULL to `htmlspecialchars()` represents a problem, as used = in templates like `

Hi {{ $name }}

`, presumably this patch should = be reverted? >=20 >=20 > I notice that the docblock didn't previously list null as valid input, = so this was only working by mistake, as the commit message admits. If = you copied the documented union to the native type declaration on the = parameter, it would immediately reject nulls, because that has always = been the behaviour of user-defined functions. Static analysis tools will = also have complained that null was not a valid input according to the = documentation. To confirm the order of events: First, the Docblock originally said this function did not accept NULL, = but at runtime it accepted/coerced NULL to an empty string. This is = exactly how `htmlspecialchars()` worked pre 8.1. Where developers using = static analysis tools can choose to treat NULL as an invalid value, and = those tools could report nullable variables as an error (via strict type = checking). Second, the Docblock and implementation was updated to allow NULL, = because NULL is common value (a backwards compatibility issue), so this = HTML encoding function, used by Blade templates by default, now accepts = NULL. This seems to undermine the argument that NULL should not be passed to = `htmlspecialchars()`. I did suggest updating the ~335 function parameters to be nullable; but = that was rejected because some developers prefer to treat NULL as an = invalid value. To keep those developers happy, and avoid the backwards = compatibility issue, it seems easier to allow NULL to be coerced (like = other contexts, e.g. string concat). > I also note that the commit message says "On PHP >=3D 8.1, an error is = thrown if `null` is passed to `htmlspecialchars`." which is of course = not true for native PHP, only if you make the highly dubious decision to = promote deprecations to errors. While I'd put the word "error" down as a typo, the intention is for 9.0 = to throw a type error. And while user-defined functions are part of the conversation (for = consistency reasons), I'm trying to find the benefits of breaking NULL = coercion for internal functions (because, if there is an overall = benefit, that Laravel Blade patch should be reverted). > As an anecdote, I was recently working on a bug involving nulls = causing unintended behaviour in an API. As part of the clean-up, I = changed a function signature from getByIdentifer($identifier) to = getByIdentifer(string $identifier), and during testing, got an error = because I'd missed a scenario where null was passed. This was a good = result - it prevented the broken behaviour and alerted me to the case = that needed fixing. If the parameter had instead been silently coerced = to an empty string, I would have got neither an error nor the original = null behaviour, causing a new bug that might have taken much longer to = track down. >=20 > If your RFC as drafted was implemented, I could only have achieved the = desired check by turning on strict_types=3D1 for whole sections of the = application, which would probably require dozens of other fixes, or = writing an ugly manual check: >=20 > function getByIdentifier(?string $identifier) { > if ( $identifier =3D=3D=3D null ) { > throw new TypeError("Function doesn't actually accept = nulls"); > } > // ... > } It sounds like you got lucky - you have a function that has a problem = with NULL (but I assume it's fine with an empty string?), and during = your testing you happened to pass NULL to this function. As noted = before, static analysis is *considerably* better at these types of = checks, because it's able check if variables *can* contain NULL. They = can also perform other checks as well (important when your code seems to = care about NULL vs an empty string). > As I have said previously, I share your concern about the impact of = the currently planned change, but I don't think loosening the existing = type rules on user-defined functions is a good solution. If you have a better solution, please let me know. I don't think breaking NULL coercion for internal functions helps (the = change seems to involve loads of tiny updates, probably by littering = projects with `$val ?? ''`, `strval($val)`, or `(string) $val`, with = hard to define benefits); in contrast, allowing NULL coercion for = user-defined functions would at least keep user-defined and internal = functions consistent, with NULL coercion continuing to work in the same = way as other contexts (e.g. string concat, =3D=3D comparisons, sprintf, = etc); or how coercion works between string/int/float/bool. That said, I would still like to know about the benefits of rejecting = NULL for `htmlspecialchars()`, in the context of that Laravel Blade = patch; because, if it is beneficial (vs the BC break), they should = revert that patch... shouldn't they? Craig