docs: English JsonRpcBundle docs, REST and MCP Bundle sections, package cards
uk/articles/from-tg/series/php-can-do/attributes.md
новий файл +163
−0
1
+
# Атрибути
2
+
3
+
*Метапрограмування без DocBlock-хаків*
4
+
5
+
- **Додано у версії:** PHP 8.0
6
+
- **Що дає:** нативний, типобезпечний механізм додавання метаданих до класів, методів, властивостей та параметрів — без необхідності при їх читанні парсити DocBlock-и та встановлювати сторонні бібліотеки.
7
+
8
+
## Трохи особистого
9
+
10
+
До PHP 8.0 в мене був один технічний “біль”, про який знають тільки ті, хто коли-небудь пробував будувати API-документацію чи SDK на основі читання коду.
11
+
12
+
Скажу лише одне: парсинг DocBlock-ів — це дуже невдячна робота.
13
+
14
+
Атрибути стали тією фічею, після якої мої бібліотеки буквально отримали "вдих свіжого повітря".
15
+
16
+
Код став чистішим, простішим, а метадані — нарешті передбачуваними.
17
+
18
+
## Навіщо вони взагалі потрібні?
19
+
20
+
До PHP 8 метадані доводилося писати так:
21
+
22
+
```php
23
+
/**
24
+
* @Route("/users")
25
+
* @SomeFlag(true)
26
+
*/
27
+
class UserController {}
28
+
```
29
+
30
+
Це був просто коментар, який не розбирається стандартними засобами PHP такими як рефлексія. Для аналізу потрібна ще одна бібліотека-парсер, яка мала вгадати, що розробник мав на увазі.
31
+
32
+
Атрибути роблять метакоментарі типізованими, тепер вони обʼєктні:
33
+
34
+
```php
35
+
#[Route('/users')]
36
+
#[SomeFlag(true)]
37
+
class UserController {}
38
+
```
39
+
40
+
Це буквально декларація екземплярів класів Route та SomeFlag, і IDE точно знає, що всередині. Це дає:
41
+
42
+
- нативне використання;
43
+
- розуміння які параметри треба передати;
44
+
- легке читання коду;
45
+
- легке використання цих даних.
46
+
47
+
## Приклади застосування
48
+
49
+
### 1. Doctrine
50
+
51
+
```php
52
+
#[ORM\Entity]
53
+
class Product
54
+
{
55
+
#[ORM\Id]
56
+
#[ORM\GeneratedValue]
57
+
#[ORM\Column]
58
+
public int $id;
59
+
60
+
#[ORM\Column(length: 255)]
61
+
public string $name;
62
+
}
63
+
```
64
+
65
+
### 2. Symfony Routing
66
+
67
+
```php
68
+
#[Route('/orders/{id}', methods: ['GET'])]
69
+
public function show(int $id) { ... }
70
+
```
71
+
72
+
### 3. Валідація
73
+
74
+
```php
75
+
class RegistrationDTO
76
+
{
77
+
#[Assert\Email]
78
+
#[Assert\NotBlank]
79
+
public string $email;
80
+
81
+
#[Assert\Length(min: 8)]
82
+
public string $password;
83
+
}
84
+
```
85
+
86
+
### 4. Кастомні атрибути
87
+
88
+
```php
89
+
#[API\Method('user.create')]
90
+
class CreateUserRequest
91
+
{
92
+
#[API\Param]
93
+
public string $email;
94
+
95
+
#[API\Param]
96
+
public string $password;
97
+
}
98
+
```
99
+
100
+
## А що можна робити з атрибутами?
101
+
102
+
Майже все, що роблять Java/Spring- або C#-атрибути:
103
+
104
+
- позначати класи й методи;
105
+
- додавати метадані;
106
+
- створювати власні декларативні правила;
107
+
- будувати роутинг;
108
+
- маркувати серіалізацію/десеріалізацію;
109
+
- автоматизувати DI/автовайринг;
110
+
- вказувати, як працює кешування чи логування;
111
+
- створювати декларативні контракти для DTO.
112
+
113
+
Фактично — інструмент, щоб писати менше імперативного коду та більше декларативного.
114
+
115
+
## Як створити власний атрибут
116
+
117
+
```php
118
+
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
119
+
class Loggable
120
+
{
121
+
public function __construct(
122
+
public string $message = 'default',
123
+
public bool $enabled = true,
124
+
) {}
125
+
}
126
+
```
127
+
128
+
Використання:
129
+
130
+
```php
131
+
#[Loggable(message: 'Auth attempt')]
132
+
class AuthService {}
133
+
```
134
+
135
+
## Як прочитати атрибут через Reflection
136
+
137
+
```php
138
+
$ref = new ReflectionClass(AuthService::class);
139
+
$attrs = $ref->getAttributes(Loggable::class);
140
+
141
+
$instance = $attrs[0]->newInstance();
142
+
echo $instance->message;
143
+
```
144
+
145
+
## Особливо важливо
146
+
147
+
- атрибути — це повноцінний об’єкт, а не просто рядок в коментарі;
148
+
- вони мають простір імен;
149
+
- можна типізувати параметри;
150
+
- можна використовувати передові конструкції (enum, readonly, VO);
151
+
- ідеально комбінуються з DI-контейнерами.
152
+
153
+
## Висновок
154
+
155
+
> Атрибути — це одна з тих фіч, після яких старий PHP здається архаїчним.
156
+
> Типобезпека, декларативність, прозора робота через Reflection, відсутність DocBlock-магії — це все робить їх інструментом, який я обожнюю використовувати у власних бібліотеках.
157
+
> Це настільки природно, що важко повірити, що колись ми писали метадані в коментарях.
158
+
159
+
← [Попередня: readonly + accessors](/articles/from-tg/series/php-can-do/readonly) · [Уся серія](/articles/from-tg/series/php-can-do)
160
+
161
+
---
162
+
163
+
*Вперше опубліковано в Telegram-каналі [«Створений щоб помирати»](https://t.me/php_while_true/96) 09.12.2025.*