docs: rewrite JsonRpcBundle docs, add SDK docs, move them under docs/api
uk/docs/api/json-rpc-bundle/functionality/events/README.md
новий файл +87
−0
1
+
# Події життєвого циклу
2
+
3
+
Обробка кожного RPC-запиту проходить через події [Symfony EventDispatcher](https://symfony.com/doc/current/event_dispatcher.html). Підписавшись на них, можна додати аудит, метрики, трасування, змінити аргументи методу чи підмінити помилку — не змінюючи процедури.
4
+
5
+
:::warning
6
+
Лістенери втручаються в роботу сервера. Перш ніж змінювати дані чи зупиняти поширення події, прочитайте сторінку відповідної події: для кожної описано, що можна змінювати безпечно, а що ні.
7
+
:::
8
+
9
+
## Порядок подій
10
+
11
+
Для звичайного запиту з `id`:
12
+
13
+
```
14
+
rpc.request
15
+
└─ (кеш: якщо є відповідь у #[RPC\Cache] — далі нічого не генерується)
16
+
rpc.pre_execute ← валідація, конвертація параметрів, #[RPC\Lock], виклик методу
17
+
├─ успіх: rpc.pre_response → rpc.post_execute
18
+
└─ помилка: rpc.error → rpc.pre_response
19
+
── відповідь відправлено клієнту ──
20
+
rpc.post_response ← CacheRelation, notifications, $rpc.callback
21
+
```
22
+
23
+
Для [notifications](/docs/api/json-rpc-bundle/functionality/notifications) і [`$rpc.callback`](/docs/api/json-rpc-bundle/functionality/callback) при прийомі генеруються лише `rpc.request` і `rpc.pre_response` з відповіддю-заглушкою, а сам метод виконується в `rpc.post_response` — зі своїми `rpc.pre_execute`, `rpc.pre_response`, `rpc.post_execute`. Повний опис — на сторінці [Життєвий цикл](/docs/api/json-rpc-bundle/developers/lifecycle).
24
+
25
+
## Перелік подій
26
+
27
+
Назви — константи `Ufo\JsonRpcBundle\EventDrivenModel\Events\RpcEvent`, класи — у тому самому просторі імен.
28
+
29
+
| Подія | Клас | Коли |
30
+
| --- | --- | --- |
31
+
| [`rpc.request`](/docs/api/json-rpc-bundle/functionality/events/request) | `RpcRequestEvent` | Запит прийнято, до перевірки кешу |
32
+
| [`rpc.pre_execute`](/docs/api/json-rpc-bundle/functionality/events/pre_execute) | `RpcPreExecuteEvent` | Перед викликом методу; тут же виконується сам метод |
33
+
| [`rpc.post_execute`](/docs/api/json-rpc-bundle/functionality/events/post_execute) | `RpcPostExecuteEvent` | Метод успішно виконано |
34
+
| [`rpc.pre_response`](/docs/api/json-rpc-bundle/functionality/events/pre_response) | `RpcPreResponseEvent` | Відповідь сформовано |
35
+
| [`rpc.post_response`](/docs/api/json-rpc-bundle/functionality/events/post_response) | `RpcPostResponseEvent` | Відповідь відправлено клієнту |
36
+
| [`rpc.error`](/docs/api/json-rpc-bundle/functionality/events/error) | `RpcErrorEvent` | Помилка під час виконання |
37
+
| [`rpc.async_output`](/docs/api/json-rpc-bundle/functionality/events/async_output) | `RpcAsyncOutputEvent` | Дочірній процес batch повернув результат |
38
+
| [`rpc.async_request`](/docs/api/json-rpc-bundle/functionality/events/async_request) | `RpcAsyncRequestEvent` | Оголошена, але бандл її не генерує |
39
+
40
+
## Підписка
41
+
42
+
```php
43
+
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
44
+
use Ufo\JsonRpcBundle\EventDrivenModel\Events\RpcEvent;
45
+
use Ufo\JsonRpcBundle\EventDrivenModel\Events\RpcPostResponseEvent;
46
+
47
+
#[AsEventListener(RpcEvent::POST_RESPONSE)]
48
+
final class AuditListener
49
+
{
50
+
public function __invoke(RpcPostResponseEvent $event): void
51
+
{
52
+
// $event->rpcRequest->getMethod(), $event->response ...
53
+
}
54
+
}
55
+
```
56
+
57
+
Пріоритет задається в атрибуті: `#[AsEventListener(RpcEvent::PRE_EXECUTE, priority: 500)]`. Більший пріоритет — раніше. Пріоритети вбудованих лістенерів наведено на сторінці кожної події: від них залежить, що ваш лістенер побачить і чи взагалі буде викликаний.
58
+
59
+
## Загальні правила
60
+
61
+
* **Властивості подій публічні й не readonly**, але присвоєння нового значення діє не завжди — див. сторінку події.
62
+
* **`stopPropagation()`** зупиняє не лише ваших сусідів, а й вбудовані лістенери з нижчим пріоритетом. На `rpc.pre_execute`, `rpc.pre_response` і `rpc.error` це може залишити запит без відповіді — не зупиняйте ці події, якщо не формуєте відповідь самі.
63
+
* **Виняток з лістенера** перетворюється на відповідь з помилкою. В одиночному HTTP-запиті він не проходить через `rpc.error` (див. [Актуальні помилки](/docs/api/json-rpc-bundle/developers/known_issues/open)).
64
+
* **Кешована відповідь** (`#[RPC\Cache]`) повертається без подій виконання: після `rpc.request` нічого не генерується.
65
+
66
+
## Контекст усередині лістенера
67
+
68
+
| Що потрібно | Звідки взяти |
69
+
| --- | --- |
70
+
| Метод, параметри, `id` | `$event->rpcRequest->getMethod()`, `getParams()` (без `$rpc`), `getId()` |
71
+
| Спеціальні параметри `$rpc` | `$event->rpcRequest->getRpcParams()` — `getTimeout()`, `hasCallback()`, `getRayId()`, `ignoreCache` |
72
+
| Notification чи ні | `$event->rpcRequest instanceof Ufo\RpcObject\RpcNotificationRequest` |
73
+
| Версія API | `$event->rpcRequest->apiVersion` |
74
+
| Група API | сервіс `Ufo\JsonRpcBundle\Server\ApiGroupContext`: `getGroup()` |
75
+
| HTTP-запит | сервіс `Ufo\JsonRpcBundle\Server\RequestPrepare\RequestCarrier`: `getCarrier()`, якщо це `IRpcHttpCarrier` — `getHttpRequest()` |
76
+
| Токен | сервіс `IRpcSecurity`: `getTokenHolder()->getToken()` |
77
+
| Попередні події цього запиту | сервіс `RpcEventFactory`: `getEvent(Клас::class)`, `getEvents(Клас::class)` |
78
+
79
+
## Власні події
80
+
81
+
`RpcEventFactory::fire()` приймає будь-яку назву. Невідома назва генерується як `rpc.custom_event` (клас `RpcEvent` з масивом `$eventData`); бандл сам таких подій не генерує й не слухає.
82
+
83
+
## Міграція зі старих версій
84
+
85
+
* З 8.0 класи подій живуть у `Ufo\JsonRpcBundle\EventDrivenModel\Events\*` (раніше — `Ufo\RpcObject\Events\*`).
86
+
* Подію `rpc.response` прибрано у 7.6 — використовуйте `rpc.pre_response` або `rpc.post_response`.
87
+
* З 10.3 бандл не зупиняє поширення `kernel.terminate` / `console.terminate`, тож terminate-лістенери застосунку теж виконуються після RPC-запитів.