## 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.

#### Removal of `E_STRICT` error level

The `E_STRICT` error level has been removed, as it was no longer in use within the PHP engine. The `E_STRICT` constant has been deprecated.

### Extension Class Constants which are now Typed

The following extension class constants now declare a type on their constants:

- Date
- Intl
- PDO
- Reflection
- SPL
- Sqlite
- XMLReader

### 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 `signals` array is empty (except for pcntl_sigprocmask() if the `mode` `SIG_SETMASK`)
- A TypeError if a value of the `signals` array is not an int
- A ValueError if a value of the `signals` array 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 `seconds` is less than `0`
- A ValueError if `nanoseconds` is less than `0` or greater than `1e9`
- A ValueError if both `seconds` and `nanoseconds` are `0`

#### 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:

`<?php
$xmlString = "<root><a><b>1</b><b>2</b><b>3</b></a></root>";
$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.

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open