Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126211 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id EF8DB1A00BD for ; Thu, 16 Jan 2025 01:49:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1736992016; bh=q/Tgu90lYzHFqrkJ0D0H/1JGTkqq7LdO/cvJ2l3o4lw=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=IEzV+HTzqE7yl0v0pk9xgd4dIMLbOcbHudeU4RtbaE4TUWALw9ilShLdSzcelK7iU H+WGo0Z3iyvCbHTQskmqO45xKS9L5aYV7G02FYgbrKq+srk19FTkcMh5Ni/sVCSAnd 61oiXKeHUUmkridVf68ch/E9k7Vqy1czHS/5xOuow5YIei5yIpqsn9zrC9ls1/Ooc/ plK7QEGS2Fk6ut8+elqLZT3nu/fJnON9EnIvQYVNTXIb4rOF4wXLdX2jp4ogpfufwj gFnlUrCq/oe8hhfg1T6YdrRBEdfOhMm6q1yuLUTU2sZvhg3WxS1eUelrfbfUkwGZBz dHCN2831ySvvw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 4E3A0180047 for ; Thu, 16 Jan 2025 01:46:55 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,HTML_MESSAGE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-ej1-f44.google.com (mail-ej1-f44.google.com [209.85.218.44]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 16 Jan 2025 01:46:54 +0000 (UTC) Received: by mail-ej1-f44.google.com with SMTP id a640c23a62f3a-aa6b4cc7270so71500866b.0 for ; Wed, 15 Jan 2025 17:49:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=craigfrancis.co.uk; s=default; t=1736992185; x=1737596985; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=fiZmjo6Bh+6/9SIgqEJWf7LFrClGjY6MOooTT8lju38=; b=ab3LIgHPxA1aGRFX53vy4Y7aHqA4H9dDRbO1NJ7qTY7zCaiyg8caOkVoIKNQ8J8CLQ +qRFvA8Eqz0zKHShWfuyk8lqkQxpXOz7uVUpG98LT2fWLt3POir8vZSx7mdJdjThfNiq emt/Yynf7yP9/tPsWfj3lzx1p3xVh/r0rmClo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1736992185; x=1737596985; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=fiZmjo6Bh+6/9SIgqEJWf7LFrClGjY6MOooTT8lju38=; b=dwBbZbI9jssAdu1vtxOJe+2QJVilTQGRPXZGQG6Mleg19HKxwHc7ZIeYlA+67k2f9h GwNphiIBKqQ8spJFYQOMamcgOSXBiJI5ke5LYM73OIP5pFAqKhacLNt24wS8FEfR2xsQ oGoU6ouS8KhZF9XePTOBGN0qkSMo7TVud58wemwcYGp0FwQGoiwhTmr4NLGxYVmkryZh BsZlv62iN5nB2c6fXhVqr3Ogb9loSLCeIKCoZ6meo4LAnWBwImoyZhM4VjFT+QdY1drl LvNJ6Wwp1xJwmlYZo4KegwneMeD2m2NlttOCsEDXRlFYLBqXSdIysI5z15/716mZRbrl fc1g== X-Forwarded-Encrypted: i=1; AJvYcCVLwdSrmRwDKKgkC7ZI6lDsmr/i1R+tzJ8YHcH6qjZ8kyZAIwpCZc+vT522AxUJwHyxFYNeyIO3lqI=@lists.php.net X-Gm-Message-State: AOJu0YzB9aTsIXRSjp54UTz2mY45DmvNwfZRPpunE/++vxCCRYkjUOO1 rIGZesB3dNhWbjjcDnEiZr9q9ODuU0mzBuOkEAnY8//Z7+YJN/Lss83qGS27LYCD7yB9o66ucjx lUO7PgHZe63pwLwi4u2w0aTEC6FwvdWiG0Yr5TA== X-Gm-Gg: ASbGncsKOdHQtMQ0227QeGy7tANkbqekgs81wrYgmnZxOrr6p7pXEPqO39HtsWMUe/O 2uazKh/861XvHjGmk9JpntKP/9ZmMN66V/iPwBA== X-Google-Smtp-Source: AGHT+IFDgcCVZrEm+G70j5frGMgV3SzNfXz0Ic4X9cwrtGu3T2DVR1PH+JnUKwtz83mvbROSRybAWdAqvBJ5ATNyYTU= X-Received: by 2002:a05:6402:3596:b0:5d0:ea4f:972f with SMTP id 4fb4d7f45d1cf-5d972e0b068mr68778953a12.8.1736992184415; Wed, 15 Jan 2025 17:49:44 -0800 (PST) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Thu, 16 Jan 2025 01:49:33 +0000 X-Gm-Features: AbW1kvY4nKRh3HM9XhAhJgY0uT3lBZyCtG7wyaRO5bqK2jLv9qdLJzPH0YRKGwk Message-ID: Subject: Re: [PHP-DEV] htmlentities(): Passing null to parameter #1 ($string) of type string is deprecated To: Kamil Tekiela Cc: Daniel Baldwin , php internals Content-Type: multipart/alternative; boundary="00000000000021521b062bc9025a" From: craig@craigfrancis.co.uk (Craig Francis) --00000000000021521b062bc9025a Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Fri, 3 Jan 2025 at 16:03, Kamil Tekiela wrote: > It is a step towards making the user-defined functions and built-in > functions consistent with each other. > Just to note, it could have been "consistent" if NULL coercion was supported for user-defined functions (when not using strict types), in the same way that user defined functions coerce the other types: function example(string $s, int $i) { var_dump($s); var_dump($i); } example("123", "321"); example(123, 321); example(1.23, 3.21); // Deprecated, 3.21 loses precision to 3 (which is fair) example(true, false); example(NULL, NULL); // Will be a fatal error in PHP 9? Fortunately NULL coercion does still work in other contexts: $nullable =3D NULL; if ('' =3D=3D $nullable) {} print($nullable); echo $nullable; sprintf('%s', $nullable); var_dump('ConCat ' . $nullable); var_dump(3 + '5' + $nullable); var_dump($nullable / 6); And, NULL coercion is documented: https://www.php.net/manual/en/language.types.string.php =E2=80=9Cnull is always converted to an empty string=E2=80=9D https://www.php.net/manual/en/language.types.integer.php =E2=80=9Cnull is always converted to zero (0)=E2=80=9D https://www.php.net/manual/en/language.types.float.php =E2=80=9CFor values of other types, the conversion is performed by converti= ng the value to int first and then to float=E2=80=9D https://www.php.net/manual/en/language.types.boolean.php =E2=80=9CWhen converting to bool, the following values are considered false= [...] the unit type NULL=E2=80=9D --- > You say that $fieldValue in your code can be either a string or null, but > you don't explain why it can be null. When working with web pages (think GET/POST/COOKIE values), or databases, NULL is common: $search =3D ($_GET['q'] ?? NULL); // Common since PHP 7 $search =3D (isset($_GET['q']) ? $_GET['q'] : NULL); $search =3D filter_input(INPUT_GET, 'q'); $search =3D $request->input('q'); // Laravel $search =3D $request->get('q'); // Symfony $search =3D $this->request->getQuery('q'); // CakePHP $search =3D $request->getGet('q'); // CodeIgniter --- > Using ?? '=E2=80=99 is a cheap solution to avoid the deprecation message,= but it's > not a good solution. An alternative is to use `(string) $var`, which some may argue is more dangerous than using coercion :-) This is how Rector deals with the issue; the NullToStrictStringFuncCallArgRector will litter your code with loads of changes, basically anything it can't be sure of the type will be coerced: -echo htmlspecialchars($var) +echo htmlspecialchars((string) $var) And it needs to do this for ~287 functions: https://github.com/rectorphp/rector-src/blob/main/rules/Php81/Enum/NameNull= ToStrictNullFunctionMap.php There are several projects I have not done this to (because it changes thousands of lines of code). With new code, I've just go into the habit of doing ugly things like `trim(strval($a))`, and for templating I've taken a similar approach to Laravel Blade (abstracting the use of htmlspecialchars): https://github.com/laravel/framework/blob/ab1506091b9f166b312b3990d07b2e21d= 971f2e6/src/Illuminate/Support/helpers.php#L119 That said, as I expect PHP 9 to trigger a Fatal Error every time this happens (last time I raised this, lets just say it wasn't a pleasant experience), I'm tempted to create a simple library to re-define all of these functions, e.g. namespace FixNullCoercion; function urlencode($string) { return \urlencode($string ?? ''); } function trim($string, $characters =3D " \n\r\t\v\x00") { return \trim($string ?? '', $characters); } function htmlspecialchars($string, $flags =3D ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, $encoding =3D null, $double_encode =3D true) { return \htmlspecialchars($string ?? '', $flags, $encoding, $double_encode); } // ... etc for the ~287 functions. Then anyone can include the library, and simply use the FixNullCoercion namespace. In the mean time, as a quick and simple way to ignore the deprecation message, I'm using: function ignore_null_coercion($errno, $errstr) { return ($errno =3D=3D=3D E_DEPRECATED && preg_match('/Passing null to par= ameter #.* of type .* is deprecated/', $errstr)); } set_error_handler('ignore_null_coercion', E_DEPRECATED); Craig --00000000000021521b062bc9025a Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
On Fri,= 3 Jan 2025 at 16:03, Kamil Tekiela <tekiela246@gmail.com> wrote:
It is a = step towards making the user-defined functions and built-in functions consi= stent with each other.


J= ust to note, it could have been "consistent" if NULL=C2=A0coercio= n was supported for=C2=A0user-defined functions (when not using strict type= s), in the same way that user defined functions coerce the other types:


function example(string $s, int $i) {<= br>=C2=A0 var_dump($s);
=C2=A0 var_dump($i);
}

example("1= 23", "321");
example(123, 321);
example(1.23, 3.21); /= / Deprecated, 3.21 loses precision to 3 (which is fair)
example(true, fa= lse);
example(NULL, NULL); // Will be a fatal error in PHP 9?
=

Fortunately NULL coercion does still work in = other contexts:

$nullable =3D NULL;
if= =C2=A0('' =3D=3D $nullable) {}
print($nullable);
echo $nullable;
sprintf('%s', $nullable);
var= _dump('ConCat ' . $nullable);
var_dump(3 + '5' + $nullab= le);
var_dump($nullable / 6);


An= d, NULL coercion is documented:


=E2=80=9Cnull i= s always converted to an empty string=E2=80=9D


=E2=80=9C= null is always converted to zero (0)=E2=80=9D


=E2=80=9CFor v= alues of other types, the conversion is performed by converting the value t= o int first and then to float=E2=80=9D


=E2=80=9CWhen con= verting to bool, the following values are considered false [...] the unit t= ype NULL=E2=80=9D


---
=C2= =A0
You say that $fi= eldValue in your code can be either a string or null, but you don't exp= lain why it can be null.


Whe= n working with web pages (think GET/POST/COOKIE values), or databases, NULL= is common:


$search =3D ($_GET['= ;q'] ?? NULL); // Common since PHP 7

$search =3D (iss= et($_GET['q']) ? $_GET['q'] : NULL);
=C2=A0
$s= earch =3D filter_input(INPUT_GET, 'q');
=C2=A0
$search =3D $r= equest->input('q'); // Laravel
$search =3D $request->get(&= #39;q'); // Symfony
$search =3D $this->request->getQuery('= q'); // CakePHP
$search =3D $request->getGet('q'); // Cod= eIgniter


---
=C2=A0
Using ?? '=E2=80=99 is a che= ap solution to avoid the deprecation message, but it's not a good solut= ion.


An alternative is to us= e `(string) $var`, which some may argue is more dangerous than using=C2=A0c= oercion :-)

This is how Rector deals with the issu= e; the=C2=A0NullToStrictStringFuncCallArgRector will litter your code with = loads of changes, basically anything it can't be sure of the type will = be coerced:

-echo htmlspecialchars($var)
+echo = htmlspecialchars((string) $var)

And it needs to do= this for ~287 functions:


Th= ere are several projects I have not done this to (because it changes thousa= nds of lines of code).

With new code, I've jus= t go into the habit of doing ugly things like `trim(strval($a))`, and for t= emplating I've taken a similar approach to=C2=A0Laravel Blade (abstract= ing the use of htmlspecialchars):


That said, as I expect PHP 9 = to trigger a Fatal Error every time this happens (last time I raised this, = lets just say it wasn't a pleasant experience), I'm tempted to crea= te a simple library to re-define all of these functions, e.g.

namespace FixNullCoercion;

<= div>function urlencode($string) {
=C2=A0 return \urlencode($string ?? &#= 39;');
}

function trim($string, $characters= =3D " \n\r\t\v\x00") {
=C2=A0 return \trim($string ?? '&#= 39;, $characters);
}

function htmlspecialchars(= $string,=C2=A0$flags =3D ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401,=C2=A0$e= ncoding =3D null,=C2=A0$double_encode =3D true) {
=C2=A0 return \= htmlspecialchars($string ?? '', $flags, $encoding, $double_encode);=
}

// ... etc for the ~287 functions.


Then anyone can include the library, and sim= ply use the=C2=A0FixNullCoercion=C2=A0namespace.

I= n the mean time, as a quick and simple way to ignore the deprecation messag= e, I'm using:


function ignore_n= ull_coercion($errno, $errstr) {
=C2=A0 return=C2=A0($errno =3D=3D=3D E_D= EPRECATED && preg_match('/Passing null to parameter #.* of type= .* is deprecated/', $errstr));
}
set_error_handler('ignore_n= ull_coercion', E_DEPRECATED);


C= raig

--00000000000021521b062bc9025a--