## 不向后兼容的变更

### PHP 内核

#### $GLOBALS 访问限制

现在访问 $GLOBALS 数组受到一些限制。对单个数组元素的读写访问 `$GLOBALS['var']` 与之前一样。也将继续支持对整个数组 $GLOBALS 的只读访问。但是，不再支持对整个 $GLOBALS 数组的写访问。例如 `array_pop($GLOBALS)` 将返回错误。

#### 在继承的方法中 static 变量的用法

当一个方法使用继承的（而不是重写的）静态变量时，继承的方法将与父级共享这个静态变量。

`<?php
class A {
public static function counter() {
static $counter = 0;
$counter++;
return $counter;
}
}
class B extends A {}
var_dump(A::counter()); // int(1)
var_dump(A::counter()); // int(2)
var_dump(B::counter()); // int(3)，之前是 int(1)
var_dump(B::counter()); // int(4)，之前是 int(2)
?>`
这意味着方法中的静态变量现在的使用方式与静态属性一样。

#### 在强制参数之前指定可选参数

现在在强制参数之前指定可选参数都被视为强制参数，即使是使用命名参数调用也是如此。自 PHP 8.0.0 起至 PHP 8.1.0 之前，在定义时会发出弃用通知，但能成功调用。自 PHP 8.1.0 起，会抛出类为 ArgumentCountError 的错误，就跟使用位置参数调用一样。

`<?php
function makeyogurt($container = "bowl", $flavour)
{
return "Making a $container of $flavour yogurt.\n";
}
try
{
echo makeyogurt(flavour: "raspberry");
}
catch (Error $e)
{
echo get_class($e), ' - ', $e->getMessage(), "\n";
}
?>`

以上示例在 PHP 8.0 中的输出：

    Deprecated: Required parameter $flavour follows optional parameter $container
     in example.php on line 3
    Making a bowl of raspberry yogurt.

以上示例在 PHP 8.1 中的输出：

    Deprecated: Optional parameter $container declared before required parameter
     $flavour is implicitly treated as a required parameter in example.php on line 3
    ArgumentCountError - makeyogurt(): Argument #1 ($container) not passed

注意，在强制参数之前可以使用 `null` 默认值来指定可为空类型，但仍然需要该参数。

#### 返回类型与内部类的兼容性

大多数非 final 的内部方法现在要求重写方法声明一个可兼容的返回类型，否则在继承时会给出方法废弃的提示。如果由于 PHP 跨版本兼容性的问题，导致不能为重写方法声明返回类型，则可以添加 ReturnTypeWillChange 注解来取消废弃提示。

#### 新的关键词

现在 `readonly` 是一个关键词。不过，它仍然可以被用作函数名。

`never` 现在是保留字，所以不能用于类，接口或者 trait，也禁止在命名空间中使用。

### Resource 类型迁移为 Object 类型

一些 resource 类型已被迁移到 object 类型。要检查返回值，应该从 is_resource() 检查是否为资源，更改为检查返回值是否等于 `false`。

- 现在 FileInfo 函数接收并返回 finfo 对象类型，而不是 `fileinfo` resource 类型。

- 现在 FTP 函数接收并返回 FTP\Connection 对象类型，而不是 `ftp` resource 类型。

- 现在 IMAP 函数接收并返回 IMAP\Connection 对象类型，而不是 `imap` resource 类型。

- 现在 LDAP 函数接收并返回 LDAP\Connection 对象类型，而不是 `ldap link` resource 类型。

- 现在 LDAP 函数接收并返回 LDAP\Result 对象类型，而不是 `ldap result` resource 类型。

- 现在 LDAP 函数接收并返回 LDAP\ResultEntry 对象类型，而不是 `ldap result entry` resource 类型。

- 现在 PgSQL 函数接收并返回 PgSql\Connection 对象类型，而不是 `pgsql link` resource 类型。

- 现在 PgSQL 函数接收并返回 PgSql\Result 对象类型，而不是 `pgsql result` resource 类型。

- 现在 PgSQL 函数接收并返回 PgSql\Lob 对象类型，而不是 `pgsql large object` resource 类型。

- 现在 PSpell 函数接收并返回 PSpell\Dictionary 对象类型，而不是 `pspell` resource 类型。

- 现在 PSpell 函数接收并返回 PSpell\Config 对象类型，而不是 `pspell config` resource 类型。

### MySQLi

现在 mysqli_fetch_fields() 和 mysqli_fetch_field_direct() 对于 max_length 将返回 `0` 值。这一信息可以迭代结果集来计算，并获取最大长度。这是之前 PHP 内部的做法。

常量 `MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH` 不再生效。

常量 `MYSQLI_STORE_RESULT_COPY_DATA` 不再生效。传递给 mysqli::store_result() 的 `mode` 参数的所有值不再生效。

现在 mysqli::connect() 成功时返回 `true` 而不再返回 `null`。

默认错误处理模式已经由 silent 变成了 exception，更多详情及如何设置该属性请参见 MySQLi 报告模式。要恢复之前的行为习惯，请使用：`mysqli_report(MYSQLI_REPORT_OFF);`

现在，扩展 mysqli_stmt::execute() 类需要指定额外的可选参数。

### MySQLnd

mysqlnd.fetch_data_copy INI 指令已被取消。这不会造成用户可见的变化。

### OpenSSL

现在 EC 私钥将以 PKCS#8 格式导出，而非像其他秘钥那样的传统格式。

现在 openssl_pkcs7_encrypt() 和 openssl_cms_encrypt() 将默认使用 AES-128-CBC，而非 RC2-40。RC2-40 加密被认为是不安全的，OpenSSL 3 默认不启用。

### PHP 数据对象

现在 `PDO::ATTR_STRINGIFY_FETCHES` 的类型从 bool 变成了字符串 `"0"` 或 `"1"`。之前的 bool 类型没有被字符串化。

现在当 `PDO::ATTR_STRINGIFY_FETCHES` 未启用时，以 `PDO::PARAM_LOB` 为参数调用 PDOStatement::bindColumn() 结果将始终绑定流。以前，结果是流或字符串，这取决于所用的数据库驱动及其执行绑定的时间。

#### MySQL 驱动

现在，当使用模拟预处理时，结果集里面的整数及浮点数将会以 PHP 原始类型返回，而不是字符串。这与原生的预处理方式一样。之前的行为方式可以通过 `PDO::ATTR_STRINGIFY_FETCHES` 恢复。

#### SQLite 驱动

现在，结果集中的整数及浮点数将会以 PHP 原始类型返回。之前的行为方式可以通过 `PDO::ATTR_STRINGIFY_FETCHES` 恢复。

### Phar

为了遵守 ArrayAccess 接口，Phar::offsetUnset() 和 PharData::offsetUnset() 不再以 bool 类型返回。

### 常规

version_compare() 函数不再接收未经记录的操作符缩写。

现在 htmlspecialchars()、htmlentities()、htmlspecialchars_decode()、html_entity_decode()、get_html_translation_table() 使用 `ENT_QUOTES | ENT_SUBSTITUTE` 作为默认值，而不再是 `ENT_COMPAT`。这意味着 `'` 被转义为 `&#039;` 而不像之前那样不作处理。此外，有缺陷的 UTF-8 将被 Unicode 替代字符（substitute character）替换，而不是产生一个空字符串。

现在 debug_zval_dump() 函数可以打印封装的引用计数了，不仅仅只是打印 `&` 引用计数的值。这更准确地模拟了自 PHP 7.0 以来的引用注解。

现在 debug_zval_dump() 可打印 `interned` 字符串，而不是 interned 字符串和不可变数组的虚拟的引用计数。

### PHP 标准库（SPL）

SplFixedArray 将会像 array 类型一样被 JSON 编码。

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