November 9, 2022

Strict types in PHP, why they are so important, and why empty() should be avoided.

Being clear about what types you're handling in your application increases the predictability and creates code that is less prone to bugs.

In this article, I’m going to write about strict types in PHP and how they can help you write more readable code with fewer bugs.

PHP has come a long way since the first version was released back in 1995. With the introduction of strict types in PHP 7, it has evolved into a more robust scripting language than the one you might be used to hacking around in WordPress years ago.

Loosely-typed comparison

When comparing values using ==, PHP operates on a “best-guess” principle, which essentially means that PHP changes the two values you’re comparing into the same type to determine whether the two values are equal.

0 == 'test' // true in PHP < 8
// This is equal because PHP converted 'test' to an integer value, which returned 0, which is equal to 0.

To avoid unexpected behavior when PHP is doing its type juggling, it’s important to always use strict comparison === when comparing values.

0 === 'test' // false

I believe that some of the bad reputations that PHP has gotten over the years are because comparisons like these don’t make sense if you don’t know what’s going on. Although, PHP is constantly evolving and I feel that especially in recent versions they make strong efforts to address these kinds of unexpected behaviors to improve the language.

Strict types and empty()

The introduction of strict types in PHP is in my opinion the best thing that could ever be introduced to the scripting language and it made me love the language even more. As mentioned, even though PHPs type juggling has improved a lot in recent versions, it’s still better to be specific about types in your code.

PHP has quite a few convenience functions like empty(). It takes many different types of data and checks if it is “empty” or not. It is the go-to function for many PHP developers out there. However, I think a majority of them (including me) are not 100% sure about when it returns true or false. This makes, using empty potentially worse than using loosely-typed comparisons in your code.

if (empty($value)) {
    // '0'
    // '0.0000'
    // null
}

As far as I’m concerned, '0' is not an empty string and null should not be allowed to be passed since it can’t be said to be “empty”. I wrote a blog article going into more depth on this [here], check it out if you like.

Be clear about your types

Bugs are often introduced by not knowing or being clear about what type of data that is being passed around in your application. We’ve covered issues with loosely-typed comparisons and issues you could get with generic functions like empty() that are not specific to a certain type of data.

However, in conditional statements, it’s common to not do strict comparisons. That is, the three equals are often omitted. As long as you’re clear about what type you’re handling it’s ok to use loosely-typed comparisons in these cases.

if (strlen($value) === 0) {} // this is often
if (strlen($value)) {} // written like this

Once we know what type our data is we can use PHP’s own functions strlen(), count(), .. for checking if a variable has a value. These functions have a clear return type which allows us to do loosely-typed comparisons without introducing any unpredictable behavior with type juggling.

if (strlen($string)) {} // strlen only returns integer
if (count($array)) {} // count only returns integer
if ($integer) {}
if ($boolean) {}

Just be cautious with integer values since negative values also return true.

if ($integer) {}     // -1 [..] and +1 [..] return true
if (! $integer) {}   // only 0
if ($integer > 0) {} // only positive numbers

Strict types in function declarations

In this last section, I’d like to show some examples of how strict type declarations can greatly improve your code quality. Not using type declarations could lead to a lot of uncertainties like in the following example:

function isEmpty($value) {
    // what type is value?
    // what PHP functions can I call on this value?
    if (empty($value)) {
        // what values will trigger this block of code?
    }
    // return type not defined which can introduce
    // bugs in other code using this function
}

At the same time, doing type checks inside your function body increases a lot of overhead and makes the function body bloated.

function isEmptyTake2($value) {
    switch (gettype($value)) {
        case 'string':
            return ! strlen($value);
        case 'integer':
            return $value === 0;
        case 'array':
            return ! count($value);
        case 'boolean':
            return ! $value;
        default:
            return empty($value);
    }
}

It’s better to use strict types and be clear about the data going in and out of your function. This way we don’t have to check for what type we’re handling in our code.

function isStringEmpty(string $value): bool {
    return ! strlen($value);
}

You could also allow null values by doing something along the lines of:

function stringOrNull(?string $value): ?string {
    if (is_null($value)) {
        return null;
    }
    return $value;
}

I hope this article was helpful and that it might act as a motivator for someone to use more strict types in their code in the future.

Until next time, have a good one!

Oliver Lundquist

Born in 🇸🇪, resident in 🇯🇵 for 15+ yrs, husband and father of a daughter and son, web developer since 2009.

Read more about me
• mail@oliverlundquist.com• Instagram (@olibalundo)