32、PHP 8.4 方法或功能修改 - exit,die 从语言结构更改为函数

作者: 温新

图书: 【PHP 8.4 新特性】

阅读: 131

时间: 2025-01-18 06:54:09

在 PHP 8.4 中,exitdie 这两个语句从 语言结构(language constructs)更改为 函数(functions)。这个变化看似微小,但实际上对 PHP 编程方式和语法解析带来了一定的影响,尤其是涉及到 PHP 内部如何处理 exitdie 时的工作方式。

exit 和 die 的历史

在 PHP 中,exitdie 最初是作为 语言结构 引入的,而不是作为普通的函数。作为语言结构,它们的行为与常规函数有所不同,主要体现在以下几个方面:

  • 语言结构:不像函数那样需要括号,也可以省略括号来传递参数。
  • 执行时机exitdie 会在执行到语句时立即停止脚本的运行,并且可以选择性地输出退出状态码或消息。

为什么做这个改变

exitdie 作为普通函数处理,PHP 可以统一语言结构和函数的差异,使得语法更加一致。这种方式有以下优点:

  • 统一性:PHP 8.4 之前,exitdie 与其他函数有明显区别。这会让开发者在学习和使用时产生混淆。将它们转为普通函数,有助于消除这种差异,使得语法更统一。
  • 简洁性:现在,所有的函数调用都要求使用括号,即使没有参数。这对于代码的规范化、可读性和一致性是有帮助的,避免了对 PHP 语言结构的特殊处理。
  • 维护和扩展性:将 exitdie 转为函数后,PHP 引擎能够更容易地对其进行优化和扩展。

函数 exit 和 die

exit; // 允许
exit(); // 允许
exit(1); // 允许
exit("Fatal error, exiting"); // 允许

在 PHP 8.4 之前,可选的 “parameter” 接受 stringint 值,但它不遵循相同类型的杂耍或严格的类型行为。

declare(strict_types=1);

exit([]);
Warning: Array to string conversion in ... on line ...
Array

在 PHP 8.4 中exitdie 被声明为 PHP 函数。它们具有特殊的处理方式,允许在不带括号的情况下调用它们,以确保与较旧的 PHP 应用程序向后兼容。

函数签名

function exit(string|int $status = 0): never {}

function die(string|int $status = 0): never {}

函数 die 等同于 exit 函数。这两个函数都在全局命名空间中声明。它们都有 return type never

不能使用 disable_functions INI 指令禁用 exitdie

不能使用 disable_functions 指令禁用新的 exitdie 函数。这样做会发出 PHP 警告,并且函数保持启用状态:

[PHP]
disable_functions=exit,die
Warning: Cannot disable function exit()
Warning: Cannot disable function die()

未更改:exitdie 不能用于函数名、类名、常量名和 goto 标签

exitdie 不能用作函数名或常量名,即使在命名空间内部也是如此。此外,exitdie 也不能用作 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 函数来声明名为 exitdie 的常量。然而,需要注意的是,尝试使用这些常量时会调用 dieexit 函数,而不是获取常量的值。

exit:
echo "Called";

Parse error: syntax error, unexpected token ":"

未更改:exitdie 可以用作枚举成员、类常量、方法和属性

exitdie 可以用作类常量名、类方法名和属性名。以下代码片段在所有 PHP 版本(包括 PHP 8.4 及更高版本)中都是有效的。此外,由于枚举(Enums)在内部扩展了 PHP 的类结构,exitdie 也可以作为有效的枚举成员名。

<?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)在额外参数上的变化

之前,在 exitdie 函数中传递多个参数会导致语法错误。而在 PHP 8.4 中,这种情况会引发 ArgumentCountError 异常。

exit(255, "foobar");

ArgumentCountError: exit() expects at most 1 argument, 2 given

PHP 8.4 中的标准类型转换(Type-Juggling)行为变化

与 PHP 中其他函数一样,exitdie 现在也遵循相同的类型转换(类型强制)规则。这一变化影响了向后兼容性,但在一定程度上可以认为是朝着更正确的方向发展。

主要变化:

  1. exit(true) 之前的行为
    • 在 PHP 8.4 之前,exit(true) 会被自动转换为 exit("1"),这会打印 1 到标准输出(STDOUT),并且退出代码为 0(表示没有错误)。
    • 在 PHP 8.4 及之后的版本中,exit(true) 现在会被解释为 exit(1),这会导致退出代码为 1(表示有错误)。
  2. 传递浮点值的行为
    • 传递浮点值时,PHP 会触发一个弃用警告:"Implicit conversion from float ... to int loses precision"(隐式转换从浮动数到整数时会丢失精度)。这意味着如果你传递一个浮点数,PHP 会隐式地将其转换为整数,但可能会丢失小数部分。
  3. 传递 null 的行为
    • 传递 null 会触发一个弃用警告,因为 exitdie 只接受 stringint 类型参数。传递 null 会被认为是不合适的参数类型,PHP 会在以后的版本中警告这种做法。

启用 strict_types 时的 TypeError 异常

strict_types=1 生效时,传递任何非 stringint 类型的值都会导致抛出 TypeError 异常。

declare(strict_types=1);

exit(null);

TypeError: exit(): Argument #1 ($status) must be of type string|int, null given

exitdie 现在可以作为可调用函数

由于 exitdie 是 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);  // 这种用法也会失败

向后兼容性影响

exitdie 现在是 PHP 函数。然而,这一变化与旧版 PHP 保持了较好的向后兼容性,因为 exitdie 仍然不允许作为类名、常量名和函数名使用,但仍然允许作为类常量、属性和方法名使用。

exitdie 作为可调用函数的功能仅在 PHP 8.4 及更高版本中得到支持。

由于在旧版本的 PHP 中,exitdie 是语言结构(language constructs),因此无法(也不需要)为它们提供 polyfill(即填补或模拟该功能的解决方案)。

请登录后再评论