Wednesday, August 10, 2011

php isset, is_null and ===null

This is post is simply for fun.

what is the difference between PHP's isset and is_null? Let's see what PHP manual states:

isset: Determine if a variable is set and is not NULL.
is_null: Finds whether the given variable is NULL.

It seems they work for exactly same purpose simply in an opposite way:
$variable = null;
isset($variable) !== is_null($variable)
or
!(isset($variable)) === is_null($variable)

Is that simple? Not really. One difference between isset and is_null is: as its name suggests, when using isset to check an undefined variable, PHP won't raise a NOTICE. But if we use is_null to check an undefined variable, a NOTICE will be raised.

//a NOTICE will be given by PHP
is_null($undefinedVar);

//no NOTICE
isset($undefinedVar);

In PHP manual, there is a note for isset:
Note: Because this (isset) is a language construct and not a function, it cannot be called using variable functions

This tells us isset is a language construct like echo, for, foreach. It is not a function. So here is one difference

//this works
$func = 'is_null';
$func($variable);

//this doesn't work
$func = 'isset';
$func($variable);

Also, is_null is a function, so it can take a function return value as its argument, while isset cannot do that.

//this works
is_null(getVariable());

//this doesn't work
isset(getVariable());

Since isset is language construct while is_null is a function, you can guess there is performance difference between them. Using language construct is more efficient than calling a function. To check the difference, I will use VLD to expose the opcode(about VLD, check this http://hengrui-li.blogspot.com/2011/07/review-php-opcode-with-vld.html)

test1.php
<?php
$name = null;
isset($name);
?>

php -dvld.active=1 test1.php

line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   2     0  >   EXT_STMT                                                 
         1      ASSIGN                                                   !0, null
   3     2      EXT_STMT                                                
         3      ZEND_ISSET_ISEMPTY_VAR                        5  ~1      !0
         4      FREE                                                     ~1
   4     5    > RETURN                                                   1

As we can see, PHP parse isset as one opcode operation: ZEND_ISSET_ISEMPTY_VAR.

test2.php
<?php
$name = null;
is_null($name);
?>

php -dvld.active=1 test2.php

line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   2     0  >   EXT_STMT                                                
         1      ASSIGN                                                   !0, null
   3     2      EXT_STMT                                                 
         3      EXT_FCALL_BEGIN                                         
         4      SEND_VAR                                                 !0
         5      DO_FCALL                                      1          'is_null'
         6      EXT_FCALL_END                                           
   4     7    > RETURN                                                   1

To call a is_null function, PHP does these opcode operations: EXT_FCALL_BEGIN, SEND_VAR, DO_FCALL, EXT_FCALL_END

The result is quite obvious now.  isset is more efficient. But sometimes, checking is null simply makes more sense than checking isset, especially when we are checking a function return value:

$name = $user->getName();
isset($name);
is_null($name);

For this example, we can still use isset($user) to check if $user is null or not, but, by the meaning of the name 'isset', here $user seems being set obviously. What we really want to check is if $user is null or not. At this situation(when we simply want to check if a variable is null, not worrying about if it is set), using $name === null is better than using is_null($name)

$name === null returns exactly same result as is_null($name), however, $name === null is more efficient. It is almost as efficient as isset. Let's check the opcode:

test3.php

<?php
$name = null;
$name === null;
?>

php -dvld.active=1 test3.php

line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   2     0  >   EXT_STMT                                                
         1      ASSIGN                                                   !0, null
   3     2      EXT_STMT                                                
         3      IS_IDENTICAL                                     ~1      !0, null
         4      FREE                                                     ~1
   4     5    > RETURN                                                   1

From micro performance optimization perspective: isset better than ===null better than is_null. 

I think the correct usage of them is: if we want to check if a variable is "set"(or existing), use isset; if we want to check if an existing variable's value is null, use === null.

The performance difference among them belongs to micro-optimization, so i have to run 5 million times of comparison to see the difference:

My testing code is quite simple:

<?php
$counter = 5000000;
$name    = 'henry';
$start   = microtime(true);
for($i=0; $i<$counter; $i++) {
    isset($name);
}

$end   = microtime(true);
echo $end - $start, "\n";
?>

I simply replace isset(name) with is_null($name) and $name === null and run the script respectively. And the result is:

isset:     1.62s
===null: 1.80s
is_null:   8.47s

It is a little out of my expect that is_null could be that slower, anyway, just a simple test for fun.

No comments: