skip to content

In PHP 8.1 and later, what is the difference between the void and never return types, and when is each used?

level: middleimportance: should knowfreq 46%

answer

  1. void returns, but with no value
  2. return; allowed, return null; not
  3. never: throw, exit or loop forever
  4. implicit return from never is TypeError
  5. both standalone; never since 8.1

basics

~20 s

void means the function returns normally but hands back no value, so callers see null. never, added in PHP 8.1, means the function does not return at all: it must throw, exit or loop forever, and falling off its end throws TypeError.

solid answer

~40 s

`void` (PHP 7.1) says the function **completes without a value**: it may use a bare `return;` or no return at all, but `return null;` or any other value is a compile error, and a caller reading the result gets `null`. `never` (PHP 8.1) says control **never comes back** to the caller: the function must throw, call `exit`, or loop forever. `return;` inside a `never` function is a compile error, and reaching the end of its body throws `TypeError` at runtime. Both are return-only and must stand alone — no `?void`, no `never|int`. Use `void` for commands such as `logFine()`, and `never` for helpers like `redirect()` that exit or `fail()` that always throws, so readers and static analysers know the code after the call is unreachable.

code

php · 18 lines
php
<?php
declare(strict_types=1);

function logFine(int $cents): void
{
    if ($cents === 0) {
        return;               // bare return is allowed
    }
    error_log("fine: $cents");
}

function redirectToLogin(): never
{
    header('Location: /login');
    exit;                     // removing this line makes PHP throw TypeError
}

var_dump(logFine(0));        // NULL

go deeper

for a junior

Recall that void returns with no value and never does not return at all. Know that return null; is not allowed in a void function.

for a middle

Explain the rules: bare return in void, no return in never, the runtime TypeError when never falls through, and why both must stand alone.

for a senior

Use never on throw and redirect helpers so analysers see unreachable code, and catch fall-through paths in tests because PHP only reports them when they run.

for a principal

Decide how far a codebase should encode control flow in types: never on terminators, void on commands, and where exceptions stay undeclared paths.

## Two return types that say "no useful value" Both `void` and `never` are declared where a return type goes, and both mean a caller gets nothing useful back. They make very different promises, though: - **`void`** (added in PHP 7.1): the function **returns normally**, it just does not return a value. - **`never`** (added in PHP 8.1): the function **does not return** at all. Control does not come back to the line after the call. In type-theory terms `never` is the **bottom type**: a type with no values, which is why the manual describes it as a subtype of every other type. ## Rules for void 1. A `void` function may end without a `return`, or use a bare `return;` to leave early. 2. Returning a value is a **compile error**. Even `return null;` is rejected, with a message that suggests the fix: `A void function must not return a value (did you mean "return;" instead of "return null;"?)`. 3. The caller can still read the result, and it is `null`. Nothing stops `$x = logFine($f);`, it is just pointless. 4. `void` must stand alone: `?void` or `void|int` fails with `Void can only be used as a standalone type`. 5. `void` cannot be a parameter type: `void cannot be used as a parameter type`. ## Rules for never 1. The function must end by **throwing**, by calling **`exit`/`die`**, or by **never terminating** (an endless loop, such as a worker that blocks forever). 2. Any `return` statement, even a bare `return;`, is a **compile error**: `A never-returning function must not return`. 3. If execution **falls off the end** of the body — a code path the author forgot — PHP throws `TypeError` at runtime: `redirectToLogin(): never-returning function must not implicitly return`. This is checked when it happens, not at compile time, because the compiler cannot prove every path throws. 4. `never` must stand alone: `never can only be used as a standalone type`, and it is not allowed as a parameter type. ## Side by side | | `void` | `never` | |---|---|---| | Available since | PHP 7.1 | PHP 8.1 | | Control returns to caller | yes | no | | `return;` | allowed | compile error | | `return $value;` | compile error | compile error | | Reaching end of body | fine, caller gets `null` | `TypeError` at runtime | | Nullable or in a union | not allowed | not allowed | | Typical use | commands and side effects | throw helpers, redirect-and-exit, endless workers | ## Why never earns its place Before PHP 8.1, a helper that always threw had to be declared `void` or left without a return type. Readers and tools then had to assume the call might return, so code after it looked reachable: ```php <?php declare(strict_types=1); function failFine(string $reason): never { throw new DomainException($reason); } function fineFor(int $days): int { if ($days < 0) { failFine('negative days'); } return $days * 25; } ``` With `never`, a static analyser knows the `if` branch cannot fall through, and a reviewer knows `failFine()` is a terminator, not a logger. The same applies to a redirect helper that sends a `Location` header and then calls `exit` — declaring it `never` documents, and at runtime enforces, that the script stops there. ## The static return type A third return-only declaration, `static`, was added in PHP 8.0. It is a type meaning "an instance of the class the method was called on", which is what fluent builders and named constructors in a class hierarchy want to promise. Like `void` and `never` it cannot be used for parameters. How `static` resolves at call time is late static binding, a separate topic. ## Choosing between them - A method that changes state and reports nothing — `recordPayment()`, `logFine()` — is `void`. - A function whose job is to stop the current flow — throw a domain error, redirect and exit, run until killed — is `never`. - If a function sometimes returns and sometimes throws, it is neither: give it its real return type and let the throw be an exceptional path.

  • In PHP, why is return null; rejected in a void function when calling that function yields null anyway?
    Because `void` declares that the function produces no value, and `return null;` is returning one. The compile error even suggests `return;` instead. The caller seeing `null` is just what PHP gives for any call that returns nothing; it is not a value the function chose. If `null` is meaningful, the right declaration is `null` or `?T`, not `void`.
  • In PHP 8.1+, is it a compile-time or runtime error when a never function can reach the end of its body?
    Runtime. The compiler rejects any explicit `return` in a `never` function, but it cannot prove that every path throws or exits. If execution actually reaches the end, PHP throws `TypeError` with `never-returning function must not implicitly return`. A path that is never taken never triggers it, so tests and static analysis are what catch the forgotten branch.
  • In PHP 8.0+, what does a static return type on a method promise?
    That the method returns an instance of the class it was called on, not just the declaring class, which suits fluent setters and named constructors in a hierarchy. `static` is return-only: it cannot type a parameter. The return check verifies the object's class at runtime like any other class type.

void is a courier who delivers the parcel and comes back empty-handed; never is a courier on a one-way trip who either stays at the destination or reports an accident, but does not come back to the depot.

saying these in an interview costs you the question

  • Says a void function may end with return null; since callers see null anyway.
  • Thinks never is just another name for void.
  • Believes a never function may use a bare return; to leave early.
  • Claims PHP proves at compile time that every path of a never function throws.
  • Writes ?void or never|int as a valid return declaration.