Why does PHP consider 0 to be equal to a string?
This is due to how PHP does the comparison operation that the ==
comparison operator denotes:
If you compare a number with a string or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically. […] The type conversion does not take place when the comparison is
===
or!==
as this involves comparing the type as well as the value.
As the first operand is a number (0
) and the second is a string ('e'
), the string is also converted to a number (see also table Comparison with Various Types). The manual page on the string data type defined how the string to number conversion is done:
When a string is evaluated in a numeric context, the resulting value and type are determined as follows.
If the string does not contain any of the characters '
.
', 'e
', or 'E
' and the numeric value fits into integer type limits (as defined byPHP_INT_MAX
), the string will be evaluated as an integer. In all other cases it will be evaluated as a float.
In this case the string is 'e'
and thus it will be evaluated as a float:
The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be
0
(zero). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an 'e
' or 'E
' followed by one or more digits.
As 'e'
does not start with a valid numeric data, it evaluates to float 0
.
"ABC" == 0
evaluates true
because first "ABC"
is converted to integer and becomes 0
then it is compared to 0
.
This is an odd behaviour of the PHP language: normally one would expect 0
to be promoted to string "0"
and then compared to "ABC"
with a result false
.
Perhaps that's what happen in other languages like JavaScript where the weak comparison "ABC" == 0
evaluates false
.
Doing a strict comparison solves the problem:
"ABC" === 0
evaluates false
.
But what if I do need to compare numbers as strings with numbers?
"123" === 123
evaluates false
because the left and right term are of different type.
What is actually needed is a weak comparison without the pitfalls of PHP type juggling.
The solution is to explicit promote the terms to string and then do a comparison (strict or weak doesn't matter anymore).
(string)"123" === (string)123
is
true
while
(string)"123" === (string)0
is
false
Applied to the original code:
$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
$item['price'] = -1;
}
You are doing ==
which sorts out the types for you.
0
is an int, so in this case it is going to cast 'e'
to an int. Which is not parsable as one and will become 0
. A string '0e'
would become 0
and would match!
Use ===
From PHP.net:
Comparisons between strings and numbers using == and other non-strict comparison operators currently work by casting the string to a number, and subsequently performing a comparison on integers or floats. This results in many surprising comparison results, the most notable of which is that 0 == "foobar" returns true.
However this behavior was changed in PHP 8.0:
When comparing to a numeric string, PHP 8 uses a number comparison. Otherwise, it converts the number to a string and uses a string comparison.
PHP 7
0 == 'foobar' // true
0 == '' // true
4 == '4e' // true (4e is cast as a number and becomes 4)
PHP 8 converts numbers to strings before making comparisons
0 == 'foobar' // false
0 == '' // false
4 == '4e' // false ('4e' is considered non-numeric therefore 4 is cast as a string and becomes '4')
This is a major change therefore it was implemented in a new major PHP version. This change breaks backward compatibility in scripts that depend on the old behavior.