PHP 8.4
Backward Incompatible Changes
Although not explicitly stated in this section, every new function, class, interface, enum, or constant may cause a redeclaration Error to be thrown.
PHP Core
exit() behavioral change
    The exit() (and die()) language
    constructs now behave more like a function.
    This means that they can now be passed like callables,
    
    are affected by the strict_types declare statement,
    and now perform the usual type coercions instead of casting any non-integer
    value to a string.
   
As such, passing invalid types to exit() and die() now consistently result in a TypeError being thrown.
Recursion during comparison
    Encountering recursion during comparison now results in an
    Error exception instead of a
    E_ERROR fatal error.
   
Indirect Modification of readonly Properties
    Indirect modification of readonly properties within __clone()
    is no longer allowed, e.g. $ref = &$this->readonly.
    This was already forbidden for readonly initialization, and was an
    oversight in the "readonly reinitialization during cloning" implementation.
   
Type Change of Constants
    The PHP_DEBUG and PHP_ZTS
    constants are now of type bool.
    Previously they were of type int.
   
Temporary Filename Length
The name of uploaded files and files created by the tempnam() function are now 13 bytes longer. The total length is still platform-dependent.
Extension Class Constants which are now Typed
The following extension class constants now declare a type on their constants:
Resource to Object Migration
   Several resources have been migrated to objects.
   Return value checks using is_resource() should be
   replaced with checks for false, unless specified otherwise.
  
DBA
    The DBA functions now accept and return
    Dba\Connection objects instead of
    dba_connection resources.
   
ODBC
    The ODBC functions now accept and return
    Odbc\Result objects instead of
    odbc_result resources.
   
    The ODBC functions now accept and return
    Odbc\Connection objects instead of
    odbc_connection resources.
   
SOAP
    The SoapClient::$httpurl property is now a
    Soap\Url object rather than a
    soap_url resource.
    Checks using is_resource() (i.e.
    is_resource($client->httpurl)) should be  replaced with checks
    for null (i.e. $client->httpurl !== null).
   
    The SoapClient::$sdl property is now a
    Soap\Sdl object rather than a
    soap_sdl resource.
    Checks using is_resource() (i.e.
    is_resource($client->sdl)) should be  replaced with checks
    for null (i.e. $client->sdl !== null).
   
New warnings and exceptions
New warnings and exceptions have been added which are triggered on programming errors, i.e. invalid values provided as arguments.
Curl
    curl_multi_select() now throws a
    ValueError if the
    timeout parameter is less than
    0 or greater than PHP_INT_MAX.
   
Gd
    imagejpeg(), imagewebp(), imagepng(), imageavif()
    now throw a ValueError when an invalid
    quality is passed.
   
    imageavif() will now throw a
    ValueError if an invalid
    speed parameter value is passed.
   
    imagescale() will now throw a
    ValueError if the
    width or height
    parameters underflows/overflows.
   
    imagescale() will now throw a
    ValueError if an invalid
    mode parameter value is passed.
   
    imagefilter() will now throw a
    ValueError with the
    IMG_FILTER_SCATTER filter
    if the sub or plus
    parameters underflows/overflows.
   
Gettext
    bind_textdomain_codeset(), textdomain(), d()*gettext()
    now throw a ValueError if
    domain is the empty string.
   
Intl
resourcebundle_get(), ResourceBundle::get(), and accessing offsets on a ResourceBundle object now throw:
- TypeError for invalid offset types
- ValueError for an empty string
- ValueError if the integer index does not fit in a signed 32 bit integer
    IntlDateFormatter::__construct() throws a
    ValueError if the
    locale is invalid.
   
    NumberFormatter::__construct() throws a
    ValueError if the
    locale is invalid.
   
MBString
    mb_encode_numericentity() and
    mb_decode_numericentity() now check that the
    map is only composed of ints,
    if not a ValueError is now thrown.
   
    mb_http_input() now always throw a
    ValueError if type
    is invalid.
   
    mb_http_output() now check that the
    encoding does not contain any null bytes,
    if it does a ValueError is now thrown.
   
ODBC
    odbc_fetch_row() returns false when
    row is less than or equal to 0.
    A warning is now emitted in this case.
   
PCNTL
The pcntl_sigprocmask(), pcntl_sigwaitinfo(), and pcntl_sigtimedwait() functions now throw:
- 
      A ValueError if the
      signalsarray is empty (except for pcntl_sigprocmask() if themodeSIG_SETMASK)
- 
      A TypeError if a value of the
      signalsarray is not an int
- 
      A ValueError if a value of the
      signalsarray is not a valid signal number
    The pcntl_sigprocmask() function now throws a
    ValueError if the
    mode is not one of SIG_BLOCK,
    SIG_UNBLOCK, or SIG_SETMASK.
   
The pcntl_sigtimedwait() function now throw:
- 
      A ValueError if
      secondsis less than0
- 
      A ValueError if
      nanosecondsis less than0or greater than1e9
- 
      A ValueError if both
      secondsandnanosecondsare0
Session
Setting a non-positive value for session.gc_divisor or a negative value for session.gc_probability now emits a warning.
SimpleXML
Calling simplexml_import_dom() with a non-XML object now throws a TypeError instead of a ValueError.
Standard
    The round() function now validates the value of the
    mode and throw a
    ValueError for invalid modes.
    Previously, invalid modes would have been interpreted as
    PHP_ROUND_HALF_UP.
   
    The str_getcsv() now throws
    ValueErrors when the
    separator and enclosure
    arguments are not one byte long, or if the escape
    argument is not one byte long nor the empty string.
    This aligns the behaviour to be identical to the one of
    fputcsv() and fgetcsv().
   
    The php_uname() function now throws a
    ValueError if the
    mode is invalid.
   
    The "allowed_classes" option for
    unserialize() now throws
    TypeErrors and
    ValueErrors if it is not an
    array of class names.
   
XMLReader
Passing an invalid character encoding to XMLReader::open() or XMLReader::XML() now throws a ValueError.
Passing a string containing null bytes previously emitted a warning and now throws a ValueError.
XMLWriter
Passing a string containing null bytes previously emitted a warning and now throws a ValueError.
XSL
XSLTProcessor::setParameter() will now throw a ValueError when its arguments contain null bytes. This never actually worked correctly in the first place, which is why it throws an exception now.
Calling XSLTProcessor::importStyleSheet() with a non-XML object now throws a TypeError instead of a ValueError.
Failure to call a PHP function callback during evaluation now throws instead of emitting a warning.
Date
   number symbols in relative formats
   once again accept multiple signs, e.g. +-2.
  
DOM
   Some DOM methods previously returned false or a
   DOMException with code
   DOM_PHP_ERR if a new node could not be allocated.
   They now consistently throw a DOMException
   with code DOM_INVALID_STATE_ERR.
   This situation is extremely unlikely and the probability of being affected
   is low.
   As a result DOMImplementation::createDocument()
   now has a tentative return type of DOMDocument
   
   instead of DOMDocument|false.
  
Previously, DOMXPath objects could be cloned, but resulted in an unusable object. This is no longer possible, and cloning a DOMXPath object now throws an Error.
The DOMImplementation::getFeature() method has been removed.
GMP
The GMP class is now final and cannot be extended anymore.
MBString
On invalid strings (those with encoding errors), mb_substr() now interprets character indices in the same manner as most other mbstring functions. This means that character indices returned by mb_strpos() can be passed to mb_substr().
For SJIS-Mac (MacJapanese) strings, character indices passed to mb_substr() now refer to the indices of the Unicode codepoints which are produced when the string is converted to Unicode. This is significant because around 40 SJIS-Mac characters convert to a sequence of multiple Unicode codepoints.
MySQLi
   The unused and undocumented constant
   MYSQLI_SET_CHARSET_DIR has been removed.
  
   The MYSQLI_STMT_ATTR_PREFETCH_ROWS constant has been
   removed. The feature is unavailable with mysqlnd.
  
   The MYSQLI_CURSOR_TYPE_FOR_UPDATE and
   MYSQLI_CURSOR_TYPE_SCROLLABLE constants have been
   removed. This functionality was never implemented,
   neither with mysqlnd nor with libmysql.
  
   The unused MYSQLI_TYPE_INTERVAL constant, which is
   currently a stub and an alias for MYSQLI_TYPE_ENUM,
   has been removed.
   
  
MySQLnd
   The error code reported for MySQL server wait timeouts has been changed from
   2006 to 4031 for MySQL server
   versions 8.0.24 and above.
  
Opcache
   The maximum value of the
   opcache.interned_strings_buffer
   setting on 64bit architectures is now 32767.
   Previously it was 4095.
  
JIT
    The default configuration values for the JIT changed from
    opcache.jit=tracing
    and opcache.jit_buffer_size=0
    to opcache.jit=disable
    and opcache.jit_buffer_size=64M, respectively.
   
This does not affect the default observable behavior, as the JIT is still disabled by default. However, it is now disabled through the opcache.jit setting, rather than opcache.jit_buffer_size. This may affect users who previously enabled JIT through opcache.jit_buffer_size exclusively, without also specifying a JIT mode using opcache.jit. To enable JIT compilation, set the opcache.jit config value accordingly.
If JIT compilation is enabled, PHP will now exit with a fatal error on startup if the initialization of the JIT compiler failed for any reason.
PCNTL
   The pcntl_sigprocmask(),
   pcntl_sigwaitinfo(), and
   pcntl_sigtimedwait() functions now always
   return false on failure.
   In some case previously it could return the value -1.
  
PCRE
   The bundled pcre2lib has been updated to version 10.44.
   As a consequence, this means {,3} is now recognized
   as a quantifier instead of as text.
   Furthermore, the meaning of some character classes in UCP mode has changed.
   Consult the » PCRE2 Changelog
   for a full changelog.
  
PDO_DBLIB
   The Pdo\Dblib::ATTR_STRINGIFY_UNIQUEIDENTIFIER and
   Pdo\Dblib::ATTR_DATETIME_CONVERT attributes now act as
   boolean attributes instead of integer attributes.
   Thus setting the attribute via PDO::setAttribute()
   and retrieving it via PDO::getAttribute() expects
   and or return a bool.
  
PDO_FIREBIRD
   The PDO::ATTR_AUTOCOMMIT attribute now act as
   boolean attributes instead of integer attributes.
   Thus setting the attribute via PDO::setAttribute()
   and retrieving it via PDO::getAttribute() expects
   and or return a bool.
  
The extension now exposes some Firebird C++ APIs, therefore building this extension now requires a C++ compiler. Moreover, the extension must now be compiled against fbclient 3.0 or higher.
PDO_MYSQL
   The PDO::ATTR_AUTOCOMMIT,
   PDO::ATTR_EMULATE_PREPARES, and
   PDO::MYSQL_ATTR_DIRECT_QUERY attributes now act as
   boolean attributes instead of integer attributes.
   Thus setting the attribute via PDO::setAttribute()
   and retrieving it via PDO::getAttribute() expects
   and or return a bool.
  
PDO_PGSQL
The DSN's credentials, when set, are given priority over their PDO constructor counterparts, being closer to the documentation states.
SimpleXML
SimpleXMLElement is not only a representation of an XML element, but it is also a RecursiveIterator. Prior to PHP 8.4.0, some of its methods (e.g. SimpleXMLElement::asXML() or SimpleXMLElement::getName()) and casting such instances to string would implicitly reset the iterator.
This could cause unexpected infinite loops as the iterator was rewound. For example:
$xmlString = "123 ";
$xml = simplexml_load_string($xmlString);
$nodes = $xml->a->b;
foreach ($nodes as $nodeData) {
    echo "nodeData: " . $nodeData . "\n";
$xml = $nodes->asXml();
}would result in an infinite loop.
nodeData: 1 nodeData: 2 nodeData: 2 nodeData: 2 nodeData: 2 nodeData: 2 nodeData: 2 // ...
However, this behavior has now been corrected, and a SimpleXMLElement will no longer implicitly reset the iterator data, unless explicitly rewound. Meaning the previous example would now result in:
nodeData: 1 nodeData: 2 nodeData: 3
SOAP
   SoapClient::$typemap is now an array
   rather than a resource.
   Checks using is_resource() (i.e.
   is_resource($client->typemap)) should be
   replaced with checks for null (i.e. $client->typemap !== null).
  
The SOAP extension gained an optional dependency on the session extension. If PHP is built without the session extension and with the --enable-rtld-now configure flag enabled, startup errors will now occur if the SOAP extension is also used. To solve this either don't use rtld-now or load the session extension.
Standard
   When using strcspn() with
   characters being the empty string,
   the length of the string is now returned instead of incorrectly stopping
   at the first null byte.
   
  
http_build_query() now correctly handles backed enums.
stream_bucket_make_writeable() and stream_bucket_new() will now return a StreamBucket instance instead of an instance of stdClass.
Tidy
Failures in the constructor now throw exceptions rather than emitting warnings and having a broken object.
XML
   The xml_set_()*_handler()
   functions now declare and check for an effective
   signature of callable|string|null for the
   handler parameters.
   Moreover, values of type string that correspond to method names,
   of object set with xml_set_object() are now checked to
   see if the method exists on the class of the previously passed object.
   This means that xml_set_object() must now always be
   called prior to setting method names as callables.
   Passing an empty string to disable the handler is still allowed,
   but deprecated.
  
However, as xml_set_object() and passing non-callable strings is deprecated. It is recommended to change such instances with a callable referring to the method directly.