36

PHP has the habit of evaluating (int)0 and (string)"0" as empty when using the empty() function. This can have unintended results if you expect numerical or string values of 0. How can I "fix" it to only return true to empty objects, arrays, strings, etc?

3
  • 4
    Personally, I think (int)0 should evaluate as false; it's just (string)"0" that shouldn't. Also, note that empty objects don't count as empty(), and the accepted answer doesn't change this. Commented Jul 24, 2013 at 18:39
  • @Brilliand - given php's overall design, that ints and numeric strings are interchangeable, that probably wouldn't be a good change. Commented Feb 24, 2019 at 21:15
  • ... IMHO It would be simpler if empty was false for values that are equivalent to 0. Then it often would be useful as-is; we wouldn't need workarounds like this Q&A. In the cases where one wanted to include 0-equivalents, would simply do if (empty($var) || $var == 0). Ah well. Commented Feb 24, 2019 at 21:36

15 Answers 15

45

Improving empty() is anything but simple.

First of all, after all those years since this question had been asked, PHP changed its approach towards much more strict typing. And therefore, using empty(), due to its ambiguous nature, is not recommended at all.

Besides, empty() is frowned upon for another reason: it does isset() under the hood (being essentially a compact form for !isset($var) || !$var) and therefore does error suppression for a possibly helpful error message, when you are trying to access a variable that doesn't exist.

Therefore, you are encouraged to write a code where all your variables are set and assigned certain type. This done, instead of using empty(), you can always use just a comparison, according to variable's type:

 $var !== 0
 $var !== ''
 $var !== []
 // etc.

However, there is also an issue with outside variables that are not under the developer's control. In this case it is advised to validate such values first, in the meaning of checking every input value against a set of rules, and then either have all input assigned to local variables if all values are OK, or reject the request. During this process some variables can be assigned default values, if not present in the request but not being required.

But well, in case you aren't familiar with strict typing and wants just a more strict substitution, here it goes:

!isset($var) || !($var || (is_scalar($var) && strlen($var)))

Here, we are essentially recreating the empty()'s behavior, but also adding another condition: in case it's a scalar value, also check its string length and only return true if it's zero. So it returns true for the following values

  • non-existent variable
  • null
  • false
  • empty string
  • empty array

Here you can check it online.

Sometimes it could be also a good idea to throw in a trim() call:

!isset($var) || !($var || (is_scalar($var) && strlen(trim($var))))

The code appears to be lengthy, but wrapping it in a function is not that simple either, as such a function will lose the ability to suppress the "Undefined variable" warning. To overcome it, we can define this function's parameter to be passed by reference.

function strict_empty(&$var)
{
    return !($var || (is_scalar($var) && strlen(trim($var))));
}

But it has possibly unwanted side effects. For example, once we check a non-existent variable with this function, this variable will appear defined after the call.

Therefore, I strongly advise to just have all variables deliberately set and assigned certain type, and then compered to a value that is considered empty for this type, as explained above.

By the way, another approach is used in Laravel's blank() function explained in this answer, which is quite smart, as it doesn't suppress the warning and also assumes a boolean value already meaningful (and therefore not "empty"), no matter if it's true or false.

Sign up to request clarification or add additional context in comments.

2 Comments

The first one fails because you use != instead of !==. (empty($variable) && $variable !== '0') would actually be a nice and concise way to test this.
@thomasrutter - hopefully your code will never encounter " 0" or "00" or "-0". Those won't be detected by !== '0', but do have contents. A good example of why one should write code that says your intent, instead of being clever.
19

I seldom use empty() for the reason you describe. It confuses legitimate values with emptiness. Maybe it's because I do a lot of work in SQL, but I prefer to use NULL to denote the absence of a value.

PHP has a function is_null() which tests for a variable or expression being NULL.

$foo = 0;
if (is_null($foo)) print "integer 0 is null!\n"; 
else print "integer 0 foo is not null!\n";

$foo = "0";
if (is_null($foo)) print "string '0' is null!\n"; 
else print "string '0' is not null!\n";

$foo = "";
if (is_null($foo)) print "string '' is null!\n"; 
else print "string '' is not null!\n";

$foo = false;
if (is_null($foo)) print "boolean false is null!\n"; 
else print "boolean false is not null!\n";

You can also use the exactly equals operator === to do a similar test:

if ($foo === null) print "foo is null!\n";

This is true if $foo is NULL, but not if it's false, zero, "", etc.

4 Comments

isset would be a more appropriate replacement than is_null. If the OP chose empty over a simple (likely implicit) cast to boolean, then I would assume that it's possible for the variable to be undefined.
I wouldn't use is_null unless of course you are wanting $foo = "" to be treated as NOT NULL.
@Brett, Right, in PHP and in SQL, a string with zero length is not the same as a null. Oracle database assumes "" is the same as NULL, but that's nonstandard.
@BillKarwin Yes I agree, but my point was more in relation to this question as it seems they would not want an empty string to be treated as valid content, but if you used is_null it would be.
8

"0" is always considered false (or empty) in PHP, and it does make alot of sense (not that I'll argue that here). If you want to have zero evaluate to true as well, use strlen($value).

2 Comments

Good advice, although you'll probably want to throw a trim() around $value.
and in the same time empty('false") return false
6

I always add to my codebase

function is_blank($value) {
    return empty($value) && !is_numeric($value);
}

and use it instead of empty(). It solves the issue of keeping zeros (int, float or string) as non-empty.

See http://www.php.net/manual/en/function.empty.php#103756 which was added May 2011.

1 Comment

great solution, I named it is_nothing() instead of is_blank, but that's just me.
4

"Fixing" is one point of view. Another would be to assume it's not "broken", and learn to work with it the way it was intentionally designed.

Given the problem as you describe it, the best thing would be to tighten up your code so the variable can't be put into an indeterminate type state. Otherwise, you'll want to test for datatype using gettype().

6 Comments

From what I understand it is now recommended that one use is_x rather than the older gettype() function.
The problem is the OP appears to want a type-independent solution. With PHP, the complete enumeration of is_? options may change. It's far better to avoid the train wreck in the first place. Actually, what they probably want is points for an abstract question. :)
+1 for the points fishing comment. It does seem like the OP was a little quick with their own solution to a non-existent problem. Still, if you want a type independent solution I would use "===" operator instead.
Right, and even then a type-independent solution is a bad thing to want in the first place.
I have a reaction opposite to @NoahGoodrich. -1 for fixating on the word "fixing", rather than responding to the key sentence "This can have unintended results if you expect numerical or string values of 0". This is a very common issue when using php - not fixed by "tightening your code".
|
4

Generally speaking you want to use the triple equals operator to determine when a value truly is null or false. This is more specific and the results ALWAYS return exactly as expected.

Creating a separate function to do something that you can do in a single line of code but using a different operator seems to be overly complicated and adds additional obfuscation.

Additionally, many programmers tend to use 0 or an empty string to denote a non-existent value but it is more correct (and I feel a better practice) to use a null value to denote any value (regardless of type) that is truly non-existent.

3 Comments

The real problem with it IMHO is that it discards precision and abets sloppy thinking.
In SQL as Bill pointed out, NULL is a non-existent value and an empty string is an empty string. I like to check for NULL's explicitly as well as invalid strings and integers depending on the needs of the program.
In other words, sometimes a zero or empty string is valid but these should be tested independent of a test for null values
1

You're trying to use empty() for something it's not intended for. Personally, I think it's behaviour comes from an older programming paradigm in PHP and it could probably be retired.

Instead, make your checks more explicit. Rather than checking if the value is not useful, check that it is. For example, if you want an array, check it's an array (is_array()) rather than checking whether it's a string.

Also, you can rely on the fact that all $_POST and $_GET variables are strings, so you can make comparisons to "", or check that strlen() == 0.

2 Comments

Sometimes $_POST or $_GET variables are arrays.
sometimes $_POST or $_GET will not have the key, meaning value of NULL
1

I use this to detect emptiness:

''  --> true
'0' --> false
0   --> false

the function:

function is_empty_or_zero($str) {
 return (is_null($str) || ($str === ''));
}

1 Comment

is_empty_or_zero isn't a good name for this function. is_null_or_empty_string is what you are testing. Specifically, "zero" returns false. Maybe you meant is_empty_but_not_zero as the name; though that's not as good as saying your intent directly. Another approach is to define the opposite (negation) of this function, and call it has_contents. if (!has_contents(...)) is quite readable, IMHO.
1

PHP’s empty() treats both 0 and "0" as empty, which can cause issues. Laravel’s blank() (Laravel docs, Github) offers a more precise alternative:

function blank($value)
    {
        if (is_null($value)) {
            return true;
        }

        if (is_string($value)) {
            return trim($value) === '';
        }

        if (is_numeric($value) || is_bool($value)) {
            return false;
        }

        if ($value instanceof Model) {
            return false;
        }

        if ($value instanceof Countable) {
            return count($value) === 0;
        }

        if ($value instanceof Stringable) {
            return trim((string) $value) === '';
        }

        return empty($value);
    }
}

Why use this?

  • "0" and 0 are not considered blank.
  • boolean values are not considered blank.
  • Trims strings before checking.
  • Handles arrays and Countable objects properly.
  • Still treats null as blank.

Use blank() instead of empty() for safer checks where 0 should be valid.

Comments

0

If you want an equivalent to empty($var) which returns false when var is the string "0", you can use:

if (($var ?? 0) != '')

The reason this works is:

  • $var ?? 0 is a shortcut for the ternery isset($var) ? $var : 0. That is, if the variable on the left doesn't exist it takes the value on the right, without issuing a warning or notice, as with isset() or empty().

  • <value> != '' does an implicit cast of the empty string when the left value isn't a string. This returns false for any false-like value of <value> such as null, false or (numeric) 0. However, for the string "0", this will return true, since no cast will be performed and "0" != "" is true.

Comments

-1

I created an IsEmpty() a terse quick function.

function IsEmpty($mData) {
    return is_int($mData) ? false : 
        is_string($mData) ? $mData=="" : 
        empty($mData);
}

2 Comments

Answering your own question before seeing any other responses? -- Your solution conflates everything into one complex statement, effectively obfuscating it (not considered good style in most places).
@le dorfier: there's no problems with answering your own questions: it's just creating more information for others to read and learn by. @null: one of the key benefits of using empty() is that you can test undefined variables without causing a warning. this function doesn't have this same ability.
-1
if ( is_int($val) || !empty($val) ) {;}

2 Comments

What advantage is there to this approach, over using is_numeric, as in Stemar's answer?
This one will issue Undefined variable warning for non-existent variables, hence not a real substitution for empty()
-1

I used ord function to check either the value passed was empty or not.

/*
* SPACE is not empty - Please do trim before using this function for your own purpose
*  var_dump(is_having_value(0));        // true
   var_dump(is_having_value(0000));     // true
   var_dump(is_having_value("    0"));  // true
   var_dump(is_having_value(""));       // false
   var_dump(is_having_value("  "));     // true
   var_dump(is_having_value('\t'));     // true
   var_dump(is_having_value(''));       // false
   var_dump(is_having_value('o'));      // true
   var_dump(is_having_value('O'));      // true
   var_dump(is_having_value('0'));      //true
   var_dump(is_having_value(null));     //false
   var_dump(is_having_value());         //false
*/
function is_having_value($val = null) {
    if (is_array($val) || is_object($val)):
        $logic_empty = empty($val);
        return !$logic_empty;
    else:
        $ascii = ord($val);
        if ($ascii == 48 || $ascii = 0 || $ascii == 32):
            return true;
        else:
            $logic_empty = empty($val);
            return !$logic_empty;
        endif;
    endif;
}

1 Comment

What advantage is there to this approach, over using is_numeric, as in Stemar's answer?
-1

If you want to check the "emptiness" of an scalar (string, bool, int, float), and you want 0 and "0" to be evaluated as non-empty, you can use the following function:

function my_empty(mixed $x): bool
{
    return $x === null || $x === ''  || $x === false;
}

It can evaluate expressions, not only variables (e.g. my_empty($m * $n)).

2 Comments

it would issue Undefined variable on non-existent variables, hence it's not a real substitution. Besides, I don't see any use case for this function. instead of my_empty($m * $n) it must be $m * $n === 0. There must be no situation when you don't know which exact type you expect. Hence instead of vague if ($x === null || $x === '' || $x === false) it must be just if ($x === null) OR if ($x === false) etc - without any function.
I know it will issue Undefined variable in that case (any user-defined function will, too). The expression was just an example. And in my many years of coding, I came across many situations where you don't know for sure the type of a variable.
-3

The most effective solution for this problem is to test if the variable is false e.g.


if (!$variable_name) {echo $variable_name;}

It doesn't matter if the variable is empty or null, it all equals to false when put in a if conditional.

3 Comments

that's pretty much the same as empty(), in that 0 or "0" will be false, which is what the OP was trying to avoid.
also throws a strict notice if the variable is not set
It's also slightly more obfuscated over empty(). I'd advise against this, unless the variables is only true or false.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.