A Tiny Secret Weapon in My Daily Toolkit

When I started building PHP web applications, I relied on a classic if/elseif/else cascade to decide which handler should process a request. The pattern looked something like this:

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;
}

That works, but the code grows noisy as you add more methods or patterns. Enter PHP 8's match expression. It feels like a switch that returns a value directly, and it encourages you to think in terms of patterns rather than fall‑through logic.

Why Match Beats Switch for Routing

First, match is *exhaustive* by default – the language will warn you if you forget a case, which is a huge safety net for request handling. Second, you can match against multiple values at once, perfect for grouping HTTP verbs that share the same logic. Finally, the syntax is more declarative; you write what* you want, not *how* you want to jump through cases.

A match expression reads almost like a sentence, making it easier for a teammate (or your future self) to understand the intent at a glance.

Real‑World Example: Dispatching API Calls

Imagine a tiny API wrapper that needs to forward calls to different endpoints based on the HTTP method. In a production environment, I'd want something that is both robust and easy to test. Below is a compact, production‑ready class that uses match to achieve exactly that.

<?php
namespace App\Http;

class RequestRouter
{
    /**
     * @var callable[] Mapping of HTTP method to handler callable.
     */
    private array $routes = [];

    /**
     * Register a handler for a specific HTTP method.
     */
    public function add(string $method, callable $handler): void
    {
        $this->routes[strtoupper($method)] = $handler;
    }

    /**
     * Dispatch the request based on the given method.
     * Returns the result of the matched handler or throws.
     */
    public function dispatch(string $method): mixed
    {
        $method = strtoupper($method);

        // Match against known methods; default to a 405 handler.
        return match ($method) {
            'GET'    => ($this->routes['GET'] ?? null)?($this->getPayload()) ?? null,
            'POST'   => ($this->routes['POST'] ?? null)?($this->getPayload()) ?? null,
            'PUT'    => ($this->routes['PUT'] ?? null)?($this->getPayload()) ?? null,
            'DELETE' => ($this->routes['DELETE'] ?? null)?($this->getPayload()) ?? null,
            default => $this->handleMethodNotAllowed($method),
        };
    }

    /**
     * Simple payload extraction – replace with actual request body parsing.
     */
    private function getPayload(): array
    {
        // In real code you'd parse JSON, XML, etc.
        return [];
    }

    /**
     * Handler for unsupported methods – could return a 405 HTTP response.
     */
    private function handleMethodNotAllowed(string $method): array
    {
        return [
            'error' => "Method {$method} not allowed",
            'status' => 405,
        ];
    }
}

The class is tiny, yet it demonstrates several best practices. First, routes are stored in an associative array, which makes it trivial to add or remove endpoints at runtime. Second, the dispatch method uses a single match expression to decide what to do, eliminating the need for a bulky switch block.

Extending the Pattern

What if you need to group multiple verbs under one handler? Match shines here because you can list several patterns in a single case:

return match ($method) {
    'GET' | 'HEAD' => $this->handleSafeRequest(),
    'POST' | 'PUT' | 'PATCH' => $this->handleWriteRequest(),
    default => $this->handleMethodNotAllowed($method),
};

You can also incorporate more complex patterns, such as checking the request path or headers, by using preg_match inside the match arms. The key is that match keeps the logic flat and readable.

When to Stick with Switch

Not every situation benefits from match. If you need to execute multiple statements before breaking, a traditional switch with break statements is clearer. Also, if you are targeting PHP versions below 8.0, match simply isn’t available. In those cases, a well‑structured switch is the pragmatic choice.

Why I Prefer Match in Production

  • Exhaustive checking. The PHP interpreter will flag missing cases, catching bugs early.
  • Pattern grouping. Multiple values can be tested in a single arm, reducing verbosity.
  • Self‑documenting. The expression reads like a sentence, making the intent obvious to any developer who reads the code.
  • Testability. Because match returns a value, you can easily mock handlers in unit tests.

In my day‑to‑day work, I reach for match whenever I have a decision tree that can be expressed as “given X, return Y”. It feels natural, reduces cognitive load, and keeps the codebase clean. If you haven't upgraded your PHP version yet, consider that match is just one of many modern features that can elevate your coding standards.

Getting Started

Start small: replace a simple switch in a helper function with match. As you become comfortable, refactor larger routing or validation logic. Remember to keep your PHP version in mind – match requires PHP 8.0 or higher – and update your CI pipeline accordingly.

Give match a try on your next feature. You might find yourself writing cleaner, more maintainable code, just like I do every day.