Php
Best way to test for a variables existence in PHP isset is clearly broken
Navigating the nuances of variable existence in PHP can often feel like a minefield, especially when you encounter unexpected behavior from what seems like a straightforward function. Many developers quickly conclude that isset() is clearly broken, particularly when trying to discern the true state of a variable. This widespread perception stems from a misunderstanding of how PHP handles different variable states—from being completely undefined to holding null, an empty string, or even the boolean false. Understanding the best way to test for a variable’s existence in PHP goes far beyond a single function; it requires a deep dive into PHP’s type system and the specific use cases for various validation tools. This article will demystify these concepts, offering robust alternatives and best practices to ensure your code is both reliable and predictable.
Unpacking the isset() “Problem”: A Deeper Look
The common frustration with isset() often arises because it doesn’t always behave as intuitively as developers might expect for all scenarios. Its primary purpose is to check if a variable has been declared and is not null. This means if a variable is explicitly set to null, isset() will return false. Similarly, if a variable simply hasn’t been defined, it also returns false. This behavior is by design, making it excellent for checking if a form field was submitted or if an array key exists and holds a non-null value.
However, where the confusion often lies is when developers try to use isset() to check for “emptiness” or “truthiness.” For instance, isset($var) will return true if $var is an empty string (""), the integer 0, or the boolean false. This is precisely why many feel isset() is “broken” when their goal is to validate that a variable contains meaningful, non-empty data. It’s crucial to remember that isset() is a specific tool for a specific job: determining if a variable exists and isn’t null, not for comprehensive content validation. According to the official PHP documentation for isset(), it “determines if a variable is declared and is different than null.”
Consider a scenario where you’re processing user input from a form. If a user submits an empty text field, $_POST['username'] will be an empty string (""), not null. In this case, isset($_POST['username']) would return true, even though the field is effectively empty from a user experience perspective. This highlights why relying solely on isset() for all validation tasks can lead to unexpected program flow and potential bugs in your application logic. Understanding these PHP variable states is the first step toward choosing the correct validation method.
Beyond isset(): Embracing empty() for Content Validation
When your objective is to determine if a variable contains any meaningful data—that is, it’s not null, an empty string, 0, false, or an empty array—the empty() language construct is often the best way to test for a variable’s existence in PHP, especially regarding its content. Unlike isset(), empty() evaluates a variable for its “emptiness” or “falsy” nature. It returns true if the variable does not exist, or if its value is false, null, 0 (as an integer or string), an empty string (""), an empty array ([]), or an empty SimpleXML object.
For validating user input, ensuring form fields aren’t blank, or checking if an array has elements, empty() is frequently the more appropriate choice. If you want to check if a variable holds any value that isn’t considered “empty” in a broad sense, using !empty($variable) is a highly readable and efficient method. This approach simplifies checks for common scenarios where a value of 0 or an empty string should be treated as non-existent or invalid input. For example, if you’re expecting a user to enter a quantity, and 0 is not a valid entry, !empty($quantity) would correctly flag 0 as empty.
The Robust Approach: Combining Checks and Strict Type Juggling
While isset() and empty() are fundamental, the most robust way to test for a variable’s existence and validity in PHP often involves a combination of these and other strict type checking functions. This approach ensures data integrity and prevents unexpected behavior, especially in critical application logic. For instance, if you need to ensure a variable is not only set and not empty, but also specifically an integer, you would combine isset(), !empty(), and is_int(). Furthermore, using the strict comparison operator === is paramount when you need to differentiate between types, such as 0 (integer) and "0" (string), or false (boolean) and null.
Consider a scenario where you’re expecting an ID that must be a positive integer. A simple isset($id) && !empty($id) would allow "0" or even "abc" to pass certain checks if not followed by strict type validation. Instead, you might use a sequence like isset($id) && is_numeric($id) && $id > 0. For more precise checks, PHP offers a suite of is_ functions, such as is_null(), is_string(), is_array(), and is_object(), which provide granular control over type validation. As stated by OWASP, “All input must be validated before use. Input validation is a critical control for protecting web applications from various attacks.” This principle extends to validating not just user input, but any variable state within Question & Answer :
From the isset() docs:
isset() will return FALSE if testing a variable that has been set to NULL.
Basically, isset() doesn’t check for whether the variable is set at all, but whether it’s set to anything but NULL.
Given that, what’s the best way to actually check for the existence of a variable? I tried something like:
if(isset($v) || @is_null($v))
(the @ is necessary to avoid the warning when $v is not set) but is_null() has a similar problem to isset(): it returns TRUE on unset variables! It also appears that:
@($v === NULL)
works exactly like @is_null($v), so that’s out, too.
How are we supposed to reliably check for the existence of a variable in PHP?
Edit: there is clearly a difference in PHP between variables that are not set, and variables that are set to NULL:
<?php $a = array('b' => NULL); var_dump($a);
PHP shows that $a['b'] exists, and has a NULL value. If you add:
var_dump(isset($a['b'])); var_dump(isset($a['c']));
you can see the ambiguity I’m talking about with the isset() function. Here’s the output of all three of these var_dump()s:
array(1) { ["b"]=> NULL } bool(false) bool(false)
Further edit: two things.
One, a use case. An array being turned into the data of an SQL UPDATE statement, where the array’s keys are the table’s columns, and the array’s values are the values to be applied to each column. Any of the table’s columns can hold a NULL value, signified by passing a NULL value in the array. You need a way to differentiate between an array key not existing, and an array’s value being set to NULL; that’s the difference between not updating the column’s value and updating the column’s value to NULL.
Second, Zoredache’s answer, array_key_exists() works correctly, for my above use case and for any global variables:
<?php $a = NULL; var_dump(array_key_exists('a', $GLOBALS)); var_dump(array_key_exists('b', $GLOBALS));
outputs:
bool(true) bool(false)
Since that properly handles just about everywhere I can see there being any ambiguity between variables that don’t exist and variables that are set to NULL, I’m calling array_key_exists() the official easiest way in PHP to truly check for the existence of a variable.
(Only other case I can think of is for class properties, for which there’s property_exists(), which, according to its docs, works similarly to array_key_exists() in that it properly distinguishes between not being set and being set to NULL.)
If the variable you are checking would be in the global scope you could do:
array_key_exists('v', $GLOBALS)