WordPress and PHP 8.4: what changes for plugins and themes

By CompatNav · Published · Last reviewed · 10 min read

Short answer

WordPress 6.7 and later work with PHP 8.4; your plugins and theme decide whether your site does. Of the PHP 8.4 changes you’re most likely to meet in plugins and themes, most only log deprecation notices, such as “Implicitly marking parameter … as nullable is deprecated”, and the code keeps working. Two can stop code: extensions that are no longer part of PHP (IMAP, OCI8, PDO_OCI, PSpell) and five removed MySQLi constants. php.net lists the full set of changes. Check before you switch.

Is WordPress ready for PHP 8.4?

PHP 8.4.0 was released on 21 November 2024. It gets bug fixes until 31 December 2026 and security fixes until 31 December 2028 (php.net).

The WordPress core team’s compatibility page lists WordPress 6.7 and later as compatible with PHP 8.4. That covers WordPress itself; the PHP 8.4 changes below concern the code of your plugins and theme.

The short version

The PHP 8.4 changes you’re most likely to meet in plugins and themes. php.net lists the full set of backward incompatible changes and deprecated features.

What changes in PHP 8.4What happensKind
IMAP, OCI8, PDO_OCI and PSpell no longer part of PHPCode using them stops, unless your host installs them separatelyBreaks
Five MySQLi constants removedFatal errorBreaks
Parameter with a null default but no ? in its typeDeprecation notice, code keeps workingNotice
trigger_error() with E_USER_ERROR, CSV functions without the escape argument, E_STRICT, lcg_value() and some mysqli_* functions, session_set_save_handler() with separate functions, a class named _, changed return types of built-in methodsDeprecation notice, code keeps workingNotices

What can stop the code

A fatal error stops PHP at that point: the page isn’t built, and WordPress shows its critical-error message. Of the PHP 8.4 changes you’re most likely to meet in plugins and themes, two can cause one.

Extensions that are no longer part of PHP

IMAP, OCI8, PDO_OCI and PSpell “have been moved to PECL and are no longer part of the PHP distribution” (php.net). A plugin that uses PHP’s IMAP functions to read email, for example, works on PHP 8.4 only if your host installs that extension separately. That is a server setting, not something a check of your code can see, so ask your host before you switch.

Five removed MySQLi constants

PHP 8.4 removed five MySQLi constants that were never usable or only an alias, for example: “The unused MYSQLI_TYPE_INTERVAL constant, which is currently a stub and an alias for MYSQLI_TYPE_ENUM, has been removed.” (php.net) Code that still names one of them stops on PHP 8.4:

Code: before and after

wp-content/plugins/table-viewer/includes/columns.php

<?php
$field_type = 247; // the type number MySQL reports for a column
if ($field_type === MYSQLI_TYPE_INTERVAL) {
    echo "List column\n";
}
echo "Done\n";

Output on PHP 8.3.35

List column
Done

Output on PHP 8.4.26the script stopped

Fatal error: Uncaught Error: Undefined constant "MYSQLI_TYPE_INTERVAL" in /var/www/html/wp-content/plugins/table-viewer/includes/columns.php:3
Stack trace:
#0 {main}
  thrown in /var/www/html/wp-content/plugins/table-viewer/includes/columns.php on line 3
The constant was an alias, so the fix is the name it stood for:

wp-content/plugins/table-viewer/includes/columns.php

<?php
$field_type = 247; // the type number MySQL reports for a column
if ($field_type === MYSQLI_TYPE_ENUM) {
    echo "List column\n";
}
echo "Done\n";

Output on PHP 8.4.26

List column
Done

This is how a check reports it in a plugin:

What only shows notices: the deprecations you’re most likely to meet

A deprecation notice means: this still works, but a future PHP version is expected to change it. The page is built as usual, and the notice goes to the error log, or onto the page if displaying errors is switched on, which should never be the case on a live site (how to keep them off your pages).

PHP 8.4 deprecates more than these eight; php.net lists the full set in its deprecated features in PHP 8.4.

Parameters with a null default but no ?

The notice reads “Implicitly marking parameter … as nullable is deprecated”. php.net explains: “A parameter’s type is implicitly widened to accept null if the default value for it is null.” (php.net) The fix is to say it in the type with ?, which works since PHP 7.1. The notice appears as soon as the file is compiled, even if the function is never called.

Code: before and after

wp-content/plugins/price-format/includes/format.php

<?php
function format_price(float $amount, string $currency = null): string {
    return number_format($amount, 2) . ' ' . ($currency ?? 'EUR');
}

echo format_price(19.9), "\n";

Output on PHP 8.3.35

19.90 EUR

Output on PHP 8.4.26

Deprecated: format_price(): Implicitly marking parameter $currency as nullable is deprecated, the explicit nullable type must be used instead in /var/www/html/wp-content/plugins/price-format/includes/format.php on line 2
19.90 EUR

wp-content/plugins/price-format/includes/format.php

<?php
function format_price(float $amount, ?string $currency = null): string {
    return number_format($amount, 2) . ' ' . ($currency ?? 'EUR');
}

echo format_price(19.9), "\n";

Output on PHP 8.4.26

19.90 EUR

trigger_error() with E_USER_ERROR

“Calling trigger_error() with error_level being equal to E_USER_ERROR is now deprecated. Such usages should be replaced by either throwing an exception, or calling exit(), whichever is more appropriate.” (php.net)

Code: before and after

wp-content/plugins/licence-check/licence-check.php

<?php
function check_licence($key) {
    if ($key === '') {
        trigger_error('A licence key is required.', E_USER_ERROR);
    }
    return true;
}

check_licence('');

Output on PHP 8.3.35the script stopped

Fatal error: A licence key is required. in /var/www/html/wp-content/plugins/licence-check/licence-check.php on line 4

Output on PHP 8.4.26the script stopped

Deprecated: Passing E_USER_ERROR to trigger_error() is deprecated since 8.4, throw an exception or call exit with a string message instead in /var/www/html/wp-content/plugins/licence-check/licence-check.php on line 4

Fatal error: A licence key is required. in /var/www/html/wp-content/plugins/licence-check/licence-check.php on line 4
With an exception, the code that calls the function can decide what to show:

wp-content/plugins/licence-check/licence-check.php

<?php
function check_licence($key) {
    if ($key === '') {
        throw new InvalidArgumentException('A licence key is required.');
    }
    return true;
}

try {
    check_licence('');
} catch (InvalidArgumentException $e) {
    echo 'Licence problem: ', $e->getMessage(), "\n";
}

Output on PHP 8.4.26

Licence problem: A licence key is required.

CSV functions without the escape argument

“Using the default value for the escape parameter for the fputcsv(), fgetcsv(), and str_getcsv() is now deprecated. It must be passed explicitly either positionally or via named arguments.” (php.net) Passing today’s default, a backslash, keeps the output exactly as it was.

Code: before and after

wp-content/plugins/order-export/order-export.php

<?php
$file = fopen('php://memory', 'r+');
fputcsv($file, ['Order', 'Total']);
rewind($file);
echo stream_get_contents($file);

Output on PHP 8.3.35

Order,Total

Output on PHP 8.4.26

Deprecated: fputcsv(): the $escape parameter must be provided as its default value will change in /var/www/html/wp-content/plugins/order-export/order-export.php on line 3
Order,Total

wp-content/plugins/order-export/order-export.php

<?php
$file = fopen('php://memory', 'r+');
fputcsv($file, ['Order', 'Total'], ',', '"', '\\');
rewind($file);
echo stream_get_contents($file);

Output on PHP 8.4.26

Order,Total

The E_STRICT constant

“Because the E_STRICT error level was removed, this constant is now deprecated.” (php.net) Old code often leaves it out of the error level; there is nothing left to leave out.

Code: before and after

wp-content/plugins/debug-helper/debug-helper.php

<?php
error_reporting(E_ALL & ~E_STRICT);
echo "Error level set\n";

Output on PHP 8.3.35

Error level set

Output on PHP 8.4.26

Deprecated: Constant E_STRICT is deprecated in /var/www/html/wp-content/plugins/debug-helper/debug-helper.php on line 2
Error level set

wp-content/plugins/debug-helper/debug-helper.php

<?php
error_reporting(E_ALL);
echo "Error level set\n";

Output on PHP 8.4.26

Error level set

lcg_value() and some mysqli_* functions

“lcg_value() is now deprecated, as the function is broken in multiple ways.” (php.net) The same page deprecates mysqli_ping(), mysqli_kill() and mysqli_refresh(), and xml_set_object().

Code: before and after

wp-content/plugins/random-quote/random-quote.php

<?php
$chance = lcg_value();
echo $chance < 1 ? "Random number OK\n" : "";

Output on PHP 8.3.35

Random number OK

Output on PHP 8.4.26

Deprecated: Function lcg_value() is deprecated since 8.4, use \Random\Randomizer::getFloat() instead in /var/www/html/wp-content/plugins/random-quote/random-quote.php on line 2
Random number OK

wp-content/plugins/random-quote/random-quote.php

<?php
$chance = mt_rand() / mt_getrandmax();
echo $chance <= 1 ? "Random number OK\n" : "";

Output on PHP 8.4.26

Random number OK

session_set_save_handler() with separate functions

“Calling session_set_save_handler() with more than two arguments is deprecated.” (php.net) The two-argument form takes an object that implements SessionHandlerInterface.

Code: before and after

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

<?php
session_set_save_handler(
    function ($path, $name) { return true; },
    function () { return true; },
    function ($id) { return ''; },
    function ($id, $data) { return true; },
    function ($id) { return true; },
    function ($max_lifetime) { return 0; }
);
echo "Session storage set\n";

Output on PHP 8.3.35

Session storage set

Output on PHP 8.4.26

Deprecated: session_set_save_handler(): Providing individual callbacks instead of an object implementing SessionHandlerInterface is deprecated in /var/www/html/wp-content/plugins/member-area/includes/sessions.php on line 2

Warning: session_set_save_handler(): Session save handler cannot be changed after headers have already been sent in /var/www/html/wp-content/plugins/member-area/includes/sessions.php on line 2
Session storage set
The second message on PHP 8.4 only appears because the first one was printed: once PHP has sent output, it can’t change the session storage any more. With messages written to a log instead of the page, only the deprecation notice appears and the session storage is set. The fix:

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

<?php
class Member_Session_Handler implements SessionHandlerInterface {
    public function open(string $path, string $name): bool { return true; }
    public function close(): bool { return true; }
    public function read(string $id): string|false { return ''; }
    public function write(string $id, string $data): bool { return true; }
    public function destroy(string $id): bool { return true; }
    public function gc(int $max_lifetime): int|false { return 0; }
}

session_set_save_handler(new Member_Session_Handler(), true);
echo "Session storage set\n";

Output on PHP 8.4.26

Session storage set

A class named _

“Naming a class _ is now deprecated” (php.net); names that only start with an underscore are fine.

Code: before and after

wp-content/themes/tiny-theme/inc/translate.php

<?php
class _ {
    public static function text($text) {
        return $text;
    }
}

echo _::text('Read more'), "\n";

Output on PHP 8.3.35

Read more

Output on PHP 8.4.26

Deprecated: Using "_" as a class name is deprecated since 8.4 in /var/www/html/wp-content/themes/tiny-theme/inc/translate.php on line 2
Read more

wp-content/themes/tiny-theme/inc/translate.php

<?php
class Tiny_Theme_Text {
    public static function text($text) {
        return $text;
    }
}

echo Tiny_Theme_Text::text('Read more'), "\n";

Output on PHP 8.4.26

Read more

Changed return types of built-in methods

A plugin class that extends a PHP class and overrides one of its methods must promise a compatible return type. PHP 8.4 narrowed some of these types, for example DateTime::modify(): “Now has a tentative return type of DateTime. Previously it was DateTime|false.” (php.net) An override written for PHP 8.3 then gets a notice.

Code: before and after

wp-content/plugins/event-calendar/includes/class-event-date.php

<?php
class Event_Date extends DateTime {
    public function modify(string $modifier): DateTime|false {
        return parent::modify($modifier);
    }
}

$date = new Event_Date('2026-05-01');
echo $date->modify('+1 day')->format('j F Y'), "\n";

Output on PHP 8.3.35

2 May 2026

Output on PHP 8.4.26

Deprecated: Return type of Event_Date::modify(string $modifier): DateTime|false should either be compatible with DateTime::modify(string $modifier): DateTime, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/html/wp-content/plugins/event-calendar/includes/class-event-date.php on line 3
2 May 2026
The fix is the new return type, DateTime. It is narrower than the old one, which PHP allows, so the same code runs without a notice from PHP 8.0 to 8.5. (The #[\ReturnTypeWillChange] attribute the message names only silences the notice.)

wp-content/plugins/event-calendar/includes/class-event-date.php

<?php
class Event_Date extends DateTime {
    public function modify(string $modifier): DateTime {
        return parent::modify($modifier);
    }
}

$date = new Event_Date('2026-05-01');
echo $date->modify('+1 day')->format('j F Y'), "\n";

Output on PHP 8.0.30

2 May 2026

Output on PHP 8.3.35

2 May 2026

Output on PHP 8.4.26

2 May 2026

Output on PHP 8.5.11

2 May 2026

What no code reader can see

Some PHP 8.4 changes depend on the values a plugin works with while it runs. For example, raising zero to a negative power “corresponds to dividing by 0 … Thus, this behavior has been deprecated.”, and “Passing invalid options to hash functions is now deprecated.” (php.net) Whether that happens depends on the data, so look at your error log for a few days after switching.

How to check your site before switching

  1. Find your current PHP version: Tools → Site Health → Info → Server.
  2. Ask your host whether the extensions your plugins use (for example IMAP) stay available on PHP 8.4.
  3. Check your plugins and theme for PHP 8.4. With CompatNav, choose PHP 8.4 next to Check readiness for and start a scan: each plugin and theme gets its own list, with what will break and what only shows notices.
  4. Fix what will break before you switch: update, ask the developer, or replace (what to do). Notices can wait for the plugins’ next updates.
  5. Switch, then check your important pages and your error log.

PHP 8.5 changes more; if your host offers it, compare with WordPress and PHP 8.5: what breaks, what only shows notices. Which version to choose and until when each gets security fixes: PHP 8.2 reaches end of life on 31 December 2026.

Key takeaways

  • PHP 8.4 was released on 21 November 2024 and gets security fixes until 31 December 2028 (php.net). WordPress 6.7 and later are compatible with it.
  • One of the notices reads “Implicitly marking parameter … as nullable is deprecated”: a parameter with a null default and no ? in its type. The code keeps working.
  • Two changes can stop code: extensions moved out of PHP (ask your host whether it still installs them) and five removed MySQLi constants.
  • Of the changes you’re most likely to meet in plugins and themes, seven others only log deprecation notices. php.net lists the full set.
  • Some changes depend on values while the code runs, which no code check can see: look at your error log after switching.

Frequently asked questions

Is PHP 8.4 stable for WordPress?

PHP 8.4 is a regular release: 8.4.0 came out on 21 November 2024; it gets bug fixes until 31 December 2026 and security fixes until 31 December 2028 (php.net). The WordPress core team lists WordPress 6.7 and later as compatible with it. Whether your site is ready depends on your plugins and theme.

PHP 8.3 or PHP 8.4?

If your plugins and theme are ready for both, 8.4: its security fixes run a year longer (until 31 December 2028, against 31 December 2027 for 8.3). PHP 8.5 runs longer still, until 31 December 2029.

What does “Implicitly marking parameter … as nullable is deprecated” mean?

A function in a plugin accepts null for a parameter only because its default value is null, without saying so in the type. PHP 8.4 still accepts it and adds this notice. The fix is one character in the code: a ? before the type. The plugin’s developer makes it; you update the plugin.

Do I have to fix the notices before switching?

No. Deprecation notices don’t stop your site. Fix what will break before you switch; notices can wait for the plugins’ next updates.

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.