Using isset() and empty() hurts your code
Tags: php
Published: 2019-06-08 18:34:03

---
title: Using isset() and empty() hurts your code
published: true
description: These functions tap really well in to the weakly typed and procedural side of PHP. If you've already been riding the wave of modern PHP for a while, you've learned to hate that side of PHP.
tags: php
---
It's a known flaw in PHP that functions in standard library are inconsistent. Some of them have a terrible API that may return anything from object to null and false. Some of them take arguments by reference, like for instance array sorting functions do. In short there's quite a bit features that work in a complicated fashion that makes the code worse.

A part of being a good programmer is to identify features in the language that hurt the code, the software we're building. Once those things are identified, a good programmer will minimize the impact of using those features or completely avoid using them if possible.

So, i've been stumbling lately quite a lot on `isset` and `empty` functions. These functions tap really well in to the weakly typed and procedural side of PHP. If you've already been riding the wave of modern PHP for a while, you've learned to hate that side of PHP.

### isset

isset function in [PHP docs](https://www.php.net/manual/en/function.isset.php):

> isset — Determine if a variable is declared and is different than NULL"

And by example:

```bash
php > var_dump(isset($foo));
bool(false)

php > $foo = 'bar';
php > var_dump(isset($foo));
bool(true)

php > $foo = null;
php > var_dump(isset($foo));
bool(false)

php > $list = ['foo' => 'bar'];
php > var_dump(isset($list['baz']));
bool(false)

php > var_dump(isset($list['foo']));
bool(true)
```

Looking at the definition in PHP docs we can see that the function does two things.

1. Determine if variable is declared
2. Determine if variable is different than null

On the example there's three usages that i've witnessed as use cases for `isset`. Null checking and testing that variable is declared, but also checking if array has a key. First two match the definition in PHP docs and the third is just something developers have started using.

Now, the thing why using `isset` should be avoided is exactly the thing it promises. It does **two things**. This causes confusion to the reader. 

We don't know if a variable is declared? How did this happen?

Either we are doing something very clever, which as we know is an awful habit, or we are working with a completely procedural legacy system.

If we do know that the variable is declared, then the only reason we'd use `isset` is for null checking. And for that, i'd recommend explicit null checks. In this code you obviously see easily where `$customer` came without any mutations. IRL there could be a lot more logic between these statements.

```php
$customer = Customer::find($customerId);
if ($customer === null) {
    // throw
}
```

An alternative using the same solution that would be even more explicit and clear to the reader would be to null check with `instanceof`. In this case we know exactly what we're dealing with when we work with `$customer`.

```php
$customer = Customer::find($customerId);
if (!$customer instanceof Customer) {
    // throw
}
```

Third situation i've seen, and personally used quite a lot, is checking if an array has a specific key:

```php
if (isset($payload['username'])) {}
```

This is also another case where there's already a more explicit way to handle the problem. 

```php
if (array_key_exists('username', $payload')) {}
```

I admit, using `isset` is not the most serious offense. But it still is usually used for either null checking or testing if variable is declared, almost never for both. Being more explicit in these cases will help readers of your code.


### empty

empty function in [PHP docs](https://www.php.net/manual/en/function.empty.php):

>  Returns FALSE if var exists and has a non-empty, non-zero value. Otherwise returns TRUE.
>
> The following values are considered to be empty:
>    "" (an empty string)
>    0 (0 as an integer)
>    0.0 (0 as a float)
>    "0" (0 as a string)
>    NULL
>    FALSE
>    array() (an empty array)

Wow, so i might be dealing with a string, integer, float, boolean, null or an array. If `isset` was bad for doing two things, `empty` is a completely different beast. It does **seven things**! It answers to seven different questions.

First step to resolve the problem is to use type hints everywhere. Use argument type hints and return type hints in **every** method and function you write. *NOTE: This applies to those developers who work with PHP7 or higher.*

Second step is to use more explicit conditionals. This means that the readers of the code don't have to dig deep to boring details when trying to understand what you're trying to accomplish. Using strict comparison (`===`) is very important.

```php
if (strlen($foo) === 0) {} // Clearly a string
if ($foo === 0) {} // Clearly a number
if ((int) $foo === 0) {} // Clearly expected to be treated as a number
if ($foo === false) {} // Clearly a boolean
if (count($foo) === 0) {} // Clearly an array
if ($foo === null) {} // Clearly null or something useful
```

As a side note, using return type hints is a very good practice. It will result to more clear interfaces. Once you have a return type hint in place, there's no way you can return multiple different things from your method (assuming you don't use nullable type hints, as you should not).

Are you ready to throw `isset()` and `empty()` in to history's trash bin? Tell me what you think!