Avoiding empty() in PHP

The language construct empty() appears rather versatile. It's like a Swiss army knife with a thousand blades, ready to hurt you if you grab it by the wrong end. Or a jack of all trades, master of none. Most of all, empty() is a poor communicator.

I spot empty() in poorly-aged closed-source projects. I discover empty() in brand-new closed-source projects from last week. And I meet empty() again in open-source projects with millions of downloads.

So what is the problem with empty() when so many people use it?

Problem

If you refer to the documentation for empty(), you will find the following:

Determine whether a variable is considered to be empty. A variable is considered empty if it does not exist or if its value equals false. empty() does not generate a warning if the variable does not exist.

If you ignore the internal implementation and possible performance issues, using empty() is identical to using isset() and a loose comparison with false.

 <?php

 declare(strict_types=1);

-if (empty($value)) {
+if (!isset($value) || $value == false) {
     // ...
 }

When you already know that a variable, a property, a function or method parameter, and a function or method return value are defined, why would you use isset()?

When you already know that a variable, a property, a function or method parameter, and a function or method return value assume multiple types, why would you use loose comparison and not handle each acceptable type and value separately?

When you already know that a variable, a property, a function or method parameter, and a function or method return value assume a single type, why would you use loose instead of strict comparison?

Using empty(), isset(), or loose comparisons is a wishy-washy business. Is that how you want to work?

As mentioned, I often find empty() in legacy code bases. Some of these projects run on outdated versions of PHP and are entirely unaware of property, parameter, and return type declarations. In these projects, you often can not find DocBlocks for properties, function and method parameters, or function and method return types.

When you pick up maintenance of these projects and begin to fix bugs, update PHP versions and eventually modernize them, you have difficulty figuring out what the original authors intended to do when they used empty().

Were the original authors aware of the quirks of empty()? Did the authors intend a property, a function, or a method parameter to accept all types and values? Did the authors plan to return all types and values from a function or method? Did the authors, who have long disappeared and ghosted the project owners, really think the use of empty() through?

The original author could be you. The maintainer could also be you, picking up the project after years. Perhaps you had a stroke or an accident in the meantime that impaired your cognitive abilities. Now you have difficulty understanding what the original author intended. Perhaps the authors and maintainers are entirely different persons. Maybe you are in perfect health and still struggle to understand the original author's intent.

While you write the code for the computer to process, you - as the author - also write the code for the person maintaining it after. By more or less carefully selecting language features, you instruct the computer, but you also communicate your intent to the maintainer.

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Let's look at all cases when empty() returns true, and explore alternatives that better communicate your situational awareness and intent!

Candidates

Undefined variable

empty() returns true when the argument is an undefined variable.

<?php

declare(strict_types=1);

var_dump(empty($value)); // (bool)true

What are scenarios where you currently use empty() to verify whether a variable is undefined?

Scenario: Expecting an included file to declare a variable

You include a file that you expect to declare a variable. The file may not exist or may not declare the variable.

Instead of using empty() to verify that an included file exists and declares a variable, set the variable to a suitable default value, include the file when it exists, and verify that the variable has an acceptable type and value.

 <?php

 declare(strict_types=1);

-include __DIR__ . '/file.php';
+$file = __DIR__ . '/file.php';

-if (empty($value)) {
-    // ....
-}
+
+$value = [];
+
+if (is_file($file)) {
+    include $file;
+
+    if (!is_array($value)) {
+        // ...
+    }
+}

Instead of using empty() to verify that an included file exists and declares a variable, set the variable to a suitable default value, expect the file to return a value, include the file when it exists, and verify that the returned value has an acceptable type and value.

 <?php

 declare(strict_types=1);

-include __DIR__ . '/file.php';
+$file = __DIR__ . '/file.php';

-if (empty($value)) {
-    // ....
-}
+
+$value = [];
+
+if (is_file($file)) {
+    $value = include $file;
+
+    if (!is_array($value)) {
+        // ...
+    }
+}

💡 Avoid writing code that relies on including files that declare variables or return values.

Undefined instance property

empty() returns true when the argument is an undefined instance property.

<?php

declare(strict_types=1);

$object = new stdClass();

var_dump(empty($object->value)); // (bool)true

As a side effect, empty() also invokes the magic method __isset() when you reference an undefined instance property of an object that declares an __isset() method.

<?php

declare(strict_types=1);

$object = new class() {
    public function __isset(string $name): bool
    {
        echo '👋';

        return true;
    }
};

var_dump(empty($object->value)); // 👋(bool)true

What are scenarios where you currently use empty() and deal with an undefined instance property?

Undefined static property

empty() returns true when the argument is an undefined static property.

<?php

declare(strict_types=1);

$object = new stdClass();

var_dump(empty($object::$value)); // (bool)true

What are scenarios where you currently use empty() and deal with an undefined static property?

Inaccessible instance property

empty() returns true when the argument is an inaccessible instance property.

As a side effect, empty() invokes the magic method __isset() when it exists.

<?php

declare(strict_types=1);

$object = new class() {
    private $value = 9000;
    protected $otherValue = 9001;

    public function __isset(string $name): bool
    {
        echo '👋';

        return true;
    }
};

var_dump(empty($object->value)); // 👋(bool)true
var_dump(empty($object->otherValue)); // 👋(bool)true

What are scenarios where you currently use empty() and deal with an inaccessible instance property?

Inaccessible static property

empty() returns true when the argument is an inaccessible static property.

<?php

declare(strict_types=1);

$object = new class() {
    private static $value = 9000;
    protected static $otherValue = 9000;
};

var_dump(empty($object::$value)); // (bool)true
var_dump(empty($object::$otherValue)); // (bool)true

What are scenarios where you currently use empty() and deal with an inaccessible static property?

Scenario: Declaring variables in files and including them

Null

empty() returns true when the argument is null.

<?php

declare(strict_types=1);

$value = null;

var_dump(empty($value)); // (bool)true

What are scenarios where you currently use empty() to verify that a value is not null?

Scenario: Instance or static property could be null

You have a class with an instance or a static property and currently use empty() to verify the property value.

Instead of using empty() to verify the property value when the property can assume multiple types, compare the instance or static property with null and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     private $value;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === null) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the property value when the property can assume null or a known type, add a nullable property declaration and compare the property value with null.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    private $value;
+    private ?string $value = null;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === null) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing classes with instance or static properties that accept multiple types. Add property type declarations or DocBlocks to document property types.

Scenario: Function or method parameter could be null

You have a function or a method with a parameter that could be null.

Instead of using empty() to verify the parameter value when the parameter can assume multiple types, compare the parameter with null and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar($value)
     {
-        if (empty($value)) {
+        if ($value === null) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the parameter value when the parameter can assume null or a known type, add a nullable parameter type declaration and compare the parameter with null.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar($value)
+    public function bar(?string $value)
     {
-        if (empty($value)) {
+        if ($value === null) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing functions or methods with parameters that accept multiple types. Add parameter type declarations or DocBlocks to document function and method parameter types.

Scenario: Function or method return value could be null

You have a function or a method with a return value that could be null.

Instead of using empty() to verify the return value at the call site when the return value can assume multiple types, compare the return value with null and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar()
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === null) {
     // ...
 }

+// handle other possible types and values

Instead of using empty() to verify the return value at the call site when the return value can assume null or a known type, add a nullable return type declaration and compare the return value with null.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar()
+    public function bar(): ?string
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === null) {
     // ...
 }

💡 Avoid writing functions or methods that return values of multiple types. Add return type declarations or DocBlocks to document function and method return types.

Array

empty() returns true when the value is an empty array.

<?php

declare(strict_types=1);

$value = [];

var_dump(empty($value)); // (bool)true

What are scenarios where you currently use empty() to verify that a value is not an empty array?

Scenario: Instance or static property could be an empty array

You have a class with an instance or a static property and currently use empty() to verify the property value.

Instead of using empty() to verify the property value when the property can assume multiple types, compare the instance or static property with an empty array and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     private $value;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === []) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the property value when the property can assume an array, add an array property declaration and compare the property value with an empty array.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    private $value;
+    private array $value = [];

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === []) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing classes with instance or static properties that accept multiple types. Add property type declarations or DocBlocks to document property types.

Scenario: Function or method parameter could be an empty array

You have a function or a method with a parameter that could be an empty array.

Instead of using empty() to verify the parameter value when the parameter can assume multiple types, compare the parameter with an empty array and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar($value)
     {
-        if (empty($value)) {
+        if ($value === []) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the parameter value when the parameter can assume an array, add an array parameter type declaration and compare the parameter with an empty array.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar($value)
+    public function bar(array $value)
     {
-        if (empty($value)) {
+        if ($value === []) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing functions or methods with parameters that accept multiple types. Add parameter type declarations or DocBlocks to document function and method parameter types.

Scenario: Function or method return value could be an empty array

You have a function or a method with a return value that could be an empty array.

Instead of using empty() to verify the return value at the call site when the return value can assume multiple types, compare the return value with an empty array and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar()
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === []) {
     // ...
 }

+// handle other possible types and values

Instead of using empty() to verify the return value at the call site when the return value can assume an empty array, add an array return type declaration and compare the return value with an empty array.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar()
+    public function bar(): array
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === []) {
     // ...
 }

💡 Avoid writing functions or methods that return values of multiple types. Add return type declarations or DocBlocks to document function and method return types.

Boolean

empty() returns true when the argument is a bool with the value false.

<?php

declare(strict_types=1);

$value = false;

var_dump(empty($value)); // (bool)true

What are scenarios where you currently use empty() to verify that a value is not a bool with the value false?

Scenario: Instance or static property could be false

You have a class with an instance or a static property and currently use empty() to verify the property value.

Instead of using empty() to verify the property value when the property can assume multiple types, compare the instance or static property with false and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     private $value;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === false) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the property value when the property can assume a bool, add a bool property declaration and use a logical expression.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    private $value;
+    private bool $value = false;

     public function bar()
     {
-        if (empty($this->value)) {
+        if (!$this->value) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing classes with instance or static properties that accept multiple types. Add property type declarations or DocBlocks to document property types.

Scenario: Function or method parameter could be false

You have a function or a method with a parameter that could be an empty array.

Instead of using empty() to verify the parameter value when the parameter can assume multiple types, compare the parameter with false and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar($value)
     {
-        if (empty($value)) {
+        if ($value === false) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the parameter value when the parameter can assume a bool, add a bool parameter type declaration and use a logical expression.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar($value)
+    public function bar(bool $value)
     {
-        if (empty($value)) {
+        if (!$value) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing functions or methods with parameters that accept multiple types. Add parameter type declarations or DocBlocks to document function and method parameter types.

Scenario: Function or method return value could be false

You have a function or a method with a return value that could be a bool with the value false.

Instead of using empty() to verify the return value at the call site when the return value can assume multiple types, compare the return value with false and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar()
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === false) {
     // ...
 }

+// handle other possible types and values

Instead of using empty() to verify the return value at the call site when the return value can assume a bool, add a bool return type declaration and use a logical expression.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar()
+    public function bar(): bool
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if (!$foo->bar()) {
     // ...
 }

💡 Avoid writing functions or methods that return values of multiple types. Add return type declarations or DocBlocks to document function and method return types.

Float

empty() returns true when a variable is a float with the value 0.0 or -0.0.

<?php

declare(strict_types=1);

$value = 0.0;

var_dump(empty($value)); // (bool)true

What are scenarios where you currently use empty() to verify that a value is not a float with the value 0.0 or -0.0?

Scenario: Instance or static property could be 0.0

You have a class with an instance or a static property and currently use empty() to verify the property value.

Instead of using empty() to verify the property value when the property can assume multiple types, compare the instance or static property with 0.0 or -0.0 and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     private $value;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === 0.0) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the property value when the property can assume a float, add a float property declaration and compare the instance or static property with 0.0 or -0.0.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    private $value;
+    private float $value = 0;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === 0.0) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing classes with instance or static properties that accept multiple types. Add property type declarations or DocBlocks to document property types.

Scenario: Function or method parameter could be 0.0

You have a function or a method with a parameter that could be a float with the value 0.0 or -0.0.

Instead of using empty() to verify the parameter value when the parameter can assume multiple types, compare the parameter with 0.0 or -0.0 and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar($value)
     {
-        if (empty($value)) {
+        if ($value === 0.0) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the parameter value when the parameter can assume a float, add a float parameter type declaration and compare the parameter with 0.0 or -0.0.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar($value)
+    public function bar(float $value)
     {
-        if (empty($value)) {
+        if ($value === 0.0) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing functions or methods with parameters that accept multiple types. Add parameter type declarations or DocBlocks to document function and method parameter types.

Scenario: Function or method return value could be 0.0

You have a function or a method with a return value that could be a float with the value 0.0 or -0.0.

Instead of using empty() to verify the return value at the call site when the return value can assume multiple types, compare the return value with 0.0 or 0.0 and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar()
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === 0.0) {
     // ...
 }

+// handle other possible types and values

Instead of using empty() to verify the return value at the call site when the return value can assume a float, add a float return type declaration and compare the return value with 0.0 or -0.0.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar()
+    public function bar(): float
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === 0.0) {
     // ...
 }

Int

empty() returns true when the argument is an int with the value 0.

<?php

declare(strict_types=1);

$value = 0;

var_dump(empty($value)); // // (bool)true

What are scenarios where you currently use empty() to verify that a value is not an int with the value 0?

Scenario: Instance or static property could be 0

You have a class with an instance or a static property and currently use empty() to verify the property value.

Instead of using empty() to verify the property value when the property can assume multiple types, compare the instance or static property with 0 and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     private $value;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === 0) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the property value when the property can assume an int, add an int property declaration and compare the instance or static property with 0.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    private $value;
+    private int $value = 0;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === 0) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing classes with instance or static properties that accept multiple types. Add property type declarations or DocBlocks to document property types.

Scenario: Function or method parameter could be 0

You have a function or a method with a parameter that could be an int with the value 0.

Instead of using empty() to verify the parameter value when the parameter can assume multiple types, compare the parameter with 0 and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar($value)
     {
-        if (empty($value)) {
+        if ($value === 0) {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the parameter value when the parameter can assume an int, add an int parameter type declaration and compare the parameter with 0.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar($value)
+    public function bar(int $value)
     {
-        if (empty($value)) {
+        if ($value === 0) {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing functions or methods with parameters that accept multiple types. Add parameter type declarations or DocBlocks to document function and method parameter types.

Scenario: Function or method return value could be 0

You have a function or a method with a return value that could be an int with the value 0.

Instead of using empty() to verify the return value at the call site when the return value can assume multiple types, compare the return value with 0 and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar()
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === 0) {
     // ...
 }

+// handle other possible types and values

Instead of using empty() to verify the return value at the call site when the return value can assume an int, add an int return type declaration and compare the return value with 0.

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar()
+    public function bar(): int
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === 0) {
     // ...
 }

String

empty() returns true when the argument is a string with the value ''.

<?php

declare(strict_types=1);

$value = '';

var_dump(empty($value)); // // (bool)true

empty() also returns true when the argument is a string with the value '0'.

<?php

declare(strict_types=1);

$value = '0';

var_dump(empty($value)); // // (bool)true

What are scenarios where you currently use empty() to verify that a value is not a string with the value '' (or '0')?

Scenario: Instance or static property could be an empty string

You have a class with an instance or a static property and currently use empty() to verify the property value.

Instead of using empty() to verify the property value when the property can assume multiple types, compare the instance or static property with '' (or '0') and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     private $value;

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === '') {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the property value when the property can assume a string, add a string property declaration and compare the instance or static property with '' (or '0').

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    private $value;
+    private string $value = '';

     public function bar()
     {
-        if (empty($this->value)) {
+        if ($this->value === '') {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing classes with instance or static properties that accept multiple types. Add property type declarations or DocBlocks to document property types.

Scenario: Function or method parameter could be an empty string

You have a function or a method with a parameter that could be a string with the value '' (or '0').

Instead of using empty() to verify the parameter value when the parameter can assume multiple types, compare the parameter with '' (or '0') and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar($value)
     {
-        if (empty($value)) {
+        if ($value === '') {
             // ...
         }

+        // handle other possible types and values
+
         // ...
     }
 }

Instead of using empty() to verify the parameter value when the parameter can assume a string, add a string parameter type declaration and compare the parameter with '' (or '0').

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar($value)
+    public function bar(string $value)
     {
-        if (empty($value)) {
+        if ($value === '') {
             // ...
         }

         // ...
     }
 }

💡 Avoid writing functions or methods with parameters that accept multiple types. Add parameter type declarations or DocBlocks to document function and method parameter types.

Scenario: Function or method return value could be an empty string

You have a function or a method with a return value that could be a string with the value '' (or '0').

Instead of using empty() to verify the return value at the call site when the return value can assume multiple types, compare the return value with '' (or '0') and handle each possible case separately.

 <?php

 declare(strict_types=1);

 final class Foo
 {
     public function bar()
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === '') {
     // ...
 }

+// handle other possible types and values

Instead of using empty() to verify the return value at the call site when the return value can assume a string, add a string return type declaration and compare the return value with '' (or '0').

 <?php

 declare(strict_types=1);

 final class Foo
 {
-    public function bar()
+    public function bar(): string
     {
         // ...

         return $value;
     }
 }

-if (empty($foo->bar()) {
+if ($foo->bar() === '') {
     // ...
 }

💡 Avoid writing functions or methods that return values of multiple types. Add return type declarations or DocBlocks to document function and method return types.

SimpleXMLElement

empty() returns true when the argument is an instance of SimpleXMLElement constructed from an XML string representing an element without attributes and children.

<?php

declare(strict_types=1);

$value = new SimpleXMLElement('<foo></foo>');

var_dump(empty($value)); // (bool)true

What are scenarios where you currently use empty() to verify whether a SimpleXMLElement is empty?

Conclusion

Do not use empty(). Do not write code that allows a variable, a property, a parameter, or a return value to assume multiple types. Use type-safe comparisons.

Do you find this article helpful?

Do you have feedback?

Do you need help with your PHP project?