Don’t Use the empty() Function!
They Lost HOW Much Data?!
Here’s a fun little story. A client wanted a form built that would collect some basic family information like whether someone was married or single, had children, income, etc… Mr. Developer says, “No problem!” and proceeds to build out this form, and they run through some simple tests and it seems to work well, so the form goes live.
A couple months later, Client calls Mr. Developer, “Hey, we have a weird problem. We aren’t getting form submissions from certain types of people. We should have a lot of stay-at-home parents, as well as people who don’t have any kids, but we’re seeing a REALLY low rates of submissions by those groups. In fact, we don’t have ANY people without kids.”
So Mr. Developer goes and looks at the form and tries to submit it without any kids:
Number of Children *
Uh-oh.
He then tests out the other numeric fields like income level, and sure enough, any field where 0 could be a valid answer was triggering that error, preventing the form from being submitted.
Then he checks the form validation code:
if(empty($_POST["numChildren"))
{
$errors["numChildren"] = "You must fill out this field!";
}
...
if(empty($_POST["personalIncome"))
{
$errors["personalIncome"] = "You must fill out this field!";
}
…and discovered that while the empty() function returns true for things like blank strings, it ALSO returns true for the number zero, and the string “0”, too.
The Damage Report
To be fair, the Client should also have their own thorough QA testing process, which should have caught this problem before the form was submitted. However, Mr. Developer is being paid for his expertise, so he failed twice – once when using the empty() function and once when he didn’t thoroughly test his work before handing it to the Client.
The Client was extremely unhappy because they depended heavily on accurate statistics of their submissions, and because entire groups of people weren’t able to submit forms, the statistics they had collected were all skewed and inaccurate. That, in turn, meant an unhappy client, lost trust, and lost business.
Why Does empty() Do That?
If Mr. Developer had paid close attention to the documentation page for empty():
…he would have seen this little snippet:
Returns false
if var
exists and has a non-empty, non-zero value, aka falsey, see conversion to boolean. Otherwise returns true
.
And if he had clicked that link and read about “falsey” values:
https://www.php.net/manual/en/language.types.boolean.php#language.types.boolean.casting
He would have seen that empty() would return true for all of these situations:
- the boolean
false
itself - the integer 0 (zero)
- the floats 0.0 and -0.0 (zero)
- the empty string, and the string “0”
- an array with zero elements
- the special type NULL (including unset variables)
- SimpleXML objects created from attributeless empty elements, i.e. elements which have neither children nor attributes.
An Alternative
The short of it is that in many cases, you can replace empty() with !strlen().
Value empty() !strlen()
--------------- ------- ---------
'A' false false
'' true true
'0' true false
0 true false
0.0 true false
false true true
'-1' false false
$missing_var true true
Because strlen() will return either 0 (which is a “falsey” value) or a positive number (which are all “truthy” values), this technique can be almost a drop-in replacement for empty().
There’s a slight catch, though, and it’s on the $missing_var scenario. Even though !strlen() did correctly return true for the missing variable, this WILL generate a warning if your PHP configuration has warnings being shown/reported!
So if you are in a scenario where you’re not sure if the variable exists (e.g. a checkbox input on a form that just won’t send the input value AT ALL if it’s not checked), then you would need to expand !strlen() into:
if(!isset($missing_var) || !strlen($missing_var))
{
...
}
Ideally, you’ve by now learned to use the null-coalescing operator and are coding your form defaults like this:
$field_x = $_POST["field_x"] ?? "";
NOTE: You can also use the filter_input function if you’re okay with the default value being NULL for missing fields.
If you are doing this kind of preparation step before validation, you can skip the isset() step because you’ll know that the variables are going to exist.
You can ALSO suppress the warning with the @ symbol like this:
if(@!strlen($missing_var))
{
...
}
However, I usually recommend that people never use the @ symbol to suppress warnings unless they are absolutely certain they’ll never care about the error message (which should be a rarity).
Leave a Reply