PHP 8.1: `never` Return Type is Dangerous

There are many features already introduced in PHP 8.1, and among them the new never return type:

function doSomething(): never {
    // do something
    die();
}

The idea is pretty straightforward: if the execution flow is never supposed to leave the function, it should be marked as never. If the execution flow somehow reaches the end of the function, PHP will throw an error.

Although it seems to me like a good idea, there’s a hidden catch we need to be cautious about.

Redirect and Die

The most popular use case for this feature, it appears, would be the redirect functionality:

function sendHome($userType): never {
    switch ($userType) {
        case 'editor':
            $url = '/dashboard';
            break;
        case 'user':
            $url = '/';
            break;
        default:
            throw new Exception('Invalid user type');
    }

    header('Location: ' . $url, true, 301);

    // Now stop the execution to prevent the page from loading any further.
    exit();
}

The exit() is necessary to prevent the page from accidentally slipping into further execution. Otherwise, it’s easy for the client (a browser or HTTP client) to ignore the header altogether and continue with loading the web page.

try {
    if ('admin' !== $userType) {
        sendHome($userType);
    }

    // Do something that throws exceptions...
} catch(Exception $e) {
    // Save the error to logs and continue execution.
}

// Do something restricted...

As you can see, we accidentally put sendHome() inside the try...catch construction. This means that if somebody manages to pass invalid user type into the sendHome() function, the exception will be logged, but execution will continue.

Why Is This a Problem

The example above may seem made up, until you consider that try...catch may wrap your sendHome() on an entirely different application level. For example, you may catch exceptions in a controller, while sendHome() will be called inside a model (I know this is a terrible practice, but I’ve seen worse). In that case catching this logical error may not be as easy as it looks.

Don’t get me wrong, never is still a good addition to PHP, and I’m glad to see that it’s already merged into PHP 8.1. However, this introduces another way for developers to “shoot themselves in a leg”, and we need to pay special attention to exception handling when using it.

3 thoughts on “PHP 8.1: `never` Return Type is Dangerous”

  1. Christian Wolf

    How would NOT having never as a return type prevent anything in your given example?
    As I understand it, ‘never’ specially helps static analyzers to show unreachable code.

    1. Sergey Mitroshin Post Author

      That’s a valid point, exact same situation may happen with or without `never`.
      The problem as I see it is the expectation. Having `never` as a return type does not necessarily mean that execution will end, although it may seem so to a developer.

  2. Ben Ramsey

    `never` doesn’t mean that execution of the script stops. It means that the method never returns anything. That can be because the script exits or, most likely, an exception is thrown. The application could continue running, depending on how you want to handle exceptions.

Leave a Reply to Christian Wolf Cancel Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.