Matt Watson

Let me live dangerously, PHP

Matt Watson

As a web developer who works mainly with PHP, I’ve been experiencing the excruciating slog that is upgrading from version 7.4 to 8+. Among the many backward incompatibilities, the one that keeps rearing its ugly head the most by far is the new E_WARNING for trying to access an undefined key on an array.

Previously, doing this would only cause an E_NOTICE. For example, given the following script:

<?php // Thanks, onlinephp.io!
$arr = ['foo', 'bar'];

var_dump($arr['baz']);

… PHP 7.4 returns NULL and throws “Notice: Undefined index: baz in /home/user/scripts/code.php on line 4”.

PHP 8+ also returns NULL but throws “Warning: Undefined array key "baz" in /home/user/scripts/code.php on line 4”.

The difference between a notice and a warning is not technically that different. In both scenarios the same thing is returned (NULL) and the script is not halted. However, many PHP frameworks do halt the script on warnings, even in production, and sometimes existing packages or one’s own code behave differently on warnings as opposed to notices. That plus the fact that accessing undefined array keys is ubiquitous in many PHP projects makes upgrading PHP a real chore.

That is why there are all these enraged commenters on Github. As the issue’s original poster, RichardNeill, says:

Rightly or wrongly, there are a lot of people, myself included, who have thousands of lines of code with things such as:

if ($_GET['xxx']) #where xxx may or may not exist.

or

if ($_GET['xxx'] == 'yyy')

and in PHP8, we now get flooded with warnings "Undefined array key", which get in the way of real warnings.

Migrating and testing this code will take a long time, and furthermore, it's really ugly and harder to read:

if (isset($_GET['xxx'))

or

if (isset($_GET['xxx']) && $_GET['xxx'] == 'yyy'))

to suppress this warning.

This has been my experience as well when working with my own code, legacy code, WordPress/WordPress plugins and various PHP packages. Especially in regards to if statements and PHP globals like $_GET or $_REQUEST. For example, here are a bunch of ugly fixes I had to make to update my home-brewed time tracker PHP app to version 8.

That project uses Fat Free Framework, which has access to a “HALT” setting I could set to false in order to stop the script from halting on warnings, but I may not want to do that on all warnings. And in my experience, writing a custom error handling function to suppress only this particular warning is easier said than done. Usually, it is overwritten by framework functions, or else my server or PHP ini settings could be getting in the way. I’m not really sure.

The easiest solution I have found is to replace references to possibly undefined array key indexes with the null coalescing operator:

<?php
// Example usage for: Null Coalesce Operator
$action = $_POST['action'] ?? 'default';

// The above is identical to this if/else statement
if (isset($_POST['action'])) {
    $action = $_POST['action'];
} else {
    $action = 'default';
}
?>

In any case, I wish PHP would just not raise undefined array keys to warnings. It is my inalienable right as a PHP/JavaScript developer to refer to variables that don't exist. I realize this is an incorrect opinion and that in the long haul, such changes might make PHP more stable, but I really just don’t care. I use PHP to get stuff done, not to have some kind of pure programming experience. PHP versions reach end-of-life status on a set schedule of every three years, and it just feels like by the time I’m caught up, it’s going to be time to upgrade again.

In the past (for example, migrating from 5 to 7) it seemed like there was far more focus on backward compatibility than there is now. I once upgraded a large project from 5.x to 7.4 in a few hours by just tweaking server settings and adding a couple of shims. I wish we could go back to those days.