It's interesting to note that 'empty()' and 'boolean : if($x)'
are paired as logical opposites, as are 'is_null()' and 'isset()'.
The following tables demonstrate behaviors of PHP types and comparison operators, for both loose and strict comparisons. This supplemental is also related to the manual section on type juggling. Inspiration was provided by various user comments and by the work over at » BlueShoes.
Before utilizing these tables, it's important to understand types and their
meanings. For example, "42"
is a string
while 42
is an int. false
is a
bool while "false"
is a
string.
Note:
HTML Forms do not pass integers, floats, or booleans; they pass strings. To find out if a string is numeric, you may use is_numeric().
Note:
Simply doing
if ($x)
while $x is undefined will generate an error of levelE_NOTICE
. Instead, consider using empty() or isset() and/or initialize your variables.
Note:
Some numeric operations can result in a value represented by the constant
NAN
. Any loose or strict comparisons of this value against any other value, including itself, but excepttrue
, will have a result offalse
. (i.e.NAN != NAN
andNAN !== NAN
) Examples of operations that produceNAN
includesqrt(-1)
,asin(2)
, andacosh(0)
.
Expression | gettype() | empty() | is_null() | isset() | bool : if($x) |
---|---|---|---|---|---|
$x = ""; |
string | true |
false |
true |
false |
$x = null; |
NULL | true |
true |
false |
false |
var $x; |
NULL | true |
true |
false |
false |
$x is undefined | NULL | true |
true |
false |
false |
$x = []; |
array | true |
false |
true |
false |
$x = ['a', 'b']; |
array | false |
false |
true |
true |
$x = false; |
bool | true |
false |
true |
false |
$x = true; |
bool | false |
false |
true |
true |
$x = 1; |
int | false |
false |
true |
true |
$x = 42; |
int | false |
false |
true |
true |
$x = 0; |
int | true |
false |
true |
false |
$x = -1; |
int | false |
false |
true |
true |
$x = "1"; |
string | false |
false |
true |
true |
$x = "0"; |
string | true |
false |
true |
false |
$x = "-1"; |
string | false |
false |
true |
true |
$x = "php"; |
string | false |
false |
true |
true |
$x = "true"; |
string | false |
false |
true |
true |
$x = "false"; |
string | false |
false |
true |
true |
true |
false |
1 |
0 |
-1 |
"1" |
"0" |
"-1" |
null |
[] |
"php" |
"" |
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
true |
true |
false |
true |
false |
true |
true |
false |
true |
false |
false |
true |
false |
false |
false |
true |
false |
true |
false |
false |
true |
false |
true |
true |
false |
true |
1 |
true |
false |
true |
false |
false |
true |
false |
false |
false |
false |
false |
false |
0 |
false |
true |
false |
true |
false |
false |
true |
false |
true |
false |
false * |
false * |
-1 |
true |
false |
false |
false |
true |
false |
false |
true |
false |
false |
false |
false |
"1" |
true |
false |
true |
false |
false |
true |
false |
false |
false |
false |
false |
false |
"0" |
false |
true |
false |
true |
false |
false |
true |
false |
false |
false |
false |
false |
"-1" |
true |
false |
false |
false |
true |
false |
false |
true |
false |
false |
false |
false |
null |
false |
true |
false |
true |
false |
false |
false |
false |
true |
true |
false |
true |
[] |
false |
true |
false |
false |
false |
false |
false |
false |
true |
true |
false |
false |
"php" |
true |
false |
false |
false * |
false |
false |
false |
false |
false |
false |
true |
false |
"" |
false |
true |
false |
false * |
false |
false |
false |
false |
true |
false |
false |
true |
true
prior to PHP 8.0.0.
true |
false |
1 |
0 |
-1 |
"1" |
"0" |
"-1" |
null |
[] |
"php" |
"" |
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
true |
true |
false |
false |
false |
false |
false |
false |
false |
false |
false |
false |
false |
false |
false |
true |
false |
false |
false |
false |
false |
false |
false |
false |
false |
false |
1 |
false |
false |
true |
false |
false |
false |
false |
false |
false |
false |
false |
false |
0 |
false |
false |
false |
true |
false |
false |
false |
false |
false |
false |
false |
false |
-1 |
false |
false |
false |
false |
true |
false |
false |
false |
false |
false |
false |
false |
"1" |
false |
false |
false |
false |
false |
true |
false |
false |
false |
false |
false |
false |
"0" |
false |
false |
false |
false |
false |
false |
true |
false |
false |
false |
false |
false |
"-1" |
false |
false |
false |
false |
false |
false |
false |
true |
false |
false |
false |
false |
null |
false |
false |
false |
false |
false |
false |
false |
false |
true |
false |
false |
false |
[] |
false |
false |
false |
false |
false |
false |
false |
false |
false |
true |
false |
false |
"php" |
false |
false |
false |
false |
false |
false |
false |
false |
false |
false |
true |
false |
"" |
false |
false |
false |
false |
false |
false |
false |
false |
false |
false |
false |
true |
It's interesting to note that 'empty()' and 'boolean : if($x)'
are paired as logical opposites, as are 'is_null()' and 'isset()'.
Note that php comparison is not transitive:
"php" == 0 => true
0 == null => true
null == "php" => false
A comparison table for <=,<,=>,> would be nice...
Following are TRUE (tested PHP4&5):
NULL <= -1
NULL <= 0
NULL <= 1
!(NULL >= -1)
NULL >= 0
!(NULL >= 1)
That was a surprise for me (and it is not like SQL, I would like to have the option to have SQL semantics with NULL...).
The truth tables really ought to be colorized; they're very hard to read as they are right now (just big arrays of TRUE and FALSE).
Also, something to consider: clustering the values which compare similarly (like is done on qntm.org/equality) would make the table easier to read as well. (This can be done simply by hand by rearranging the order of headings to bring related values closer together).
Some function to write out your own comparisson table in tsv format. Can be easily modified to add more testcases and/or binary functions. It will test all comparables against each other with all functions.
<?php
$funcs = array(
/* Testing equality */
'eq' => '==',
'ne' => '!=',
'gt' => '>',
'lt' => '<',
'ne2' => '<>',
'lte' => '<=',
'gte' => '>=',
/* Testing identity */
'id' => '===',
'nid' => '!=='
);
class Test {
protected $a;
public $b;
public function __construct($a,$b){
$this->a = $a;
$this->b = $b;
}
public function getab(){
return $this->a.",". $this->b;
}
}
$tst1 = new Test(1,2);
$tst2 = new Test(1,2);
$tst3 = new Test(2,2);
$tst4 = new Test(1,1);
$arr1 = array(1,2,3);
$arr2 = array(2,3,4);
$arr3 = array('a','b','c','d');
$arr4 = array('a','b','c');
$arr5 = array();
$comp1 = array(
'ints' => array(-1,0,1,2),
'floats' => array(-1.1,0.0,1.1,2.0),
'string' => array('str', 'str1', '', '1'),
'bools' => array(true, false),
'null' => array(null),
'objects' => array($tst1,$tst2,$tst3,$tst4),
'arrays' => array($arr1, $arr2, $arr3, $arr4, $arr5)
);
$fbody = array();
foreach($funcs as $name => $op){
$fbody[$name] = create_function('$a,$b', 'return $a ' . $op . ' $b;');
}
$table = array(array('function', 'comp1', 'comp2', 'f comp1 comp2', 'type'));
/* Do comparisons */
$comp2 = array();
foreach($comp1 as $type => $val){
$comp2[$type] = $val;
}
foreach($comp1 as $key1 => $val1){
foreach($comp2 as $key2 => $val2){
addTableEntry($key1, $key2, $val1, $val2);
}
}
$out = '';
foreach($table as $row){
$out .= sprintf("%-20s\t%-20s\t%-20s\t%-20s\t%-20s\n", $row[0], $row[1], $row[2], $row[3], $row[4]);
}
print $out;
exit;
function addTableEntry($n1, $n2, $comp1, $comp2){
global $table, $fbody;
foreach($fbody as $fname => $func){
foreach($comp1 as $val1){
foreach($comp2 as $val2){
$val = $func($val1,$val2);
$table[] = array($fname, gettype($val1) . ' => ' . sprintval($val1), gettype($val2) .' => ' . sprintval($val2), gettype($val) . ' => ' . sprintval($val), gettype($val1) . "-" . gettype($val2) . '-' . $fname);
}
}
}
}
function sprintval($val){
if(is_object($val)){
return 'object-' . $val->getab();
}
if(is_array($val)){
return implode(',', $val);
}
if(is_bool($val)){
if($val){
return 'true';
}
return 'false';
}
return strval($val);
}
?>
Be aware of the difference between checking the *value* of an array item, and checking the *existence* of an array item:
<?php
$arr = [
'x' => 0,
'y' => null,
];
isset($arr['x']); // true, same as isset(0)
isset($arr['y']); // false, same as isset(null)
array_key_exists('y', $arr); // true, though the value is null
array_key_exists('z', $arr); // false
There is also 0.0 which is not identical to 0.
$x = 0.0;
gettype($x); // double
empty($x); // true
is_null($x); //false
isset($x); // true
is_numeric($x); // true
$x ? true : false; // false
$x == 0; // true
$x == "0"; // true
$x == "0.0"; // true
$x == false; // true
$x == null; // true
$x === 0; // false
$x === false; // false
$x === null; // false
$x === "0"; // false
$x === "0.0"; // false
In some languages, a boolean is promoted to an integer (with a value of 1 or -1, typically) if used in an expression with an integer. I found that PHP has it both ways:
If you add a boolean with a value of true to an integer with a value of 3, the result will be 4 (because the boolean is cast as an integer).
On the other hand, if you test a boolean with a value of true for equality with an integer with a value of three, the result will be true (because the integer is cast as a boolean).
Surprisingly, at first glance, if you use either < or > as the comparison operator the result is always false (again, because the integer as cast as a boolean, and true is neither greater nor less than true).