Emergency WordPress Hack Cleanup?
sales@inceptusdigital.com |
← Back to Insights

WordPress Broke After a PHP 8 Upgrade? How to Fix the Fatal Errors

By Sameer Hussain WordPress
WordPress Broke After a PHP 8 Upgrade? How to Fix the Fatal Errors

Your host emailed that PHP 7.4 is end-of-life, you switched to PHP 8, and the site broke. Maybe it's a critical error, maybe a blank page, maybe just one feature that stopped working. WordPress core itself runs fine on PHP 8. What breaks is old theme and plugin code. Here's how to find that code and fix it, using the actual error messages you'll see.

Get the site back first. In your hosting panel (cPanel "Select PHP Version", Plesk "PHP Settings", or your host's dashboard), switch back to the previous PHP version. That restores the site in seconds. Then fix the code on a staging copy and switch PHP there first.

Step 1: Find every incompatible line

Don't fix errors one at a time as they show up in production. Scan the code upfront.

Scan with PHPCompatibility

PHPCompatibilityWP is a ruleset for PHP_CodeSniffer that flags code that won't run on a target PHP version. On your local machine or staging server:

mkdir php-scan && cd php-scan
composer require --dev dealerdirect/phpcodesniffer-composer-installer phpcompatibility/phpcompatibility-wp:"*"

# Scan a theme for PHP 8.0 and newer
vendor/bin/phpcs -p /path/to/wp-content/themes/my-theme \
  --standard=PHPCompatibilityWP \
  --extensions=php \
  --runtime-set testVersion 8.0-

Composer will ask whether to allow the dealerdirect/phpcodesniffer-composer-installer plugin. Answer yes. Run the scan against your theme, child theme and every plugin that isn't from a major, actively maintained vendor. Errors (marked ERROR) will break on PHP 8. Warnings are deprecations that still run but should be fixed.

Then confirm on staging with the debug log

Static scans miss some runtime problems, like type errors that depend on data. Switch staging to PHP 8, enable WP_DEBUG_LOG (see the critical error guide for the exact lines), click through every important page and form, and read wp-content/debug.log.

Step 2: Fix the common fatal errors

These are the PHP 8.0 changes behind most broken WordPress sites I see.

create_function() removed

PHP Fatal error: Uncaught Error: Call to undefined function create_function()

Very common in old widgets and theme option panels. Replace it with an anonymous function:

// Before (PHP 7 and older)
add_action( 'widgets_init', create_function( '', 'return register_widget("My_Widget");' ) );

// After
add_action( 'widgets_init', function () {
    register_widget( 'My_Widget' );
} );

each() removed

PHP Fatal error: Uncaught Error: Call to undefined function each()
// Before
while ( list( $key, $value ) = each( $array ) ) { /* ... */ }

// After
foreach ( $array as $key => $value ) { /* ... */ }

count() on null or a string

PHP Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given

PHP 7.2 gave a warning here. PHP 8 throws an error. It usually happens when get_option() or get_post_meta() returns an empty value instead of an array:

// Before
if ( count( $items ) > 0 ) { /* ... */ }

// After
if ( is_array( $items ) && count( $items ) > 0 ) { /* ... */ }
// or simply
if ( ! empty( $items ) ) { /* ... */ }

Unsupported operand types

PHP Fatal error: Uncaught TypeError: Unsupported operand types: string + int

PHP 8 throws on arithmetic with arrays or with strings that aren't numbers. Typical WooCommerce example: a price meta field that is empty ('') being added to a number. Cast and validate:

// Before
$total = $price + $shipping;

// After
$total = (float) $price + (float) $shipping;

Casting hides bad data, so if the value should never be empty, also find out why it is.

Curly-brace string offsets

PHP Fatal error: Array and string offset access syntax with curly braces is no longer supported
// Before
$first = $string{0};

// After
$first = $string[0];

Other removed functions

  • get_magic_quotes_gpc(): remove the check entirely. Magic quotes haven't existed since PHP 5.4.
  • money_format(): use number_format() or the NumberFormatter class from the intl extension.
  • ereg(), split(), mysql_*(): removed long before PHP 8. If you still find these, the code is very old and needs a careful review, not just a patch.

Step 3: Fix deprecations before they become errors

Deprecations still run, but they fill your logs and turn into fatal errors in later versions. The ones I see most in WordPress code:

Optional parameter before a required one (PHP 8.0)

Deprecated: Optional parameter $args declared before required parameter $id is implicitly treated as a required parameter
// Before
function my_render( $args = array(), $id ) { }

// After: reorder, or make both optional
function my_render( $id, $args = array() ) { }

If other code calls this function, update those calls too.

Creation of dynamic properties (PHP 8.2)

Deprecated: Creation of dynamic property My_Plugin::$settings is deprecated

Declare the property in the class:

class My_Plugin {
    public $settings = array(); // declare it

    public function __construct() {
        $this->settings = get_option( 'my_plugin_settings', array() );
    }
}

For classes that really need arbitrary properties, add the #[\AllowDynamicProperties] attribute above the class as a stop-gap.

${var} string interpolation (PHP 8.2)

// Deprecated
echo "Hello ${name}";

// Fine
echo "Hello {$name}";

Implicitly nullable parameters (PHP 8.4)

// Deprecated in 8.4
function my_func( WP_Post $post = null ) { }

// Fine
function my_func( ?WP_Post $post = null ) { }

Step 4: Third-party plugins you can't edit

If the incompatible code is in a plugin you didn't write:

  1. Update it. Most maintained plugins fixed PHP 8 issues years ago.
  2. Check if it's abandoned. On WordPress.org, a plugin page that says "This plugin hasn't been tested with the latest 3 major releases of WordPress" is a red flag. Look for a maintained alternative.
  3. Patch it as a last resort, and keep it from being overwritten by an update. I cover the update-safe way to do this in how to customize a plugin without losing changes.

Step 5: Switch production safely

  1. Fix everything on staging and run it on the target PHP version for a few days with the debug log on.
  2. Deploy the code changes to production while it's still on the old PHP version. All the fixes above also run on PHP 7.4, as long as you don't add PHP 8-only functions like str_contains() along the way.
  3. Take a full backup, switch production to PHP 8, and watch the error log for an hour.
  4. If something breaks, switch PHP back. That's your instant rollback.

Done in that order, the upgrade becomes a non-event instead of an outage.

SH

Written by

Sameer Hussain, Senior Shopify & WordPress Engineer

Top Rated on Upwork and the Shopify & WordPress engineer at Inceptus Digital. Short on time, or not comfortable editing code? Send me your URL and the error on Upwork and I'll tell you how I'd fix it before you commit.

Share this article

Need Custom Engineering or Growth Systems?

Our team helps ambitious brands build bespoke software, AI integrations, and high-conversion platforms.

Chat on WhatsApp