使用 PHP 8.5 的 `#[\NoDiscard]` 注解不再忽视重要的返回
在 PHP 中编写函数或方法时,我们经常返回对调用者处理至关重要的值。通常,这些返回值需要被消耗。请查看以下示例。
function calculateSum(int $a, int $b): int
{
return $a + $b;
}
$result = calculateSum(5, 10);
但你可能会无意中忘记使用返回值,比如这样:
// 忘记使用返回值
calculateSum(5, 10);
现在,这可能看起来没什么大不了的,但它可能会导致更大的应用程序出现问题,在这些应用程序中,您正在处理某些内容,此外,如果出现问题,还会返回错误或异常信息。所以,像这样的场景会导致可能永远不会出现的错误,直到为时已晚,这正是最烦人的错误最终发生的原因。
这就是 PHP 8.5 中引入的 #[\NoDiscard]
属性发挥作用的地方。
#[\NoDiscard] 注解是什么?
当调用带有 #[\NoDiscard]
的函数或者方法时,该注解用以向开发者表明,函数或方法的返回值应该被消费而不可忽视。因此,如果开发者调用了一个标记为 #[\NoDiscard]
的函数,并且没有使用返回值做任何事(没有赋值、使用或者强制转换)时,PHP 会发出警告(E_WARNING
或 E_USER_WARNING
)。
该警告可以包含自定义信息说明为什么返回值很重要。
将其结果赋值给变量,在表达式中使用,或者对其强制转换都可视作”使用“该返回值。
以下是如何定义这个注解。
#[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 {
// Perform some operation
return 1; // 1 for success, 0 for failure
}
// Calling the function without using the return value
// Warning: The return value of function performOperation() is expected to be consumed, as the operation result is important in test.php on line 10
performOperation();
// Calling the function and using the return value
// This will not trigger a warning
$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) {
// Pretend to do something useful with $item,
// which will succeed in 99.99% of cases.
echo "Processing {$item}", PHP_EOL;
$error = null;
} else {
$error = new \Exception("Failed to process {$item}.");
}
$results[$key] = $error;
}
return $results;
}
$items = [ 'foo', 'bar', 'baz' ];
// Warning: The return value of function bulk_process() is expected to be consumed, as processing might fail for individual items in test.php on line 34
bulk_process($items);
// No warning, because the return value is consumed by the assignment.
$results = bulk_process($items);
上例中,bulk_process
函数处理了数组项并返回结果数组。如果其返回值未被使用,PHP 将会抛出警告说明该返回值期待被使用。
显式强制转换成 void 或者其他返回类型
如果你想驯服警告,你可以显式地将返回值强制转换为 void
(在 PHP 8.5 中引入)或其他返回类型。当你有意忽略返回值时,这很有用。
在前面的示例中,你可以这样做。
// No warning, because the (void) cast is used.
(void)bulk_process($items);
// No warning, because the return value is consumed by
// the cast (but the (bool) cast may be optimized away by OPcache).
(bool)bulk_process($items);
启发
该特性的灵感来自 C++、Rust 和其他语言中的类似注解,这些注解有助于防止错误被忽略返回值。
小结
#[\NoDiscard]
对 PHP 开发者而言是一项安全特性,它可以更容易地发现忽略函数返回值可能导致错误的错误。它对于返回值表示操作成功或失败的重要信息的函数特别有用。
你可以在 PHP RFC 中了解有关 #[\NoDiscard]
注解的更多信息。