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
1
+
# Enums
2
+
3
+
*Only the intended values*
4
+
5
+
- **Added in:** PHP 8.1
6
+
- **What it gives you:** a native mechanism for enumerated types — with clear, typed values instead of "magic strings and numbers" and endless consts.
7
+
8
+
## A bit personal
9
+
10
+
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.
11
+
12
+
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.
13
+
14
+
## Types of enums
15
+
16
+
PHP has two kinds of enums.
17
+
18
+
### 1. Backed Enum — with a backing value (`string` or `int`)
19
+
20
+
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.
21
+
22
+
```php
23
+
enum Status: string
24
+
{
25
+
case DRAFT = 'draft';
26
+
case ACTIVE = 'active';
27
+
case ARCHIVED = 'archived';
28
+
}
29
+
```
30
+
31
+
What for:
32
+
33
+
- handy for statuses of orders, documents, payments, message types, employee types, etc.;
34
+
- easy to store in the database as a plain string;
35
+
- you can get the enum via `Status::from('draft')`, or safely via `tryFrom('draft')`.
36
+
37
+
### 2. Pure Enum — without a backing value
38
+
39
+
Just a set of unique cases without values. Used when a value isn't needed — what matters is the "state" or "mode" itself.
40
+
41
+
```php
42
+
enum Direction
43
+
{
44
+
case NORTH;
45
+
case SOUTH;
46
+
case EAST;
47
+
case WEST;
48
+
}
49
+
```
50
+
51
+
What for:
52
+
53
+
- for internal logical states or operating modes;
54
+
- when nothing needs to be stored in the database;
55
+
- when a simple check like `if ($direction === Direction::NORTH)` is enough.
56
+
57
+
## More than just a list
58
+
59
+
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.
60
+
61
+
### An enum with methods
62
+
63
+
```php
64
+
enum Status: string
65
+
{
66
+
case DRAFT = 'draft';
67
+
case ACTIVE = 'active';
68
+
case ARCHIVED = 'archived';
69
+
70
+
// 🔹 Non-static method — for a specific value
71
+
public function label(): string
72
+
{
73
+
return match($this) {
74
+
self::DRAFT => 'Draft',
75
+
self::ACTIVE => 'Active',
76
+
self::ARCHIVED => 'Archived',
77
+
};
78
+
}
79
+
80
+
// 🔹 Static method — shared by all
81
+
public static function visible(): array
82
+
{
83
+
return [self::DRAFT, self::ACTIVE];
84
+
}
85
+
}
86
+
87
+
echo Status::ACTIVE->label(); // Active
88
+
print_r(Status::visible()); // [Status::DRAFT, Status::ACTIVE]
89
+
```
90
+
91
+
### Another example
92
+
93
+
Statuses of documents, channels, roles:
94
+
95
+
```php
96
+
enum Channel: string
97
+
{
98
+
case EMAIL = 'email';
99
+
case SMS = 'sms';
100
+
case TELEGRAM = 'telegram';
101
+
102
+
public function icon(): string
103
+
{
104
+
return match($this) {
105
+
self::EMAIL => '✉️',
106
+
self::SMS => '📱',
107
+
self::TELEGRAM => '💬',
108
+
};
109
+
}
110
+
111
+
public function getSenderFQCN(): string
112
+
{
113
+
return match($this) {
114
+
self::EMAIL => EmailSender::class,
115
+
self::SMS => SMSSender::class,
116
+
self::TELEGRAM => TelegramSender::class,
117
+
};
118
+
}
119
+
120
+
public static function fromString(string $value): ?self
121
+
{
122
+
return self::tryFrom($value);
123
+
}
124
+
}
125
+
```
126
+
127
+
## Conclusion
128
+
129
+
> Enums are one of the best PHP updates in recent years.
130
+
> They discipline the code, make the data model predictable and provide real type safety.
131
+
132
+
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.
133
+
134
+
## Doctrine + Enum
135
+
136
+
I love enums so much that I even built my own package — [ufo-tech/doctrine-helper](https://packagist.org/packages/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`.
137
+
138
+
### 1. Create your PHP Enum for the entity field
139
+
140
+
```php
141
+
<?php
142
+
namespace App;
143
+
144
+
enum MyEnum: string
145
+
{
146
+
case CASE_1 = 'My option 1';
147
+
case CASE_2 = 'My option 2';
148
+
}
149
+
```
150
+
151
+
### 2. Create your own DBAL type extending `Ufo\DoctrineHelper\DBAL\AbstractEnumType`
152
+
153
+
```php
154
+
<?php
155
+
namespace App\DBAL;
156
+
157
+
use BackedEnum;
158
+
use App\MyEnum;
159
+
160
+
class MyEnumType extends AbstractEnumType
161
+
{
162
+
protected string|BackedEnum $enum = MyEnum::class;
163
+
}
164
+
```
165
+
166
+
### 3. Register the new type in Doctrine
167
+
168
+
```yaml
169
+
doctrine:
170
+
dbal:
171
+
types:
172
+
App\MyEnum: 'App\DBAL\MyEnumType'
173
+
mapping_types:
174
+
enum: string
175
+
```
176
+
177
+
### 4. Use the PHP Enum to store and change the value
178
+
179
+
```php
180
+
<?php
181
+
182
+
namespace App\Entity;
183
+
184
+
use Doctrine\ORM\Mapping as ORM;
185
+
use App\MyEnum;
186
+
187
+
#[ORM\Entity]
188
+
#[ORM\Table(name: 'demo')]
189
+
class Demo
190
+
{
191
+
#[ORM\Column(type: MyEnum::class, nullable: false)]
192
+
protected string $case;
193
+
194
+
public function __construct(MyEnum $case)
195
+
{
196
+
$this->case = $case->value;
197
+
}
198
+
199
+
public function changeCase(MyEnum $case): void
200
+
{
201
+
$this->case = $case->value;
202
+
}
203
+
204
+
public function getCase(): string
205
+
{
206
+
return $this->case;
207
+
}
208
+
}
209
+
```
210
+
211
+
This way a MySQL ENUM behaves like a full-fledged PHP Enum — with type safety, IDE autocompletion and no manual casting.
212
+
213
+
← [Previous: Named arguments](/articles/from-tg/series/php-can-do/named-arguments) · [Whole series](/articles/from-tg/series/php-can-do) · [Next: readonly + accessors →](/articles/from-tg/series/php-can-do/readonly)
214
+
215
+
---
216
+
217
+
*First published in Ukrainian in the Telegram channel [“Created to die”](https://t.me/php_while_true/84) on October 24, 2025; the “Doctrine + Enum” section was a [separate post](https://t.me/php_while_true/85) on the same day.*