← Enums

Ashterix

docs: English JsonRpcBundle docs, REST and MCP Bundle sections, package cards

en/articles/from-tg/series/php-can-do/enums.md new file +217 −0

Enums

Only the intended values

  • Added in: PHP 8.1
  • What it gives you: a native mechanism for enumerated types — with clear, typed values instead of "magic strings and numbers" and endless consts.

A bit personal

This is one of my all-time favourite tools in PHP. Since they appeared, I can't imagine development without enums at all — they reduce the number of bugs, make code readable and guarantee type safety.

But talking to developers shows that far from everyone shares this excitement. So I want to share my love for them and explain why they're not just "a list of values", but a real step forward for PHP.

Types of enums

PHP has two kinds of enums.

1. Backed Enum — with a backing value (string or int)

Each case has a bound value, which lets you compare it with other values, serialize it and store it in the database, while being sure the value will always be one of those allowed by the enumeration.

enum Status: string 
{
    case DRAFT = 'draft';
    case ACTIVE = 'active';
    case ARCHIVED = 'archived';
}

What for:

  • handy for statuses of orders, documents, payments, message types, employee types, etc.;
  • easy to store in the database as a plain string;
  • you can get the enum via Status::from('draft'), or safely via tryFrom('draft').

2. Pure Enum — without a backing value

Just a set of unique cases without values. Used when a value isn't needed — what matters is the "state" or "mode" itself.

enum Direction 
{
    case NORTH;
    case SOUTH;
    case EAST;
    case WEST;
}

What for:

  • for internal logical states or operating modes;
  • when nothing needs to be stored in the database;
  • when a simple check like if ($direction === Direction::NORTH) is enough.

More than just a list

Most people think an enum is just a replacement for const. In fact it's a class-like construct to which you can (and should!) add methods, behaviour, static methods and value conversions.

An enum with methods

enum Status: string 
{
    case DRAFT = 'draft';
    case ACTIVE = 'active';
    case ARCHIVED = 'archived';

    // 🔹 Non-static method — for a specific value
    public function label(): string 
    {
        return match($this) {
            self::DRAFT => 'Draft',
            self::ACTIVE => 'Active',
            self::ARCHIVED => 'Archived',
        };
    }

    // 🔹 Static method — shared by all
    public static function visible(): array 
    {
        return [self::DRAFT, self::ACTIVE];
    }
}

echo Status::ACTIVE->label(); // Active
print_r(Status::visible());   // [Status::DRAFT, Status::ACTIVE]

Another example

Statuses of documents, channels, roles:

enum Channel: string 
{
    case EMAIL = 'email';
    case SMS = 'sms';
    case TELEGRAM = 'telegram';

    public function icon(): string 
    {
        return match($this) {
            self::EMAIL => '✉️',
            self::SMS => '📱',
            self::TELEGRAM => '💬',
        };
    }

    public function getSenderFQCN(): string 
    {
        return match($this) {
            self::EMAIL => EmailSender::class,
            self::SMS => SMSSender::class,
            self::TELEGRAM => TelegramSender::class,
        };
    }

    public static function fromString(string $value): ?self 
    {
        return self::tryFrom($value);
    }
}

Conclusion

Enums are one of the best PHP updates in recent years. They discipline the code, make the data model predictable and provide real type safety.

I can no longer imagine development without enums. And if you still don't use them in your projects — just give them a try: after a week you won't be able to write the old way.

Doctrine + Enum

I love enums so much that I even built my own package — ufo-tech/doctrine-helper, which teaches Doctrine to understand an enum in typed entity fields and to create an ENUM() column type for them in MySQL.

1. Create your PHP Enum for the entity field

<?php
namespace App;

enum MyEnum: string
{
    case CASE_1 = 'My option 1';
    case CASE_2 = 'My option 2';
}

2. Create your own DBAL type extending Ufo\DoctrineHelper\DBAL\AbstractEnumType

<?php
namespace App\DBAL;

use BackedEnum;
use App\MyEnum;

class MyEnumType extends AbstractEnumType
{
    protected string|BackedEnum $enum = MyEnum::class;
}

3. Register the new type in Doctrine

doctrine:
    dbal:
        types:
            App\MyEnum: 'App\DBAL\MyEnumType'
        mapping_types:
            enum: string

4. Use the PHP Enum to store and change the value

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use App\MyEnum;

#[ORM\Entity]
#[ORM\Table(name: 'demo')]
class Demo
{
    #[ORM\Column(type: MyEnum::class, nullable: false)]
    protected string $case;

    public function __construct(MyEnum $case)
    {
        $this->case = $case->value;
    }

    public function changeCase(MyEnum $case): void
    {
        $this->case = $case->value;
    }

    public function getCase(): string
    {
        return $this->case;
    }
}

This way a MySQL ENUM behaves like a full-fledged PHP Enum — with type safety, IDE autocompletion and no manual casting.


First published in Ukrainian in the Telegram channel “Created to die” on October 24, 2025; the “Doctrine + Enum” section was a separate post on the same day.