PHP operator== tests by Gynvael Coldwind (2012).
Generated with PHP version 5.3.6-13ubuntu3.9.
<?php
var_dump(array() == array()); // equal (obviously)
// Standard arrays.
var_dump(
array(NULL,true,2,3.0,"0.4e1","0x5")
==
array(0,1,2,3,4,5)
); // equal (since the normal == operator is used everywhere)
// Deeper.
var_dump(
array(NULL,true,array("a"=>"16"))
==
array(array(),array("x","y","z"),array("a"=>"0x10"))
); // equal (since the normal == operator is used everywhere)
// Associative.
var_dump(array("10"=>5) == array("0xa"=>5)); // not equal (key strings
// must be exact match)
var_dump(array("5"=>"10") == array("5"=>"0xa")); // equal
Results:bool(true) bool(true) bool(true) bool(false) bool(true)
<?php
// Array is never equal to an object, unless that object has a
// cast_object handler supporting to ARRAY casts (I couldn't find
// such a handler, even in SplFixedArray).
// Array vs same array as object?
echo "--- ARRAY vs anonymous object based on that ARRAY\n";
$arr = array("a" => "b");
$obj = (object)$arr;
var_dump($arr == $obj); // not equal
// Sadly (as of PHP 5.4.10) SplFixedArray has no cast_object defined
// for ARRAY, which it totally should have, since it even has a
// toArray() method.
echo "--- SplFixedArray (no to ARRAY cast_object)\n";
$splarr = new \SplFixedArray(2);
$splarr[0] = 2;
$splarr[1] = "foo";
var_dump($splarr);
var_dump($splarr == array(2, "foo")); // not equal ($splarr has no casts)
//var_dump((array)$splarr); // it can be cast
// var_dump((array)$splarr == array(2, "foo")); this would be equal
Results:--- ARRAY vs anonymous object based on that ARRAY bool(false) --- SplFixedArray (no to ARRAY cast_object) object(SplFixedArray)#2 (2) { [0]=> int(2) [1]=> string(3) "foo" } bool(false)
<?php
// Never equal.
$f = fopen("php://output", "w");
var_dump($f);
var_dump(array() == $f);
var_dump(array(1,2,3) == $f);
var_dump(array("a"=>"b") == $f);
echo "--- closing resource\n";
fclose($f);
var_dump($f);
var_dump(array() == $f);
var_dump(array(1,2,3) == $f);
var_dump(array("a"=>"b") == $f);
Results:resource(11) of type (stream) bool(false) bool(false) bool(false) --- closing resource resource(11) of type (Unknown) bool(false) bool(false) bool(false)
<?php
var_dump(false == array()); // equal (array empty is cast to false)
var_dump(true == array(1,2,3)); // equal (non-empty array is cast to true)
var_dump(true == array(1=>1,2=>2,3=>3)); // equal
Results:bool(true) bool(true) bool(true)
<?php
// Equals:
var_dump(true == true);
var_dump(false == false);
// Not equal:
var_dump(true == false);
Results:bool(true) bool(true) bool(false)
<?php
echo("--- equals\n");
var_dump(false == 0.0); // equal
var_dump(true == 1234.0); // equal
var_dump(true == -INF); // equal
echo("--- not equals\n");
var_dump(true == 0.0); // not equal
var_dump(false == 1234.0); // not equal
echo("--- NANs\n");
var_dump(true == NAN); // equal (non IEEE 754?)
var_dump(false == NAN); // not equal (I would expect this to be equal)
Results:--- equals bool(true) bool(true) bool(true) --- not equals bool(false) bool(false) --- NANs bool(true) bool(false)
<?php
var_dump(false == 0); // equal
var_dump(true == 1234); // equal
var_dump(true == -1234); // equal
var_dump(true == 0); // not equal
var_dump(false == 1234); // not equal
Results:bool(true) bool(true) bool(true) bool(false) bool(false)
<?php
// Standard PHP objects cast_object always returns bool(TRUE) when
// the object is cast to BOOL.
echo "--- standard PHP objects\n";
class Test {}; // A standard class.
$oTest = new Test;
$oStd = (object)array("a"=>"b");
var_dump(true == $oTest); // equal
var_dump(true == $oStd); // equal
// SimpleXML object's cast_object actually does does a smart BOOL
// conversion
echo "--- SimpleXMLElement (sxe) objects\n";
$sxe_empty = new \SimpleXMLElement("<a></a>");
$sxe_good = new \SimpleXMLElement("<a><b>xyz</b></a>");
var_dump(true == $sxe_empty); // not equal
var_dump(true == $sxe_good); // this should be equal, and honestly I have no
// idea why this is not equal, especially that
// (bool)$sxe_good is true
var_dump(false == $sxe_empty); // equal
var_dump(false == $sxe_good); // equal (?why? see above note)
// FilesystemIterator's cast_object returns FAILURE for BOOL conversion
echo "--- FilesystemIterator's FAILURE to cast objects\n";
$i = new \FilesystemIterator(dirname(__FILE__));
var_dump(true == $i); // not equal
var_dump(false == $i); // not equal (sic!)
// These tests do not cover objects with no cast_object handler. However
// in such case it would be:
// var_dump(true == $obj); // equal
// var_dump(false == $obj); // not equal
Results:--- standard PHP objects bool(true) bool(true) --- SimpleXMLElement (sxe) objects bool(false) bool(false) bool(true) bool(true) --- FilesystemIterator's FAILURE to cast objects bool(false) bool(false)
<?php
$f = fopen("php://output", "w");
var_dump($f);
var_dump(true == $f); // equal
var_dump(false == $f); // not equal
// Free up resource.
fclose($f);
var_dump($f);
var_dump(true == $f); // still equal
var_dump(false == $f); // still not equal
// false == $f is equal only for resource id == 0 (not sure how to get such)
Results:resource(25) of type (stream) bool(true) bool(false) resource(25) of type (Unknown) bool(true) bool(false)
<?php
var_dump(false == ""); // equal \ only these two exact strings are
var_dump(false == "0"); // equal / cast to false
var_dump(false == "00"); // not equal
var_dump(true == "asdf"); // equal
Results:bool(true) bool(true) bool(false) bool(true)
<?php
// Never equal.
var_dump(0.0 == array()); // Not equal even in case of empty array.
var_dump(1.0 == array());
var_dump(1.0 == array(1,2,3));
var_dump(3.0 == array(1,2,3));
var_dump(1.0 == array(1=>1,2=>2,3=>3));
var_dump(3.0 == array(1=>1,2=>2,3=>3));
Results:bool(false) bool(false) bool(false) bool(false) bool(false) bool(false)
<?php
var_dump(0.0 == 1.0); // not equal
echo "--- out of precission test\n";
var_dump(9223372036854775806.0 == 9223372036854775808.0); // equal (expected)
echo "--- NaN test\n";
var_dump(0.0 == NAN); // not equal
var_dump(NAN == NAN); // not equal (IEEE-754 compliant)
var_dump(INF == NAN); // not equal
Results:bool(false) --- out of precission test bool(true) --- NaN test bool(false) bool(false) bool(false)
<?php
// Identical to LONG vs OBJECT.
// Standard PHP objects cast_object always returns DOUBLE(1.0) and raises
// an E_NOTICE (I wonder why it doesn't just return FAILURE).
echo "--- standard PHP objects\n";
class Test {}; // A standard class.
$oTest = new Test;
$oStd = (object)array("a"=>"b");
var_dump(1.0 == $oTest); // equal
var_dump(1.0 == $oStd); // equal
// FilesystemIterator's cast_object returns FAILURE for DOUBLE conversion
echo "--- FilesystemIterator's FAILURE to cast objects\n";
$i = new \FilesystemIterator(dirname(__FILE__));
var_dump(1.0 == $i); // not equal
var_dump(0.0 == $i); // not equal
// These tests do not cover objects with no cast_object handler. However
// in such case it would be:
// var_dump(1.0 == $obj); // equal (and not equal for any other)
Results:--- standard PHP objects Notice: Object of class DOUBLE_vs_OBJECT\Test could not be converted to double in TESTS(27) : eval()'d code on line 11 bool(true) Notice: Object of class stdClass could not be converted to double in TESTS(27) : eval()'d code on line 12 bool(true) --- FilesystemIterator's FAILURE to cast objects bool(false) bool(false)
<?php
$f = fopen("php://output", "w");
// Other tests.
var_dump(0.0 == $f); // not equal
var_dump(1.0 == $f); // not equal
// Let's get a textual description of the resource, extract
// the resource id as long, and compare it.
// Note: Alternative way would be to do (int)$f.
echo "--- compare with exact (to integer part) resource id\n";
var_dump($f); // resource(5) of type (stream)
var_dump((string)$f); // "Resource id #5"
list($id) = sscanf((string)$f, "Resource id #%d");
$id = (double)$id;
var_dump($id);
var_dump($id == $f); // equal
var_dump(($id+0.1) == $f); // not equal since DOUBLE is cast to LONG
// before comparison
echo "--- close, compare again\n";
fclose($f);
var_dump($id == $f); // still equal
Results:bool(false) bool(false) --- compare with exact (to integer part) resource id resource(37) of type (stream) string(15) "Resource id #37" float(37) bool(true) bool(false) --- close, compare again bool(true)
<?php
// Some weird non numeric strings.
var_dump(0.0 == "hello there"); // equal
var_dump(0.0 == ""); // equal
// Numeric strings.
echo "--- numeric strings\n";
var_dump(5.0 == "5"); // equal
var_dump(5.0 == "0.05e2"); // equal (double supported)
var_dump(5.0 == " \t\n\r +5"); // equal (whitespace ignored)
var_dump(5.0 == "5and anything else"); // equal (additional chars ignored)
var_dump(5.0 == "0x5"); // equal (hex supported)
echo "--- out of precission test\n";
var_dump(9223372036854775806.0 == "9223372036854775808.0"); // equal (expected)
// Note: is there any case where STRING->DOUBLE cast would give
// different result than a STRING->LONG->DOUBLE cast? That's quite
// possible.
Results:bool(true) bool(true) --- numeric strings bool(true) bool(true) bool(true) bool(true) bool(true) --- out of precission test bool(true)
<?php
// Always not equal.
var_dump(0 == array()); // Not equal even in case of empty array.
var_dump(1 == array());
var_dump(1 == array(1,2,3));
var_dump(3 == array(1,2,3));
var_dump(1 == array(1=>1,2=>2,3=>3));
var_dump(3 == array(1=>1,2=>2,3=>3));
Results:bool(false) bool(false) bool(false) bool(false) bool(false) bool(false)
<?php
var_dump(1 == 1.0); // equal
var_dump(1 == 2.0); // not equal
var_dump(1 == INF); // not equal
var_dump(1 == NAN); // not equal
// 64-bit PHP only (this matters since the conversion boundary is
// in a different place; so on 32-bit PHP the first operand is
// double)
echo "--- long number vs double\n";
if(is_int(9223372036854775807)) {
// All are equal. This is over DOUBLE precission, so this is
// expected and correct behavior.
var_dump(9223372036854775807 == 9223372036854775806.0);
var_dump(9223372036854775807 == 9223372036854775807.0);
var_dump(9223372036854775807 == 9223372036854775808.0);
} else {
echo "info: skipping long number tests due to 32-bit PHP\n";
}
Results:bool(true) bool(false) bool(false) bool(false) --- long number vs double bool(true) bool(true) bool(true)
<?php
// No magic here.
var_dump(1 == 1); // equal
var_dump(1 == 2); // not equal
Results:bool(true) bool(false)
<?php
// Standard PHP objects cast_object always returns LONG(1) and raises
// an E_NOTICE (I wonder why it doesn't just return FAILURE).
echo "--- standard PHP objects\n";
class Test {}; // A standard class.
$oTest = new Test;
$oStd = (object)array("a"=>"b");
var_dump(1 == $oTest); // equal
var_dump(1 == $oStd); // equal
// FilesystemIterator's cast_object returns FAILURE for BOOL conversion
echo "--- FilesystemIterator's FAILURE to cast objects\n";
$i = new \FilesystemIterator(dirname(__FILE__));
var_dump(1 == $i); // not equal
var_dump(0 == $i); // not equal
// These tests do not cover objects with no cast_object handler. However
// in such case it would be:
// var_dump(1 == $obj); // equal
Results:--- standard PHP objects Notice: Object of class LONG_vs_OBJECT\Test could not be converted to int in TESTS(27) : eval()'d code on line 9 bool(true) Notice: Object of class stdClass could not be converted to int in TESTS(27) : eval()'d code on line 10 bool(true) --- FilesystemIterator's FAILURE to cast objects bool(false) bool(false)
<?php
$f = fopen("php://output", "w");
// Other tests.
var_dump(0 == $f); // not equal
var_dump(1 == $f); // not equal
// Let's get a textual description of the resource, extract
// the resource id as long, and compare it.
// Note: Alternative way would be to do (int)$f.
echo "--- compare with exact resource id\n";
var_dump($f); // resource(5) of type (stream)
var_dump((string)$f); // "Resource id #5"
list($id) = sscanf((string)$f, "Resource id #%d");
var_dump($id);
var_dump($id == $f); // equal
echo "--- close, compare again\n";
fclose($f);
var_dump($id == $f); // still equal
Results:bool(false) bool(false) --- compare with exact resource id resource(51) of type (stream) string(15) "Resource id #51" int(51) bool(true) --- close, compare again bool(true)
<?php
echo "--- long vs strange strings\n";
var_dump(0 == "any nonparsable string is equal to zero"); // equal
var_dump(5 == "5"); // equal
var_dump(5 == "0.05e2"); // equal (double supported)
var_dump(5 == " \t\n\r +5"); // equal (whitespace ignored)
var_dump(5 == "5and anything else"); // equal (additional chars ignored)
var_dump(5 == "0x5"); // equal (hex supported)
// 64-bit PHP only (this matters since the conversion boundary is
// in a different place; so on 32-bit PHP the first operand is
// double)
echo "--- long number vs long number in a string\n";
if(is_int(9223372036854775807)) {
var_dump(9223372036854775807 == "9223372036854775806"); // not eq (== LONG)
var_dump(9223372036854775807 == "9223372036854775807"); // eq (== LONG)
var_dump(9223372036854775807 == "9223372036854775808"); // eq (== DOUBLE)
} else {
echo "info: skipping long number tests due to 32-bit PHP\n";
}
Results:--- long vs strange strings bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) --- long number vs long number in a string bool(false) bool(true) bool(true)
<?php
var_dump(NULL == array()); // equal (only if array empty)
var_dump(NULL == array(1,2,3)); // not equal
var_dump(NULL == array(1=>1,2=>2,3=>3)); // not equal
Results:bool(true) bool(false) bool(false)
<?php
var_dump(NULL == true); // not equal
var_dump(NULL == false); // equal
Results:bool(false) bool(true)
<?php
var_dump(NULL == 0.0); // equal
var_dump(NULL == NAN); // not equal (hmm non IEEE754 NAN?)
var_dump(NULL == INF); // not equal
var_dump(NULL == -INF); // not equal
Results:bool(true) bool(false) bool(false) bool(false)
<?php
var_dump(NULL == -1); // not equal
var_dump(NULL == 0); // equal
var_dump(NULL == 1); // not equal
Results:bool(false) bool(true) bool(false)
<?php var_dump(NULL == NULL); // always equal
Results:bool(true)
<?php
date_default_timezone_set("Europe/Zurich");
class Test {}; // A standard class.
$oTest = new Test;
$oStdEmpty = (object)array();
$oStd = (object)array("a"=>"b");
$oDate = new \DateTime; // php_date builtin class.
var_dump(NULL == $oTest); // not equal
var_dump(NULL == $oStdEmpty); // not equal
var_dump(NULL == $oStd); // not equal
var_dump(NULL == $oDate); // not equal
// Should never be equal.
Results:bool(false) bool(false) bool(false) bool(false)
<?php
$f = fopen("php://output", "w");
var_dump($f);
var_dump(NULL == $f); // not equal
// Free up resource.
fclose($f);
var_dump($f);
var_dump(NULL == $f); // not equal
// Equal only for resource id == 0 (not sure how to get such)
Results:resource(68) of type (stream) bool(false) resource(68) of type (Unknown) bool(false)
<?php
var_dump(NULL == ""); // equal (only in this case)
var_dump(NULL == "0"); // not equal
var_dump(NULL == "00"); // not equal
var_dump(NULL == "anything"); // not equal
Results:bool(true) bool(false) bool(false) bool(false)
<?php
echo "--- standard object comparator (zend_std_compare_objects)\n";
class Test {
public $x = 5;
}
$anonobj1 = (object)array("x" => 5);
$anonobj2 = (object)array("x" => " 5 <-- five");
$testobj1 = new Test;
$testobj2 = new Test;
var_dump($testobj1 == $anonobj1); // not equal (different class)
var_dump($anonobj1 == $anonobj2); // equal, all anonymous objects are of the same class
echo "--- deep recursion comparison\n";
echo "skipping, since it would ".
"<a href=\"https://bugs.php.net/bug.php?id=63882\">crash the PHP ".
"interpreter</a>\n";
// $testobj1->x = $testobj2;
// $testobj2->x = $testobj1;
// var_dump($testobj1 == $testobj2);
// Skipping, this would crash the interpreter (see bug #63882)
// Zend closures are object and they have a compare handler that says they
// are equal only if it's actually the same object.
echo "--- closure comparison\n";
$closure1 = function(){};
$closure2 = function(){};
var_dump($closure1 == $closure2); // not equal, different objects
$ptr = &$closure1;
var_dump($ptr == $closure1); // equal, same object
// Two objects of different class
echo "--- different object comparison\n";
date_default_timezone_set("Europe/Zurich");
class A {}
$a = new A;
$b = new \DateTime;
var_dump($a == $b); // equal in PHP 5.4.6 + raises 2x E_NOTICE
// not equal in PHP 5.4.10
// Note: I totally ignored proxy objects (since I don't undersand them at
// the moment of creating these tests; not sure where are they used etc;
// actually the code for them exists, but hard to find a reference; TODO)
Results:--- standard object comparator (zend_std_compare_objects) bool(false) bool(true) --- deep recursion comparison skipping, since it would crash the PHP interpreter --- closure comparison bool(false) bool(true) --- different object comparison Notice: Object of class OBJECT_vs_OBJECT\A could not be converted to int in TESTS(27) : eval()'d code on line 43 Notice: Object of class DateTime could not be converted to int in TESTS(27) : eval()'d code on line 43 bool(true)
<?php
$f = fopen('php://output', 'w');
var_dump($f);
// Standard PHP objects cast_object will always fail (so it's always not equal).
$obj = (object)array("a"=>"b");
var_dump($obj == $f); // not equal
fclose($f); // Changes nothing.
var_dump($obj == $f); // not equal
// Note: I don't know of any built-in / standard extension class that has the
// cast_object handler supporting to RESOURCE conversion.
// PDORow using sqlite (has no cast_object, so it will be cast to LONG(1),
// and will be compared with the resource cast to LONG).
echo "--- PDORow object (has no cast_object handler)\n";
try {
// Create a PDORow object using sqlite.
$r = new \PDO("sqlite::memory:");
$r = $r->query("SELECT sqlite_version()");
$r = $r->fetch(\PDO::FETCH_LAZY);
var_dump($r == $f); // This would be equal if this would be resource #1.
// Note: this will raise an E_NOTICE.
} catch (Exception $e) {
echo "skipping PDORow test ({$e->message})\n";
}
Results:resource(75) of type (stream) bool(false) bool(false) --- PDORow object (has no cast_object handler) Notice: Object of class PDORow could not be converted to int in TESTS(27) : eval()'d code on line 25 bool(false)
<?php
$f = fopen("php://output", "w");
$g = fopen("php://input", "r");
var_dump($f == $f); // equal (obviously)
var_dump($f == $g); // not equal
Results:bool(true) bool(false)
<?php
// Never equal.
var_dump("" == array());
var_dump("" == array(1,2,3));
var_dump("1" == array(1,2,3));
var_dump("1,2,3" == array(1,2,3));
Results:bool(false) bool(false) bool(false) bool(false)
<?php
// Anonymous object with no __tostring method (not equal to anything).
echo "--- anonymous object without __tostring\n";
$obj = (object)array("a"=>"b");
var_dump($obj == ""); // not equal
var_dump($obj == "0"); // not equal
var_dump($obj == "1"); // not equal
var_dump($obj == "asdf"); // not equal
// Custom objects with __tostring method.
echo "--- custom objects with __tostring method\n";
class Test {
function __tostring() {
return "5";
}
}
$test = new Test;
var_dump($test == "5"); // equal
var_dump($test == "+0.5e1"); // equal
echo "--- custom objects with __tostring method returning non-string\n";
set_error_handler(function($errno,$errstr){return true;});
class TestNonString {
function __tostring() {
return $this; // in case this returns non-string, it will
// throw an error, but will return an
// empty string ("")
}
}
var_dump(new TestNonString == ""); // bool(equal)
// This code would crash the PHP interpreter (stack exhaustion):
echo "--- recursive == operator in __tostring\n";
echo "skipping since it would ".
"<a href=\"https://bugs.php.net/bug.php?id=46754\">crash the interpreter</a>\n";
//class Xyz {
// function __tostring() {
// //return $this->__tostring(); // -- this would not crash
// return $this == "crash me"; // -- stack overflow crash
// }
// }
//
// new Xyx == "plz die";
//
// known: https://bugs.php.net/bug.php?id=46754
// PDORow using sqlite (has no cast_object, so it will be cast to LONG(1),
// so will be the string).
echo "--- PDORow object (has no cast_object handler)\n";
try {
// Create a PDORow object using sqlite.
$r = new \PDO("sqlite::memory:");
$r = $r->query("SELECT sqlite_version()");
$r = $r->fetch(\PDO::FETCH_LAZY);
var_dump($r == "0"); // not equal
var_dump($r == ""); // not equal
var_dump($r == "anything"); // not equal
var_dump($r == "1"); // equal
var_dump($r == " \t\r\n+0.01e2"); // equal
} catch (Exception $e) {
echo "skipping PDORow test ({$e->message})\n";
}
// Note: This test does not cover a custom built-in cast_object supporting
// to STRING coversion. Some classess like Tidy do support it, but in the end
// the behavior is identical to that of the stdObjects (i.e. it returns a string
// if it's able to).
Results:--- anonymous object without __tostring bool(false) bool(false) bool(false) bool(false) --- custom objects with __tostring method bool(true) bool(true) --- custom objects with __tostring method returning non-string bool(true) --- recursive == operator in __tostring skipping since it would crash the interpreter --- PDORow object (has no cast_object handler) bool(false) bool(false) bool(false) bool(true) bool(true)
<?php
$f = fopen("php://output", "w");
// Other tests.
var_dump("" == $f); // not equal
var_dump("asdf" == $f); // not equal
// Let's get a textual description of the resource, extract
// the resource id as long, and compare it.
// Note: Alternative way would be to do (int)$f.
echo "--- compare with exact resource id\n";
var_dump($f); // resource(5) of type (stream)
var_dump((string)$f); // "Resource id #5"
list($id) = sscanf((string)$f, "Resource id #%s");
var_dump($id);
var_dump($id == $f); // equal
var_dump(" +{$id}e0" == $f); // equal
echo "--- close, compare again\n";
fclose($f);
var_dump($id == $f); // equal
var_dump(" +{$id}e0" == $f); // equal
Results:bool(false) bool(false) --- compare with exact resource id resource(86) of type (stream) string(15) "Resource id #86" string(2) "86" bool(true) bool(true) --- close, compare again bool(true) bool(true)
<?php
// STRING vs STRING is one strange thing. Don't use it unless you know
// EXACTLY what you're doing.
echo "-- equals\n";
var_dump("asdf" == "asdf"); // obviously equal
var_dump("1.00000000000000001" == "0.1e1"); // equal
var_dump("+1" == "0.1e1"); // equal
var_dump("1e0" == "0.1e1"); // equal
var_dump("-0e10" == "0"); // equal
var_dump("1000" == "0x3e8"); // equal
var_dump("1234" == " 1234"); // equal
echo "-- non equals\n";
var_dump("1xyz" == "0.1e1"); // not equal
var_dump("1xyz" == "0.1e1xyz"); // not equal
var_dump("asdf" == "ASDF"); // not equal
var_dump("asdf" == " asdf"); // not equal
var_dump("0" == ""); // not equal
// Out of double precision comparison:
echo "-- double out of precission\n";
var_dump("1234512345123451234512345" == "1234512345123451234512346");
// equal for 32-bit PHP
// not equal for 64-bit PHP
Results:-- equals bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) -- non equals bool(false) bool(false) bool(false) bool(false) bool(false) -- double out of precission bool(true)