## 不向后兼容的变更

尽管本节没有明确说明，但每个新函数、类、interface、枚举或常量都可能导致抛出重复声明 Error。

### PHP 核心

#### exit() 行为变更

exit()（和 die()）语言结构现在的行为更像函数。这意味着现在可以传递 callable，受 `strict_types` 声明语句的影响，并且现在执行往常的类型强制，而不是将任何非整数值转换为字符串。

因此，将无效类型传递给 exit() 和 die() 现在会导致抛出 TypeError。

#### 比较过程中的递归

比较期间遇到递归现在会导致 Error 异常，而不是 `E_ERROR` 致命错误。

#### 间接修改 readonly 属性

不再允许在 `__clone()` 里间接修改 readonly 属性，例如 `$ref = &$this->readonly`。这在 readonly 初始化中已禁止，并且是“克隆期间 readonly 重新初始化”实现中的一个疏忽。

#### 常量的类型变更

`PHP_DEBUG` 和 `PHP_ZTS` 常量现在为 bool 类型。以前为 int 类型。

#### 临时文件名长度

上传文件和 tempnam() 函数创建的文件名现在为 13 个字节。总长度仍取决于平台。

#### 移除 `E_STRICT` 错误级别

已移除 `E_STRICT` 错误级别，因为其在 PHP 引擎中不再使用。已弃用 `E_STRICT` 常量。

### 扩展类常量现已类型化

以下扩展类常量现在在其常量上声明类型：

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

### 迁移资源到对象

多个 resource 已迁移到 object。除非另有说明，否则 is_resource() 的返回值检查应替换为 `false` 检查。

#### DBA

DBA 函数现在接受并返回 Dba\Connection 对象，而不是 `dba_connection` resource。

#### ODBC

ODBC 函数现在接受并返回 Odbc\Result 对象，而不是 `odbc_result` resource。

ODBC 函数现在接受并返回 Odbc\Connection 对象，而不是 `odbc_connection` resource。

#### SOAP

SoapClient::$httpurl 属性现在是 Soap\Url 对象，而不是 `soap_url` resource。 检查使用 is_resource()（即 `is_resource($client->httpurl)`）应该替换为检查 是否为 `null`（即 `$client->httpurl !== null`）。

SoapClient::$sdl 属性现在是 Soap\Sdl 对象，而不是 `soap_sdl` resource。 检查使用 is_resource()（即 `is_resource($client->sdl)`）应该替换为检查 是否为 `null`（即 `$client->sdl !== null`）。

### 新的警告和异常

添加了因编程错误（即将无效值提供给参数）而触发新的警告和异常。

#### Curl

如果 `timeout` 参数小于 `0` 或大于 `PHP_INT_MAX`，curl_multi_select() 现在会抛出 ValueError。

#### Gd

当传入无效的 `quality` 时，imagejpeg(), imagewebp(), imagepng(), imageavif() 现在抛出 ValueError。

当传递无效的 `speed` 参数值时，imageavif() 现在将抛出 ValueError。

如果 `width` 或 `height` 参数溢出，imagescale() 现在将抛出 ValueError。

当传入无效的 `mode` 参数时，imagescale() 现在将抛出 ValueError。

如果 `sub` 或 `plus` 参数溢出，imagefilter() 现在使用 `IMG_FILTER_SCATTER` 过滤器将抛出 ValueError。

#### Gettext

如果 `domain` 是空字符串，bind_textdomain_codeset(), textdomain(), d*gettext 现在 抛出 ValueError。

#### Intl

resourcebundle_get()、ResourceBundle::get() 和在 ResourceBundle 对象上访问偏移量现在会抛出：

- 无效偏移类型的 TypeError
- 空 string 的 ValueError
- 如果整数索引不符合有符号的 32 位整数，则 ValueError

如果 `locale` 无效，则 IntlDateFormatter::__construct() 抛出 ValueError。

如果 `locale` 无效，则 NumberFormatter::__construct() 抛出 ValueError。

#### MBString

mb_encode_numericentity() 和 mb_decode_numericentity() 现在检测 `map` 是否仅由 int 构成，如果不是，则抛出 ValueError。

如果 `type` 无效，mb_http_input() 现在始终抛出 ValueError。

mb_http_output() 现在检测 `encoding` 是不是不包含 null 字节，如果包含，会抛出 ValueError。

#### ODBC

当 `row` 小于或等于 `0` 时，odbc_fetch_row() 返回 `false`。在这种情况下会发出 warning。

#### PCNTL

pcntl_sigprocmask()、pcntl_sigwaitinfo() 和 pcntl_sigtimedwait() 函数现在会抛出：

- 如果 `signals` 数组为空，则抛出 ValueError（如果 `mode` `SIG_SETMASK` 则 pcntl_sigprocmask() 除外）
- 如果 `signals` 数组的值不是 int，则抛出 TypeError
- 如果 `signals` 数组不是有效的信号编号，则抛出 ValueError

如果 `mode` 不是 `SIG_BLOCK`、`SIG_UNBLOCK` 或 `SIG_SETMASK` 中的一个，pcntl_sigprocmask() 现在将抛出 ValueError。

pcntl_sigtimedwait() 函数现在将抛出：

- 如果 `seconds` 小于 `0` 则抛出 ValueError
- 如果 `nanoseconds` 小于 `0` 或者大于 `1e9`，则抛出 ValueError
- 如果 `seconds` 和 `nanoseconds` 是 `0`，则抛出 ValueError

#### Session

设置一个非正值的 session.gc_divisor 或者一个负值的 session.gc_probability 现在会发出警告。

#### SimpleXML

使用非 XML 对象调用 simplexml_import_dom() 将抛出 TypeError 而不是 ValueError

#### 标准

round() 函数现在会验证 `mode` 的值，如果无效，则抛出 ValueError。之前无效模式会解释为 `PHP_ROUND_HALF_UP`。

当 `separator` 和 `enclosure` 参数值的长度不为一个字节，或者 `escape` 参数值不为一个字节或者空字符串，str_getcsv() 现在将抛出 ValueError。该行为已与 fputcsv() 和 fgetcsv() 行为相同。

如果 `mode` 无效，php_uname() 函数现在会抛出 ValueError。

unserialize() 的 `"allowed_classes"` 选项如果不是类名 array，现在会抛出 TypeError 和 ValueError。

#### XMLReader

传递无效的字符编码给 XMLReader::open() 或 XMLReader::XML() 现在会引发 ValueError。

传递包含 null 字节的 string，之前会发出 warning，现在会引发 ValueError。

#### XMLWriter

传递包含 null 字节的 string，之前会发出 warning，现在会引发 ValueError。

#### XSL

XSLTProcessor::setParameter() 现在会在参数包含 null 字节时抛出 ValueError。这实际上从一开始就没有正确运行过，这就是它现在抛出异常的原因。

现在，使用非 XML 对象调用 XSLTProcessor::importStyleSheet() 会引发 TypeError 而不是 ValueError。

如果在求值期间调用 PHP 函数回调失败，则现在会抛出而不是发出 warning。

### Date

相对格式的 `number` 符号再次接受多个符号，例如 `+-2`。

### DOM

如果无法分配新节点，某些 DOM 方法以前会返回 `false` 或带有 code `DOM_PHP_ERR` 的 DOMException。现在，它们会一致地抛出带有 code `DOM_INVALID_STATE_ERR` 的 DOMException。这种情况极少发生，受影响的可能性也很低。因此，DOMImplementation::createDocument() 现在暂定返回类型为 DOMDocument，而不是 `DOMDocument|false`。

以前，可以克隆 DOMXPath 对象，但会导致对象不可用。现在则不可以，克隆 DOMXPath 对象会引发 Error。

已移除 DOMImplementation::getFeature() 方法。

### GMP

GMP 类现在是 final，并且再也不能继承。

### MBString

对于无效字符串（包含编码错误），mb_substr() 现在以与大多数其他 mbstring 函数相同的方式解释字符索引。这意味着 mb_strpos() 返回的字符索引可以传递给 mb_substr()。

对于 SJIS-Mac (MacJapanese) 字符串，传递给 mb_substr() 的字符索引现在指向的是将字符串转换为 Unicode 时生成的 Unicode 码点的索引。这很重要，因为大约 40 个 SJIS-Mac 字符在转换为 Unicode 时会变成多个 Unicode 代码点的序列。

### MySQLi

已移除未使用和未记录的常量 `MYSQLI_SET_CHARSET_DIR`。

已移除 `MYSQLI_STMT_ATTR_PREFETCH_ROWS` 常量。mysqlnd 无法使用该功能。

已移除 `MYSQLI_CURSOR_TYPE_FOR_UPDATE` 和 `MYSQLI_CURSOR_TYPE_SCROLLABLE` 常量。此功能从未实现过，无论是 mysqlnd 还是 libmysql。

已移除未使用的 `MYSQLI_TYPE_INTERVAL` 常量（当前是 `MYSQLI_TYPE_ENUM` 的存根（stub）和别名）。

### MySQLnd

对于 MySQL 服务器 8.0.24 及以上版本，MySQL 服务器等待超时报告的错误 code 已从 `2006` 更改为 `4031`。

### Opcache

64 位架构上 opcache.interned_strings_buffer 设置的最大值现在是 `32767`。以前是 `4095`。

#### JIT

JIT 的默认配置值分别从 `opcache.jit=tracing` 和 `opcache.jit_buffer_size=0` 更改为 `opcache.jit=disable` 和 `opcache.jit_buffer_size=64M`。

这不会影响默认的可观察行为，因为默认情况下 JIT 仍处于禁用状态。但是，现在是通过 opcache.jit 设置而不是 opcache.jit_buffer_size 禁用。这可能会影响之前仅通过 opcache.jit_buffer_size 启用 JIT 的用户，而无需使用 opcache.jit 指定 JIT 模式。要启用 JIT 编译，请相应地设置 opcache.jit 配置值。

如果启用了 JIT 编译，当 JIT 编译器的初始化因任何原因而失败时，PHP 将在启动时退出并出现致命错误。

### PCNTL

pcntl_sigprocmask()、pcntl_sigwaitinfo() 和 pcntl_sigtimedwait() 函数现在在失败时始终返回 `false`。以前在某些情况下，它可能返回值 `-1`。

### PCRE

捆绑的 pcre2lib 已更新至版本 10.44。因此，这意味着 `{,3}` 现在识别为量词而不是文本。此外，UCP 模式下某些字符类的含义已发生改变。请参阅 » PCRE2 变更日志，了解完整的变更日志。

### PDO_DBLIB

`Pdo\Dblib::ATTR_STRINGIFY_UNIQUEIDENTIFIER` 和 `Pdo\Dblib::ATTR_DATETIME_CONVERT` 属性现在是 bool 属性，而不是 int 属性。因此，通过 PDO::setAttribute() 设置属性并通过 PDO::getAttribute() 检索属性并返回 bool 值。

### PDO_FIREBIRD

`PDO::ATTR_AUTOCOMMIT` 属性现在是 bool 属性，而不是 int 属性。因此，通过 PDO::setAttribute() 设置属性并通过 PDO::getAttribute() 检索属性并返回 bool 值。

该扩展现在公开了一些 Firebird C++ API，因此编译此扩展现在需要 C++ 编译器。此外，现在必须针对 fbclient 3.0 或更高版本编译该扩展。

### PDO_MYSQL

`PDO::ATTR_AUTOCOMMIT`、`PDO::ATTR_EMULATE_PREPARES` 和 `PDO::MYSQL_ATTR_DIRECT_QUERY` 属性现在是 bool 属性，而不是 int 属性。因此，通过 PDO::setAttribute() 设置属性并通过 PDO::getAttribute() 检索属性并返回 bool 值。

### PDO_PGSQL

DSN 的凭证在设置时优先于 PDO 构造方法中的凭证，更接近文档状态。

### SimpleXML

SimpleXMLElement 不仅是 XML 元素的表示，而且还是 RecursiveIterator。在 PHP 8.4.0 之前，它的某些方法（例如 SimpleXMLElement::asXML() 或 SimpleXMLElement::getName()）以及将此类实例转换 string 会隐式重置迭代器。

这可能会导致意外的无限循环，因为已倒回迭代器。例如：

    <?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();
    }

会导致无限循环。

    nodeData: 1
    nodeData: 2
    nodeData: 2
    nodeData: 2
    nodeData: 2
    nodeData: 2
    nodeData: 2
    // ...

但是，此行为现已得到纠正，除非明确倒回，否则 SimpleXMLElement 将不再隐式重置迭代器数据。这意味着前面的示例现在将导致：

    nodeData: 1
    nodeData: 2
    nodeData: 3

### SOAP

SoapClient::$typemap 现在是 array，而不是 resource。使用 is_resource() 检查（即 `is_resource($client->typemap)`）应替换为对 `null` 的检查（即 `$client->typemap !== null`）。

SOAP 扩展获得了对 session 扩展的可选依赖。如果在编译 PHP 时未使用 session 扩展，并且启用了 --enable-rtld-now 配置 flag，则如果还使用了 SOAP 扩展，则将发生启动错误。要解决此问题，请不要使用 rtld-now 或加载 session 扩展。

### 标准

当使用 strcspn() 并将 `characters` 设为空字符串时，现在将返回字符串的长度，而不是错误地停止在第一个 null 字节处。

http_build_query() 现在可以正确处理支持的枚举。

stream_bucket_make_writeable() 和 stream_bucket_new() 现在将返回 StreamBucket 实例而不是 stdClass 实例。

### Tidy

构造方法失败现在会引发 exception，而不是发出 warning 并导致对象损坏。

### XML

xml_set_*_handler 函数现在声明并检查 `handler` 参数的 callable|string|null 有效签名。此外，现在检查与使用 xml_set_object() 设置的对象方法名称相对应的 string 类型的值，以查看该方法是否存在于先前传递的对象的类中。这意味着现在必须始终在将方法名称设置为 callable 之前调用 xml_set_object()。传递空字符串来禁用处理程序仍然是允许的，但已弃用。

然而，由于 xml_set_object() 和传递非 callable 的字符串已弃用。建议使用直接引用该方法的 callable 来更改此类实例。

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