PHP 8.4's #[\NoDiscard] Attribute: Preventing Silent Return Value Mistakes

PHP 8.4's #[\NoDiscard] Attribute: Preventing Silent Return Value Mistakes

PHP 8.4 adds the #[\NoDiscard] attribute. Apply it to a function, and static analysis tools will warn you if the return value is ignored. This catches bugs where you call a function expecting side effects, but the function only returns a result.

#[\NoDiscard]
function validate(string $input): bool {
    return strlen($input) > 0 && strlen($input) < 100;
}

validate($userInput);  // Warning: return value of validate() is not used

Without #[\NoDiscard], this mistake is silent. With it, static analyzers flag it. The code runs the same way—PHP doesn't enforce this at runtime—but your tooling catches the error during development.

The Problem It Solves

Some functions return values you're meant to check. Validation functions, for example, tell you whether input is acceptable. If you call validate() but don't check the result, validation isn't happening:

function processForm(array $data): void {
    validate($data['email']);  // Oops - not checking the result
    saveToDatabase($data);
}

The validation runs, returns true or false, and the result disappears. Invalid data gets saved. The bug is invisible in the code—there's no error, no exception, just missing logic.

#[\NoDiscard] marks functions where ignoring the return value is likely a mistake:

#[\NoDiscard]
function validate(array $data): bool {
    // Validation logic
    return $isValid;
}

validate($data);  // Static analyzer warns: result is discarded

How It Works

The attribute is metadata. It doesn't change runtime behavior. PHP doesn't throw errors or warnings when you ignore a return value from a #[\NoDiscard] function. Instead, static analysis tools like PHPStan and Psalm check for this during code review or CI:

vendor/bin/phpstan analyse src/

If PHPStan sees a #[\NoDiscard] function called without using its return value, it reports an error. You fix it before the code reaches production.

When to Use It

Use #[\NoDiscard] on functions where the return value is the point:

  • Validation functions: isValid(), checkPermissions(), authorize()
  • Pure functions: Functions with no side effects that only compute and return results
  • Data transformations: normalize(), sanitize(), transform()
  • Builders: Methods that return modified copies (immutable patterns)

Don't use it on functions with side effects. If a function's job is to log, send an email, or update a database, ignoring the return value might be fine:

function sendEmail(string $to, string $subject, string $body): bool {
    // Send email
    return $success;
}

sendEmail($user, "Welcome", $message);  // Fine to ignore - side effect happened

Though even here, you might want to check $success. The attribute forces you to make that choice explicitly.

Real-World Example

Consider a string sanitization function:

#[\NoDiscard]
function sanitize(string $input): string {
    return htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
}

$username = $_POST['username'];
sanitize($username);  // Warning: not using the result
echo $username;       // Still unsanitized!

The sanitized string is returned but never assigned. $username stays unsanitized. This is a security bug. #[\NoDiscard] catches it:

$username = sanitize($_POST['username']);  // Correct
echo $username;

Comparison to Other Languages

This pattern exists elsewhere:

  • Rust: The #[must_use] attribute does the same thing. Ignoring a Result triggers a compiler warning.
  • C++: [[nodiscard]] (C++17) warns if the return value is discarded.
  • Go: No attribute, but unused variables are compile errors. You must explicitly ignore with _.

PHP's version is opt-in and tooling-dependent, which fits PHP's runtime-focused nature.

Static Analysis Integration

PHPStan and Psalm both support #[\NoDiscard] starting with PHP 8.4. If you're running static analysis in CI, the attribute integrates automatically:

// phpstan.neon
parameters:
    level: 8

At level 8, PHPStan enforces #[\NoDiscard]. Lower levels might not. Check your configuration.

Psalm handles it similarly:

// psalm.xml
<psalm errorLevel="1">
</psalm>

Limitations

Runtime enforcement doesn't exist. If someone runs code without static analysis, #[\NoDiscard] does nothing. This is by design—attributes are metadata, not behavior. But it means the attribute is only effective in codebases with static analysis in the development workflow.

Also, it doesn't prevent intentional ignoring. You can still explicitly discard the value:

$_ = validate($input);  // Explicitly ignored - no warning

Static analyzers see you're assigning to a variable (even $_), so they don't complain. Whether this is a loophole or an escape hatch depends on your perspective.

Adoption Strategy

Start by marking obvious candidates:

#[\NoDiscard]
function hash(string $input): string { /* ... */ }

#[\NoDiscard]
function authorize(User $user, string $action): bool { /* ... */ }

#[\NoDiscard]
function parse(string $json): array { /* ... */ }

Run static analysis. Fix warnings. Gradually expand to other functions where ignoring the return value is likely a bug.

For existing codebases, be careful. Retroactively adding #[\NoDiscard] to widely-used functions can create noise if callers are legitimately ignoring return values. Add it to new functions first, then selectively backfill.

Why Now?

PHP 8.4's focus is incremental improvements to developer experience. Attributes like #[\NoDiscard] don't fundamentally change the language, but they make common mistakes easier to catch. Combined with property hooks, asymmetric visibility, and other 8.4 features, the language is getting more expressive without adding complexity.

Further Reading

The PHP RFC for NoDiscard explains the motivation and design decisions in detail.

For static analysis setup, see the PHPStan documentation and Psalm's getting started guide.

PHP 8.4's release notes cover #[\NoDiscard] alongside other new attributes and language features: PHP 8.4 Release Announcement.

Small additions like #[\NoDiscard] improve code quality without changing how you write PHP.

Wear the code

Product mockup

#[\NoDiscard] Developer T-Shirt (PHP Edition — Dark Mode)

£25.00

View product
Product mockup

#[\NoDiscard] Developer T-Shirt (PHP Edition — Light Mode)

£25.00

View product

0 comments

Leave a comment

Please note, comments need to be approved before they are published.