PHP 8.4 and Bitrix in Production: What Actually Breaks
The Bitrix release notes say "PHP 8.4 supported." Our first client to upgrade staging got 47 deprecated notices on a single homepage request and a broken payment module. None of it was in Bitrix core.
Here's what I check on every Bitrix PHP 8.4 upgrade before touching production.
What PHP 8.4 actually changes — the parts that matter for Bitrix
PHP 8.4 shipped in November 2024. Most of the changes are new features: property hooks, asymmetric visibility, lazy objects. None of those break existing code. Three other things do.
First — implicitly nullable types are now E_DEPRECATED. If your codebase has function foo(CIBlockResult $result = null) without the ?, PHP 8.4 logs a deprecated notice on every call. In code written this decade, this was usually cleaned up. In five-year-old custom components, it wasn't.
Second — PHP 8.4 added native array_find(), array_find_key(), array_any(), and array_all(). If your /local/php_interface/ already defines a function with one of those names, you get a fatal "Cannot redeclare" on the first request after the upgrade.
Third — third-party modules that vendors haven't touched since 2019. Outdated DOM patterns, removed alias functions, PHP version checks hardcoded to "supported up to 8.2." This isn't a PHP 8.4 problem. It's accumulated debt that the upgrade surfaces.
Three places legacy Bitrix code is guaranteed to fail
Legacy Bitrix projects break in predictable spots on PHP 8.4: deprecated implicit nullables buried in custom components, function name collisions introduced by PHP 8.4's new stdlib additions, and third-party modules that haven't been updated in years.
Implicitly nullable parameters in custom components
An implicitly nullable parameter is a typed function argument that accepts null as a default without the ? prefix on its type declaration — for example, CIBlockResult $result = null instead of ?CIBlockResult $result = null. PHP 8.4 logs an E_DEPRECATED notice on every call to such a function. The pattern shows up everywhere a component accepts a Bitrix class object with a null fallback:
// PHP 8.3 — silently accepted
// PHP 8.4 — E_DEPRECATED on every call
function MyComponent(CIBlockResult $arResult = null, CPrice $arPrice = null) { ... }
On a 28,000 SKU store, we found 23 instances of this. Every catalog page fired multiple deprecated notices. The logs looked like everything was broken. The store was actually running fine — you just couldn't tell from the noise.
Check before upgrading:
grep -rn 'function.*[A-Z][A-Za-z]*\s\+\$[a-z].*=\s*null' /path/to/local/
This won't kill the site immediately. But it floods error_log badly enough that real errors disappear in the noise.
Function name collisions
A classic pattern: define array_find() as a helper in php_interface.php or in a shared utility include. PHP 8.4 now ships array_find() as a native function. Result:
PHP Fatal error: Cannot redeclare array_find() in /local/php_interface/utils.php on line 47
This kills the site on the first request after the upgrade.
Grep before upgrading:
grep -rn 'function array_find\|function array_any\|function array_all\|function array_find_key' /path/to/local/
Find it, rename it. Five minutes of work. But only if you find it before upgrading production, not after.
Payment and delivery modules
A Robokassa module from 2020, a CloudPayments integration, a DPD API — these often ship their own PHP compatibility check, hardcoded to "supported up to 8.2." On PHP 8.4 they exit silently, refusing transactions. The customer sees a blank page at checkout.
We had exactly this. A delivery module last updated in 2021. The vendor responded in three days. Three days the store couldn't accept orders through that delivery method.
The 7-step staging checklist
Staging has to match production: same Bitrix version, same module set, same /local/ files. If it doesn't, the results don't mean anything.
- Enable E_DEPRECATED | E_NOTICE in php.ini on staging. Write to a separate log file so it doesn't mix with application logs. Read only that file after your first browse-through.
- Run the implicitly nullable grep (command above). Fix every signature you find: add
?before the type or drop the= nulldefault.
- Run the array function collision grep (command above). Rename the custom helpers.
- Update all Marketplace modules through the admin panel's "Update Solutions" before you change the PHP version. Not after.
- HTTP smoke check. curl across 20 key URLs (homepage, catalog, product card, cart, search). Every one should return 200 or the expected 302.
- Test a real transaction through each payment module. Separately. Takes 15 minutes. Saves days.
- Watch error_log for 48 hours after switching production. Not one day — 48 hours. Some deprecated notices only appear under specific usage patterns, like processing an order above a certain value threshold.
When to delay the upgrade
Three situations where waiting is the right call.
Peak season. November is the start of the holiday spike. Upgrading PHP during high load isn't brave — it's the wrong tradeoff. Document the plan and execute in January.
Payment module hasn't been updated in eight months. If the vendor hasn't released updates and isn't responding to PHP 8.4 compatibility questions, upgrading before you have an answer can stop payment processing entirely.
No staging that matches production. This is the one case where I'd delay not the upgrade but the staging setup first. Upgrading PHP in production without prior testing isn't confidence. It's recklessness.
For the broader playbook on planning Bitrix upgrades before they become emergencies, I covered the lifecycle approach in We plan the next Bitrix upgrade before the project launches.
PHP 8.4 is fine. Your 2019 modules aren't.
PHP 8.4 isn't the problem. The Bitrix team did their part. The problem is a five-year-old payment module nobody updated and an array_find() helper in php_interface.php that nobody remembers writing.
Seven steps on staging. Zero surprises on production.
If TTFB climbs after the upgrade, the first thing to check is OPcache config: PHP OPcache and Bitrix in production. After a PHP version change, validate_timestamps often needs to be temporarily enabled before you disable it again.
If the performance issues are broader than OPcache, start with the diagnostic approach in Bitrix doesn't slow down. The way teams use it does.