SnowDriven Research · Reference
PHP 8.2 to 8.4: what breaks in WordPress plugins, themes and PHP apps
PHP’s migration guides for 8.3 and 8.4 list every backward-incompatible change, deprecation and removed extension. We read them all for a WordPress plugin or theme and for a custom PHP app: 2 are likely to need attention, 24 are possible and 40 are rare. Each links to php.net, checked on 26 October 2026.
- For
- WordPress developers and PHP teams
- Guides read
- PHP 8.3 and 8.4, php.net
- On this page
- 66 changes
- Likely
- 2
- Checked
- 26 Oct 2026
The short answer
The 2 changes we rate likely for a typical plugin, theme or app, and the extensions PHP 8.4 no longer ships.
Implicitly nullable parameter
PHP 8.4 · Deprecation. A typed parameter with a default of null, implicitly widened to accept null, is deprecated. Declare the type nullable explicitly.
fputcsv(), fgetcsv(), str_getcsv() need an explicit escape argument
PHP 8.4 · Deprecation. Using the default value of the escape parameter for
fputcsv(),fgetcsv()andstr_getcsv()is now deprecated; it must be passed explicitly.
Removed from PHP 8.4
php.net says these extensions “have been moved to PECL and are no longer part of the PHP distribution”. Code that uses one stops working on PHP 8.4 unless the host installs it from PECL:
Before you change anything. This page is general information, not advice for your site or app. Each entry summarizes php.net’s own page, checked on 26 October 2026, and the PHP project can change its documentation, so read the linked page before you act on an entry. Whether a change is likely, possible or rare for you is our judgement for a typical plugin, theme or app, not the PHP project’s, and so is any fix marked as our suggestion. Back up, test every change on a copy that isn’t in production, keep a way back, and follow your own change process. You follow these steps at your own risk (terms). SnowDriven isn’t affiliated with the PHP project, WordPress or WooCommerce (trademarks).
Before you upgrade
The upgrade steps (back up, staging, update, switch, test) are in our PHP 8.2 guide. This page is the detail behind “what changes”.
WordPress itself is ready
WordPress’s compatibility table marks PHP 8.4 as supported for WordPress 6.7 and later. WooCommerce 10.8 and later lists “PHP version 8.3 or greater (tested up to PHP 8.4)”. So for a site or store, the question is the plugins and the theme (WordPress, WooCommerce, checked 26 October 2026).
Deprecations warn; the rest can break
A deprecation logs an
E_DEPRECATEDnotice and the code keeps working on PHP 8.4. Backward-incompatible changes and removed extensions are the ones that can stop a page, a checkout or a job.Search for each “How to tell”
Each entry names what to look for: a function, a constant, a setting or a warning. Our guide lists the tools that find these for you, static analysis and automated fixes.
Watch the logs on a copy
Switch a staging copy to PHP 8.4 with deprecation notices logged, not shown, then run the forms, logins and orders that matter. The log tells you which entries apply.
Security support for PHP 8.2 ends on 31 December 2026. What that means and the upgrade path: PHP 8.2 end of life.
Likely (2)
Changes most plugins, themes or apps moving from PHP 8.2 will meet. Check each one.
Implicitly nullable parameter
A typed parameter with a default of null, implicitly widened to accept null, is deprecated. Declare the type nullable explicitly.
- How to tell
- Signatures like
function foo(T $a = null), a typed parameter defaulting to null with no ? or |null;E_DEPRECATEDnotices. - What to do
- Use
?T $a = nullorT|null $a = null; if a required parameter follows, drop the default:?T $b. - Why it’s likely (our view)
- A very common pattern in older plugin, theme and library code.
fputcsv(), fgetcsv(), str_getcsv() need an explicit escape argument
Using the default value of the escape parameter for
fputcsv(),fgetcsv()andstr_getcsv()is now deprecated; it must be passed explicitly.- How to tell
fputcsv(),fgetcsv()andstr_getcsv()calls that omit the escape argument.- What to do
- Pass the escape argument explicitly, positionally or via a named argument.
- Why it’s likely (our view)
- CSV import and export is common in plugins and apps, and most existing calls omit the escape argument.
Possible (24)
Changes that hit some plugins, themes or apps, depending on what the code uses. Search for what’s under “How to tell”.
Backward-incompatible changes · 9
Date extension throws its own errors and exceptionsThe
DateTimeextension now usesDateErrorandDateExceptionhierarchies instead of warnings and generic exceptions, so code must check for these errors and exceptions.- How to tell
DateTime-related calls whose failures were handled via warnings or generic exceptions; the classesDateErrorandDateException.- What to do
- Catch
DateErrorandDateExceptionwhere date input can be invalid; php.net notes that the change means checking for them. - Why it’s possible (our view)
- Date parsing of user or stored input is common; code relying on warnings may now see thrown errors.
range() now throws errors and warnings for bad inputs
range()throwsTypeError/ValueErrorfor objects, arrays, resources, zero or wrong-sign steps, INF/NAN; warns on empty or multibyte strings;range('9', 'A')now returns characters.- How to tell
range()calls with object/array/resource bounds, step 0 or negative step on increasing ranges, INF/NAN, empty-string or mixed number/letter bounds.- What to do (our suggestion)
- Pass valid integer, float or single-character bounds and a positive step for increasing ranges.
- Why it’s possible (our view)
range()is common, but only odd or unvalidated inputs trigger the new errors and warnings.
Removal of E_STRICT error levelThe
E_STRICTerror level was removed because the engine no longer used it, and theE_STRICTconstant is deprecated.- How to tell
E_STRICTinerror_reporting()masks, theerror_reportingini value,set_error_handler()callbacks orwp-config.phpsnippets; anE_DEPRECATEDnotice where the constant is used.- What to do (our suggestion)
- Remove
E_STRICTfrom error masks and handlers; the level no longer exists. - Why it’s possible (our view)
- Older plugins, themes, wp-config snippets and legacy apps often mask or test for
E_STRICT.
GD image functions throw ValueError on invalid arguments
imagejpeg(),imagewebp(),imagepng()andimageavif()throwValueErrorfor invalid quality;imageavif(),imagescale()andimagefilter()(IMG_FILTER_SCATTER) also validate further parameters.- How to tell
imagejpeg(),imagewebp(),imagepng(),imageavif()quality arguments;imagescale()width, height and mode;imagefilter()withIMG_FILTER_SCATTER; uncaughtValueError.- What to do (our suggestion)
- Validate quality and size arguments before calling, or catch
ValueError. - Why it’s possible (our view)
- WordPress: Image optimization and thumbnail plugins pass configurable quality values to GD. Custom apps: Apps doing image processing usually go through libraries with fixed, valid settings.
MySQL wait-timeout error code changed from 2006 to 4031For MySQL server 8.0.24 and above, the error code reported for server wait timeouts changed from 2006 to 4031.
- How to tell
- Code that compares MySQL error numbers with 2006, for example reconnect or retry logic.
- What to do
- Handle error code 4031 wherever 2006 is checked.
- Why it’s possible (our view)
- WordPress: Few plugins inspect raw MySQL error codes. Custom apps: Long-running workers and queue consumers often reconnect on specific error codes.
JITJIT defaults changed to
opcache.jit=disableandopcache.jit_buffer_size=64M. Enabling JIT throughopcache.jit_buffer_sizealone no longer works; JIT initialization failure is now fatal at startup.- How to tell
opcache.jitandopcache.jit_buffer_sizeinphp.inior FPM pool configuration.- What to do
- To enable JIT, set
opcache.jitaccordingly (php.net’s advice). - Why it’s possible (our view)
- WordPress: A host setting; most WordPress sites do not tune JIT. Custom apps: Teams that enabled JIT via
opcache.jit_buffer_sizeonly will silently lose it.
Bundled PCRE2 updated to 10.44Bundled pcre2lib is now 10.44:
{,3}is recognized as a quantifier instead of text, and some character classes changed meaning in UCP mode.- How to tell
preg_*patterns containing a literal{,n}; patterns using character classes in UCP mode (our note: PHP’s/umodifier turns UCP on).- What to do
- Escape the brace where a literal is meant; review the PCRE2 changelog that php.net links.
- Why it’s possible (our view)
- Regex-heavy content, shortcode and validation code is common, though the affected patterns are uncommon.
PDO MySQL attributes now boolean
PDO::ATTR_AUTOCOMMIT,PDO::ATTR_EMULATE_PREPARESandPDO::MYSQL_ATTR_DIRECT_QUERYnow act as boolean attributes;setAttribute()expects andgetAttribute()returns a bool.- How to tell
- Those attributes in
PDO::setAttribute(),PDO::getAttribute()or constructor options, especially integer values or comparisons such as=== 1. - What to do
- Set and compare these attributes with true/false rather than integers.
- Why it’s possible (our view)
- WordPress: Most WordPress code talks to MySQL through wpdb, not
PDO. Custom apps: Framework and plain-PHP apps commonly setPDO::ATTR_EMULATE_PREPARES.
xml_set_*_handler now checks callables; xml_set_object() first
xml_set_*_handlerfunctions now check forcallable|string|null; method-name strings are checked against thexml_set_object()object, which must be set first. Empty-string handlers are deprecated.- How to tell
xml_set_*_handlercalls (for examplexml_set_element_handler()) that pass method-name strings;xml_set_object().- What to do
- Pass a callable that refers to the method directly, as php.net recommends, instead of a method-name string with
xml_set_object(). - Why it’s possible (our view)
- WordPress: Older feed, import and XML-RPC parsing code in plugins uses
xml_set_object()with method names. Custom apps: Modern apps rarely use the expatxml_*functions directly.
Removed extensions · 1
IMAP extension moved to PECLIMAP has been moved to PECL and is no longer part of the PHP distribution; new PECL releases are published ad hoc according to user demand.
- How to tell
- Calls to
imap_open()and otherimap_*functions;extension_loaded()checks; “undefined function” errors when the extension is absent. - What to do
- Install IMAP from PECL if you still need it, since php.net says it moved there.
- Why it’s possible (our view)
- WordPress: Email-to-ticket, helpdesk and mailbox-polling plugins use IMAP. Custom apps: Apps that read mailboxes may call
imap_*directly.
Deprecations · 9
Saner Increment/Decrement operators
++on empty, non-numeric or non-alphanumeric strings, and--on empty or non-numeric strings, are deprecated. Incrementing non-numeric strings is soft-deprecated.- How to tell
E_DEPRECATEDfrom++or--applied to string variables that are empty or non-numeric (for example a counter initialized to'').- What to do
- php.net says the new
str_increment()function should be used instead of incrementing non-numeric strings. Initialize numeric counters as ints. - Why it’s possible (our view)
- Loosely typed counters initialized as
''are a known pattern in older code.
get_class()/get_parent_class() call without argumentsCalling
get_class()andget_parent_class()without arguments is now deprecated.- How to tell
get_class()orget_parent_class()with empty parentheses, typically inside class methods;E_DEPRECATED.- What to do (our suggestion)
- Pass the object explicitly, for example
get_class($this)orget_parent_class($this). - Why it’s possible (our view)
- WordPress: Argument-less
get_class()appears in older plugin and framework code. Custom apps: Modern app code rarely uses it, but older dependencies may.
Using trigger_error() with E_USER_ERRORCalling
trigger_error()withE_USER_ERRORas the error level is now deprecated.- How to tell
trigger_error(..., E_USER_ERROR).- What to do
- Throw an exception or call
exit(), whichever is more appropriate (php.net’s advice). - Why it’s possible (our view)
- Some plugins and legacy apps use
E_USER_ERRORto abort on fatal conditions.
CURLOPT_BINARYTRANSFER constant deprecatedThe
CURLOPT_BINARYTRANSFERconstant is now deprecated.- How to tell
curl_setopt()orcurl_setopt_array()usingCURLOPT_BINARYTRANSFER.- What to do (our suggestion)
- Remove the
CURLOPT_BINARYTRANSFERoption. - Why it’s possible (our view)
- Older HTTP download and API-client code still sets this option.
mysqli ping, kill, refresh and store_result mode deprecatedDeprecated:
mysqli_ping()/ping(),mysqli_kill()/kill(),mysqli_refresh()/refresh()withMYSQLI_REFRESH_*constants, and passing mode tomysqli_store_result()(MYSQLI_STORE_RESULT_COPY_DATA).- How to tell
mysqli_ping(),mysqli::ping(),mysqli_kill(),mysqli::kill(),mysqli_refresh(),mysqli::refresh(),MYSQLI_REFRESH_*,mysqli_store_result()with a mode argument.- What to do
- Use an SQL
KILLcommand instead ofkill(), and SQLFLUSHinstead ofrefresh(). php.net names noping()replacement. - Why it’s possible (our view)
- WordPress: Plugins and database drop-ins that use mysqli directly sometimes call
mysqli_ping()to check connections. Custom apps: Framework apps mostly usePDO.
lcg_value() deprecated
lcg_value()is now deprecated, as the function is broken in multiple ways.- How to tell
lcg_value().- What to do
- Use
Random\Randomizer::getFloat()instead. - Why it’s possible (our view)
- Still used in some plugins and older code for random floats.
Session INI settings, SID constant and save-handler signature deprecatedDeprecated:
session_set_save_handler()with more than two arguments; changingsession.sid_length,sid_bits_per_character,use_only_cookies,use_trans_sid,trans_sid_tags,trans_sid_hostsorreferer_check; theSIDconstant.- How to tell
session_set_save_handler()with more than two arguments; thosesession.*settings inphp.ini,.user.iniorini_set(); theSIDconstant.- What to do
- Use the two-argument
session_set_save_handler(); accept 32-character hexadecimal session IDs and stop changing these INI settings. - Why it’s possible (our view)
- WordPress: WordPress itself does not rely on PHP sessions; only some plugins do. Custom apps: Apps and hardened host
php.inifiles sometimes setsession.sid_lengthorsession.use_only_cookies.
SplFileObject CSV methods need an explicit escape argumentRelying on the default escape parameter of
SplFileObject::setCsvControl(),fputcsv()andfgetcsv()is deprecated, unlesssetCsvControl()has set a new default.- How to tell
SplFileObject::setCsvControl(),SplFileObject::fputcsv()andSplFileObject::fgetcsv()calls without an escape argument.- What to do
- Pass the escape argument explicitly, positionally or as a named argument.
- Why it’s possible (our view)
- CSV handling through
SplFileObjectis fairly common in import and export code.
xml_set_object() and non-callable handler strings deprecatedThe
xml_set_object()function is deprecated, as is passing non-callable strings to thexml_set_*functions.- How to tell
xml_set_object();xml_set_*handler calls given method-name strings.- What to do
- Pass a callable that refers to the method directly (php.net’s recommendation on the incompatible-changes page).
- Why it’s possible (our view)
- WordPress: Older feed, import and XML-RPC parsing code in plugins uses this pattern. Custom apps: Modern apps rarely use the expat
xml_*functions directly.
Other changes · 5
unserialize() notices promoted to warningsThe
E_NOTICEs emitted byunserialize()have been promoted toE_WARNING.- How to tell
unserialize()on malformed or truncated data;E_WARNINGin logs whereE_NOTICEappeared before.- What to do (our suggestion)
- Validate or repair serialized data, and handle
unserialize()returning false. - Why it’s possible (our view)
- WordPress: Plugins unserialize stored options, meta and transients; corrupted data now logs warnings. Custom apps: Apps unserializing cached or stored data may see louder logs.
unserialize() warns on trailing unconsumed bytes
unserialize()now emits a newE_WARNINGif the input contains unconsumed bytes.- How to tell
- New
E_WARNINGfromunserialize()for inputs with extra data after the serialized value. - What to do (our suggestion)
- Ensure serialized strings are stored and passed without trailing data.
- Why it’s possible (our view)
- Serialized data altered by search-replace or concatenation can carry trailing bytes.
array_sum()/array_product() warn on unconvertible values
array_sum()andarray_product()now warn when values cannot be converted to int/float; arrays and objects were previously ignored. Numeric-castable objects (for exampleGMP) are now cast.- How to tell
array_sum()orarray_product()on arrays that may contain nested arrays, objects or non-numeric values; new warnings.- What to do (our suggestion)
- Filter or cast array values to numbers before summing.
- Why it’s possible (our view)
- Summing loosely typed data (form input, meta values) is common.
password_hash() bcrypt default cost raised from 10 to 12The default “cost” value for the
PASSWORD_BCRYPTalgorithm inpassword_hash()increased from 10 to 12.- How to tell
password_hash()withPASSWORD_BCRYPTorPASSWORD_DEFAULT(bcrypt, per thepassword_hash()manual page) and no explicit cost; tests or code that expect a cost of 10 in stored bcrypt hashes.- What to do (our suggestion)
- Pass an explicit “cost” option if you need a specific value.
- Why it’s possible (our view)
- Hashing becomes slower, and rehash-on-login logic may start rehashing stored passwords (our inference).
Rounding with round()
round()was rewritten and “pre-rounding” removed, so some inputs round differently (for example 0.49999999999999994 now gives 0.0); mode acceptsRoundingMode; precision extended one digit.- How to tell
round()calls, especially in price, tax and currency calculations.- What to do (our suggestion)
- Re-test code whose results depend on
round()edge cases. - Why it’s possible (our view)
- E-commerce and finance code rounds constantly, though most results are unchanged.
Rare (40)
Changes that need a specific function, setting or pattern. Read the ones for features you use.
Backward-incompatible changes · 22
Programs that were very close to overflowing the call stackPrograms very close to overflowing the call stack may now throw an
Erroronce stack use exceedszend.max_allowed_stack_sizeminuszend.reserved_stack_size(fiber.stack_sizefor fibers).- How to tell
- An
Errorthrown during deep or runaway recursion; INI settingszend.max_allowed_stack_size,zend.reserved_stack_size,fiber.stack_size. - What to do (our suggestion)
- Reduce recursion depth; the INI section of Other Changes documents
zend.max_allowed_stack_sizeandzend.reserved_stack_size. - Why it’s rare (our view)
- Only code that already recursed almost to a crash is affected.
Executing proc_get_status() multiple timesRepeated
proc_get_status()calls now return the right value on POSIX;proc_close()afterproc_get_status()now returns the real exit code instead of -1.- How to tell
proc_get_status()called more than once, orproc_close()afterproc_get_status(), especially code expecting -1.- What to do
- If the previous behavior is required, check the
cachedkey in the array returned byproc_get_status(). - Why it’s rare (our view)
- WordPress: Process-spawning code is uncommon in themes and plugins. Custom apps: Only apps that manage child processes and depended on the old buggy value.
Uses of traits with static propertiesA trait’s static properties now redeclare those inherited from the parent class, giving the using class its own separate static property storage.
- How to tell
- Classes that use a trait declaring a static property that the parent class also has, where code expects parent and child to share the value.
- What to do (our suggestion)
- Review code that relied on a parent and a trait-using child sharing one static property.
- Why it’s rare (our view)
- Needs a specific trait-plus-inheritance pattern that relies on shared static state.
Assigning a negative index to an empty arrayAfter assigning a negative index
$nto an empty array, the next appended index is now$n + 1instead of 0.- How to tell
- Code that assigns a negative key to an empty array (for example
$a[-5] = x) and then appends with$a[] = y. - What to do (our suggestion)
- Use explicit keys where code depended on the next appended index being 0.
- Why it’s rare (our view)
- Negative keys on an empty array followed by appends is an unusual pattern.
Class constant visibility variance checkClass constant visibility variance is now correctly checked when constants are inherited from interfaces.
- How to tell
- Classes that redeclare a constant inherited from an interface with a different visibility; surfaces as a compile-time error.
- What to do (our suggestion)
- Make the redeclared constant’s visibility compatible with the interface’s declaration.
- Why it’s rare (our view)
- Redeclaring interface constants with changed visibility is uncommon.
after()/before()/replaceWith() on parentless node is a no-op
DOMChildNode::after(),before()andreplaceWith()on a node with no parent are now a no-op instead of throwing a hierarchy exception.- How to tell
- Calls to
DOMChildNode::after(),::before(),::replaceWith()inside try/catch blocks expecting aDOMException. - What to do (our suggestion)
- Do not rely on an exception here; check the node’s parent explicitly if needed.
- Why it’s rare (our view)
- HTML-manipulating plugins use DOM, but relying on this exception is unusual.
DOMParentNode/DOMChildNode methods work without a document
DOMParentNodeandDOMChildNodemethods used without a document now work instead of throwing aDOM_HIERARCHY_REQUEST_ERRDOMException.- How to tell
- Code catching
DOMExceptionwith codeDOM_HIERARCHY_REQUEST_ERRaroundDOMParentNode/DOMChildNodemethods. - What to do (our suggestion)
- Remove logic that depended on the
DOM_HIERARCHY_REQUEST_ERRexception being thrown. - Why it’s rare (our view)
- Depending on this exception is unusual.
createAttributeNS() without prefix no longer creates default namespace
DOMDocument::createAttributeNS()without a prefix wrongly created a default namespace, putting the element in the namespace; this bug is fixed.- How to tell
- Calls to
DOMDocument::createAttributeNS()with a qualified name that has no prefix. - What to do (our suggestion)
- Re-check XML output from code calling
createAttributeNS()without a prefix. - Why it’s rare (our view)
- WordPress: Namespaced attribute creation is mostly in XML/SVG/feed generation code. Custom apps: Possible only in apps generating namespaced XML.
createAttributeNS() picks a new prefix on conflict
DOMDocument::createAttributeNS()no longer throwsDOM_NAMESPACE_ERRNAMESPACE_ERRwhen a prefix is used for a different URI; it chooses a different prefix.- How to tell
- Calls to
DOMDocument::createAttributeNS()wrapped in try/catch forDOMException, and XML output with reused prefixes. - What to do (our suggestion)
- Check generated XML prefixes and remove handling for the old exception.
- Why it’s rare (our view)
- Requires namespaced XML with conflicting prefixes.
New DOM methods/properties may clash with subclass declarationsNew methods and properties were added to some DOM classes; userland subclasses declaring the same names must be compatible or a compile error is thrown.
- How to tell
- Classes extending DOM classes (for example
DOMDocument,DOMElement,DOMNode) that declare methods or properties; compile error about incompatible declarations. - What to do
- Make the subclass declarations compatible; php.net points to the new-features and new-functions DOM lists for the new names.
- Why it’s rare (our view)
- WordPress: A few HTML-processing plugins extend DOM classes; a name clash is needed to break. Custom apps: Possible in apps or libraries that extend DOM classes.
opcache.consistency_checks INI directive removedThe
opcache.consistency_checksINI directive was removed; it was broken and had been disabled since PHP 8.1.18 and 8.2.5.- How to tell
opcache.consistency_checksinphp.ini,conf.dfiles,.user.inior pool config.- What to do (our suggestion)
- Remove the
opcache.consistency_checksline from configuration. - Why it’s rare (our view)
- WordPress: A server-config item that plugin code does not set. Custom apps: Host or Docker configs only; rarely set.
number_format() handles negative decimals
number_format()now rounds num toabs($decimals)digits before the decimal point for negative decimals; previously negative decimals were ignored.- How to tell
number_format()calls whose decimals argument can be negative (for example computed values).- What to do (our suggestion)
- Ensure decimals is not negative unless rounding before the decimal point is intended.
- Why it’s rare (our view)
- Negative decimals are usually a bug in the caller.
file() rejects all invalid flags, including FILE_APPENDThe
file()flags error check now catches all invalid flags; notablyFILE_APPEND, previously silently accepted, is now caught.- How to tell
file()calls passingFILE_APPENDor other flags thatfile()does not support.- What to do (our suggestion)
- Remove
FILE_APPENDor other unsupported flags fromfile()calls. - Why it’s rare (our view)
- Passing
FILE_APPENDtofile()is a mistake that exists in some copy-pasted code.
exit() behavioral change
exit()anddie()now behave more like functions: callable, affected bystrict_types, using normal type coercion. Invalid argument types now consistently throwTypeError.- How to tell
exit(...)anddie(...)calls, especially in files withdeclare(strict_types=1); an uncaughtTypeErrorfromexit()/die().- What to do (our suggestion)
- Check what is passed to
exit()/die()and correct any value of an invalid type. - Why it’s rare (our view)
- Most exit/die calls pass nothing, a string or an int; invalid types are uncommon.
Extension Class Constants which are now TypedClass constants of the Date, Intl,
PDO, Reflection, SPL, Sqlite and XMLReader extensions now declare a type.- How to tell
- Constants of classes from those extensions, chiefly where a user class extends one and redeclares a constant (our inference; the page does not say).
- What to do (our suggestion)
- Check any subclass that redeclares one of these constants with a value of a compatible type.
- Why it’s rare (our view)
- Redeclaring extension class constants in subclasses is unusual.
SoapClient $httpurl and $sdl are now objects
SoapClient::$httpurlis now aSoap\Urlobject andSoapClient::$sdlaSoap\Sdlobject, rather than resources, sois_resource()checks on them fail.- How to tell
is_resource($client->httpurl)oris_resource($client->sdl).- What to do
- Replace with null checks, as php.net shows:
$client->httpurl !== nulland$client->sdl !== null. - Why it’s rare (our view)
- Few codebases inspect these internal
SoapClientproperties.
Invalid session GC ini values now emit a warningSetting a non-positive value for
session.gc_divisoror a negative value forsession.gc_probabilitynow emits a warning.- How to tell
session.gc_divisorandsession.gc_probabilityinphp.ini,.user.ini,.htaccess, pool config orini_set().- What to do
- Set
session.gc_divisorto a positive value andsession.gc_probabilityto zero or more. - Why it’s rare (our view)
- Such invalid values are unusual in host or app configuration.
round(), str_getcsv(), php_uname(), unserialize() now validate arguments
round()throwsValueErrorfor invalid modes (formerly treated asPHP_ROUND_HALF_UP);str_getcsv(),php_uname()and theallowed_classesoption ofunserialize()now throw on invalid arguments.- How to tell
round()mode argument;str_getcsv()separator, enclosure and escape not one byte;php_uname()mode;unserialize()allowed_classesnot an array of class names or bool.- What to do
- Pass a valid
round()mode, one-byte CSV separator/enclosure/escape, a validphp_uname()mode, andallowed_classesas class-name array or bool. - Why it’s rare (our view)
- These invalid arguments were already bugs and are uncommon in practice.
DOMXPath cloning throws; DOMImplementation::getFeature() removedSome DOM allocation failures now consistently throw
DOMException(DOM_INVALID_STATE_ERR); cloning aDOMXPathnow throwsError;DOMImplementation::getFeature()has been removed.- How to tell
cloneonDOMXPathobjects;DOMImplementation::getFeature(); checks for false orDOM_PHP_ERRafter DOM node creation.- What to do (our suggestion)
- Remove
getFeature()calls andDOMXPathcloning (the clone was unusable anyway). - Why it’s rare (our view)
- php.net itself says the allocation case is extremely unlikely; the other two are uncommon.
GMP class is now finalThe
GMPclass is now final and cannot be extended anymore.- How to tell
class ... extends GMP.- What to do (our suggestion)
- Stop extending
GMP; wrap aGMPinstance instead. - Why it’s rare (our view)
- Extending
GMPis uncommon in plugins and apps.
Unused MySQLi constants removed
MYSQLI_SET_CHARSET_DIR,MYSQLI_STMT_ATTR_PREFETCH_ROWS,MYSQLI_CURSOR_TYPE_FOR_UPDATE,MYSQLI_CURSOR_TYPE_SCROLLABLEandMYSQLI_TYPE_INTERVALwere removed as unused, unimplemented or aliases.- How to tell
- Those constant names; an “undefined constant”
Error. - What to do
- Remove the references. php.net notes
MYSQLI_TYPE_INTERVALwas an alias forMYSQLI_TYPE_ENUM. - Why it’s rare (our view)
- php.net describes these constants as unused, undocumented or never implemented.
SimpleXMLElement methods no longer reset the iterator
asXML(),getName()and string casts no longer implicitly reset aSimpleXMLElement’s iterator, which could cause infinite loops; it is reset only when explicitly rewound.- How to tell
- Loops over
SimpleXMLElementthat callasXML(),getName()or cast to string inside the loop. - What to do (our suggestion)
- Code that relied on the implicit reset must rewind the iterator explicitly.
- Why it’s rare (our view)
- Code relying on the old reset was usually already buggy (php.net’s example loops forever).
Removed extensions · 3
OCI8 extension moved to PECLOCI8 has been moved to PECL and is no longer part of the PHP distribution; new PECL releases are published ad hoc according to user demand.
- How to tell
- Calls to
oci_connect()and otheroci_*functions;extension_loaded()checks; “undefined function” errors when the extension is absent. - What to do
- Install OCI8 from PECL if you still need it, since php.net says it moved there.
- Why it’s rare (our view)
- WordPress: WordPress runs on MySQL/MariaDB. Custom apps: Only apps connecting to Oracle Database use it.
PDO_OCI extension moved to PECL
PDO_OCIhas been moved to PECL and is no longer part of the PHP distribution; new PECL releases are published ad hoc according to user demand.- How to tell
- Calls to the
PDOOracle driver (PDODSNs for Oracle);extension_loaded()checks; “undefined function” errors when the extension is absent. - What to do
- Install
PDO_OCIfrom PECL if you still need it, since php.net says it moved there. - Why it’s rare (our view)
- WordPress: WordPress runs on MySQL/MariaDB. Custom apps: Only apps connecting to Oracle Database through
PDOuse it.
PSpell extension moved to PECLPSpell has been moved to PECL and is no longer part of the PHP distribution; new PECL releases are published ad hoc according to user demand.
- How to tell
- Calls to
pspell_new()and otherpspell_*functions;extension_loaded()checks; “undefined function” errors when the extension is absent. - What to do
- Install PSpell from PECL if you still need it, since php.net says it moved there.
- Why it’s rare (our view)
- WordPress: Spell-checking in WordPress is normally done in the browser. Custom apps: Server-side spell-checking with pspell is uncommon.
Deprecations · 9
ldap_connect() with separate hostname and port deprecatedCalling
ldap_connect()with separate hostname and port arguments is deprecated.- How to tell
ldap_connect($host, $port)with two arguments;E_DEPRECATED.- What to do (our suggestion)
- See the
ldap_connect()manual page for the supported call forms. - Why it’s rare (our view)
- WordPress: Only LDAP/SSO login plugins call it. Custom apps: Possible in apps with LDAP or Active Directory login.
Negative width in mb_strimwidth() deprecatedPassing a negative width to
mb_strimwidth()is now deprecated.- How to tell
mb_strimwidth()calls whose width argument is negative or computed and may go negative.- What to do (our suggestion)
- Pass a non-negative width.
- Why it’s rare (our view)
mb_strimwidth()is used for excerpts, but negative widths are unusual.
ReflectionProperty::setValue() with one parameter deprecatedCalling
ReflectionProperty::setValue()with only one parameter is deprecated.- How to tell
ReflectionProperty::setValue($value)with a single argument, typically for static properties; often in tests, DI or ORM code.- What to do
- To set static properties, pass null as the first parameter:
setValue(null, $value). - Why it’s rare (our view)
- WordPress: Reflection is rare in plugins. Custom apps: Test suites and older libraries may set static properties this way.
assert_options() function deprecatedThe
assert_options()function is now deprecated.- How to tell
- Calls to
assert_options();E_DEPRECATED. - What to do
- php.net names no direct replacement; for the related
assert.*INI settings it says to usezend.assertionsinstead. - Why it’s rare (our view)
- Assertion configuration is uncommon in production plugin and app code.
ASSERT_* constants deprecatedThe
ASSERT_ACTIVE,ASSERT_BAIL,ASSERT_CALLBACK,ASSERT_EXCEPTIONandASSERT_WARNINGconstants have been deprecated.- How to tell
- References to
ASSERT_ACTIVE,ASSERT_BAIL,ASSERT_CALLBACK,ASSERT_EXCEPTIONorASSERT_WARNING. - What to do (our suggestion)
- Use the
zend.assertionsINI setting, which php.net recommends in place of the relatedassert.*settings. - Why it’s rare (our view)
- Usually appear alongside
assert_options(), which is uncommon.
assert.* INI settings deprecated
assert.active,assert.bail,assert.callback,assert.exceptionandassert.warningare deprecated; no notice is emitted when a setting equals its default.- How to tell
assert.*lines inphp.inior.user.ini, orini_set('assert.…')calls with non-default values.- What to do
- The
zend.assertionsINI setting should be used instead (per the Changes to INI File Handling section). - Why it’s rare (our view)
- Only non-default values warn; few sites change them.
SQLite3::enableExceptions(false) triggers deprecationExceptions are now preferred and warnings will be removed; calling
SQLite3::enableExceptions(false)triggers a deprecation warning.- How to tell
SQLite3::enableExceptions(false)calls.- What to do (our suggestion)
- Use exceptions: stop calling
enableExceptions(false)and handle exceptions instead. - Why it’s rare (our view)
- WordPress: Few plugins use the SQLite3 class directly. Custom apps: Only apps that use the SQLite3 class rather than
PDO.
ReflectionMethod constructor with one argument deprecatedCalling
ReflectionMethod::__construct()with one argument is deprecated.- How to tell
new ReflectionMethod(...)with a single argument.- What to do
- Use
ReflectionMethod::createFromMethodName()instead. - Why it’s rare (our view)
- Mostly found in framework and library internals, which have their own release cycles.
stream_context_set_option() with two arguments deprecatedCalling
stream_context_set_option()with two arguments is deprecated.- How to tell
stream_context_set_option($context, $optionsArray).- What to do
- Use
stream_context_set_options()instead. - Why it’s rare (our view)
- The two-argument array form is less common than the four-argument form.
Other changes · 6
++/-- on bool values now emits warnings
++and--on bool values now emit warnings: they have no effect today but will behave like$bool += 1in future.- How to tell
- Warnings from
++or--applied to variables holding true or false. - What to do (our suggestion)
- Do not increment or decrement booleans; use an explicit int.
- Why it’s rare (our view)
- Usually a bug where a flag and counter share a variable.
-- on null values now emits warnings
--on null values now emits a warning: it has no effect today but will behave like$null -= 1in future.- How to tell
- Warnings from
--applied to an uninitialized or null variable. - What to do (our suggestion)
- Initialize the variable to an int before decrementing.
- Why it’s rare (our view)
- Decrementing an unset variable is uncommon.
strtok() warns when token omitted at start
strtok()raises anE_WARNINGwhen token is not provided when starting tokenization.- How to tell
strtok($string)called with one argument as the first call of a tokenization;E_WARNING.- What to do (our suggestion)
- Pass both the string and the token on the first
strtok()call. - Why it’s rare (our view)
- Misuse of
strtok()is uncommon.
mb_detect_encoding() non-strict mode now matches documentation
mb_detect_encoding()in non-strict mode now returns the closest matching encoding, as documented, instead of its previous elimination behavior; results may differ.- How to tell
mb_detect_encoding()calls with strict set to false (the default); code that checks for a false return.- What to do (our suggestion)
- Re-test encoding detection on representative input, or use strict mode.
- Why it’s rare (our view)
- WordPress: Detected encodings change only for invalid or ambiguous input. Custom apps: Possible in apps that import CSV, email or legacy-encoded text.
DOMDocument load methods have tentative bool return type
DOMDocument::loadHTML(),loadHTMLFile()andloadXML()now have a tentative return type of bool, replacing the documentedDOMDocument|bool.- How to tell
- Classes extending
DOMDocumentthat overrideloadHTML(),loadHTMLFile()orloadXML(). - What to do (our suggestion)
- Declare overriding methods with a compatible bool return type.
- Why it’s rare (our view)
- Only plugins with
DOMDocumentsubclasses that override these methods.
imagerotate() ignore_transparent parameter removedThe signature of
imagerotate()changed: theignore_transparentparameter was removed, as it was ignored since PHP 5.5.0.- How to tell
imagerotate()calls passing a fourth argument.- What to do (our suggestion)
- Drop the
ignore_transparentargument fromimagerotate()calls; it had no effect since PHP 5.5.0. - Why it’s rare (our view)
- Image plugins call
imagerotate(), but rarely with the fourth argument.
What this page leaves out
The pages of each guide we read on 26 October 2026, and how many of their changes are here:
| Guide | Page | Here |
|---|---|---|
| PHP 8.3 | Backward Incompatible Changes | 15 |
| PHP 8.3 | Deprecated Features | 9 |
| PHP 8.3 | Other Changes | 9 |
| PHP 8.4 | Backward Incompatible Changes | 16 |
| PHP 8.4 | Deprecated Features | 11 |
| PHP 8.4 | Removed Extensions | 4 |
| PHP 8.4 | Other Changes | 2 |
Out of scope
The guides’ pages of new features, classes, functions and constants (additions, not breaks), and PHP 8.3’s Windows support page.
Judged not to apply
The rest are changes a typical plugin, theme or app doesn’t meet, such as ones for extension authors, internal APIs or Windows builds. If your code uses one, its page has it.
No grand total
php.net’s pages mix headings and paragraphs, so a count of “all changes” depends on how you count. We give the number on this page instead.
Sources
php.net’s two migration guides, every page read in full on the day shown, and what WordPress and WooCommerce say about PHP 8.4.
- Migrating from PHP 8.2.x to PHP 8.3.xphp.net, checked
- PHP 8.3: Backward Incompatible Changesphp.net, checked
- PHP 8.3: Deprecated Featuresphp.net, checked
- PHP 8.3: Other Changesphp.net, checked
- Migrating from PHP 8.3.x to PHP 8.4.xphp.net, checked
- PHP 8.4: Backward Incompatible Changesphp.net, checked
- PHP 8.4: Deprecated Featuresphp.net, checked
- PHP 8.4: Removed Extensionsphp.net, checked
- PHP 8.4: Other Changesphp.net, checked
- PHP compatibility and WordPress versionsWordPress.org, checked
- Server requirementsWooCommerce, checked
About this page
Cite this page
SnowDriven. “PHP 8.2 to 8.4: what breaks in WordPress plugins, themes and PHP apps.” SnowDriven Research. https://snowdriven.com/research/php-8-4-breaking-changes/ (last updated 26 October 2026).
One click or tap selects the whole citation, ready to copy.
About these facts
Each entry summarizes the PHP project’s own page for that change, linked and checked on the day shown; the likely, possible and rare ratings, and the reasons for them, are ours. The PHP project can change its lists and pages without notice, and our summary can fall behind or contain mistakes, so read the linked page before you change a system. Information only, provided as is (terms). Reuse: free to quote, with credit to SnowDriven and a link (terms of use).
Start a project
Want this list checked against your site or app?
We can go through your plugins, theme or codebase with it: which entries apply, what each takes to fix, and the order to do it in. Tell us whether it’s a WordPress site, a store or a custom app; we reply within one business day.
Pittsburgh, PA · Since 2015 · First step: a 30-minute call