<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rasel Hakkani</title>
    <description>The latest articles on DEV Community by Rasel Hakkani (@rasel_hk77).</description>
    <link>https://dev.to/rasel_hk77</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4246434%2F27496b95-2891-4528-bb30-3b2336901139.jpg</url>
      <title>DEV Community: Rasel Hakkani</title>
      <link>https://dev.to/rasel_hk77</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rasel_hk77"/>
    <language>en</language>
    <item>
      <title>How to Upgrade WordPress to PHP 8 Without Breaking Your Site</title>
      <dc:creator>Rasel Hakkani</dc:creator>
      <pubDate>Sun, 15 Nov 2026 19:23:39 +0000</pubDate>
      <link>https://dev.to/rasel_hk77/how-to-upgrade-wordpress-to-php-8-without-breaking-your-site-8ig</link>
      <guid>https://dev.to/rasel_hk77/how-to-upgrade-wordpress-to-php-8-without-breaking-your-site-8ig</guid>
      <description>&lt;p&gt;&lt;em&gt;This guide was first published on &lt;a href="https://swiftwebdev.com/blog/php-version-compatibility-fix-wordpress/" rel="noopener noreferrer"&gt;Swift Web Dev&lt;/a&gt;, where it stays up to date.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;This is the process we follow on our &lt;a href="https://swiftwebdev.com/services/php-version-compatibility-fix/" rel="noopener noreferrer"&gt;PHP version compatibility fix service&lt;/a&gt;, 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  WordPress and PHP version support (November 2026)
&lt;/h2&gt;

&lt;p&gt;Short answer: WordPress 6.7 and newer run on PHP 8.4, WordPress 6.9 and newer also run on PHP 8.5, and &lt;a href="https://wordpress.org/about/requirements/" rel="noopener noreferrer"&gt;wordpress.org recommends&lt;/a&gt; “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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which PHP versions each WordPress release supports
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;WordPress&lt;/th&gt;
&lt;th&gt;PHP 8.5&lt;/th&gt;
&lt;th&gt;PHP 8.4&lt;/th&gt;
&lt;th&gt;PHP 8.3&lt;/th&gt;
&lt;th&gt;PHP 8.2&lt;/th&gt;
&lt;th&gt;PHP 8.1&lt;/th&gt;
&lt;th&gt;PHP 8.0&lt;/th&gt;
&lt;th&gt;PHP 7.4&lt;/th&gt;
&lt;th&gt;PHP 7.2 and 7.3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7.1&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7.0&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.9&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.8&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.7&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.6&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Source: &lt;a href="https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/" rel="noopener noreferrer"&gt;PHP compatibility and WordPress versions&lt;/a&gt; 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.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP support dates
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PHP&lt;/th&gt;
&lt;th&gt;Active support until&lt;/th&gt;
&lt;th&gt;Security fixes until&lt;/th&gt;
&lt;th&gt;Status on 6 November 2026&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8.5&lt;/td&gt;
&lt;td&gt;31 Dec 2027&lt;/td&gt;
&lt;td&gt;31 Dec 2029&lt;/td&gt;
&lt;td&gt;Active support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.4&lt;/td&gt;
&lt;td&gt;31 Dec 2026&lt;/td&gt;
&lt;td&gt;31 Dec 2028&lt;/td&gt;
&lt;td&gt;Active support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.3&lt;/td&gt;
&lt;td&gt;31 Dec 2025&lt;/td&gt;
&lt;td&gt;31 Dec 2027&lt;/td&gt;
&lt;td&gt;Security fixes only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.2&lt;/td&gt;
&lt;td&gt;31 Dec 2024&lt;/td&gt;
&lt;td&gt;31 Dec 2026&lt;/td&gt;
&lt;td&gt;Security fixes only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.1&lt;/td&gt;
&lt;td&gt;Ended&lt;/td&gt;
&lt;td&gt;31 Dec 2025&lt;/td&gt;
&lt;td&gt;End of life&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;td&gt;Ended&lt;/td&gt;
&lt;td&gt;26 Nov 2023&lt;/td&gt;
&lt;td&gt;End of life&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7.4&lt;/td&gt;
&lt;td&gt;Ended&lt;/td&gt;
&lt;td&gt;28 Nov 2022&lt;/td&gt;
&lt;td&gt;End of life&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Source: &lt;a href="https://www.php.net/supported-versions.php" rel="noopener noreferrer"&gt;php.net supported versions&lt;/a&gt; and &lt;a href="https://www.php.net/eol.php" rel="noopener noreferrer"&gt;php.net unsupported branches&lt;/a&gt;, checked on 6 November 2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this means for your upgrade:&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why PHP 8 breaks WordPress sites that ran fine on 7.4
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;create_function()&lt;/code&gt; and &lt;code&gt;each()&lt;/code&gt; were removed in 8.0. Old themes and plugins used both, especially in widget and shortcode code.&lt;/li&gt;
&lt;li&gt;Internal functions now throw a &lt;code&gt;TypeError&lt;/code&gt; instead of a warning when passed the wrong type. &lt;code&gt;count( null )&lt;/code&gt;, &lt;code&gt;strlen( [] )&lt;/code&gt; and &lt;code&gt;implode()&lt;/code&gt; with reversed arguments turn from noise into fatal errors.&lt;/li&gt;
&lt;li&gt;Passing &lt;code&gt;null&lt;/code&gt; to non-nullable internal parameters is deprecated in 8.1, so &lt;code&gt;strlen( $undefined )&lt;/code&gt; and &lt;code&gt;trim( get_option( 'missing' ) )&lt;/code&gt; fill the error log.&lt;/li&gt;
&lt;li&gt;Dynamic properties are deprecated in 8.2. Any class that sets &lt;code&gt;$this-&amp;gt;foo&lt;/code&gt; without declaring it logs a notice on every request; in 9.0 it will be an error.&lt;/li&gt;
&lt;li&gt;A required parameter after an optional one is deprecated. Older plugin function signatures do this constantly.&lt;/li&gt;
&lt;li&gt;Extensions that no longer exist: &lt;code&gt;mcrypt&lt;/code&gt; is gone, and plugins that bundled an old PHPMailer, Guzzle or PHPExcel fail on their own.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tested on:&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Audit before you change anything
&lt;/h2&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp core version
wp plugin list --fields=name,status,version,update_version,auto_update
wp theme list --fields=name,status,version,update_version

&lt;span class="c"&gt;# Which PHP version is the site on now, and which extensions does it load?&lt;/span&gt;
wp &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s1"&gt;'echo PHP_VERSION . PHP_EOL; echo implode(", ", get_loaded_extensions());'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require --dev phpcompatibility/phpcompatibility-wp \
  dealerdirect/phpcodesniffer-composer-installer

vendor/bin/phpcs -p --standard=PHPCompatibilityWP \
  --runtime-set testVersion 8.2- \
  --extensions=php --ignore=&lt;span class="s1"&gt;'*/vendor/*,*/node_modules/*'&lt;/span&gt; \
  --report=summary wp-content/plugins wp-content/themes wp-content/mu-plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Reproduce on a staging copy with the target PHP
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;wp-config.php&lt;/code&gt; and walk every critical path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'WP_DEBUG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'WP_DEBUG_LOG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/var/log/wp/staging-debug.log'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'WP_DEBUG_DISPLAY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'SCRIPT_DEBUG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 (&lt;code&gt;wp cron event run --due-now&lt;/code&gt;) 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: The PHP 8 fix checklist
&lt;/h2&gt;

&lt;p&gt;With the list in hand, the fixes themselves are mostly mechanical. The patterns below account for the majority of what we patch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common PHP 8.x errors and fixes
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h4&gt;
  
  
  PHP 8.0: Removed functions
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;create_function()&lt;/code&gt; and &lt;code&gt;each()&lt;/code&gt; are removed. Calling them is a fatal error (“Call to undefined function”). Replace &lt;code&gt;create_function&lt;/code&gt; with an anonymous function, and &lt;code&gt;each()&lt;/code&gt; loops with &lt;code&gt;foreach&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="nv"&gt;$fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'$a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'return $a * 2;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="nv"&gt;$fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PHP 8.2.12 prints &lt;code&gt;Error: Call to undefined function create_function()&lt;/code&gt; and &lt;code&gt;Error: Call to undefined function each()&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  PHP 8.0: Curly brace string offsets
&lt;/h4&gt;

&lt;p&gt;Support for &lt;code&gt;$string{0}&lt;/code&gt; is removed. Write &lt;code&gt;$string[0]&lt;/code&gt;. PHP 8.2.12 stops at parse time with &lt;code&gt;Fatal error: Array and string offset access syntax with curly braces is no longer supported&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  PHP 8.0: Arithmetic on arrays throws TypeError
&lt;/h4&gt;

&lt;p&gt;Operators like &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt; now throw a &lt;code&gt;TypeError&lt;/code&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'my_plugin_count'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arithmetic on a non-numeric string also throws a &lt;code&gt;TypeError&lt;/code&gt; now. The old “A non-numeric value encountered” warning became that error. On PHP 8.2.12, &lt;code&gt;array() * 2&lt;/code&gt; gives &lt;code&gt;TypeError: Unsupported operand types: array * int&lt;/code&gt; and &lt;code&gt;'abc' * 2&lt;/code&gt; gives &lt;code&gt;TypeError: Unsupported operand types: string * int&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  PHP 8.0: String to number comparison changed
&lt;/h4&gt;

&lt;p&gt;Comparing a number with a non-numeric string now compares them as strings. &lt;code&gt;0 == "foo"&lt;/code&gt; is now false. Old code that relied on the old result, such as checking an option against &lt;code&gt;0&lt;/code&gt;, behaves differently without any error. Use strict comparison and cast values you read from the database.&lt;/p&gt;

&lt;h4&gt;
  
  
  PHP 8.1: Passing null to a non-nullable parameter
&lt;/h4&gt;

&lt;p&gt;The notice reads “Passing null to parameter #N ($name) of type T is deprecated.” It appears when a built-in function such as &lt;code&gt;str_contains()&lt;/code&gt; or &lt;code&gt;strlen()&lt;/code&gt; receives &lt;code&gt;null&lt;/code&gt;. It is common with &lt;code&gt;get_post_meta()&lt;/code&gt; and &lt;code&gt;get_option()&lt;/code&gt; values that do not exist yet. Fix it where the value is created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_post_meta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'subtitle'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$title&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact text on PHP 8.2.12 is &lt;code&gt;strlen(): Passing null to parameter #1 ($string) of type string is deprecated&lt;/code&gt;. In a debug.log it also names the file and line, so you can find the caller.&lt;/p&gt;

&lt;h4&gt;
  
  
  PHP 8.1: Serializable
&lt;/h4&gt;

&lt;p&gt;Classes that implement &lt;code&gt;Serializable&lt;/code&gt; should implement &lt;code&gt;__serialize()&lt;/code&gt; and &lt;code&gt;__unserialize()&lt;/code&gt;. Keep the old methods only if you support PHP below 7.4.&lt;/p&gt;

&lt;h4&gt;
  
  
  PHP 8.2: Dynamic properties
&lt;/h4&gt;

&lt;p&gt;Creating a property that the class never declared is deprecated. Declare the property. The attribute &lt;code&gt;#[\AllowDynamicProperties]&lt;/code&gt; opts a class and its children out, but declaring is preferred. &lt;code&gt;stdClass&lt;/code&gt; is not affected, and neither are classes using &lt;code&gt;__get()&lt;/code&gt; and &lt;code&gt;__set()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;My_Widget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact text on PHP 8.2.12 is &lt;code&gt;Creation of dynamic property W::$title is deprecated&lt;/code&gt;, where &lt;code&gt;W&lt;/code&gt; is the class name.&lt;/p&gt;

&lt;h4&gt;
  
  
  PHP 8.4: Implicitly nullable parameters
&lt;/h4&gt;

&lt;p&gt;A typed parameter with a &lt;code&gt;null&lt;/code&gt; default no longer counts as nullable by itself. Write the type as nullable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;my_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nc"&gt;WP_Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;my_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nc"&gt;WP_Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A parameter with a default that comes before a required parameter is also deprecated. Make the earlier parameter required or nullable without a default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Libraries bundled inside plugins
&lt;/h3&gt;

&lt;p&gt;Plugins that vendor their own copy of PHPMailer, Guzzle, TCPDF or a payment SDK need the library updated, not the plugin code. Check &lt;code&gt;composer.json&lt;/code&gt; inside the plugin directory, update the dependency on the target PHP version and rebuild the &lt;code&gt;vendor/&lt;/code&gt; folder. If the plugin is abandoned, that is often the moment to replace it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Abandoned plugins: Replace, fork or shim
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The PHPCompatibilityWP ruleset and Composer setup we use for the audit are on GitHub: &lt;a href="https://github.com/raselhk/wordpress-snippets/tree/main/php-compatibility" rel="noopener noreferrer"&gt;raselhk/wordpress-snippets/php-compatibility&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Switch without downtime
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;cPanel or Plesk:&lt;/strong&gt; MultiPHP Manager or the PHP selector changes the handler per domain. Switching back takes the same thirty seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed hosts:&lt;/strong&gt; the dashboard PHP selector, usually applied within a minute, with the previous version one click away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx and PHP-FPM on a VPS or EC2:&lt;/strong&gt; install the new PHP version alongside the old one, create a pool for it, and switch the socket in the server block.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /etc/nginx/sites-available/example.com
&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; \&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;snippets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fastcgi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;# fastcgi_pass unix:/run/php/php7.4-fpm.sock;   # old, keep the service running
&lt;/span&gt;    &lt;span class="n"&gt;fastcgi_pass&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fpm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;# new&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;nginx&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;systemctl&lt;/span&gt; &lt;span class="n"&gt;reload&lt;/span&gt; &lt;span class="n"&gt;nginx&lt;/span&gt;
&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fpm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;wp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: After the switch
&lt;/h2&gt;

&lt;p&gt;PHP 8 is faster than 7.4 on the same hardware, and OPcache settings decide how much of that you get. Set &lt;code&gt;opcache.memory_consumption&lt;/code&gt; to at least 192 MB on a site with a large plugin set, &lt;code&gt;opcache.max_accelerated_files&lt;/code&gt; to 20000 or more, and leave &lt;code&gt;opcache.validate_timestamps&lt;/code&gt; 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; &lt;a href="https://swiftwebdev.com/services/fix-woocommerce-checkout-slow-loading/" rel="noopener noreferrer"&gt;our WooCommerce checkout speed service&lt;/a&gt; traces which plugin, query or external API is holding it up. Verify nothing was altered during the emergency with &lt;code&gt;wp core verify-checksums&lt;/code&gt; and &lt;code&gt;wp plugin verify-checksums --all&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://swiftwebdev.com/blog/wordpress-database-optimization-checklist/" rel="noopener noreferrer"&gt;WordPress database optimization checklist&lt;/a&gt; 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 &lt;a href="https://swiftwebdev.com/blog/hacked-wordpress-site-recovery-process/" rel="noopener noreferrer"&gt;hacked WordPress site recovery process&lt;/a&gt; explains what a clean-up involves, and the &lt;a href="https://swiftwebdev.com/services/hacked-wordpress-site-recovery/" rel="noopener noreferrer"&gt;hacked site recovery service&lt;/a&gt; is the fast route.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check compatibility with PHPCompatibilityWP and PHPCS
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In a working folder outside the live site, install the ruleset:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer &lt;span class="kc"&gt;true&lt;/span&gt;
   composer config minimum-stability dev
   composer config prefer-stable &lt;span class="kc"&gt;true&lt;/span&gt;
   composer require --dev phpcompatibility/phpcompatibility-wp:&lt;span class="s2"&gt;"^3.0@dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Confirm the standards loaded. PHPCompatibilityWP 3 is a pre-release, so Composer refuses it under the default stability setting. Without the two &lt;code&gt;config&lt;/code&gt; lines above, our install failed with “does not match your minimum-stability”. The output should list &lt;code&gt;PHPCompatibility&lt;/code&gt; and &lt;code&gt;PHPCompatibilityWP&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   vendor/bin/phpcs -i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Scan a plugin or theme folder. Set the target version, here 8.3 and up:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   vendor/bin/phpcs -p wp-content/plugins/your-plugin --standard=PHPCompatibilityWP --extensions=php --ignore=vendor/*,node_modules/* --runtime-set testVersion 8.3-
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Read the report. Each line gives the file, line number and the problem. Fix the errors first, then the warnings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without &lt;code&gt;testVersion&lt;/code&gt;, 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.&lt;/p&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;WP_DEBUG_LOG&lt;/code&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PHP Deprecated:  Using ${var} in strings is deprecated, use {$var} instead in .../wp-content/plugins/wordpress-seo/frontend/class-frontend.php on line 585
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;vendor_prefixed&lt;/code&gt; folder was scanned too, because the &lt;code&gt;vendor/*&lt;/code&gt; ignore pattern does not match that folder name. Add it to &lt;code&gt;--ignore&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Then check what WordPress itself reports on the staging copy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp core version
wp plugin list --fields=name,status,version
wp core verify-checksums
wp plugin verify-checksums --all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'WP_DEBUG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'WP_DEBUG_LOG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'WP_DEBUG_DISPLAY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read &lt;code&gt;wp-content/debug.log&lt;/code&gt; after the click-through. Turn debugging off again when done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rollback plan
&lt;/h3&gt;

&lt;p&gt;Write this down before the switch. Do the upgrade on staging first, then repeat on live.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Back up first.&lt;/strong&gt; Export the database and copy the files. Keep both until the site has run cleanly.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   wp db &lt;span class="kd"&gt;export&lt;/span&gt; before-php-upgrade.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Note the current PHP version&lt;/strong&gt; and where you change it (host control panel, or server config). A switch back is a setting change, not a restore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switch the PHP version&lt;/strong&gt;, then check the front page, login, checkout, forms and wp-admin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If one plugin is the cause&lt;/strong&gt;, deactivate only that plugin and keep the new PHP version while you wait for a fix:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   wp plugin deactivate plugin-slug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If wp-admin is unreachable, rename that plugin folder in &lt;code&gt;wp-content/plugins&lt;/code&gt; over SFTP.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If the site is broken in several places&lt;/strong&gt;, set the PHP version back to the old one. Then restore the database export only if data was changed during the window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep watching the log&lt;/strong&gt; for a few days. Some code paths run only on cron, on a renewal, or at month end.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Abandoned plugins are the usual blocker. If the author has stopped updating, replace the plugin rather than patching its code.&lt;/p&gt;

&lt;p&gt;Sources:&lt;br&gt;&lt;br&gt;
&lt;a href="https://wordpress.org/about/requirements/" rel="noopener noreferrer"&gt;WordPress requirements&lt;/a&gt;,&lt;br&gt;&lt;br&gt;
&lt;a href="https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/" rel="noopener noreferrer"&gt;PHP compatibility and WordPress versions&lt;/a&gt;,&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.php.net/manual/en/migration80.incompatible.php" rel="noopener noreferrer"&gt;PHP 8.0 backward incompatible changes&lt;/a&gt;,&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.php.net/manual/en/migration81.deprecated.php" rel="noopener noreferrer"&gt;PHP 8.1 deprecated features&lt;/a&gt;,&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.php.net/manual/en/migration82.deprecated.php" rel="noopener noreferrer"&gt;PHP 8.2 deprecated features&lt;/a&gt;,&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.php.net/manual/en/migration84.deprecated.php" rel="noopener noreferrer"&gt;PHP 8.4 deprecated features&lt;/a&gt;,&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/PHPCompatibility/PHPCompatibilityWP" rel="noopener noreferrer"&gt;PHPCompatibilityWP&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to do this yourself vs hire someone
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://swiftwebdev.com/services/website-maintenance-support/" rel="noopener noreferrer"&gt;website maintenance and support&lt;/a&gt; plans.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the upgrade done before the deadline
&lt;/h2&gt;

&lt;p&gt;Send us your current PHP version, the target version, your host and the plugin list output from step one through our &lt;a href="https://swiftwebdev.com/contact/" rel="noopener noreferrer"&gt;contact page&lt;/a&gt;. Within 24 hours you will have a fixed scope and fixed price in writing for a &lt;a href="https://swiftwebdev.com/services/php-version-compatibility-fix/" rel="noopener noreferrer"&gt;PHP version compatibility fix WordPress&lt;/a&gt; owners can rely on, from the engineers who will do the work, with no downtime during the switch.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://swiftwebdev.com/blog/php-version-compatibility-fix-wordpress/" rel="noopener noreferrer"&gt;swiftwebdev.com&lt;/a&gt;. I build custom WordPress and WooCommerce sites and free WordPress plugins at &lt;a href="https://swiftwebdev.com" rel="noopener noreferrer"&gt;Swift Web Dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>php</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
