Stop Ignoring Important Returns with PHP 8.5’s `#[\NoDiscard]` Attribute
When writing functions or methods in PHP, we often return values that are crucial for the caller to handle. Usually, those returned values need to be consumed. Check the following example.
function calculateSum(int $a, int $b): int
{
return $a + $b;
}
$result = calculateSum(5, 10);
But you might stumble upon a situation where you forget to use the return value, like this:
// Forgetting to use the return value
calculateSum(5, 10);
Now, this might not seem like a big deal, but it can lead to problems in larger applications where you are processing something and, in addition, returning error or exception information if something goes wrong. So, scenarios like these lead to bugs that may never show up until it’s too late, which is exactly how the most annoying bugs end up happening.
This is where the new #[\NoDiscard]
attribute introduced in PHP 8.5 comes into play.
- What is
#[\NoDiscard]
attribute? - Using
#[\NoDiscard]
attribute - A more practical example
- Explicitly casting to
void
or the return type - Inspiration
- In Closing
What is #[\NoDiscard]
attribute?
When a function or method is called with the #[\NoDiscard]
, it serves as an indication to developers that the return value of a function or method should be consumed and not ignored. So, if a developer calls a function marked with #[\NoDiscard]
and does nothing with the return value (doesn’t assign it, use it, or cast it), PHP will emit a warning (E_WARNING
or E_USER_WARNING
).
The warning can include a custom message explaining why the return value is important.
Assigning the result to a variable, using it in an expression, or casting it (even to a dummy variable) counts as “using” the return value.
Here’s what the attribute definition looks like.
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION)]
final class NoDiscard
{
public readonly ?string $message;
public function __construct(?string $message = null) {}
}
Using #[\NoDiscard]
attribute
Let’s say we have a function that returns a status code indicating whether an operation was successful or not. We can use the #[\NoDiscard]
attribute to ensure that the return value is not ignored.
#[\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();
As you can see in the example above, if we call the performOperation()
function without using its return value in a variable, PHP will throw a warning indicating that the return value is expected to be consumed.
Consuming the return value won’t trigger the warning, as shown in the second call where we assign the return value to the $status
variable.
A more practical example
Here’s a more practical example from the 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);
In this example, the bulk_process
function processes an array of items and returns an array of results. If the return value is not consumed, PHP will throw a warning indicating that the return value is expected to be consumed.
Explicitly casting to void
or the return type
If you want to tame down the warning, you can explicitly cast the return value to void
(introduced in PHP 8.5 itself) or the return type of the function. This is useful when you want to ignore the return value intentionally.
Here’s how you can do it in the previous example.
// 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);
Inspiration
The feature is inspired by similar attributes in C++, Rust, and other languages, which help prevent bugs from ignored return values.
In Closing
The #[\NoDiscard]
is a safety feature for PHP developers, making it easier to catch mistakes where ignoring a function’s return value could lead to bugs. It’s especially useful for functions where the return value signals something important about the operation’s success or failure.
You can learn more about the #[\NoDiscard]
attribute in the PHP RFC.
👋 Hi there! This is Amit, again. I write articles about all things web development. If you enjoy my work (the articles, the open-source projects, my general demeanour... anything really), consider leaving a tip & supporting the site. Your support is incredibly appreciated!