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.PHP manual: empty()If you ignore the internal implementation and possible performance issues, using
empty()is identical to usingisset()and a loose comparison withfalse.<?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 ofempty()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.
Martin Fowler, RefactoringLet's look at all cases when
empty()returnstrue, and explore alternatives that better communicate your situational awareness and intent!Candidates
- undefined variable
- undefined instance property
- undefined static property
- inaccessible instance property
- inaccessible static property
- null
- array
- bool
- float
- int
- string
- SimpleXMLElement
Undefined variable
empty()returnstruewhen the argument is an undefined variable.<?php declare(strict_types=1); var_dump(empty($value)); // (bool)trueWhat 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()returnstruewhen the argument is an undefined instance property.<?php declare(strict_types=1); $object = new stdClass(); var_dump(empty($object->value)); // (bool)trueAs 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)trueWhat are scenarios where you currently use
empty()and deal with an undefined instance property?Undefined static property
empty()returnstruewhen the argument is an undefinedstaticproperty.<?php declare(strict_types=1); $object = new stdClass(); var_dump(empty($object::$value)); // (bool)trueWhat are scenarios where you currently use
empty()and deal with an undefinedstaticproperty?Inaccessible instance property
empty()returnstruewhen 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)trueWhat are scenarios where you currently use
empty()and deal with an inaccessible instance property?Inaccessible static property
empty()returnstruewhen the argument is an inaccessiblestaticproperty.<?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)trueWhat are scenarios where you currently use
empty()and deal with an inaccessiblestaticproperty?Scenario: Declaring variables in files and including them
Null
empty()returnstruewhen the argument isnull.<?php declare(strict_types=1); $value = null; var_dump(empty($value)); // (bool)trueWhat are scenarios where you currently use
empty()to verify that a value is notnull?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 orstaticproperty withnulland 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 assumenullor a known type, add a nullable property declaration and compare the property value withnull.<?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 withnulland 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 assumenullor a known type, add a nullable parameter type declaration and compare the parameter withnull.<?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 withnulland 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 valuesInstead of using
empty()to verify the return value at the call site when the return value can assumenullor a known type, add a nullable return type declaration and compare the return value withnull.<?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()returnstruewhen the value is an emptyarray.<?php declare(strict_types=1); $value = []; var_dump(empty($value)); // (bool)trueWhat are scenarios where you currently use
empty()to verify that a value is not an emptyarray?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 orstaticproperty with an emptyarrayand 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 anarray, add anarrayproperty declaration and compare the property value with an emptyarray.<?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 emptyarrayand 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 anarray, add anarrayparameter type declaration and compare the parameter with an emptyarray.<?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 emptyarrayand 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 valuesInstead of using
empty()to verify the return value at the call site when the return value can assume an emptyarray, add anarrayreturn type declaration and compare the return value with an emptyarray.<?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()returnstruewhen the argument is aboolwith the valuefalse.<?php declare(strict_types=1); $value = false; var_dump(empty($value)); // (bool)trueWhat are scenarios where you currently use
empty()to verify that a value is not aboolwith the valuefalse?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 orstaticproperty withfalseand 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 abool, add aboolproperty 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 withfalseand 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 abool, add aboolparameter 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
boolwith the valuefalse.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 withfalseand 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 valuesInstead of using
empty()to verify the return value at the call site when the return value can assume abool, add aboolreturn 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()returnstruewhen a variable is afloatwith the value0.0or-0.0.<?php declare(strict_types=1); $value = 0.0; var_dump(empty($value)); // (bool)trueWhat are scenarios where you currently use
empty()to verify that a value is not afloatwith the value0.0or-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 orstaticproperty with0.0or-0.0and 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 afloat, add afloatproperty declaration and compare the instance orstaticproperty with0.0or-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
floatwith the value0.0or-0.0.Instead of using
empty()to verify the parameter value when the parameter can assume multiple types, compare the parameter with0.0or-0.0and 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 afloat, add afloatparameter type declaration and compare the parameter with0.0or-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
floatwith the value0.0or-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 with0.0or0.0and 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 valuesInstead of using
empty()to verify the return value at the call site when the return value can assume afloat, add afloatreturn type declaration and compare the return value with0.0or-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()returnstruewhen the argument is anintwith the value0.<?php declare(strict_types=1); $value = 0; var_dump(empty($value)); // // (bool)trueWhat are scenarios where you currently use
empty()to verify that a value is not anintwith the value0?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 orstaticproperty with0and 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 anint, add anintproperty declaration and compare the instance orstaticproperty with0.<?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
intwith the value0.Instead of using
empty()to verify the parameter value when the parameter can assume multiple types, compare the parameter with0and 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 anint, add anintparameter type declaration and compare the parameter with0.<?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
intwith the value0.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 with0and 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 valuesInstead of using
empty()to verify the return value at the call site when the return value can assume anint, add anintreturn type declaration and compare the return value with0.<?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()returnstruewhen the argument is astringwith the value''.<?php declare(strict_types=1); $value = ''; var_dump(empty($value)); // // (bool)true
empty()also returnstruewhen the argument is astringwith the value'0'.<?php declare(strict_types=1); $value = '0'; var_dump(empty($value)); // // (bool)trueWhat are scenarios where you currently use
empty()to verify that a value is not astringwith 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 orstaticproperty 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 astring, add astringproperty declaration and compare the instance orstaticproperty 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
stringwith 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 astring, add astringparameter 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
stringwith 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 valuesInstead of using
empty()to verify the return value at the call site when the return value can assume astring, add astringreturn 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()returnstruewhen the argument is an instance ofSimpleXMLElementconstructed 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)trueWhat are scenarios where you currently use
empty()to verify whether aSimpleXMLElementis 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?
Just published: Avoiding empty() in PHP.
— Andreas Möller (@localheinz) May 10, 2023
↓https://t.co/tjOcGWBrxyDo you need help with your PHP project?