编程

使用 PHP 8.5 的 `#[\NoDiscard]` 注解不再忽视重要的返回

16 2025-08-22 16:20:00

在 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_WARNINGE_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] 注解的更多信息。