How misusing empty() function can lead to subtle bugs

Even though you've been programming for a decade, even though you think you can write code flawlessly, even though you believe you've covered all cases in your test cases, even though you think you know PHP too well, there's always a possibility of a subtle bug slipping through the cracks.Here’s one such case.

We had a Laravel Job class that calculates CO2e emissions for a particular activity. It looks something like this:

class CalculateCarbonEmissions {
    //...
    function callApi() {
        //...
        if ($this->isSuccess($data)) {
            $this->saveEmissions($data);
$this->recalculateProjectEmissions();
            //...
        }
    }

    protected function isSuccess(array $data): bool {
        return isset($data['status']) && $data['status'] === 'success' && isset($data['co2e']);
    }

}

A successful API call usually returned a 200 response like this:

{
    "co2e": 12223.833,
    "status": "success",
    "co2e_unit": "kg"
}

Everything seemed fine, my code was working properly, the test cases were passing, and I was confident that nothing could go wrong. So, I happily pushed it to staging. Job done.

But then QA reported that the results were inaccurate, specifically that zero carbon emissions were not showing up correctly in the database. How could that be?! I started debugging. After two hours without a clue, I finally found the issue!

This was the API response when the carbon emission was zero:

{
    "co2e": 0,
    "status": "success",
    "co2e_unit": "kg"
}

Did you catch it? Did you find the cause of the bug? Did you? 🧐

Here's what happened: When the result was co2e = 0, the code we had: !empty($data['data']['co2e']) returned false, causing the isSuccess method to return false. Oops! According to the PHP documentation:

Code Example

See the problem? If the value is 0, !empty(0) will return false. It’s really a poorly named function, at least for some people like me. The fix was simple: remove the empty check and use a specific function like is_numeric, which was made purely for checking numeric values.

Like this:

protected function isSuccess(array $data): bool {
    return isset($data['status']) && $data['status'] === 'success' && isset($data['co2e']) && is_numeric($data['co2e']);
}

Problem solved! These kinds of issues are particularly tricky to spot and are a good example of why we PHP devs need to be careful about using !empty. I have to remember this again and again, or maybe I need a CoPilot to remind me whenever I use !empty() to write all scenarios or never use empty at all.

Thanks for reading, and remember, always be cautious with empty()! 🙂