“in_array(): Argument #2 ($haystack) must be of type array, null given”: the fix, and array_merge() too

By CompatNav · Published · Last reviewed · 4 min read

Short answer

It’s a fatal error (a TypeError) from PHP 8: a plugin or theme gives a built-in function such as in_array(), array_merge() or array_key_exists() something that isn’t a list, usually null or false from a setting that was never saved. PHP 7.4 warned and returned nothing, so the code went on with a wrong result; PHP 8 stops the page. Switch PHP back if the site is down, then update the plugin or theme, or send the message to its developer. The developer fix is to make sure the value is a list before the call.

The message, on PHP 7.4 and PHP 8

A members plugin checks whether editors may enter. The list of allowed roles was never saved, so it’s null:

wp-content/plugins/member-area/member-area.php

<?php
$allowed_roles = null; // the setting was never saved
if (in_array('editor', $allowed_roles)) {
    echo "Editors may enter\n";
}
echo "Checked\n";

Output on PHP 7.4.33

Warning: in_array() expects parameter 2 to be array, null given in /var/www/html/wp-content/plugins/member-area/member-area.php on line 3
Checked

Output on PHP 8.0.30the script stopped

Fatal error: Uncaught TypeError: in_array(): Argument #2 ($haystack) must be of type array, null given in /var/www/html/wp-content/plugins/member-area/member-area.php:3
Stack trace:
#0 /var/www/html/wp-content/plugins/member-area/member-area.php(3): in_array('editor', NULL)
#1 {main}
  thrown in /var/www/html/wp-content/plugins/member-area/member-area.php on line 3

On PHP 7.4 the check quietly failed: no “Editors may enter”, with a warning nobody reads. On PHP 8 the page stops.

Why PHP 8 stops

Before PHP 8, built-in functions handled a value of the wrong type their own way. The PHP 8.0 proposal behind this change describes it: “For internal functions, the behavior depends on multiple factors, but the default is to throw a warning and return null.” It proposed “to consistently generate TypeError exceptions for all invalid parameter types, regardless of whether the function is user-defined or extension-defined.” (wiki.php.net) That’s what PHP 8 does.

The same error from other functions

Any built-in function that expects a list reacts the same way. Two common ones in WordPress plugins:

array_merge() with false, typically defaults merged with a setting that was never saved. Note how PHP 8.3 says “false given” where 8.0 said “bool given”:

wp-content/plugins/member-area/settings.php

<?php
$defaults = ['color' => 'blue'];
$saved = false; // what get_option() returns for a setting never saved
$settings = array_merge($defaults, $saved);
echo 'Color: ', $settings['color'] ?? '(none)', "\n";

Output on PHP 7.4.33

Warning: array_merge(): Expected parameter 2 to be an array, bool given in /var/www/html/wp-content/plugins/member-area/settings.php on line 4
Color: (none)

Output on PHP 8.0.30the script stopped

Fatal error: Uncaught TypeError: array_merge(): Argument #2 must be of type array, bool given in /var/www/html/wp-content/plugins/member-area/settings.php:4
Stack trace:
#0 /var/www/html/wp-content/plugins/member-area/settings.php(4): array_merge(Array, false)
#1 {main}
  thrown in /var/www/html/wp-content/plugins/member-area/settings.php on line 4

Output on PHP 8.3.35the script stopped

Fatal error: Uncaught TypeError: array_merge(): Argument #2 must be of type array, false given in /var/www/html/wp-content/plugins/member-area/settings.php:4
Stack trace:
#0 /var/www/html/wp-content/plugins/member-area/settings.php(4): array_merge(Array, false)
#1 {main}
  thrown in /var/www/html/wp-content/plugins/member-area/settings.php on line 4

On PHP 7.4 the merge returned nothing, so even the default color was lost.

array_key_exists() on an object. Here the reason is a different PHP 8.0 change: “The ability to use array_key_exists() with objects has been removed.” (php.net)

wp-content/plugins/member-area/profile.php

<?php
$profile = new stdClass(); // an object, not an array
$profile->nickname = 'sam';
if (array_key_exists('nickname', $profile)) {
    echo "Nickname: {$profile->nickname}\n";
}

Output on PHP 7.4.33

Deprecated: array_key_exists(): Using array_key_exists() on objects is deprecated. Use isset() or property_exists() instead in /var/www/html/wp-content/plugins/member-area/profile.php on line 4
Nickname: sam

Output on PHP 8.0.30the script stopped

Fatal error: Uncaught TypeError: array_key_exists(): Argument #2 ($array) must be of type array, stdClass given in /var/www/html/wp-content/plugins/member-area/profile.php:4
Stack trace:
#0 {main}
  thrown in /var/www/html/wp-content/plugins/member-area/profile.php on line 4

Fix it, in order

  1. Site down? Switch PHP back or pause the plugin: the critical error guide.
  2. Find the plugin or theme from the path in the message.
  3. Open its settings page and save once: if the cause is a setting never saved, that can give it a value. Then update the plugin.
  4. Still there? Send the full message to its developer.

Find it before you switch

A code check can’t know that a setting will be empty on your site, so CompatNav reports none of the three examples above; we checked. Your PHP 7.4 log can: these are warnings there, which WordPress reports even with WP_DEBUG off (WordPress 7.1.2 source). Search your host’s PHP error log for “expects parameter” and “Expected parameter”: each line is a fatal error on PHP 8. The object case is a deprecation notice on 7.4, so it only shows with WP_DEBUG on (the WordPress debug log). Finding PHP notices on real visits covers doing this on a live site.

The same PHP 8 change is behind “count(): Argument #1 ($value) must be of type Countable|array” and “implode(): Argument #2 ($array) must be of type ?array”.

Key takeaways

  • PHP 7.4: “in_array() expects parameter 2 to be array, null given”, a warning, and a wrong result.
  • PHP 8: a TypeError, “in_array(): Argument #2 ($haystack) must be of type array, null given”, and the page stops.
  • Same pattern for array_merge(), array_key_exists(), implode() and other functions that expect a list.
  • PHP 8.0 to 8.2 say “bool given” for false; PHP 8.3 and later say “false given”.
  • Your PHP 7.4 error log already lists these as warnings: each one is a fatal error on PHP 8.

Frequently asked questions

Why did this work on PHP 7.4?

It didn’t really work. PHP 7.4 refused the value too, with a warning, and returned nothing; the code carried on with that. In our test, a role check silently failed and a list of settings came out empty. PHP 8 stops instead.

What does “null given” or “false given” tell me?

What the variable held instead of a list. In WordPress, false often comes from get_option() for a setting that was never saved, and null from something that wasn’t found.

And “array_key_exists(): Argument #2 ($array) must be of type array, stdClass given”?

The code checks a key on an object, which PHP 8.0 no longer allows (PHP 7.4 showed a deprecation notice). The fix is isset() or property_exists() on the object.

Check your own site before you upgrade

CompatNav is a free WordPress plugin. It reads the code of your plugins and themes on your own server and tells you, in plain words, what will break and what will only show notices on the PHP version you choose. It never changes your code. It can’t see problems that only appear while code runs with real data, so a quick check of your site after the upgrade still matters.

Get CompatNav on wordpress.org How it works

Sources

About the code examples: each output is the real output of the code shown, run with the official PHP builds, without a php.ini, with all errors reported and displayed. Only the file path was replaced by a neutral server path.