Події життєвого циклу

← Події життєвого циклу

Ashterix

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-запитів.