在 PHP 中编写函数或方法时,我们通常会返回对调用者处理至关重要的值。这些返回值通常需要被使用。以下是一个简单的示例:
function calculateSum(int $a, int $b): int
{
return $a + $b;
}
$result = calculateSum(5, 10);
但有时,你可能会无意中忘记使用返回值,例如:
// 忘记使用返回值
calculateSum(5, 10);
这看似不是什么大问题,但在大型应用程序中,如果你在处理某些操作时返回了错误或异常信息,而这些返回值被忽略,可能会导致难以察觉的 bug。这种 bug 往往在问题发生很久后才会显现,成为最令人头疼的问题之一。
PHP 8.5 引入的新 #[\NoDiscard]
属性正是为了解决这个问题。
什么是 #[\NoDiscard] 属性?
当函数或方法被标记为 #[\NoDiscard]
时,它向开发者表明该函数或方法的返回值应当被使用,而不应被忽略。如果开发者调用了一个标记为 #[\NoDiscard]
的函数但没有处理其返回值(例如未赋值、未使用或未进行类型转换),PHP 会抛出警告( E_WARNING
或 E_USER_WARNING
)。
警告信息可以包含自定义的说明,解释为什么返回值是重要的。
将返回值赋值给变量、在表达式中使用或进行类型转换(即使是赋值给一个临时变量)都算是“使用了”返回值。
以下是 #[\NoDiscard]
属性的定义:
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION)]
final class NoDiscard
{
public readonly ?string $message;
public function __construct(?string $message = null) {}
}
使用 #[\NoDiscard] 属性
假设我们有一个函数返回一个状态码,指示操作是否成功。我们可以使用 #[\NoDiscard]
属性来确保返回值不被忽略。
#[\NoDiscard("as the operation result is important")]
function performOperation(): int {
// 执行某些操作
return 1; // 1 表示成功,0 表示失败
}
// 调用函数但不使用返回值
performOperation();
// 调用函数并使用返回值
// 这不会触发警告
$status = performOperation();
如上例所示,如果我们调用 performOperation()
函数但不使用其返回值,PHP 会抛出警告,提示返回值需要被使用。在第二个调用中,我们将返回值赋值给 $status
变量,因此不会触发警告。
实际例子
以下是一个来自 RFC 的更实际的例子:
#[\NoDiscard("as processing might fail for individual items")]
function bulk_process(array $items): array {
$results = [];
foreach ($items as $key => $item) {
if (\random_int(0, 9999) < 9999) {
// 模拟对 $item 进行处理,99.99% 的情况下成功
echo "Processing {$item}", PHP_EOL;
$error = null;
} else {
$error = new \Exception("Failed to process {$item}.");
}
$results[$key] = $error;
}
return $results;
}
$items = [ 'foo', 'bar', 'baz' ];
bulk_process($items);
// 没有警告,因为返回值被赋值使用了
$results = bulk_process($items);
在这个例子中, bulk_process
函数处理一个数组的项并返回结果数组。如果返回值未被使用,PHP 会抛出警告,提示返回值需要被处理。
显式转换为 void 或返回类型
如果你希望有意忽略返回值并避免警告,可以显式将返回值转换为 void
(PHP 8.5 引入)或函数的返回类型。这在你明确想忽略返回值时非常有用。
以下是基于前例的示例:
// 没有警告,因为使用了 (void) 转换
(void)bulk_process($items);
// 没有警告,因为返回值被类型转换使用了
// (但 (bool) 转换可能被 OPcache 优化掉)
(bool)bulk_process($items);
灵感来源
该功能受到 C++、Rust 等语言中类似属性的启发,这些属性有助于防止因忽略返回值而导致的 bug。
总结
#[\NoDiscard]
是 PHP 开发者的一项安全功能,有助于捕获忽略函数返回值可能导致的错误。尤其对于返回值表示操作成功或失败的函数,此功能特别有用。