Empty string comparison to zero gives different result in PHP 8 than in previous versions
You are right, this is a major change.
As with any version upgrade, you can find a guide to Migrating to PHP 8.0 in the official PHP manual. If you click on Backward Incompatible Changes you will see that this change is the very first thing on that page:
Non-strict comparisons between numbers and non-numeric strings now work by casting the number to string and comparing the strings.
As well as an example in the next sentence, there is a before-and-after comparison table which includes the exact example you gave:
Comparison:
0 == ""
; Before:true
; After:false
If you have code that was relying on the old behaviour, you will need to update it to be more explicit about the values expected. For instance, all of the following work in all versions of PHP:
if ( $value === 0 || $value === "" ) { ... }
if ( (string)$zero === "" ) { ... }
if ( (int)$emptyString === 0 ) { ... }
For more background on the change, you can read the original proposal here: PHP RFC: Saner string to number comparisons
Unexpected result of greater than or less than comparison on PHP 8
There is no obviously correct result for a comparison between a string and a number. In many languages, it would just give an error; in others, including PHP, the language tries to make sense of it by converting both operands to the same type, but this involves a judgement of which type to "prefer".
Historically, PHP has preferred comparing numbers to comparing strings: it treated "U0M262" > 100000
as (int)"U0M262" > 100000
. Since (int)"U0M262"
has no obvious value, it is evaluated as 0
, and the expression becomes 0 > 100000
, which is false.
As of PHP 8, this behaviour has changed and PHP now only uses a numeric comparison for "numeric strings", e.g. "42"
clearly "looks like" 42
.
Since "U0M262"
doesn't fit the requirements for a numeric string, "U0M262" > 100000
is now treated as "U0M262" > (string)100000
. This does a byte-wise comparison of the sort order for the two strings, and finds that since "U" comes after "1" in ASCII (and any ASCII-derived encoding, including UTF-8), the result is true.
Because of how ASCII (and compatible encodings such as UTF-8) is arranged:
- A string starting with a control character or space will be "less than" any number
- A string starting with a letter will be "more than" any number
- A string starting with any of "! " # $ % & ' ( ) * + , - . /" will be "less than" any number
- For a string starting with a digit, you need to look at the individual bytes
- Any other string will be "more than" any number
As ever, you can tell PHP which comparison you intended, and get the correct behaviour in all versions, using explicit casts:
var_dump((int)"U0M262" > (int)100000); // bool(false)
var_dump((string)"U0M262" > (string)100000); // bool(true)
(Obviously, this makes no sense if you're hard-coding both sides anyway, but assuming one or both is a variable, this is how you'd do it.)
PHP 8.0 changes how loose comparison works
PHP Documentation maintainer here, PHP 8 did change the semantics and this is shown in the migration guide. However other parts of the documentations are still lagging behind as we don't have the manpower/time for editing and documenting all the changes related to PHP 8.
So this is not a bug and more a fact that the current type juggling page is out of date in regards to PHP 8.0.
It is possible to contribute to the docs via a Pull Request to the GitHub repository.
substr returns an empty string when string is less than start characters long in PHP 8
It seems like this was an intentional change as mentioned in
https://php.watch/versions/8.0/substr-out-of-bounds
and implemented here:
https://github.com/php/php-src/pull/6182
Migration to PHP 8.1 - how to fix Deprecated Passing null to parameter error - rename build in functions
Firstly, two things to bear in mind:
- PHP 8.1 deprecates these calls, it does not make them errors. The purpose of deprecation is to give authors advance notice to fix their code, so you and the authors of libraries you use have until PHP 9.0 comes out to fix things. So, don't panic that not everything is fixed right away, and be patient with library maintainers, who will get to this in their own time.
- The quick fix in most cases is to use the null coalescing operator to provide a default value as appropriate, so you don't need a long null check around every use. For instance,
htmlspecialchars($something)
can be replaced withhtmlspecialchars($something ?? '')
Next, some options:
- Depending how many cases you have, you may be able to just fix them manually a few at a time, either adding
?? ''
or fixing a logic bug where you weren't expecting a null anyway. - Create custom functions like
nullable_htmlspecialchars
and do a straight-forward find and replace in your code. - Create custom namespaced functions like
nullableoverride\htmlspecialchars
; then in any file where you adduse function nullableoverride\htmlspecialchars;
that function will be used instead of the built-in one. This has to be added in each file, though, so you may need a tool to automate adding it. - Use Rector to automate adding
?? ''
to appropriate function calls, so you don't have to edit them all by hand. Unfortunately, there doesn't seem to be a built-in rule for this (yet), so you'd have to learn to write your own. - Possibly simpler, depending on your skills, use a regular expression find-and-replace to add the
?? ''
to simple cases.
Why does someString == 0 evaluate to true in PHP
When using the comparison (==
) operator strings will be converted to an integer when compared to another integer. This is because of type juggling in PHP. So "someString"
evaluates to zero because it is converted to an integer and has no leading digits. If you use the the identical operator (===
) type conversions are not done so "someString"
is treated a literal string and your statement will then evaluate to false.
The following will evaluate to false when type juggling is performed. Everything else will be evaluated as true:
- "" (an empty string)
- 0 (0 as an integer)
- 0.0 (0 as a float)
- "0" (0 as a string)
- NULL
- FALSE
- array() (an empty array)
- $var; (a variable declared, but without a value)
empty() returns true if a zero float value was passed as string
This zero/empty condition works the same way in both PHP 7 and 8 versions:
if (empty($var) || (is_numeric($var) && (float)$var == 0))
It checks if $var is any of the following:
not set, null, (bool)false, (int)0, (float)0.00, (string)"", (string)"0", (string)"0.00", or (array)[]
And to substitute empty():
// Checks if variable is not set, null, (bool)false, (int)0, (float)0.00, (string)"", (string)"0", (string)"0.00", (array)[], or array with nil nodes
function nil(&$var) { // https://www.dictionary.com/browse/nil
return (empty($var) || (is_numeric($var) && (float)$var == 0));
}
To be used like:
if (nil($var)) {
echo 'The value is either not set, an empty string, empty array, null, or equals zero.';
}
It can be expanded to check subnodes for arrays as well:
// Checks if variable is not set, null, (bool)false, (int)0, (float)0.00, (string)"", (string)"0", (string)"0.00", (array)[], or array with nil nodes
function nil(&$var) {
if (is_array($var)) {
foreach ($var as $node) {
if (!nil($node)) return !1;
}
}
return (empty($var) || (is_numeric($var) && (float)$var == 0));
}
Related Topics
Why Do I Need to Use a Popular Framework
Pcre Regular Expression Overlapping Matches
Pdo Lastinsertid() Always Return 0
Supplied Key Param Cannot Be Coerced into a Private Key with Google APIs
PHP Optional Parameters - Specify Parameter Value by Name
PHP Session Seemingly Not Working
Get the Values of 2 HTML Input Tags Having the Same Name Using PHP
Laravel: I Can't Send More Then 2 Variables from Controller to a View
Display an Array in a Readable/Hierarchical Format
How to Make Short Random Unique Keys, Like Youtube Video Ids, in PHP
Sending Post Parameters with Postman Doesn't Work, But Sending Get Parameters Does
Installing Pdo Driver on MySQL Linux Server
Laravel Gmail Not Working, "Username and Password Not Accepted. Learn More..."