Categories
How To

The Importance of Correct Casting

When upgrading to PHP 7.4 makes your plugins throw errors about string offsets and broken arrays, we have to read the notes.

So PHP 7.4 is rolling out, WP is hawking it, and you know your code is fine so you don’t worry. Except you start getting complaints from your users! How on earth could that be!? Your plugin is super small and simple, why would this happen? You manage to get your hands on the error messages and it’s weird:

NOTICE: PHP message: PHP Warning:  Illegal string offset 'TERM' in /wp-content/plugins/foobar/search-form.php on line 146
NOTICE: PHP message: PHP Warning:  ksort() expects parameter 1 to be array, string given in /wp-content/plugins/foobar/search-form.php on line 151
NOTICE: PHP message: PHP Warning:  Invalid argument supplied for foreach() in /wp-content/plugins/foobar/search-form.php on line 153
NOTICE: PHP message: PHP Warning:  session_start(): Cannot start session when headers already sent in /wp-content/plugins/foobar/config.php on line 12

But that doesn’t make any sense because your code is pretty straight forward:

[...]

$name_array = '';
if( !empty( $term_children )){
    foreach ( $term_children as $child ) {
        $term = get_term_by( 'id', $child, $taxonomy );

        $name_array[ $term->name ]= $child;
    }

    if( !empty( $name_array ) ){

        ksort( $name_array );

        foreach( $name_array as $key => $value ) {
    [...]

Why would this be a problem?

Error 1: Illegal string offset Error 1: Illegal string offset

The first error we see is this:

Illegal string offset 'TERM' in /wp-content/plugins/foobar/search-form.php on line 146

We’re clearly trying to save things into an array, but the string offset actually means you’re trying to use a string as an array.

An example of how we might force this error is as follows:

$fruits_basket = array(
    'persimmons' => 1, 
    'oranges'    => 5,
    'plums'      => 0,
);

echo $fruits_basket['persimmons']; // echoes 1

$fruits_basket = "a string";

echo $fruits_basket['persimmons']; // illegal string offset error
$fruits_basket['peaches'] = 2; // this will also throw the same error in your logs

Simply, you cannot treat a string as an array. Makes sense, right? The second example (peaches) fails because you had re-set $fruits_basket to a string, and once it’s that, you have to re-declare it as an array.

But with our error, we can see line 146 is $name_array[ $term->name ]= $child; and that should be an array, right?

Well. Yes, provided $name_array is an array. Hold on to that. Let’s look at error 2.

Top ↑

Error 2: ksort expects an array Error 2: ksort expects an array

The second error is that the function wanted an array and got a string:

NOTICE: PHP message: PHP Warning: ksort() expects parameter 1 to be array, string given in /wp-content/plugins/foobar/search-form.php on line 151

We use ksort() to sort the order of an array and here it’s clearly telling us “Buddy, $name_array isn’t an array!” Now, one fix here would be to edit line 149 to be this:

if( !empty( $name_array ) && is_array( $name_array ) ){

That makes sure it doesn’t try to do array tricks on a non-array, but the question is … why is that not an array to begin with? Hold on to that again, we want to look at the next problem…

Top ↑

Error 3: Invalid argument Error 3: Invalid argument

Now that we’ve seen the other two, you probably know what’s coming here:

NOTICE: PHP message: PHP Warning: Invalid argument supplied for foreach() in /wp-content/plugins/foobar/search-form.php on line 153

This is foreach() telling us that the argument you passed isn’t an array. Again.

Top ↑

What Isn’t An Array? What Isn’t An Array?

We’re forcing the variable $name_array to be an array on line 146. Or at least we thought we were.

From experience, using $name_array[KEY] = ITEM; was just fine from PHP 5.4 up through 7.3, but as soon as I updated a site to 7.4, I got that same error all over.

The issue was resolved by changing line 141 to this: $name_array = array();

Instead of defaulting $name_array as empty with'', I used the empty array() which makes it an array.

An alternative is this: $name_array = (array) '';

This casts the variable as an array. Since the array is meant to be empty here, it’s not really an issue either way.

Top ↑

Backward Incompatibility Backward Incompatibility

Where did I learn this? I read the PHP 7.4 migration notes, and found it in the backward incompatibility section.

Array-style access of non-arrays

Trying to use values of type nullboolintfloat or resource as an array (such as $null["key"]) will now generate a notice.

The lesson here is that PHP 7.4 is finally behaving in a strict fashion, which regards to data types. Whenever a non-array variable is being used like an array, you get an error because you didn’t say “Mother May I…” it was an array.

Whew.

Now to be fair, this was a warning previously, but a lot of us (hi) missed it.

So. Since WordPress is pushing PHP 7.4, go check all your plugins and themes for that and clean it up. Declare an array or break.

Oh and that last error? Headers already sent? Went away as soon as we fixed the variable.