Underlying philosophy behind php type comparisons
Asked Answered
G

7

5

So there's this page on the php site which shows the result of comparing different values:

http://php.net/manual/en/types.comparisons.php

This is a helpful reference, but I would rather not have to visit this page every time I want to make sure that I'm doing type comparison right. So my question is

Is there some kind of underlying philosophy/reasoning behind the logic of type comparisons on PHP?

For example, I can see that for loose comparisons:

  • 1, -1, "1" and "-1" can be treated as TRUE and 0 and "0" can be treated as FALSE;
  • Comparing the string value of a number against the number itself with yield TRUE;

but it becomes a bit hairy from then on trying to establish a pattern.

Gainful answered 16/4, 2013 at 22:28 Comment(2)
This might be interesting.Deville
The question is related to mine, but it's a superset of what I'm asking. I'm interested specifically in the equality comparison, and having a fixed set of rules to guide me in determining whether two values are loosely or strictly equal.Gainful
C
6

For casting directly to a boolean this is how it works.

  • All string with a length > 0 are true
  • All non 0 numbers are true
  • All non-empty arrays are true
  • All objects are true

Then these rules for comparing variables of the same type:

  1. Objects are equivalent if their properties are equal
  2. Arrays are equivalent if their keys and elements are equal
  3. Strings are equivalent if they would produce the same output
  4. Numbers are equivalent if they are mathematically equivalent
  5. Booleans are equivalent if they have the same value.

For variable of different types the type that is higher on the above list is cast to the one that is lower then the comparison is made.

=== and !== operators don't cast prior to comparing but you should note objects are only === if they are the same instance.

The really odd one is arrays, they are === if they have the same keys and values defined in the same order.

$a = array("a"=>1, "b"=>2);
$b = array("b"=>2, "a"=>1);

$a == $b; // true
$a === $b; // false

and empty() is equivalent to !(bool)$var

EXCEPTIONS

  • Casting an array to a string will trigger a notice and unhelpfully cast as the text Array
  • Casting an object without a __toString method to a string will get you a fatal error.
  • Objects will not implicitly cast to an array, so any time you compare an object to an array it will yield a false (UPDATE confirmed that this is true even if object implemtents the ArrayAccess interface)
Crim answered 14/5, 2013 at 22:0 Comment(10)
So if you have to compare an empty array to "0" (a string), how do you cast the array to the string for comparison?Gainful
Okay exception 1. Arrays and objects without a __toString method can't be cast to a string (php issues a warning in these cases)Crim
@TolaOdejayi Updated the post with exception cases... I am going to run a test because I believe that an object that implements the ArrayAccess interface may be directly comparable to an array.Crim
Let it be that there are two different objects of the same class with the same values. When you compare them strict (===) will this comparison return false. If you compare them loose (==) then will this comparison return true. Hence equivalent objects are not strictly equal.Foundry
True === for objects basically means "is the same instance".Crim
@loekbergman updated post regarding === and how it works with objects and arrays at your suggestion.Crim
Let's use your rules to compare "0" to TRUE. The "0" is a non-empty string, and should (according to your proposal) be TRUE, which means that comparing it to TRUE should yield TRUE. Yet the table in my link says that "0" == TRUE is false. Am I missing something?Gainful
There is a type difference between the boolean true and the string 0. The string 0 gets cast down to the integer 0 which is falsey.Crim
So to be clear, when a string that can be cast to an integer is loosely compared to anything, it is first automatically cast to an integer before comparison - is this right? Or is this only for comparisons with strings, integers and booleans?Gainful
Booleans are basically a sub type of integer. I will update the answer.Crim
B
4

For strict === comparision, the logic is easy: each value entity is equal only to itself, so TRUE === TRUE, "1" === "1", but "1" !== 1 etc.

When it comes to == comparision, unfortunately there is no rule of thumb nor a clear logic. This is probably because the various forms of the operator were implemented by different programmers, without a central design decision. The best I can do is providing you with this graph to print and stick over the monitor:

PHP equality graph

The key of the grap is: A == B will be TRUE if and only if A and B are of two types directly connected by a line in the graph above. For instance, array() == NULL is TRUE because array() and NULL are directly connected, while array() == 0 is FALSE because there is no line connecting the two.

Lines marked in red are the tricky (non obvious) equalities.

I've omitted that each entity will be equal to itself (e.g. "1" == "1" etc.) but that should be easy to remember.

As a final note, I'd like to explain why "php" == 0 is TRUE (non empty, non number string is equal to 0): because PHP casts "php" to number before comparision and, since it's not a number, it defaults to 0 and makes the test TRUE.

Fun fact: there is no partition in this relation! If ever a transitive closure was allowed, you could easily say that True is False and False is True, destroying millennia of philosphy in four easy PHP statements :D

Bohaty answered 18/5, 2013 at 11:40 Comment(2)
Very helpful pictorial representation of the link I posted in my original post. Unfortunately, even though it expresses the what, it doesn't really address the why.Gainful
As I said, there is no why. Features evolved without a central design direction and this is the (sad) result.Bohaty
E
2

If the value contains something then it can be said to be true. For example, 1, 1.123, array("value"), etc. are all treated as true.

If the value can be said to be empty or void (i.e. lacking something) then it is seen as false. For example, 0, 0.0, array(), and so on.

This way of thinking about variables is not special to PHP. Many other languages do it in the same or similar way. E.g. Perl, C and Javascript, just to name a few.

Exculpate answered 16/4, 2013 at 22:35 Comment(3)
This is a helpful answer, as it largely explains a lot of the comparison results, but looking at the table on the php site that I referred to in my original question, it says that when you loosely compare "0" (having nothing) with array() (also having nothing), the answer is FALSE. Shouldn't that be TRUE instead?Gainful
@TolaOdejayi I think in this case array() casts to string ('Array') and PHP compares string '0' with string 'Array'.Kranz
Well, according to the original poster, when evaluating expressions for loose comparisons, an empty array is FALSE. Why should it be case to a string?Gainful
F
2

There is imo a very straightforward guideline and a bug in the specification, which might be confusing.

Strict comparison checks equality in datatype and value. Loose comparison checks equality in value only.

For an object (not part of the comparison table) is php quite straightforward: if the object is the same instance as the other one, then is it strictly equal, otherwise might it be loosely equal.

Therefor is a 0 and a "0" loosely equal to each other and to false (and to any string). The latter can be understood as all strings are not numeric, hence false and the number that is equal to false is 0, hence all strings are equal to 0.

The comparison between null and array() is more complicated. If you check an array created with array() and compare that loosely and strictly, then will it return true. If you however check it with is_null, then will it return false. I think the latter is more logical, because an array() created with array() is not equal to '', where null is. I would think that this functional inconsistency between the function is_null() and the checks == null or === null a bug, because it should not happen that using two different valid methods to check for a value return different results. Null is also not an array according to the function is_array(), which is true. An empty array is an array according to the function is_array(), which should be true too. Hence should it never be true that null is equal to array().

Foundry answered 16/5, 2013 at 14:37 Comment(0)
K
1

There is no particular logic, but you can figure out some patterns.

  • "empty" values (null, false, 0, empty string and string '0') evaluate to false
  • comparison of numeric values is done implicitly converting them to integers until some version (there was a bug when two actually different long numeric strings counted as equal, now it's fixed)
  • when working with arrays, there is no difference between integer and numeric indexes, except when you call array_key_exists with explicit strict parameter
  • comparing number with string implicitly converts right argument to the type of the left one
  • return ($something); implicitly converts $something to string if it is not scalar
Kranz answered 15/5, 2013 at 20:31 Comment(1)
That's why so many people loathe it and laugh at it. There are also sites like phpsadness and some others where PHP is compared to other languages.Kranz
P
1

The base pattern is the same to the one used in C: anything non-zero is true for the sake of boolean comparisons.

In this sense, an empty string or array is also false.

The hairy scalar to look out for is '0', which is (very inconveniently) treated as empty too because it gets converted to an integer. array(0) is just as thorny on the array front.

When using strict comparisons (=== and !==), things are a lot more sane. In practice, it's often a good idea to cast input coming from superglobals and the database as appropriate, and to use these operators from that point forward.

Prorate answered 16/5, 2013 at 21:13 Comment(0)
C
0

I look at it the following way:

  1. PHP is designed as a web programming language and all the input of the pages is based on strings [human-like perception] [This is by the way is also true for JavaScript]
  2. Hence, all the strings which look like numbers (is_numeric() function), preliminary behave like numbers [comparison, casting].
  3. That explains why extreme cases, like "0" are first implicitly thought to be cast to (int)0 and only then to false.
Cairo answered 17/5, 2013 at 14:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.