This guide was first published on Swift Web Dev, where it stays up to date.
The email from the host is polite and firm: PHP 7.4 (or 8.0) is end of life, your site will be moved to PHP 8.2 on a given date, please make sure you are compatible. Then someone clicks the switch early, the homepage turns white, and the emergency starts. The fix your site needs is rarely one big change. It is a hundred small ones spread across ten-year-old plugins, a custom theme somebody wrote in 2016 and a handful of libraries bundled inside plugins, and the work is in finding them before your visitors do.
This is the process we follow on our PHP version compatibility fix service, written for a site owner or technical lead who has received that email and needs a plan. The goal is an upgrade with zero downtime: audit, fix, test on a copy running the target PHP version, switch with a rollback ready, then clean up. Every command below is one we run on client sites.
WordPress and PHP version support (November 2026)
Short answer: WordPress 6.7 and newer run on PHP 8.4, WordPress 6.9 and newer also run on PHP 8.5, and wordpress.org recommends “PHP version 8.3 or greater.” PHP 8.1 and older no longer get security fixes, and PHP 8.2 stops getting them on 31 December 2026.
Which PHP versions each WordPress release supports
| WordPress | PHP 8.5 | PHP 8.4 | PHP 8.3 | PHP 8.2 | PHP 8.1 | PHP 8.0 | PHP 7.4 | PHP 7.2 and 7.3 |
|---|---|---|---|---|---|---|---|---|
| 7.1 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No |
| 7.0 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No |
| 6.9 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| 6.8 | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| 6.7 | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| 6.6 | No | No | Yes | Yes | Yes | Yes | Yes | Yes |
Source: PHP compatibility and WordPress versions in the WordPress core handbook, last updated 4 November 2026. WordPress 7.0 dropped support for PHP 7.2 and 7.3, so PHP 7.4 is now the minimum. The handbook has retired its “beta support” and “compatible with exceptions” labels, so each cell is now a plain yes or no.
PHP support dates
| PHP | Active support until | Security fixes until | Status on 6 November 2026 |
|---|---|---|---|
| 8.5 | 31 Dec 2027 | 31 Dec 2029 | Active support |
| 8.4 | 31 Dec 2026 | 31 Dec 2028 | Active support |
| 8.3 | 31 Dec 2025 | 31 Dec 2027 | Security fixes only |
| 8.2 | 31 Dec 2024 | 31 Dec 2026 | Security fixes only |
| 8.1 | Ended | 31 Dec 2025 | End of life |
| 8.0 | Ended | 26 Nov 2023 | End of life |
| 7.4 | Ended | 28 Nov 2022 | End of life |
Source: php.net supported versions and php.net unsupported branches, checked on 6 November 2026.
What this means for your upgrade: if your host still runs PHP 8.1 or older, your site no longer gets PHP security patches. A move to PHP 8.2 now means moving again within two months, so plan for PHP 8.3 or 8.4 instead. Check your WordPress row in the first table, then test your theme and plugins on a staging copy.
WordPress core being compatible does not make your theme and plugins compatible. The usual blockers are third-party code, especially plugins that stopped receiving updates.
Why PHP 8 breaks WordPress sites that ran fine on 7.4
PHP 8.0, 8.1 and 8.2 each removed or tightened things that older WordPress code relied on. The ones we hit most often:
-
create_function()andeach()were removed in 8.0. Old themes and plugins used both, especially in widget and shortcode code. - Internal functions now throw a
TypeErrorinstead of a warning when passed the wrong type.count( null ),strlen( [] )andimplode()with reversed arguments turn from noise into fatal errors. - Passing
nullto non-nullable internal parameters is deprecated in 8.1, sostrlen( $undefined )andtrim( get_option( 'missing' ) )fill the error log. - Dynamic properties are deprecated in 8.2. Any class that sets
$this->foowithout declaring it logs a notice on every request; in 9.0 it will be an error. - A required parameter after an optional one is deprecated. Older plugin function signatures do this constantly.
- Extensions that no longer exist:
mcryptis gone, and plugins that bundled an old PHPMailer, Guzzle or PHPExcel fail on their own.
Tested on: WordPress 7.1.3, PHP 8.2.12, PHP_CodeSniffer 4.0.4, PHPCompatibilityWP 3.0.0-alpha2 (with PHPCompatibility 10.0.0-alpha2), on 16 October 2026. The old plugin releases came from the wordpress.org archive: Contact Form 7 5.1.1 and Yoast SEO 9.4.
Step 1: Audit before you change anything
Start with an inventory. You cannot fix what you have not listed, and you will find plugins that have not been updated since PHP 5 was current:
wp core version wp plugin list --fields=name,status,version,update_version,auto_update wp theme list --fields=name,status,version,update_version # Which PHP version is the site on now, and which extensions does it load? wp eval 'echo PHP_VERSION . PHP_EOL; echo implode(", ", get_loaded_extensions());' Update everything that has an update available and note anything with no update in more than two years; those are your likely problems. Then run static analysis with PHPCompatibilityWP, which understands WordPress polyfills and flags real issues rather than every WordPress function:
composer require --dev phpcompatibility/phpcompatibility-wp \ dealerdirect/phpcodesniffer-composer-installer vendor/bin/phpcs -p --standard=PHPCompatibilityWP \ --runtime-set testVersion 8.2- \ --extensions=php --ignore='*/vendor/*,*/node_modules/*' \ --report=summary wp-content/plugins wp-content/themes wp-content/mu-plugins The summary report gives you a count of errors and warnings per file. Errors are things that will fatal on the target version; warnings are deprecations that will fill the log now and break later. Sort by errors, and for each plugin decide: update, replace, or patch.
Step 2: Reproduce on a staging copy with the target PHP
Static analysis misses runtime behaviour, so build a copy of the site running the target version. Most hosts (Cloudways, Kinsta, WP Engine, SiteGround) provide a staging environment with a per-site PHP selector; on a VPS or AWS, spin up a second PHP-FPM pool or a Docker container with the target version and point a staging hostname at it. Then turn logging on in the staging wp-config.php and walk every critical path:
define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', '/var/log/wp/staging-debug.log' ); define( 'WP_DEBUG_DISPLAY', false ); define( 'SCRIPT_DEBUG', false ); Critical paths means the homepage, one of each post type, search, the contact form submission, login, the admin post editor, the plugin settings screens, cron (wp cron event run --due-now) and, for stores, the full checkout including the payment gateway in test mode and the order emails. Tail the log while you do it. A warm PHP 8.2 run of a site that was clean on 7.4 will typically produce dozens of distinct deprecation lines from three or four sources; that list is your work order.
Step 3: The PHP 8 fix checklist
With the list in hand, the fixes themselves are mostly mechanical. The patterns below account for the majority of what we patch.
Common PHP 8.x errors and fixes
These are documented changes on php.net. For each one, we show the fix. The error strings below are the exact text PHP 8.2.12 printed when we ran each case in a small test script.
PHP 8.0: Removed functions
create_function() and each() are removed. Calling them is a fatal error (“Call to undefined function”). Replace create_function with an anonymous function, and each() loops with foreach.
// Before $fn = create_function( '$a', 'return $a * 2;' ); // After $fn = function ( $a ) { return $a * 2; }; PHP 8.2.12 prints Error: Call to undefined function create_function() and Error: Call to undefined function each().
PHP 8.0: Curly brace string offsets
Support for $string{0} is removed. Write $string[0]. PHP 8.2.12 stops at parse time with Fatal error: Array and string offset access syntax with curly braces is no longer supported.
PHP 8.0: Arithmetic on arrays throws TypeError
Operators like +, - and * now throw a TypeError when an operand is an array, resource or non-overloaded object. Array + array merging still works. The usual cause is a variable that holds an array where the code expects a number, often an option that never got saved. Fix the data source, and cast or validate at the boundary.
$count = (int) get_option( 'my_plugin_count', 0 ); Arithmetic on a non-numeric string also throws a TypeError now. The old “A non-numeric value encountered” warning became that error. On PHP 8.2.12, array() * 2 gives TypeError: Unsupported operand types: array * int and 'abc' * 2 gives TypeError: Unsupported operand types: string * int.
PHP 8.0: String to number comparison changed
Comparing a number with a non-numeric string now compares them as strings. 0 == "foo" is now false. Old code that relied on the old result, such as checking an option against 0, behaves differently without any error. Use strict comparison and cast values you read from the database.
PHP 8.1: Passing null to a non-nullable parameter
The notice reads “Passing null to parameter #N ($name) of type T is deprecated.” It appears when a built-in function such as str_contains() or strlen() receives null. It is common with get_post_meta() and get_option() values that do not exist yet. Fix it where the value is created.
$title = get_post_meta( $post_id, 'subtitle', true ); $length = strlen( (string) $title ); The exact text on PHP 8.2.12 is strlen(): Passing null to parameter #1 ($string) of type string is deprecated. In a debug.log it also names the file and line, so you can find the caller.
PHP 8.1: Serializable
Classes that implement Serializable should implement __serialize() and __unserialize(). Keep the old methods only if you support PHP below 7.4.
PHP 8.2: Dynamic properties
Creating a property that the class never declared is deprecated. Declare the property. The attribute #[\AllowDynamicProperties] opts a class and its children out, but declaring is preferred. stdClass is not affected, and neither are classes using __get() and __set().
class My_Widget { public $title = ''; public $settings = array(); } The exact text on PHP 8.2.12 is Creation of dynamic property W::$title is deprecated, where W is the class name.
PHP 8.4: Implicitly nullable parameters
A typed parameter with a null default no longer counts as nullable by itself. Write the type as nullable.
// Before function my_fn( WP_Post $post = null ) {} // After function my_fn( ?WP_Post $post = null ) {} A parameter with a default that comes before a required parameter is also deprecated. Make the earlier parameter required or nullable without a default.
Libraries bundled inside plugins
Plugins that vendor their own copy of PHPMailer, Guzzle, TCPDF or a payment SDK need the library updated, not the plugin code. Check composer.json inside the plugin directory, update the dependency on the target PHP version and rebuild the vendor/ folder. If the plugin is abandoned, that is often the moment to replace it.
Abandoned plugins: Replace, fork or shim
When a plugin has no maintainer, you have three options in order of preference. Replace it with a maintained alternative and migrate the data. Fork it into a private repository, patch it, and treat it as your own code from now on. Or, for a single offending function, add an mu-plugin that removes the broken hook and re-adds a fixed version. Shims are quick but they are debt; write down what they cover.
The PHPCompatibilityWP ruleset and Composer setup we use for the audit are on GitHub: raselhk/wordpress-snippets/php-compatibility.
Step 4: Switch without downtime
Once staging runs clean on the target version, the production switch should be boring. The mechanism depends on the stack, but the principle is the same: keep the old PHP runtime available and change only the routing.
- cPanel or Plesk: MultiPHP Manager or the PHP selector changes the handler per domain. Switching back takes the same thirty seconds.
- Managed hosts: the dashboard PHP selector, usually applied within a minute, with the previous version one click away.
- Nginx and PHP-FPM on a VPS or EC2: install the new PHP version alongside the old one, create a pool for it, and switch the socket in the server block.
# /etc/nginx/sites-available/example.com location ~ \.php$ { include snippets/fastcgi-php.conf; # fastcgi_pass unix:/run/php/php7.4-fpm.sock; # old, keep the service running fastcgi_pass unix:/run/php/php8.2-fpm.sock; # new } sudo nginx -t && sudo systemctl reload nginx sudo tail -f /var/log/php8.2-fpm.log /var/log/wp/debug.log Do the switch at a low-traffic hour with the error logs open, run the same critical path checklist against production, and keep the old pool running for a week. Rollback is one line in the config and a reload. This is the same discipline we use when moving sites to new infrastructure in our AWS migration work, and it is why “without downtime” is achievable rather than a marketing phrase.
Step 5: After the switch
PHP 8 is faster than 7.4 on the same hardware, and OPcache settings decide how much of that you get. Set opcache.memory_consumption to at least 192 MB on a site with a large plugin set, opcache.max_accelerated_files to 20000 or more, and leave opcache.validate_timestamps on unless you have a deploy step that resets the cache. If a store’s checkout is still slow after the switch, PHP was not the bottleneck; our WooCommerce checkout speed service traces which plugin, query or external API is holding it up. Verify nothing was altered during the emergency with wp core verify-checksums and wp plugin verify-checksums --all.
Two follow-ups are worth scheduling. Sites that have been avoiding a PHP upgrade for years usually have a database that has been neglected for just as long, and the cleanup in our WordPress database optimization checklist compounds the speed gain from the new runtime. And an unsupported PHP version is a security problem in its own right: a large share of the compromised sites we handle were running PHP that stopped receiving security patches long before the breach. If you suspect that has already happened, our hacked WordPress site recovery process explains what a clean-up involves, and the hacked site recovery service is the fast route.
Check compatibility with PHPCompatibilityWP and PHPCS
PHPCompatibilityWP is a PHP_CodeSniffer ruleset that checks code for PHP version problems. It scans code. It does not run it. Install it with Composer, which is the only supported method.
- In a working folder outside the live site, install the ruleset:
composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true composer config minimum-stability dev composer config prefer-stable true composer require --dev phpcompatibility/phpcompatibility-wp:"^3.0@dev" - Confirm the standards loaded. PHPCompatibilityWP 3 is a pre-release, so Composer refuses it under the default stability setting. Without the two
configlines above, our install failed with “does not match your minimum-stability”. The output should listPHPCompatibilityandPHPCompatibilityWP:
vendor/bin/phpcs -i - Scan a plugin or theme folder. Set the target version, here 8.3 and up:
vendor/bin/phpcs -p wp-content/plugins/your-plugin --standard=PHPCompatibilityWP --extensions=php --ignore=vendor/*,node_modules/* --runtime-set testVersion 8.3- - Read the report. Each line gives the file, line number and the problem. Fix the errors first, then the warnings.
Without testVersion, the ruleset only reports removed and deprecated features. Setting it also reports newer PHP features your code uses. Requirements at the time of reading: PHP above 5.4 and PHP_CodeSniffer 3.13.3 or newer.
Here is a real excerpt. We ran the command above (target 8.3 and up) on Yoast SEO 9.4, a release from 2018, with PHPCompatibilityWP 3.0.0-alpha2 and PHPCS 4.0.4. The scan found 1 error and 20 warnings in 16 files. Part of the output:
FILE: wordpress-seo/frontend/class-frontend.php 585 | WARNING | Using ${var} in strings is deprecated since PHP 8.2, use | | {$var} instead. Found: ${marker} FILE: wordpress-seo/inc/class-wpseo-replace-vars.php 339 | WARNING | Function utf8_decode() is deprecated since PHP 8.2; Use | | mb_convert_encoding(), UConverter::transcode() or iconv | | instead FILE: wordpress-seo/admin/class-yoast-network-admin.php 97 | WARNING | The constant "FILTER_SANITIZE_STRING" is deprecated since PHP | | 8.1 FILE: wordpress-seo/vendor_prefixed/j4mie/idiorm/idiorm.php 1283 | ERROR | Passing the $glue and $pieces parameters in reverse order to | | join has been deprecated since PHP 7.4 and is removed since | | PHP 8.0; $glue should be the first parameter and $pieces the | | second Many of the other warnings were “Implicitly marking a parameter as nullable is deprecated since PHP 8.4”. The same release is a useful runtime comparison: we activated it on PHP 8.2.12 with WP_DEBUG_LOG on and loaded the front page and a search. It did not crash. debug.log held this deprecation, which is the same line PHPCS flagged at line 585:
PHP Deprecated: Using ${var} in strings is deprecated, use {$var} instead in .../wp-content/plugins/wordpress-seo/frontend/class-frontend.php on line 585 Note that the vendor_prefixed folder was scanned too, because the vendor/* ignore pattern does not match that folder name. Add it to --ignore if you do not want bundled libraries in the report. The scan also reports things the runtime log never shows, such as the 8.4 nullable notices, which is the point of running it before the upgrade.
Then check what WordPress itself reports on the staging copy:
wp core version wp plugin list --fields=name,status,version wp core verify-checksums wp plugin verify-checksums --all Static checks miss runtime problems, such as a null where a string is expected. Turn on logging on staging and click through the site, checkout and admin.
define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); define( 'WP_DEBUG_DISPLAY', false ); Read wp-content/debug.log after the click-through. Turn debugging off again when done.
Rollback plan
Write this down before the switch. Do the upgrade on staging first, then repeat on live.
- Back up first. Export the database and copy the files. Keep both until the site has run cleanly.
wp db export before-php-upgrade.sql - Note the current PHP version and where you change it (host control panel, or server config). A switch back is a setting change, not a restore.
- Switch the PHP version, then check the front page, login, checkout, forms and wp-admin.
- If one plugin is the cause, deactivate only that plugin and keep the new PHP version while you wait for a fix:
wp plugin deactivate plugin-slug If wp-admin is unreachable, rename that plugin folder in wp-content/plugins over SFTP.
- If the site is broken in several places, set the PHP version back to the old one. Then restore the database export only if data was changed during the window.
- Keep watching the log for a few days. Some code paths run only on cron, on a renewal, or at month end.
Abandoned plugins are the usual blocker. If the author has stopped updating, replace the plugin rather than patching its code.
Sources:
WordPress requirements,
PHP compatibility and WordPress versions,
PHP 8.0 backward incompatible changes,
PHP 8.1 deprecated features,
PHP 8.2 deprecated features,
PHP 8.4 deprecated features,
PHPCompatibilityWP.
When to do this yourself vs hire someone
If the audit shows a handful of warnings in actively maintained plugins, update them, test on staging and switch; you do not need us. Hire when the PHPCompatibilityWP report shows errors in a custom theme or in plugins with no update, when the site is a store or membership site where a broken checkout costs money by the hour, or when there is no staging environment and the host has given you a deadline. A PHP version compatibility fix WordPress engagement with us typically runs three to ten working days depending on how much abandoned code is involved, and ends with the site on a supported version, a written list of everything patched, and a rollback that was tested rather than assumed. Ongoing version management afterwards is part of our website maintenance and support plans.
Get the upgrade done before the deadline
Send us your current PHP version, the target version, your host and the plugin list output from step one through our contact page. Within 24 hours you will have a fixed scope and fixed price in writing for a PHP version compatibility fix WordPress owners can rely on, from the engineers who will do the work, with no downtime during the switch.
Originally published at swiftwebdev.com. I build custom WordPress and WooCommerce sites and free WordPress plugins at Swift Web Dev.
