Why match beats a traditional switch

When I first saw PHP 8.0 introduce the match expression, I thought it was just syntactic sugar. After a few projects I realized it’s more than that. Unlike a switch, match returns a value, can be used inline, and supports strict type checking. The result is code that reads like a small function, yet stays in a single expression.

In a typical web application you often need to map a request method or a status code to a handler. A switch works, but it forces you to duplicate break statements and you lose the ability to treat the mapping as a value. match solves both problems: you get a single expression that yields a callable, and you avoid accidental fall‑through.

Using match makes the intent explicit—what value you expect—and the compiler will warn you if you forget a case.

Real‑world example: routing HTTP methods

Imagine a lightweight router that decides which controller to invoke based on the request method. Previously I wrote something like:

switch ($method) {
    case 'GET':
        $handler = $getHandler;
        break;
    case 'POST':
        $handler = $postHandler;
        break;
    case 'PUT':
        $handler = $putHandler;
        break;
    case 'DELETE':
        $handler = $deleteHandler;
        break;
    default:
        $handler = $notAllowedHandler;
}

// later
$handler();

That works, but it feels verbose and is prone to typos (e.g., 'GET' vs 'GET '). The match version is shorter and self‑documenting:

$handler = match (trim($method)) {
    'GET'    => $getHandler,
    'POST'   => $postHandler,
    'PUT'    => $putHandler,
    'DELETE' => $deleteHandler,
    default  => $notAllowedHandler,
};

$handler();

Notice the trim. In a real router you might also want to reject unknown methods early, but the match structure makes it easy to add validation later without breaking the flow.

Putting it together – a production‑ready snippet

Below is a small class I keep in a utility folder. It demonstrates a few extra patterns I like to layer on top of match:

  • Using an enum for HTTP methods – eliminates magic strings.
  • Applying match to map the enum to a callable.
  • Adding a fallback that logs an error before invoking the not‑allowed handler.

The code is ready for a PHP 8.1+ project and follows my team’s coding standards.

routes[HttpMethod::from($method)] = $handler;
    }

    /**
     * Resolve the appropriate handler based on the supplied method.
     */
    public function resolve(string $method): callable
    {
        $methodEnum = HttpMethod::tryFrom($method);

        // Use match for a clean, type‑safe lookup.
        $handler = match ($methodEnum) {
            HttpMethod::GET    => $this->routes[HttpMethod::GET] ?? null,
            HttpMethod::POST   => $this->routes[HttpMethod::POST] ?? null,
            HttpMethod::PUT    => $this->routes[HttpMethod::PUT] ?? null,
            HttpMethod::DELETE => $this->routes[HttpMethod::DELETE] ?? null,
            null               => null,
        };

        if ($handler === null) {
            // Fallback – you might log here and return a 405 response.
            return $this->routes[HttpMethod::GET] ?? static fn (): never => throw new RuntimeException('No handler found');
        }

        return $handler;
    }
}

/**
 * Simple enum for HTTP methods (PHP 8.1+).
 */
enum HttpMethod: string
{
    case GET    = 'GET';
    case POST   = 'POST';
    case PUT    = 'PUT';
    case DELETE = 'DELETE';
}
?>

The enum guarantees that only valid constants can be passed, so a typo like 'GET ' will be caught at runtime (or caught earlier by static analysis tools). The match expression then becomes a reliable dispatch table.

Final thoughts

I still see many PHP projects rely on sprawling switch statements for request handling, configuration mapping, or validation errors. Switching to match not only reduces boilerplate but also encourages you to think about the return value upfront. In a high‑traffic application where routing decisions happen on every request, that small reduction in cognitive load pays off quickly.

Try replacing a few of your switches with match this week. You’ll notice the code reads better, and you’ll have a single expression that can be unit‑tested just like any other function.