Ran into this error today which is confusing and seems broken. Can someone explain why this would throw an error and what the intended solution is? Thanks
htmlentities()
: Passing null to parameter #1 ($string) of type string is deprecated: PHP 8.3
This code happens to be in a form building class so sometime $fieldValue is a string and other times it is null. Both are valid.
$fieldValue = null;
echo htmlentities($fieldValue ?? '’);
Thanks
Daniel Baldwin
541-401-7797
Hi Daniel,
I am not sure that the mailing list is the best place to ask such a
question. In fact, this question already exists on Stack Overflow:
https://stackoverflow.com/q/71707325
As of PHP 8.4, this is still not an error. It was deprecated in PHP
8.1 and won't be turned into an error until at least PHP 9.0. The
deprecation of this confusing behaviour has been heavily contested,
but many agree that it is a good step forward. It is a step towards
making the user-defined functions and built-in functions consistent
with each other.
You say that $fieldValue in your code can be either a string or null,
but you don't explain why it can be null. Anytime you have a union of
two types, you need to design your code around it, so I presume you
have a good reason to allow null. Using ?? '’ is a cheap solution to
avoid the deprecation message, but it's not a good solution. You're
not really handling the null value in any meaningful way. You're
equating it to an empty string, so it begs the question of why the
value is a union of two types in the first place. So in summary, the
"error" isn't confusing or broken; your code is.
Your email isn't a very good discussion starter, so I imagine you
won't get many replies. I hope my explanation and the link to Stack
Overflow will help you in your current situation. I also encourage you
to read the mailing list rules if you haven't done so already:
https://github.com/php/php-src/blob/master/docs/mailinglist-rules.md
Regards,
Kamil Tekiela
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 = NULL;
if ('' == $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
“null is always converted to an empty string”
https://www.php.net/manual/en/language.types.integer.php
“null is always converted to zero (0)”
https://www.php.net/manual/en/language.types.float.php
“For values of other types, the conversion is performed by converting the
value to int first and then to float”
https://www.php.net/manual/en/language.types.boolean.php
“When converting to bool, the following values are considered false [...]
the unit type NULL”
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 = ($_GET['q'] ?? NULL); // Common since PHP 7
$search = (isset($_GET['q']) ? $_GET['q'] : NULL);
$search = filter_input(INPUT_GET, 'q');
$search = $request->input('q'); // Laravel
$search = $request->get('q'); // Symfony
$search = $this->request->getQuery('q'); // CakePHP
$search = $request->getGet('q'); // CodeIgniter
Using ?? '’ 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:
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):
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 = " \n\r\t\v\x00") {
return \trim($string ?? '', $characters);
}
function htmlspecialchars($string, $flags = ENT_QUOTES
| ENT_SUBSTITUTE
|
ENT_HTML401, $encoding = null, $double_encode = 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 === E_DEPRECATED
&& preg_match('/Passing null to parameter
#.* of type .* is deprecated/', $errstr));
}
set_error_handler('ignore_null_coercion', E_DEPRECATED);
Craig
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:
null is not a scalar type, and it shouldn't be treated as such. The
fact that it still silently coerced when used as operand is something to
tackle in the future, but not an argument to revert a long-standing
deprecation.
And of course, it's up to everyone to just brush null returns under the
carpet one way or the other, but at least for functions which are not
documented to return null (like htmlspecialchars()
), it might be better
to look for a cleaner solution, since otherwise you rely on undefined
behavior.
https://www.php.net/manual/en/functions.internal.php:
| If the parameters given to a function are not what it expects, such as
| passing an array where a string is expected, the return value of the
| function is undefined. In this case it will likely return null but
| this is just a convention, and cannot be relied upon. As of PHP 8.0.0,
| a TypeError exception is supposed to be thrown in this case.
And wrt other potential null values like for superglobals and database
results, you also may be better off to handle these differently than
empty strings.
Note that I'm generally in the coercive type camp.
Christoph