March 15, 2011

!isset, is_null, empty: The Good, the Bad and the Ugly

Hi everyone, you programmers trying to grasp the intricacies of PHP. As coders, our raw material is, and will be, information and knowledge. But lack of knowledge does not give us some information, too? Is of this absence that we will discuss today: How to know if we don't know something in PHP. And our main tools are isset, is_null and empty. Want to know more about them? Which one is The Good, The Bad and The Ugly?

The Question

So, our question is not only "what do we know" but, more specifically, "How do our tools behave against different values ​​of a variable?". Let's talk about our "falsy values" and then check them with our tools:

undefined
There's no var in scope with that name or it was removed with unset. The end. Finito. Capput. That's not a value itself, but it's a possible state of a variable.
null
Our beloved friend null is a valid value, but in this state the variable is considered to be not setted.
false
The falsy value par excelence. PHP has a native boolean type and it's a valid state.
zero
The integer value 0 is also a common false value for any programming language.
empty string
A zero-length string, like ''.
zero string
A string containing only the zero character, like this: '0'.
empty array
The simplest array of all, an array with no elements, like array().
empty object
An object with no members defined, like (object)array() or new StdClass.

The Code

In order to test how our tools behave with falsy values, I coded the following snippet:

function bool2str($bool)
{
    return $bool ? 'true' : 'false';
}

// Detects command line
define('IS_CLI', php_sapi_name() === 'cli');

// Define initial state
unset($undef);
$null    = null;
$false   = false;
$zero    = 0;
$empty   = '';
$zeroStr = '0';
$array   = array();
$object  = (object)$array;

// Show PHP Version
echo (IS_CLI ? '' : '<pre>'),
     'PHP Version: ', phpversion(), PHP_EOL;

// !isset() tests
echo '!isset($undef)   : ', bool2str(!isset($undef)),   PHP_EOL,
     '!isset($null)    : ', bool2str(!isset($null)),    PHP_EOL,
     '!isset($false)   : ', bool2str(!isset($false)),   PHP_EOL,
     '!isset($zero)    : ', bool2str(!isset($zero)),    PHP_EOL,
     '!isset($empty)   : ', bool2str(!isset($empty)),   PHP_EOL,
     '!isset($zeroStr) : ', bool2str(!isset($zeroStr)), PHP_EOL,
     '!isset($array)   : ', bool2str(!isset($array)),   PHP_EOL,
     '!isset($object)  : ', bool2str(!isset($object)),  PHP_EOL,
     PHP_EOL;

// is_null() tests
echo 'is_null($undef)   : ', bool2str(is_null($undef)),   PHP_EOL,
     'is_null($null)    : ', bool2str(is_null($null)),    PHP_EOL,
     'is_null($false)   : ', bool2str(is_null($false)),   PHP_EOL,
     'is_null($zero)    : ', bool2str(is_null($zero)),    PHP_EOL,
     'is_null($empty)   : ', bool2str(is_null($empty)),   PHP_EOL,
     'is_null($zeroStr) : ', bool2str(is_null($zeroStr)), PHP_EOL,
     'is_null($array)   : ', bool2str(is_null($array)),   PHP_EOL,
     'is_null($object)  : ', bool2str(is_null($object)),  PHP_EOL,
     PHP_EOL;

// empty() tests
echo 'empty($undef)   : ', bool2str(empty($undef)),   PHP_EOL,
     'empty($null)    : ', bool2str(empty($null)),    PHP_EOL,
     'empty($false)   : ', bool2str(empty($false)),   PHP_EOL,
     'empty($zero)    : ', bool2str(empty($zero)),    PHP_EOL,
     'empty($empty)   : ', bool2str(empty($empty)),   PHP_EOL,
     'empty($zeroStr) : ', bool2str(empty($zeroStr)), PHP_EOL,
     'empty($array)   : ', bool2str(empty($array)),   PHP_EOL,
     'empty($object)  : ', bool2str(empty($object)),  PHP_EOL,
     PHP_EOL;

echo (IS_CLI ? '' : '</pre>');

Note how I've used !isset in order to maintain coherence with is_null and empty returning false for setted values. Let's see the results!

The Good: !isset

!isset
stateresultnotes
undefined
null As expected
false
zero
empty string
zero string
empty array
empty object

Our best friend in the "what we don't know" problem is isset without doubt. Use it whenever you need to check for existence of a variable. Since it's a construction of the language, it doesn't choke on the non-existent variable. When the variable exists, and it's not null, will return true.

isset does not work on expressions, so isset(functionCall()) will always throw a parser error.

The Bad: is_null

is_null
stateresultnotes
undefined Throws a notice, since is_null is a function!
null
false
zero
empty string
zero string
empty array
empty object

is_null is great for doing one thing, and it should be clear reading it's name: checks if a variable value is null. But if the variable isn't set, there is no value to check for!

Use it wisely, but only when you know the variable exists.

The Ugly: empty

empty
stateresultnotes
undefined
null
false
zero This is somehow we can expect…
empty string
zero string Surprise! String-to-number attacks!
empty array
empty object On PHP < 5, returns true

empty is a language construct too smart for me. Like isset, it only works on variable names, so empty(0) does not work. It can detect undefined ones, but it also returns true for other values, even doing things like automatic conversion between types, and inconsistencies. Like the following code:

$double = 0.0;
echo bool2str(empty($double)); // Prints TRUE!!
$double = '0.0';
echo bool2str(empty($double)); // Prints false

Conclusion

isset is your best bet to check for variable existence. If completeness is a must, I use isset($variable) && !is_null($variable), but it's a bit tricky. I also try to avoid magic, so I only use empty when I truly need it's whole range of smart cumbersomeness.

Hope this helps someone not to make the mistakes I did in my past.

And always remember: When you have to shoot, shoot. Don't talk.