PHP 8.1中的“never”返回类型是什么?

18

PHP 8.1 推出了 never 返回类型,它是什么?和 void 有什么区别?


https://php.watch/versions/8.1/never-return-type - 0stone0
2个回答

21

never类型声明被引入作为函数返回类型提示,用于那些从未使用return语句(无论是显式还是隐式)的函数。这些函数必须通过抛出异常或使用exit / die函数终止。

function redirect(string $uri): never 
{
    header('Location: ' . $uri);
    exit();
}

在这里,redirect被称为一个永远不会返回的函数,因为:

1)它没有显式定义的return语句。

function redirect(string $uri): never 
{
    exit();
    return 'something';
}

会产生:

PHP致命错误:永远不会返回的函数不能返回

2) 它没有隐式定义的return语句。

function redirect(string $uri): never
{
    if (false) {
        header('Location: ' . $uri);
        exit();
    }
}

由于条件永远不会满足,执行会跳过if语句并返回隐式的NULL,导致如下错误:

PHP致命错误:Uncaught TypeError:redirect():不能隐式返回从不返回的函数

3) 函数以exit函数结束其执行

void与never

  • void 可以使用return;,但never不能。
  • never 强制函数抛出异常或使用exit/die终止,但void不需要。
  • never 是PHP类型系统中所有其他类型的子类型,包括void(这允许返回类型协变)。
  • 两者都不允许返回值。
  • 两者都不能用作参数或属性类型。
  • 两者都不能用作箭头函数的返回类型。
  • 两者只能用作独立类型(不允许使用联合或交集)。

何时选择voidnever

当您期望PHP在函数调用后执行下一条语句时,请声明函数返回类型为void。当您不希望PHP在函数调用后执行下一条语句时,请声明函数返回类型为never


4

使用never (PHP 8.1),您可以保护函数永不返回,例如,如果您想确保一个无限循环真正无限 (从调用方的角度来看):

<?php

function eternity(string $forEternity): never
{
    start:
    usleep(1000);
    goto start;
}

eternity('the future');
// <- we are here only after the next big-bang, not in this current universe.

这段代码非常简洁,通常忘记了在这里使用goto语句会触发错误。

无限循环通常也不必要用于保护for(因为通常我们永远不希望它们发生),但never对于函数执行die()exit()throw异常是有用的,总是需要的。

虽然这三种离开函数的方式在标准控制流中并不被推荐使用(前两种甚至在某些SAPI中控制着运行进程)。但最后(无意冒犯),通过在代码中编码的返回类型信息,IDE和工具可以看到一个函数从不会返回。这也包括使用这些工具或阅读代码的人类。


使用void(PHP 7.1)可以保证函数永远不会返回类型,包括底部类型never(因为你只能使用void 或者 never)。

<?php

function back_from_eternity(string $forEternity): void
{
    usleep(100000); // not so long

    return $forEternity
}

$echo = back_from_eternity('soon');

这个功能可以让PHP防止永远回传echo,也可以让读者知道$echo = (以及后续对该变量的任何操作)仍然需要常规的维护/调试/测试或开发。


两者都有一个好处,即在违反规则时会抛出异常,因此程序永远不会像原样继续运行并返回void(所有双关语都是有意的)。

调用函数产生的任何副作用仍应被考虑。通常就是这个问题。


1
为什么没有赞?甚至连楼主都没有赞。 - Timo
有时候,总是抛出异常的情况很有用,但并没有被提及太多。 - MAChitgarha
@hakre 实际上我的意思是,很好(谢谢你提到了)你提到了 never 的一个可能用例是始终抛出异常。虽然有用,但我之前没有在其他地方看到过它被提到。 - MAChitgarha
1
@MAChitgarha:好的,我道歉,我误读了你的评论。也许今天早上喝咖啡太少了^^ - hakre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接