Where you stand
PHP 8.1 reached end of life on 31 Dec 2025. For PHP2Shell (CVE-2026-109618 and CVE-2026-109619) the PHP project published only a source backport branch for 8.1. There is no 8.1 release with the fixes. The fixed releases are 8.2.35, 8.3.36, 8.4.27 and 8.5.12. Moving up is the supported fix. See PHP 8.1 and, if you cannot move yet, what to do meanwhile.
The target is 8.4 (fixed in 8.4.27). Which version to pick explains why not 8.2.
If you are coming from 7.4 or 8.0, use the 7.4 to 8.4 guide, which also covers the 8.0 and 8.1 changes.
Order of work
- Back up files and database, and test a restore.
- Build a staging copy on 8.4 with the same extensions and
php.inivalues. - Update your CMS, framework, plugins and Composer packages to versions that declare 8.4 support.
composer why-not php 8.4lists the blockers. - Run the compatibility scan below.
- Fix findings from the 8.2 list first. Dynamic properties and
utf8_encode()are the usual offenders. - Run staging with
error_reporting = E_ALLandlog_errors = On. Exercise logins, forms, uploads, checkout and cron jobs. Read the log. - Switch production, restart every PHP worker and confirm the running version.
Going straight from 8.1 to 8.4 is fine. Treat the three version sections below as one checklist.
PHP 8.2
- Dynamic properties are deprecated. Assigning to a property the class never declared logs a deprecation. Declare the property. As a stopgap, add
#[\AllowDynamicProperties]to the class.stdClassand classes with__get()and__set()are not affected. Old plugins are the most common source. "${var}"and"${expr}"interpolation is deprecated. Use"{$var}"or"$var".utf8_encode()andutf8_decode()are deprecated. Usemb_convert_encoding($s, 'UTF-8', 'ISO-8859-1')oriconv().- Relative callables such as
"self::method"and["parent", "method"]are deprecated. Use[self::class, 'method']orself::method(...). - mbstring encodings.
Base64,QPrint,UuencodeandHTML-ENTITIESas mbstring encodings are deprecated. Usebase64_encode(),quoted_printable_encode(),convert_uuencode()andhtmlentities(). - Case functions ignore the locale.
strtolower(),strtoupper(),ucfirst(),ucwords()and similar now work as in the "C" locale. For accented or non-Latin text, use themb_*versions. - Smaller changes.
str_split('')returns[]instead of[""].var_export()puts a leading backslash on class names.glob()returns an empty array instead offalsewhenopen_basedirblocks every path.
PHP 8.3
8.3 breaks little, which is why it is a fair fallback target.
range()is stricter. A step of0, a negative step on an increasing range, and infinity or NAN throwValueError. Some mixed-type calls such asrange(5, 'z')now emit a warning.number_format()with a negative$decimalsnow rounds before the decimal point instead of ignoring the value.- Array keys. After assigning a negative index to an empty array, the next
$a[]uses that index plus one, not0. - Date errors use the new
DateErrorandDateExceptionclasses instead of warnings. - Stack overflow detection. Very deep recursion can now throw an
Errorbefore the process crashes. - Deprecated:
get_class()andget_parent_class()with no arguments,assert_options()and theassert.*INI settings,ReflectionProperty::setValue()with a single argument,ldap_connect()with separate host and port, and theMT_RAND_PHPmode. - Incrementing non-numeric strings such as
$s = 'a'; $s++is soft deprecated. New code should callstr_increment(). - Removed INI setting:
opcache.consistency_checks.
PHP 8.4
- Implicitly nullable parameters.
function f(Foo $a = null)is deprecated. Write?Foo $a = null. Outdated libraries trigger this on every request, so update packages before judging your own code. E_STRICTis removed as an error level, andtrigger_error()withE_USER_ERRORis deprecated. Throw an exception instead.- CSV functions. Relying on the default
escapeargument offgetcsv(),fputcsv()andstr_getcsv()is deprecated. Pass it explicitly.str_getcsv()also throws if separator or enclosure is not one byte. - Moved to PECL: IMAP, OCI8, PDO_OCI and PSpell. Install them from PECL or switch to another library.
- Session INI changes are deprecated for
session.sid_length,session.sid_bits_per_character,session.use_only_cookies,session.use_trans_sid,session.trans_sid_tags,session.trans_sid_hostsandsession.referer_check. Remove them fromphp.iniand.user.iniunless you need them. - mysqli:
mysqli_ping(),mysqli_kill()andmysqli_refresh()are deprecated. round()throwsValueErrorfor an invalid mode. The old pre-rounding step was removed, so a few edge results change.exit()behaves like a function. It now respectsstrict_typesand throwsTypeErrorfor an invalid type.- JIT defaults changed. If you turned JIT on only by setting
opcache.jit_buffer_size, you must now setopcache.jitas well. - bcrypt cost defaults to 12 instead of 10, so password hashing is slower. Existing hashes still verify.
- Name collisions. Any function or class newly added in 8.4 breaks code that already defines it. Guard polyfills with
function_exists(). - Minimum libraries rose: libcurl 7.61.0, libxml2 2.9.4, OpenSSL 1.1.1. Apache 2.4 is the minimum for
mod_php.
Scan and fix commands
Run these on a workstation or in CI.
composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer require --dev phpcompatibility/php-compatibility:"^10.0.0@dev"
vendor/bin/phpcs -i
vendor/bin/phpcs -ps --standard=PHPCompatibility --runtime-set testVersion 8.1-8.4 \
--ignore='*/vendor/*,*/node_modules/*' --report-full=compat-report.txt .
testVersion 8.1-8.4 reports problems that affect any version in the range, so the code stays safe while 8.1 and 8.4 coexist. Switch to 8.4 after cutover. The ^10.0.0@dev constraint is the one the PHPCompatibility README shows on 22 Oct 2026. It needs PHP_CodeSniffer 4.0.2 or newer.
Rector can apply some fixes, such as explicit nullable types. Review every diff.
composer require rector/rector --dev
vendor/bin/rector process --dry-run
vendor/bin/rector process
With return RectorConfig::configure()->withPaths([__DIR__ . '/src'])->withPhpSets(); in rector.php, Rector reads the target version from composer.json.
Leave plugin and theme code you do not own out of the scan and the rewrite. Update it instead. WordPress steps are in change the PHP version.
Last reviewed on 22 October 2026.