Two jobs, in this order
PHP 8.2 gets security fixes until 31 Dec 2026, about ten weeks from today. After that there are no more official releases for it. See PHP 8.2.
- Today: install 8.2.35. It carries the PHP2Shell fixes for CVE-2026-109618 and CVE-2026-109619. Restart every PHP worker and confirm the running version.
- Before the end of the year: move to 8.4 (fixed in 8.4.27). It gets security fixes until 31 Dec 2028. Which version to pick has the comparison.
Do the first job even if the second takes weeks. A patch release inside a branch is a small change. The minor-version jump needs staging and tests.
If your distribution or host has not shipped 8.2.35, the package guides under fix say what each vendor has released. On shared hosting, ask the host and see shared hosting.
Order of work for the jump
- Back up files and database. Test a restore.
- Build a staging copy on 8.4 with the same extensions and
php.inivalues. - Update CMS, framework, plugins and Composer packages to versions that declare 8.4 support.
composer outdatedandcomposer why-not php 8.4show what blocks you. - Run the compatibility scan below.
- Run staging with
error_reporting = E_ALLandlog_errors = On. Test logins, forms, uploads, checkout and scheduled jobs. Read the log. - Switch production, restart every PHP worker and confirm the version.
- Keep 8.2 one panel switch away for a few days.
Your 8.2 logs already show the 8.2 deprecations, mainly dynamic properties and utf8_encode(). Fix those first, since they are still deprecated on 8.4.
PHP 8.3 changes
8.3 breaks little. The items below are the ones that come up in real projects.
range()is stricter. A step of0, a negative step on an increasing range, and infinity or NAN throwValueError. Mixed calls such asrange(5, 'z')emit a warning.number_format()with a negative$decimalsnow rounds before the decimal point instead of ignoring it.- Negative array index. After
$a[-5] = 'x'on an empty array, the next$a[]gets-4, not0. - Date errors now use
DateErrorandDateExceptionclasses instead of warnings. Check code that wraps date parsing intry/catch. - Deep recursion can throw an
Errorwhen the call stack nears its limit. - Deprecated:
get_class()andget_parent_class()without arguments,assert_options()with theassert.*INI settings,ReflectionProperty::setValue()with one argument, and theMT_RAND_PHPmode. - Incrementing non-numeric strings is soft deprecated. Use
str_increment()in new code. - Removed: the
opcache.consistency_checksINI directive. Delete it fromphp.inito avoid startup noise.
PHP 8.4 changes
- Implicitly nullable parameters.
function f(Foo $a = null)is deprecated. Write?Foo $a = null. Outdated libraries trigger this on every request. Update packages before you judge your own code. E_STRICTis removed as an error level.trigger_error()withE_USER_ERRORis deprecated, so throw an exception.- CSV functions. Relying on the default
escapeargument offgetcsv(),fputcsv()andstr_getcsv()is deprecated. Passescapeexplicitly. - Moved to PECL: IMAP, OCI8, PDO_OCI and PSpell. If you call
imap_*functions, install the PECL package or change libraries. - Session settings. Changing
session.sid_length,session.sid_bits_per_character,session.use_only_cookies,session.use_trans_sid,session.trans_sid_tags,session.trans_sid_hostsorsession.referer_checkis deprecated. So is theSIDconstant. - mysqli:
mysqli_ping(),mysqli_kill()andmysqli_refresh()are deprecated. round()throwsValueErrorfor an invalid mode, and the old pre-rounding was removed, so a few edge results change.exit()acts like a function. It respectsstrict_typesand throwsTypeErrorfor an invalid type.- JIT defaults changed. If you enabled JIT only through
opcache.jit_buffer_size, setopcache.jittoo. - bcrypt cost defaults to 12 instead of 10. Logins take longer. Existing hashes still verify.
- Name collisions. A function or class added in 8.4 breaks code that defines the same name. Guard your own helpers with
function_exists(). - Minimum libraries: libcurl 7.61.0, libxml2 2.9.4 and OpenSSL 1.1.1. Check older servers. 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.2-8.4 \
--ignore='*/vendor/*,*/node_modules/*' --report-full=compat-report.txt .
The range 8.2-8.4 reports anything that affects either version, so the code is safe while both run. After cutover, use --runtime-set testVersion 8.4. The ^10.0.0@dev constraint is the one in the PHPCompatibility README on 22 Oct 2026. It needs PHP_CodeSniffer 4.0.2 or newer.
Rector can fix some of the findings, such as explicit nullable types. Review the diff before you commit.
composer require rector/rector --dev
vendor/bin/rector process --dry-run
vendor/bin/rector process
Use return RectorConfig::configure()->withPaths([__DIR__ . '/src'])->withPhpSets(); in rector.php. Rector then reads the PHP version from composer.json.
Never rewrite code in plugins or themes you did not write. Update them. For WordPress, see change the PHP version.
Last reviewed on 22 October 2026.