When empty is not empty original

by Freek Van der Herten – 2 minute read

Recently when I was working on a project I got some strange results when using the empty function. Here's what I was debugging. I've simplified the code a bit to share with you.

var_dump(
   $person->firstName, 
   empty($person->firstName)
);

This was the result:

string(5) "Freek"
bool(true)

That's really odd. How can a variable hold a string and be empty at the same time? Let's try a few other functions on $person->firstName:

var_dump(
   $person->firstName, 
   empty($person->firstName), 
   isset($person->firstName), 
   is_null($person->firstName)
);

The result:

string(5) "Freek"
bool(true) // empty
bool(true) // isset
bool(false) // is_null

isset and is_null behave like expected, only empty returns the wrong result.

Let's take a look at the implementation of the Person class.

class Person
{
    protected $attributes = [];

    public function __construct(array $attributes)
    {
        $this->attributes = $attributes;
    }

    public function __get($name)
    {
        return $this->attributes[$name] ?? null;
    }
}

Here you can see that the properties on a Person object are retrieved from the $attributes array using that magic __get() method.

When passing variables to normal functions $person->firstName will be evaluated first. The result of that evaluation will get passed to the function.

But empty is not a function, it's a language construct. So what you pass to it will not be evaluated first. When passing $person->firstName to it, it will check the contents of the firstName property on $person. Since that property doesn't exist, it will return false.

If you want to make the empty function work in this case you'll need to implement the magic __isset method.

class Person
{
    protected $attributes = [];

    public function __construct(array $attributes)
    {
        $this->attributes = $attributes;
    }

    public function __get($name)
    {
        return $this->attributes[$name] ?? null;
    }

    public function __isset($name)
    {
        $attribute = $this->$name;

        return !empty($attribute);
    }
}

When empty does it's job it'll use that magic method to determine the result.

Let's try dumping again:

var_dump(
   $person->firstName, 
   empty($person->firstName)
);

The new results:

string(5) "Freek"
bool(false)

Perfect!

Join 9,500+ smart developers

Get my monthly newsletter with what I learn from running Spatie, building Oh Dear, and maintaining 300+ open source packages. Practical takes on Laravel, PHP, and AI that you can actually use.

No spam. Unsubscribe anytime. You can also follow me on X.

Found something interesting to share? Submit a link to the community section.