There has been some discussion recently among our dev team regarding PHP type conversion. I’ll give some of the problems we’ve run into and then try to shed some light on the inner workings of PHP when it does comparisons.
The first example may seem familiar to most seasoned developers, but when chained together it brings up an interesting point about PHP: The = = operator isn’t transitive.
echo (null = = 0 ? "YES" : "NO") . "\ "; //YES echo ("null" = = 0 ? "YES" : "NO") . "\ "; //YES echo ("null" = = null ? "YES" : "NO") . "\ "; //NO
As you can see, null = = 0 = = "null", but null != "null"
You may be familiar with the following kind of error. The erroneous code is usually similar to:
if ( $a = "Hello" && $b != "World" )
Seeded with $b = "World", the function assigned FALSE to $a. This is because there was a single = instead of = = in $a = "Hello", so PHP was interpreting the whole thing as an assignment operator. Since $b was not equal to "World" $b != "World" was returning TRUE, and TRUE was && with "Hello", so "Hello" was converted to FALSE, then FALSE && TRUE was assigned to $a.
PHP has a certain order of precedence for data types. It is defined loosely in the manual’s comparison operators page, but I will try to spell it out more explicitly here. There are 8 basic types of data in PHP. In order of operator precedence, they are:
That is to say, if you compare any two data types on the list, the variable with the data type lower on the list will be converted to the upper variable’s data type, and then the comparison is applied. However, when applying the first example to this hard and fast rule, we find it lacking. In reality, there are certain comparisons that are so far off PHP converts BOTH data types to a third data type. The first example actually works out like:
It’s much more easily represented as a table:
Boolean | Object | Array | Integer | String | Resource | NULL | ||
Boolean | Boolean Objects always resolve to true |
Boolean Empty arrays are false, all others are true |
Boolean 0 resolves to false, all others are true |
Boolean 0 resolves to false, all others are true |
Boolean "" resoves to false, all others are true |
Boolean Resources always resolve to true |
Boolean NULL is always false |
|
Object | Boolean Objects always resolve to true |
Objects are always greater-than |
Objects are always greater-than |
Objects are always greater-than |
Objects are always greater-than |
Objects are always greater-than |
Boolean Objects always resolve to true |
|
Array | Boolean Empty arrays are false, all others are true |
Objects are always greater-than |
Arrays are always greater-than |
Arrays are always greater-than |
Arrays are always greater-than |
Arrays are always greater-than |
Boolean Empty arrays are false, all others are true |
|
Floating Point | Boolean 0 resolves to false, all others are true |
Objects are always greater-than |
Arrays are always greater-than |
Floating Point | Floating Point | Floating Point | Boolean 0 resolves to false, all others are true |
|
Integer | Boolean 0 resolves to false, all others are true |
Objects are always greater-than |
Arrays are always greater-than |
Floating Point | Floating Point | Integer | Boolean 0 resolves to false, all others are true |
|
String | Boolean 0 resolves to false, all others are true |
Objects are always greater-than |
Arrays are always greater-than |
Floating Point | Floating Point | Floating Point | String NULL is converted to "" |
|
Resource | Boolean Resources always resolve to true |
Objects are always greater-than |
Arrays are always greater-than |
Floating Point | Integer | Floating Point | Boolean Resources always resolve to true |
|
NULL | Boolean NULL resolves to false |
Boolean Objects always resolve to true Never = = null |
Boolean Empty arrays are false, all others are true |
Boolean 0 resolves to false, all others are true |
Boolean 0 resolves to false, all others are true |
String NULL is converted to "" |
Boolean Resources always resolve to true Never = = null |
In the table where you see the phrase "No Conversion Made" that means that those two data types will never = = each other. However, in most of those situations data types are given specific return values for quantitative comparisons, such as greater-than and less-than. Note the specific case of NULL, where almost every instance of comparing to NULL results in both types being converted to Boolean.
Armed with this information, we are now capable of determining the outcome of almost any comparison in PHP. We know, for instance, that array() is greater than "Hello", but "Hello" is less than 2. We know that stdClass() is greater than array(), but both of them are equal to TRUE. There are plenty of places where PHP contradicts normal logic, because of the sometimes convoluted process involved in comparing different data types.
The fact that PHP sometimes internally converts two operands to a third, unrelated data type can be quite confusing. I hope, however, that the chart in this article will help you work out exactly what it’s doing.
Of course, as one of our lead developers is quick to point out, this whole discussion would be moot if everyone used = = =.