## 不向后兼容的变更

### PHP 核心中不向后兼容的变更

#### 字符串与数字的比较

数字与非数字形式的字符串之间的非严格比较现在将首先将数字转为字符串，然后比较这两个字符串。 数字与数字形式的字符串之间的比较仍然像之前那样进行。 请注意，这意味着 `0 == "not-a-number"` 现在将被认为是 false 。

比较 | 之前 | 之后
--- | --- | ---
`0 == "0"` | `true` | `true`
`0 == "0.0"` | `true` | `true`
`0 == "foo"` | `true` | `false`
`0 == ""` | `true` | `false`
`42 == " 42"` | `true` | `true`
`42 == "42foo"` | `true` | `false`

#### 其它不向后兼容的变更

- `match` 现在是一个保留字。

- 断言（Assertion）失败现在默认抛出异常。如果想要改回之前的行为，可以在 INI 设置中设置 `assert.exception=0` 。

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

- 与类名相同的方法名将不再被当做构造方法。应该使用 __construct() 来取代它。

- 不再允许通过静态调用的方式去调用非静态方法。因此 is_callable() 在检查一个类名与非静态方法时将返回失败（应当检查一个类的实例）。

- `(real)` 和 `(unset)` 转换已被移除。

- 移除 track_errors 执行。这意味着不能再用 php_errormsg。可以改用 error_get_last() 函数。

- 移除定义不区分大小写的常量功能。define() 的第三个参数可能不再为 `true`。

- 移除使用 __autoload() 函数指定自动加载器的功能。应该改用 spl_autoload_register()。

- `errcontext` 参数将不再传递给使用 set_error_handler() 设置的自定义错误处理程序。

- 移除 create_function()。应该改用匿名函数。

- 移除 each()。应该改用 `foreach` 或者 ArrayIterator。

- 移除在方法中使用 Closure::fromCallable() 或 ReflectionMethod::getClosure() 创建的匿名函数中解绑 this 的能力。

- 移除了从包含 this 使用的正常闭包中解绑 this 的能力。

- 移除对对象使用 array_key_exists() 的能力。应该改用 isset() 或 property_exists()。

- array_key_exists() 中 `key` 参数类型的行为已经和 isset() 和正常数组访问一致。所有的 key 类型现在使用通用的强制转换，数组/对象 key 会抛出 TypeError。

- 任意一个数组，将数字 n 作为第一个数字 key，下一个隐式键将会是 n+1。即使 n 为负数也是如此。

- error_reporting 默认级别现在是 `E_ALL`。之前排除 `E_NOTICE` 和 `E_DEPRECATED`。

- 现在默认启用 display_startup_errors。

- 在没有父级的类中使用 parent 将会导致 fatal compile-time 错误。

- `@` 操作将不再屏蔽 fatal 错误（`E_ERROR`、`E_CORE_ERROR`、`E_COMPILE_ERROR`、`E_USER_ERROR`、`E_RECOVERABLE_ERROR`、`E_PARSE`）。当使用 `@` 时，接受 error_reporting 为 `0` 的错误处理程序，应该调整为使用位掩码检查：

  `<?php
  // 之前
  function my_error_handler($err_no, $err_msg, $filename, $linenum) {
  if (error_reporting() == 0) {
  return false;
  }
  // ...
  }
  // 现在
  function my_error_handler($err_no, $err_msg, $filename, $linenum) {
  if (!(error_reporting() & $err_no)) {
  return false;
  }
  // ...
  }
  ?>`

  此外，应注意在生产环境中不显示错误消息，这可能会导致信息泄露。确保 `display_errors=Off` 与错误记录一起使用。

- `#[` 不再解释为注释的开头，因为此语法现在用于注解。

- 由于不兼容的方法签名（违反 LSP）导致的继承错误现在将始终生成致命错误。以前在某些情况下会生成警告。

- 相对于位移、加法还有减法，连接运算符的优先级已经更改。

  `<?php
  echo "Sum: " . $a + $b;
  // 之前解释为：
  echo ("Sum: " . $a) + $b;
  // 现在解释为：
  echo "Sum:" . ($a + $b);
  ?>`

- 在运行时默认值解析为 `null` 的参数，将不在默默将参数类型标记为可为 null。必须改用指定可为 null 类型或者默认值为 `null`。

  `<?php
  // 之前：
  function test(int $arg = CONST_RESOLVING_TO_NULL) {}
  // 之后：
  function test(?int $arg = CONST_RESOLVING_TO_NULL) {}
  // 或者是
  function test(int $arg = null) {}
  ?>`

- 一些警告已转换为 Error 异常：

  - 尝试向非对象写入属性。之前会默默的为 null、false 和空字符串创建 stdClass 对象。
  - 尝试追加元素到已使用 PHP_INT_MAX 作为 key 的数组。
  - 尝试使用无效类型（array 或 object）作为数组的 key 或者字符串的 offset。
  - 尝试向标量值写入数组索引。
  - 尝试解包非数组或 Traversable。
  - 尝试访问未定义的常量，之前，访问未定义的常量将会导致警告并解释为字符串。
  - 传递错误的参数数量到非可变参数内置函数将引发 ArgumentCountError。
  - 传递无效的可数的类型到 count() 将抛出 TypeError。

  一些通知已转换为警告：

  - 尝试读取未定义的变量。
  - 尝试读取未定义的属性。
  - 尝试读取未定义的数组 key。
  - 尝试读取非对象的属性。
  - 尝试读取非数组的数组索引。
  - 尝试转换数组为字符串。
  - 尝试使用资源作为数组 key。
  - 尝试使用 null、bool、float 作为字符串 offset。
  - 尝试读取越界的字符串 offset。
  - 尝试将空字符串分配给字符串 offset。

- 尝试将多字节字符串分配给字符串 offset 现在将发出警告。

- 源文件中的异常字符（比如字符串边界外的 NUL 字节）现在将导致 ParseError 异常而不是编译警告。

- 未捕获异常现在会经过“clean shutdown”，这意味着未捕获的异常之后调用析构方法。

- 编译时 fatal error“Only variables can be passed by reference”已延迟到运行时，并转换为“Argument cannot be passed by reference”Error 异常。

- 一些“Only variables should be passed by reference”通知已转换为“Argument cannot be passed by reference”异常。

- 匿名类生成的名称已经发生了改变。现在生成的名称将包括第一个父类或接口的名称：

  `<?php
  new class extends ParentClass {};
  // -> ParentClass@anonymous
  new class implements FirstInterface, SecondInterface {};
  // -> FirstInterface@anonymous
  new class {};
  // -> class@anonymous
  ?>`

  上面显示的名称仍然后跟 NUL 字节和唯一的后缀。

- trait 别名适配中的非绝对 trait 方法引用现在必须明确：

  `<?php
  class X {
  use T1, T2 {
  func as otherFunc;
  }
  function func() {}
  }
  ?>`

  如果同时存在 `T1::func()` 和 `T2::func()`，以前此代码会默默接受，并且 func 指向 `T1::func`。现在则会生成 fatal error，必须明确编写 `T1::func` or `T2::func`。

- trait 中定义的 abstract 方法签名现在会检查类中实现的方法：

  `<?php
  trait MyTrait {
  abstract private function neededByTrait(): string;
  }
  class MyClass {
  use MyTrait;
  // 错误，因为返回类型不匹配。
  private function neededByTrait(): int { return 42; }
  }
  ?>`

- 现在将禁用的函数视为不存在的函数，调用禁用函数将报告未知，并且现在可以重新定义已禁用的函数。

- `data://` 流封装协议不在可写，这与文档中的行为相匹配。

- 算术和位操作符 `+`、`-`、`*`、`/`、`**`、`%`、`<<`、`>>`、`&`、`|`、`^`、`~`、`++`、`--` 的其中一个操作符是 array、resource 或非重载 object 时，现在将始终抛出 TypeError。唯一的例外是仍然支持 array `+` array 的合并操作。

- 浮点数到字符串的强制转换现在始终不会受到独立 locale 的影响。

  `<?php
  setlocale(LC_ALL, "de_DE");
  $f = 3.14;
  echo $f, "\n";
  // Previously: 3,14
  // Now: 3.14
  ?>`

  参阅 printf()、number_format() 和 NumberFormatter() 了解自定义数字格式的方法。

- 已经移除对使用大括号（花括号）进行偏移量访问的弃用支持。

  `<?php
  // 将：
  $array{0};
  $array{"key"};
  // 改为：
  $array[0];
  $array["key"];
  ?>`

- 在私有方法上应用 final 修饰符现在将产生警告，除非该方法是构造方法。

- 如果对象的构造方法 exit()，那么将不再调用对象的析构方法，这与构造方法抛出异常时的行为相匹配。

- 命名空间名不能再包含空格：`Foo\Bar` 将识别为命名空间名称，而 `Foo \ Bar` 则不能。相反，保留关键字现在允许作为命名空间片段，这可能也会改变代码的解释：`new\x` 现在与 `constant('new\x')` 相同，而不是 `new \x()`。

- 嵌套的三元表达式现在需要的括号。

- debug_backtrace() 和 Exception::getTrace() 将不再提供对参数的引用。通过回溯将不可能更改函数参数。

- 数字字符串的处理方式已经更改，以便更直观和更少出错。现在允许在数字字符串中使用尾随空格，以便与起始空格的处理方式保持一致。这主要影响：

  - is_numeric() 函数
  - 字符串间的比较
  - 类型声明
  - 递增/减操作

  “前置数字字符串”的概念已删除大部分；仍然存在的目的是为了简化迁移。发出 `E_NOTICE`“A non well-formed numeric value encountered”的字符串现在将发出 `E_WARNING`“A non-numeric value encountered”，并且所有发出 `E_WARNING`“A non-numeric value encountered”的字符串现在将抛出 TypeError。这主要会影响：

  - 算术运算
  - 位运算

  `E_WARNING` 到 TypeError 的变更也影响了 `E_WARNING`“Illegal string offset 'string'”的非法字符串偏移。从字符串到 int/float 的明确强制转换的行为没有改变。

- 如果魔术方法声明了参数和返回类型，那么它们现在将会检查这些。签名应该与以下列表匹配：

  - `__call(string $name, array $arguments): mixed`
  - `__callStatic(string $name, array $arguments): mixed`
  - `__clone(): void`
  - `__debugInfo(): ?array`
  - `__get(string $name): mixed`
  - `__invoke(mixed $arguments): mixed`
  - `__isset(string $name): bool`
  - `__serialize(): array`
  - `__set(string $name, mixed $value): void`
  - `__set_state(array $properties): object`
  - `__sleep(): array`
  - `__unserialize(array $data): void`
  - `__unset(string $name): void`
  - `__wakeup(): void`

- call_user_func_array() 数组的键现在将解释为参数名，而不是默默忽略。

- 不再允许在命名空间内声明名为 `assert()` 的函数，并发出 `E_COMPILE_ERROR`。assert() 函数受引擎的特殊处理，当定义具有相同名称的命名空间函数时，可能会导致不一致的行为。

### 迁移资源到对象

一些 resource 已经迁移到了 object。返回值检测从使用 is_resource() 替换为是否是 `false`。

- curl_init() 现在将返回 CurlHandle 对象而不是 resource。curl_close() 函数不再有作用，相反，如果不再引用，CurlHandle 实例将自动销毁。

- curl_multi_init() 现在将返回 CurlMultiHandle 对象而不是 resource。curl_multi_close() 函数不再有作用，相反，如果不再引用，CurlMultiHandle 实例将自动销毁。

- curl_share_init() 现在将返回 CurlShareHandle 对象而不是 resource。curl_share_close() 函数不再有作用，相反，如果不再引用，CurlShareHandle 实例将自动销毁。

- enchant_broker_init() 现在将返回 EnchantBroker 对象而不是 resource。

- enchant_broker_request_dict() 和 enchant_broker_request_pwl_dict() 现在将返回 EnchantDictionary 对象而不是 resource。

- GD 扩展现在使用 GdImage 对象作为图像的底层数据结构，而不是 resource。imagedestroy() 函数不再有作用，相反，如果不再引用，GdImage 实例将自动销毁。

- openssl_x509_read() 和 openssl_csr_sign() 现在将返回 OpenSSLCertificate 对象而不是 resource。弃用 openssl_x509_free() 函数且不再起作用，相反，如果不再引用，OpenSSLCertificate 实例将自动销毁。

- openssl_csr_new() 现在将返回 OpenSSLCertificateSigningRequest 对象而不是 resource。

- openssl_pkey_new() 现在将返回 OpenSSLAsymmetricKey 对象而不是 resource。弃用 openssl_pkey_free() 函数且不再起作用，相反，如果不再引用，OpenSSLAsymmetricKey 实例将自动销毁。

- shmop_open() 现在将返回 Shmop 对象而不是 resource。弃用 shmop_close() 函数且不再起作用，相反，如果不再引用，Shmop 实例将自动销毁。

- socket_create()、socket_create_listen()、socket_accept()、socket_import_stream()、socket_addrinfo_connect()、socket_addrinfo_bind() 和 socket_wsaprotocol_info_import() 现在将返回 Socket 对象而不是 resource。socket_addrinfo_lookup() 现在将返回 AddressInfo 对象组成的数组，而不是 resource。

- msg_get_queue() 现在将返回 SysvMessageQueue 对象而不是 resource。

- sem_get() 现在将返回 SysvSemaphore 对象而不是 resource。

- shm_attach() 现在将返回 SysvSharedMemory 对象而不是 resource。

- xml_parser_create() 和 xml_parser_create_ns() 现在将返回 XMLParser 对象而不是 resource。xml_parser_free() 函数不再有作用，相反，如果不再引用，XmlParser 实例将自动销毁。

- XMLWriter 函数现在分别接受和返回 XMLWriter 对象而不是 resource。

- inflate_init() 现在将返回 InflateContext 对象而不是 resource。

- deflate_init() 现在将返回 DeflateContext 对象而不是 resource。

### COM 和 .Net (Windows)

移除从类型库导入不区分大小写常量的功能。com_load_typelib() 的第二个参数不再是 false；不再禁用 com.autoregister_casesensitive；忽略 com.typelib_file 中的不区分大小写标记。

### CURL

`CURLOPT_POSTFIELDS` 不再接受对象作为数组。要将对象解释为数组，手动执行 `(array)` 转换。这同样适用于接受数组的其它选项。

### 日期和时间

mktime() 和 gmmktime() 现在强制需要至少一个参数。time() 可以用来获取当前时间戳。

### DOM

已移除 DOM 扩展中无行为和包含测试数据的的未实现类。这些类也已在最新版本的 DOM 标准中移除：

- DOMNameList
- DomImplementationList
- DOMConfiguration
- DomError
- DomErrorHandler
- DOMImplementationSource
- DOMLocator
- DOMUserDataHandler
- DOMTypeInfo
- DOMStringExtend

已移除 DOM 扩展中没有实现行为的方法：

- DOMNamedNodeMap::setNamedItem()
- DOMNamedNodeMap::removeNamedItem()
- DOMNamedNodeMap::setNamedItemNS()
- DOMNamedNodeMap::removeNamedItemNS()
- DOMText::replaceWholeText()
- DOMNode::compareDocumentPosition()
- DOMNode::isEqualNode()
- DOMNode::getFeature()
- DOMNode::setUserData()
- DOMNode::getUserData()
- DOMDocument::renameNode()

### Enchant

- enchant_broker_list_dicts()、enchant_broker_describe() 和 enchant_dict_suggest() 现在将返回空数组而不是 `null`。

### Exif

已移除 read_exif_data()；应该使用 exif_read_data() 替代。

### Filter

- 已移除 `FILTER_VALIDATE_URL` 过滤中的 `FILTER_FLAG_SCHEME_REQUIRED` 和 `FILTER_FLAG_HOST_REQUIRED` flag。`scheme` 和 `host` 始终（并且一直）必需。

- filter_input() 等函数的 `INPUT_REQUEST` 和 `INPUT_SESSION` 源码已经移除。这些从未实现过，并且使用它们将始终生成警告。

### GD

- 移除弃用函数 image2wbmp()。

- 移除弃用函数 png2wbmp() 和 jpeg2wbmp()。

- imagecropauto() 的 `mode` 参数默认不再接受 `-1`。而是应该使用 `IMG_CROP_DEFAULT`。

- Windows 上，php_gd2.dll 已经重命名为 php_gd.dll。

### GMP

已经移除 gmp_random()。应该使用 gmp_random_range() 或 gmp_random_bits()。

### Iconv

不再支持在发生错误时没有正确设置 errno 的 iconv 实现。

### IMAP

- imap_headerinfo() 已移除未使用的 `default_host` 参数。

- 已移除 imap_headerinfo() 的别名函数 imap_header()。

### 国际化函数

- 已移除弃用常量 `INTL_IDNA_VARIANT_2003`。

- 移除弃用常量 `Normalizer::NONE`。

### LDAP

- 移除弃用函数 ldap_sort()、ldap_control_paged_result() 和 ldap_control_paged_result_response()。

- ldap_set_rebind_proc() 的接口已修改；`callback` 不再接受空字符串；应该使用 `null` 代替。

### MBString

- 已移除 mbstring.func_overload 指令。与其相关的 `MB_OVERLOAD_MAIL`、`MB_OVERLOAD_STRING` 和 `MB_OVERLOAD_REGEX` 常量也已移除。最后，mb_get_info() 中的 `"func_overload"` 和 `"func_overload_list"` 条目也已移除。

- 使用 mb_parse_str() 时必须指定结果数组。

- 一些弃用的的 mbregex 别名已移除。请参阅下面的列表，以了解应该使用哪些函数：

  - mbregex_encoding() → mb_regex_encoding()
  - mbereg() → mb_ereg()
  - mberegi() → mb_eregi()
  - mbereg_replace() → mb_ereg_replace()
  - mberegi_replace() → mb_eregi_replace()
  - mbsplit() → mb_split()
  - mbereg_match() → mb_ereg_match()
  - mbereg_search() → mb_ereg_search()
  - mbereg_search_pos() → mb_ereg_search_pos()
  - mbereg_search_regs() → mb_ereg_search_regs()
  - mbereg_search_init() → mb_ereg_search_init()
  - mbereg_search_getregs() → mb_ereg_search_getregs()
  - mbereg_search_getpos() → mb_ereg_search_getpos()
  - mbereg_search_setpos() → mb_ereg_search_setpos()

- mb_ereg_replace() 已移除 `e` 修饰符。应该使用 mb_ereg_replace_callback() 代替。

- mb_ereg_replace() 的非字符串模式参数现在将解释为字符串而不是 ASCII 码点。可以通过调用 chr() 来恢复以前的行为。

- mb_strpos()、mb_strrpos()、mb_stripos()、mb_strripos()、mb_strstr()、mb_stristr()、mb_strrchr() 和 mb_strrichr() 的 `needle` 参数现在可以为空。

- 已移除 mb_decode_numericentity() 内部不使用的 `is_hex` 参数。

- mb_strrpos() 函数的第三个参数传递编码而不是偏移量的遗留行为已经移除；应该将带编码的 `0` 偏移量作为第四个参数提供。

- `ISO_8859-*` 字符编码的别名已替换为 `ISO8859-*` 别名，以便与 iconv 扩展实现更好的互操作性。带下划线的 mbregex ISO 8859 别名（`ISO_8859_*` 和 `ISO8859_*`）也已移除。

- mb_ereg() 和 mb_eregi() 现在将在匹配成功时返回布尔值 `true`。之前，如果未传递 `matches`，则返回整数 `1`；如果传递了 `matches`，则返回 `max(1, strlen($matches[0]))`。

### OCI8

- OCI-Lob 类现在叫做 OCILob，OCI-Collection 类现在叫做 OCICollection，以实现 PHP 8 arginfo 类型注释工具强制执行的名称合规。

- 多个别名函数已标记为已弃用。

- oci_internal_debug() 及其 ociinternaldebug() 已移除。

### ODBC

- odbc_connect() 不再重用连接。

- odbc_exec() 未使用的 `flags` 参数已移除。

### OpenSSL

- openssl_seal() 和 openssl_open() 现在需要传递 `method`，现在认为之前的 `"RC4"` 默认值不安全。

### 正则表达式（Perl 兼容）

当传递无效的转义序列时，将不再解释为文字。此行为之前需要 `X` 修饰符——现在已忽略。

### PHP 数据对象

- 默认错误处理模式已经从“silent”变更为“exceptions”。参阅错误和错误处理获取详情。

- 一些 PDO 方法的签名已更改：

  - `PDO::query(string $query, ?int $fetchMode = null, mixed ...$fetchModeArgs)`
  - `PDOStatement::setFetchMode(int $mode, mixed ...$args)`

### PDO ODBC

php.ini 指令 pdo_odbc.db2_instance_name 已移除。

### PDO MySQL

PDO::inTransaction() 现在报告连接的实际事务状态，而不是 PDO 维护的近似状态。如果执行的是“隐式事务”查询，PDO::inTransaction() 将会随后返回 `false`，因为事务不再活动状态。

### PostgreSQL

- 弃用的 pg_connect() 使用多个参数的语法不再支持，而是使用连接字符串。

- 弃用的 pg_lo_import() 和 pg_lo_export() 将 connection 作为最后一个参数传递的签名不再支持。应该将 connection 作为第一个参数传递。

- pg_fetch_all() 对于 0 条记录的结果集，现在将返回空数组而不是 `false`。

### Phar

与 phar 关联的元数据将不再自动反序列化，以修复由于对象实例化、自动加载等导致的潜在安全漏洞。

### Reflection

- 方法签名

  - `ReflectionClass::newInstance($args)`
  - `ReflectionFunction::invoke($args)`
  - `ReflectionMethod::invoke($object, $args)`

  已变更为：

  - `ReflectionClass::newInstance(...$args)`
  - `ReflectionFunction::invoke(...$args)`
  - `ReflectionMethod::invoke($object, ...$args)`

  必须同时兼容 PHP 7 和 PHP 8 的代码可以使用以下签名来兼容这两个版本：

  - `ReflectionClass::newInstance($arg = null, ...$args)`
  - `ReflectionFunction::invoke($arg = null, ...$args)`
  - `ReflectionMethod::invoke($object, $arg = null, ...$args)`

- ReflectionType::__toString() 方法现在将返回该类型的完整调试表示，并且不再弃用。特别是，结果将包括可空类型的可空性指示符。返回值的格式不稳定，并且可能在不同 PHP 版本之间发生变化。

- Reflection export() 方法已经移除。相反，反射对象可以转换为字符串。

- ReflectionMethod::isConstructor() 和 ReflectionMethod::isDestructor() 现在也为接口的 __construct() 和 __destruct() 方法返回 `true`。之前只用于类方法和 trait。

- ReflectionType::isBuiltin() 方法已移至 ReflectionNamedType。ReflectionUnionType 没有。

### Sockets

- socket_addrinfo_lookup() 弃用的 `AI_IDN_ALLOW_UNASSIGNED` 和 `AI_IDN_USE_STD3_ASCII_RULES` `flags` 已移除。

### PHP 标准库（SPL）

- SplFileObject::fgetss() 已移除。

- SplFileObject::seek() 总是寻找到一行的开头。之前，位置 `>=1` 会搜索到下一行的开头。

- SplHeap::compare() 现在指定方法签名。实现此方法的继承类现在必须使用兼容的方法签名。

- SplDoublyLinkedList::push()、SplDoublyLinkedList::unshift() 和 SplQueue::enqueue() 现在返回 void 而不是 `true`。

- spl_autoload_register() 现在将始终对无效参数抛出 TypeError，因此将会忽略第二个参数 `do_throw`，并且如果将其设置为 `false`，则会发出 notice。

- SplFixedArray 现在是 IteratorAggregate 而不是 Iterator。SplFixedArray::rewind()、SplFixedArray::current()、SplFixedArray::key()、SplFixedArray::next() 和 SplFixedArray::valid() 已移除。在其位置上新增了 SplFixedArray::getIterator()。现在任何在 SplFixedArray 上迭代的代码都必须通过 SplFixedArray::getIterator() 获取 Iterator。这意味着 SplFixedArray 现在可以安全地在嵌套循环中使用。

### 标准库

- assert() 将不再执行字符串参数，而是像其他参数一样对待它们。应该使用 `assert($a == $b)` 而不是 `assert('$a == $b')`。assert.quiet_eval ini 指令和 `ASSERT_QUIET_EVAL` 常量也已移除，因为不再有任何效果。

- 如果不指定 result 数组，则无法再使用 parse_str()。

- string.strip_tags 过滤器已移除。

- strpos()、strrpos()、stripos()、strripos()、strstr()、strchr()、strrchr() 和 stristr() 的 `needle` 参数将始终解释为字符串。非字符串的 needle 之前将解释为 ASCII 码点。现在手动调用 chr() 可用于恢复以前的行为。

- strpos()、strrpos()、stripos()、strripos()、strstr()、stristr() 和 strrchr() 的 `needle` 参数现在可为空。

- substr()、substr_count()、substr_compare() 和 iconv_substr() 的 `length` 参数现在可为 `null`。`null` 值的行为就像是没有提供 length 参数，因此会返回字符串的剩余部分而不是空字符串。

- array_splice() 的 `length` 参数现在可为 `null`。`null` 值的行为跟忽略参数时相同，从而移除从偏移量到数组末尾的所有内容。

- vsprintf()、vfprintf() 和 vprintf() 的 `args` 参数现在必须是数组。之前接受任何类型。

- password_hash() 不再支持 `'salt'` 选项。如果使用 `'salt'` 选项则会生成 warning，提供的 salt 将会忽略，并使用生成的 slat 代替。

- 如果传递空字符串， quotemeta() 函数现在将返回空字符串。之前返回 `false`。

- 下列函数将移除：

  - hebrevc()
  - convert_cyr_string()
  - money_format()
  - ezmlm_hash()
  - restore_include_path()
  - get_magic_quotes_gpc()
  - get_magic_quotes_runtime()
  - fgetss()

- `FILTER_SANITIZE_MAGIC_QUOTES` 已移除。

- 不再支持以相反的参数顺序 `($pieces, $glue)` 调用 implode()。

- parse_url() 现在将区分不存在或空的查询和片段：

  - `http://example.com/foo → query = null, fragment = null`
  - `http://example.com/foo? → query = "", fragment = null`
  - `http://example.com/foo# → query = null, fragment = ""`
  - `http://example.com/foo?# → query = "", fragment = ""`
  以前所有情况都会导致查询和片段为 `null`。

- var_dump() 和 debug_zval_dump() 现在将使用 serialize_precision 而不是 precision 来打印浮点数。在默认配置中，这意味着这些调试函数现在可以完全准确地打印浮点数。

- 如果 __sleep() 返回的数组包含不存在的属性，这些属性现在会默认忽略。之前会序列化这些属性，就好像它们的值为 `null` 一样。

- 启动时的默认区域设置现在始终为 `"C"`。默认情况下，不会从环境中继承任何区域设置。之前，`LC_ALL` 设置为 `"C"`，而 `LC_CTYPE` 是从环境中继承。但是，如果不手动调用 setlocale()，某些函数将不尊重继承的语言环境。如果应更改默认区域设置组件，现在始终需要手动调用 setlocale()。

- 已移除 crypt() 中已弃用的 DES 回退。如果将未知的 salt 格式传递给 crypt()，该函数将失败并返回 `*0`，而不是立即回退到弱 DES 散列。

- crypt() 现在指定超出 SHA256/SHA512 范围的 rounds 时将失败，返回 `*0`，而不是修改为最接近的限制。这与 glibc 的行为相匹配。

- 如果数组包含比较后相等的元素，则排序函数的结果可能会发生改变。

- 如果使用带有引用参数的回调，则任何没有手动指定参数是引用的时候，接受该回调的函数都会发出 warn。示例包括 array_filter() 和 array_reduce()。以前大多数（但不是全部）函数都是这种情况。

- file_get_contents() 等函数使用的 HTTP stream 封装协议现在默认使用 HTTP/1.1 而不是 HTTP/1.0。这不会改变客户端的行为，但可能会导致服务器做出不同的响应。要保留旧行为，请设置 `'protocol_version'` stream 上下文选项，例如

  `<?php
  $ctx = stream_context_create(['http' => ['protocol_version' => '1.0']]);
  echo file_get_contents('http://example.org', false, $ctx);
  ?>`

- 不再支持在没有指定 salt 时调用 crypt()。如果想使用自动生成的 salt 产出强散列，改用 password_hash()。

- substr()、mb_substr()、iconv_substr() 和 grapheme_substr() 现在始终控制超出范围的 offset 控制到字符串的边界。在某些情况下，之前会返回 `false` 而不是空字符串。

- 在 Windows 上，使用 shell 的程序执行函数（proc_open()、exec()、popen()）现在执行 %comspec% /s /c "$commandline"，始终与执行 $commandline 效果相同 （没有额外的引号）。

### Sysvsem

- sem_get() 的 `auto_release` 参数已从接受 int 更改为接受 bool。

### Tidy

- 从 tidy_repair_string() 中删除内部未使用的 `use_include_path` 参数。

- tidy::repairString() 和 tidy::repairFile() 成为静态方法。

### Tokenizer

- `T_COMMENT` 记号将不再包含尾随的换行符。换行符将成为以下 `T_WHITESPACE` 记号的一部分。需要注意的是，`T_COMMENT` 后面并不总是跟空格，也可能跟着 `T_CLOSE_TAG` 或者文件结尾。

- namespace 名称现在使用 `T_NAME_QUALIFIED`（`Foo\Bar`）、`T_NAME_FULLY_QUALIFIED`（`\Foo\Bar`） 和 `T_NAME_RELATIVE`（`namespace\Foo\Bar`）记号表示。`T_NS_SEPARATOR` 仅用于独立 namespace 分隔符，仅与 group use 声明结合的有效语法。

### XMLReader

XMLReader::open() 和 XMLReader::XML() 现在是静态方法。也可以作为实例方法调用，但如果继承类需要覆盖这些方法，要声明为 static。

### XML-RPC

XML-RPC 扩展已移动到 PECL，不再是 PHP 发行版的一部分。

### Zip

`ZipArchive::OPSYS_Z_CPM` 已移除（名字错误）。使用 `ZipArchive::OPSYS_CPM` 代替。

### Zlib

- gzgetss() 已移除。

- zlib.output_compression 不再对 `Content-Type: image/*` 自动禁用。

### Windows PHP 测试包

测试运行器从 run-test.php 重命名为 run-tests.php，以匹配其在 php-src 中的名字。

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