PHP 8.1: never return type

Version8.1
TypeNew Feature

never is a new return type added in PHP 8.1.

A function/method that is declared with the never return type indicates that it will never return a value, and always throws an exception or terminates with a die/exit call.

never return type is similar to the existing void return type, but the never type guarantees that the program will terminate or throw. In other words, a function/method declared never must not call return at all, even in the return; form.

function redirect(string $url): never {
    header('Location: ' . $url);
    exit();
}

The goal of the never return type is to indicate and enforce a function that prevents the rest of the called code from being executed. IDEs, and PHP itself can recognize functions with the never return types, and safely assume that the rest of the code in scope will not run.

function redirect(string $url): never {
    header('Location: ' . $url);
    exit();
}

redirect('Test'); // The rest of the code is GUARANTEED to not continue.
do_something_else();

A function/method with the never return type must prevent the rest of the code in scope from being executed. This is achieved by either terminating the request (die/exit), or throwing an exception.

If function/method with the never return type does not throw an exception, or terminate the program, PHP will throw a TypeError exception:

function dispatch(string $message): never {
    echo $message;
}

dispatch('test');
TypeError: dispatch(): never-returning function must not implicitly return

On the other hand, if a never-declared function calls return, it will throw a TypeError exception. This is enforced at the call-time, rather than at the parsing time.

function foo(): never {
    return;
}
foo();
Fatal error: A never-returning function must not return in ... on line 2

The original RFC proposed to use noreturn for this purpose. A secondary vote in the RFC contested noreturn vs never, and never was elected.

never is a return-type

Similar to void, never is a return type. Using never in a parameter or a property type is not allowed.

function foo(never $a) {}
Fatal error: never cannot be used as a parameter type in ... on line ...

Using never as a class property type causes a similar fatal error:

class Foo {
    private never $bar;
}
Fatal error: Property Foo::$bar cannot have type never in ... on line 3

never cannot be used in a Union Type

Using never in a Union Type is not allowed. This is similar to how the void type is enforced as well.

function foo(): never|string {}
Fatal error: never can only be used as a standalone type in ... on line 3

never vs void

The new never return type and void return type share many common traits. They both communicate that the function/method will never return a value. However, the never type also enforces that an exception is thrown, or the program is terminated.

  • void and never both are return types. They cannot be used as parameter types or property types.
  • Using void or never

The difference with void and never is that void allows calling return without an explicit value. i.e. return;. However, calling return (even without a value) is not allowed at all in a never-declared function.

Variance with never

never is a "bottom" type. This effectively means that a class method with never return type cannot change the return type to any other type, because there is no other smaller type than never.

class Foo {
    public function test(): string {}
}
class FooBar extends Foo{
    public function test(): never {}
}

The snippet above is valid, because never is a bottom type. With Liskov Substitution Principle in PHP, changing the return type with a subset (smaller type) is allowed.

Note that with void and never, never is the smaller type. A method that is declared void can be extended with a method that declares never as the return type.


class Foo {
    public function test(): never {}
}
class FooBar extends Foo{
    public function test(): string {}
}
Fatal error: Declaration of FooBar::test(): string must be compatible with Foo::test(): never in ... on line ...

FooBar::test() method is not allowed to declare the return type as string, because it is a wider type than never.


Backwards Compatibility Impact

never is a new return type in PHP 8.1. It carries certain engine changes to enforce a function call declared never follows the rules. This behavior cannot be brought-back to older PHP versions using user-land PHP code.

Further, never becomes a reserved keyword in PHP, and using never for function names, class names, whole namespace, etc. will cause parse errors or other types of fatal errors.

@return noreturn Doc Annotations

PHP static analysis tools such as Psalm and PHPStan support a return type noreturn that infers the same data. In fact, the never type was proposed to PHP by the authors of Psalm and PHPStan tools.

With the never type supported in PHP, it is now possible to remove existing Doc Block annotations if the minimum PHP version is set to PHP >= 8.1.

/**
-* @return noreturn
 */
-function redirect(string $url): void {
+function redirect(string $url): never {
    header('Location: ' . $url);
    exit();
}

RFC Discussion Implementation