# The `match` Expression in PHP

5 minutes read

Ashley Allen · 18th September 2025

## In this article

- [Introduction](/content/blog/php-match-expression#content-introduction/index.html)
- [The match Expression in PHP](/content/blog/php-match-expression#content-the-match-expression-in-php/index.html)
  - [Multiple Conditions per Branch](/content/blog/php-match-expression#content-multiple-conditions-per-branch/index.html)
- [Passing true to the match Expression](/content/blog/php-match-expression#content-passing-true-to-the-match-expression/index.html)
- [Pairing Match with Enums](/content/blog/php-match-expression#content-pairing-match-with-enums/index.html)
- [Conclusion](/content/blog/php-match-expression#content-conclusion/index.html)

## Introduction [Permalink](/content/blog/php-match-expression#content-introduction "Permalink"/index.html)

The `match` expression is a [PHP](/content/blog?category=php/index.html) feature that I love using. It was introduced in PHP 8.0 (released in November 2020), so it's been around for a while now. But I thought I'd put together a [Quickfire](/content/blog?category=quickfire/index.html) article about it to help spread the word, for anyone (such as those new to PHP) who may not be aware of it.

## The `match` Expression in PHP [Permalink](/content/blog/php-match-expression#content-the-match-expression-in-php "Permalink"/index.html)

The `match` expression allows you to compare a value against multiple conditions and return a given result (although a result doesn't necessarily have to be returned). It's similar to a `switch` statement in the sense that you can define "branches" based on different conditions.

However, a key difference is that the `match` expression uses strict comparison (`===`), whereas a `switch` statement uses loose comparison (`==`). This means that with `match`, the types of the values being compared must be the same for a match to occur.

I think the best way to understand what a `match` expression is to see it in action. So let's take a look at an example of how we might use it in a real-world scenario.

We'll imagine we have an `App\Services\Git\RepositoryManager` class that allows us to interact with different Git repository hosting services, such as GitHub, GitLab, and Bitbucket. Each service has its own driver class that implements a common interface called `App\Interfaces\Git\Drivers\RepositoryDriver`. Using `if`/`elseif`/`else`, we might write something like this:

```php
namespace App\Services\Git;

use App\Interfaces\Git\Drivers\RepositoryDriver;
use App\Services\Git\Drivers\BitbucketDriver;
use App\Services\Git\Drivers\GitHubDriver;
use App\Services\Git\Drivers\GitLabDriver;

final readonly class RepositoryManager
{
    // ...

public function driver(string $driver): RepositoryDriver
    {
        if ($driver === 'github') {
            return new GitHubDriver();
        } elseif ($driver === 'gitlab') {
            return new GitLabDriver();
        } elseif ($driver === 'bitbucket') {
            return new BitbucketDriver();
        } else {
            throw new \InvalidArgumentException("Unsupported driver: $driver");
        }
    }
}
```

Now, with the above code example, we can do something like this to get a driver instance for interacting with GitHub:

```php
$githubDriver = new RepositoryManager()->driver('github');
```

However, we can simplify our `driver` method using a `match` expression, like so:

```php
namespace App\Services\Git;

final readonly class RepositoryManager
{
    // ...

public function driver(string $driver): RepositoryDriver
    {
        return match ($driver) {
            'github' => new GitHubDriver(),
            'gitlab' => new GitLabDriver(),
            'bitbucket' => new BitbucketDriver(),
            default => throw new \InvalidArgumentException("Unsupported driver: $driver"),
        };
    }
}
```

In the code example above, we have used the `match` expression to define 4 separate branches. The first 3 branches check if the `$driver` variable matches one of the supported drivers, and if so, it returns a new instance of the corresponding driver class. The `default` branch is used to handle any unsupported driver values by throwing an `\InvalidArgumentException`.

In my personal opinion, I now find this function much easier to read and understand at a glance. I also like that it's reduced the number of `return` statements in the method. Although there's nothing wrong with having multiple `return` statements, I find it easier to understand the flow of the method when there's a single `return` point.

### Multiple Conditions per Branch [Permalink](/content/blog/php-match-expression#content-multiple-conditions-per-branch "Permalink"/index.html)

`match` expressions can also have multiple conditions for a single branch.

For example, imagine we have our own self-hosted Git service that uses the same API as GitHub. As a result, we can use the `App\Services\Git\Drivers\GitHubDriver` for both GitHub and our custom Git service. We'll assume that we are referring to this driver name as `'self-hosted'`. Let's update our `driver` method to account for this:

```php
use App\Interfaces\Git\Drivers\RepositoryDriver;
use App\Services\Git\Drivers\BitbucketDriver;
use App\Services\Git\Drivers\GitHubDriver;
use App\Services\Git\Drivers\GitLabDriver;

namespace App\Services\Git;

final readonly class RepositoryManager {
    // ...

public function driver(string $driver): RepositoryDriver
    {
        return match ($driver) {
            'github', 'self-hosted' => new GitHubDriver(),
            'gitlab' => new GitLabDriver(),
            'bitbucket' => new BitbucketDriver(),
            default => throw new \InvalidArgumentException("Unsupported driver: $driver"),
        };
    }
}
```

We can see in the code example above that we have added `'self-hosted'` as an additional condition for the first branch. This means that if the `$driver` variable is either `'github'` or `'self-hosted'`, it will return a new instance of the `App\Services\Git\Drivers\GitHubDriver`.

## Passing `true` to the `match` Expression [Permalink](/content/blog/php-match-expression#content-passing-true-to-the-match-expression "Permalink"/index.html)

A cool use case for the `match` expression is to pass `true` to it. This allows you to then evaluate conditions in the arms of the `match` statement.

As a basic example, let's say we want to set a message based on a user's role:

```php
// Assume we have retrieved a user from the database
// and that the user is an admin.
$user = \App\Models\User::first();

$message = match (true) {
    $user->isAdmin() => 'User is an admin',
    $user->isEditor() => 'User is an editor',
    $user->isSubscriber() => 'User is a subscriber',
    default => 'User role is unknown',
};

// $message will be 'User is an admin'
```

## Pairing Match with Enums [Permalink](/content/blog/php-match-expression#content-pairing-match-with-enums "Permalink"/index.html)

One of the places that I've found `match` expressions pair really nicely is with PHP enums. For example, let's say we have an enum that defines different job types:

```php
enum JobType: string
{
    case WebDeveloper = 'web_developer';
    case Designer = 'designer';
    case ProjectManager = 'project_manager';
    case SalesManager = 'sales_manager';
}
```

Although we might want to use the raw enum values in some places (such as the database and application code), we wouldn't want to display these to the user. For instance, if the user can select one of the job type options in a form, we'd much rather show them a more user-friendly label, such as "Web Developer" instead of "web_developer".

So let's add a `toFriendly` method to our `JobType` enum that uses a `match` expression to return a user-friendly label for each job type:

```php
enum JobType: string
{
    case WebDeveloper = 'web_developer';
    case Designer = 'designer';
    case ProjectManager = 'project_manager';
    case SalesManager = 'sales_manager';

public function toFriendly(): string
    {
        return match ($this) {
            self::WebDeveloper => 'Web Developer',
            self::Designer => 'Designer',
            self::ProjectManager => 'Project Manager',
            self::SalesManager => 'Sales Manager',
        };
    }
}
```

You may have noticed that we've not defined a `default` branch in our `match` expression. This is because our expression is exhaustive, meaning that all the possible cases have been covered, so the `match` expression can't evaluate to anything other than one of the defined cases.

Now we can use the `toFriendly` method to get a user-friendly label for any `JobType` enum value:

```php
$jobType = JobType::WebDeveloper;
echo $jobType->toFriendly(); // Outputs: Web Developer
```

In my opinion, pairing the `match` expression with enums like this is a great way to keep your code clean and maintainable. I feel like they just work really well together.

An alternative approach you could take to adding helper methods, such as `toFriendly`, to your enums could be to use PHP attributes. I have an article which covers how to do this, if you're interested: [A Guide to PHP Attributes](/content/blog/php-attributes/index.html).

## Conclusion [Permalink](/content/blog/php-match-expression#content-conclusion "Permalink"/index.html)

Hopefully, this article has given you a quick overview of the `match` expression in PHP, along with some practical examples of how you can use it in your own code.
