32、PHP 8.4 方法或功能修改 - exit,die 从语言结构更改为函数
在 PHP 8.4 中,exit
和 die
这两个语句从 语言结构(language constructs)更改为 函数(functions)。这个变化看似微小,但实际上对 PHP 编程方式和语法解析带来了一定的影响,尤其是涉及到 PHP 内部如何处理 exit
和 die
时的工作方式。
exit 和 die 的历史
在 PHP 中,exit
和 die
最初是作为 语言结构 引入的,而不是作为普通的函数。作为语言结构,它们的行为与常规函数有所不同,主要体现在以下几个方面:
- 语言结构:不像函数那样需要括号,也可以省略括号来传递参数。
-
执行时机:
exit
和die
会在执行到语句时立即停止脚本的运行,并且可以选择性地输出退出状态码或消息。
为什么做这个改变
将 exit
和 die
作为普通函数处理,PHP 可以统一语言结构和函数的差异,使得语法更加一致。这种方式有以下优点:
-
统一性:PHP 8.4 之前,
exit
和die
与其他函数有明显区别。这会让开发者在学习和使用时产生混淆。将它们转为普通函数,有助于消除这种差异,使得语法更统一。 - 简洁性:现在,所有的函数调用都要求使用括号,即使没有参数。这对于代码的规范化、可读性和一致性是有帮助的,避免了对 PHP 语言结构的特殊处理。
-
维护和扩展性:将
exit
和die
转为函数后,PHP 引擎能够更容易地对其进行优化和扩展。
函数 exit 和 die
exit; // 允许
exit(); // 允许
exit(1); // 允许
exit("Fatal error, exiting"); // 允许
在 PHP 8.4 之前,可选的 “parameter” 接受 string
或 int
值,但它不遵循相同类型的杂耍或严格的类型行为。
declare(strict_types=1);
exit([]);
Warning: Array to string conversion in ... on line ...
Array
在 PHP 8.4 中,exit
和 die
被声明为 PHP 函数。它们具有特殊的处理方式,允许在不带括号的情况下调用它们,以确保与较旧的 PHP 应用程序向后兼容。
函数签名
function exit(string|int $status = 0): never {}
function die(string|int $status = 0): never {}
函数 die
等同于 exit
函数。这两个函数都在全局命名空间中声明。它们都有 return type never
。
不能使用 disable_functions
INI 指令禁用 exit
和 die
不能使用 disable_functions
指令禁用新的 exit
和 die
函数。这样做会发出 PHP 警告,并且函数保持启用状态:
[PHP]
disable_functions=exit,die
Warning: Cannot disable function exit()
Warning: Cannot disable function die()
未更改:exit
和 die
不能用于函数名、类名、常量名和 goto
标签
exit
和 die
不能用作函数名或常量名,即使在命名空间内部也是如此。此外,exit
和 die
也不能用作 goto
语句的标签。
以下所有代码片段在所有 PHP 版本(包括 PHP 8.4 及更高版本)中都会导致语法错误。
function exit() {}
namespace Test;
function exit() {}
分析错误:语法错误,意外的标记“exit”,应为“(“
const die = 42;
const EXIT = 42;
Parse error: syntax error, unexpected token "exit", expecting identifier
仍然可以使用 define
函数来声明名为 exit
和 die
的常量。然而,需要注意的是,尝试使用这些常量时会调用 die
或 exit
函数,而不是获取常量的值。
exit:
echo "Called";
Parse error: syntax error, unexpected token ":"
未更改:exit
和 die
可以用作枚举成员、类常量、方法和属性
exit
和 die
可以用作类常量名、类方法名和属性名。以下代码片段在所有 PHP 版本(包括 PHP 8.4 及更高版本)中都是有效的。此外,由于枚举(Enums)在内部扩展了 PHP 的类结构,exit
和 die
也可以作为有效的枚举成员名。
<?php
class Test {
public int $exit = 442;
public int $die = 116;
public const int exit = 42;
public const int die = 16;
public function exit() {
return self::exit;
}
public function die() {
return self::die;
}
}
$c = new Test();
echo $c->exit(); // 42
echo $c->die(); // 16
echo $c->exit; // 442
echo $c->die; // 116
echo Test::exit; // 42
echo Test::die; // 16
类型处理更改
因为 exit 和 die 是 PHP 8.4 和更高版本中的 PHP 函数,它们现在遵循其他 PHP 函数遵循的标准类型处理。
参数数量错误(ArgumentCountError)在额外参数上的变化
之前,在 exit
或 die
函数中传递多个参数会导致语法错误。而在 PHP 8.4 中,这种情况会引发 ArgumentCountError
异常。
exit(255, "foobar");
ArgumentCountError: exit() expects at most 1 argument, 2 given
PHP 8.4 中的标准类型转换(Type-Juggling)行为变化
与 PHP 中其他函数一样,exit
和 die
现在也遵循相同的类型转换(类型强制)规则。这一变化影响了向后兼容性,但在一定程度上可以认为是朝着更正确的方向发展。
主要变化:
-
exit(true)
之前的行为:- 在 PHP 8.4 之前,
exit(true)
会被自动转换为exit("1")
,这会打印1
到标准输出(STDOUT),并且退出代码为0
(表示没有错误)。 - 在 PHP 8.4 及之后的版本中,
exit(true)
现在会被解释为exit(1)
,这会导致退出代码为1
(表示有错误)。
- 在 PHP 8.4 之前,
-
传递浮点值的行为:
- 传递浮点值时,PHP 会触发一个弃用警告:"Implicit conversion from float ... to int loses precision"(隐式转换从浮动数到整数时会丢失精度)。这意味着如果你传递一个浮点数,PHP 会隐式地将其转换为整数,但可能会丢失小数部分。
-
传递
null
的行为:- 传递
null
会触发一个弃用警告,因为exit
和die
只接受string
或int
类型参数。传递null
会被认为是不合适的参数类型,PHP 会在以后的版本中警告这种做法。
- 传递
启用 strict_types
时的 TypeError
异常
当 strict_types=1
生效时,传递任何非 string
或 int
类型的值都会导致抛出 TypeError
异常。
declare(strict_types=1);
exit(null);
TypeError: exit(): Argument #1 ($status) must be of type string|int, null given
exit
和 die
现在可以作为可调用函数
由于 exit
和 die
是 PHP 函数,它们现在可以作为标准的 PHP 可调用函数使用。
在 PHP 8.3 及更早版本中,以下代码片段会失败,因为在这些版本中,exit
并没有被声明为一个可调用函数。
$callable = 'exit';
$status = "My success message";
$callable($status); // 这种用法在 PHP 8.3 及更早版本中会失败
$callable = exit(...);
$status = "My success message";
$callable($status); // 这种用法也会失败
向后兼容性影响
exit
和 die
现在是 PHP 函数。然而,这一变化与旧版 PHP 保持了较好的向后兼容性,因为 exit
和 die
仍然不允许作为类名、常量名和函数名使用,但仍然允许作为类常量、属性和方法名使用。
将 exit
和 die
作为可调用函数的功能仅在 PHP 8.4 及更高版本中得到支持。
由于在旧版本的 PHP 中,exit
和 die
是语言结构(language constructs),因此无法(也不需要)为它们提供 polyfill(即填补或模拟该功能的解决方案)。