rpc.pre_execute

← rpc.pre_execute

Ashterix

docs: rewrite JsonRpcBundle docs, add SDK docs, move them under docs/api

uk/docs/api/json-rpc-bundle/functionality/events/pre_execute.md новий файл +127 −0

1 + # rpc.pre_execute
2 +
3 + ::::::block class="floatinginfobox floatinginfobox--fit"
4 + :::::columns border zebra
5 + ::::row
6 + :::col width="40%"
7 + **Назва події**
8 + :::
9 + :::col
10 + `rpc.pre_execute`
11 + :::
12 + ::::
13 + ::::row
14 + :::col width="40%"
15 + **Константа**
16 + :::
17 + :::col
18 + `RpcEvent::PRE_EXECUTE`
19 + :::
20 + ::::
21 + ::::row
22 + :::col width="40%"
23 + **Клас**
24 + :::
25 + :::col
26 + `RpcPreExecuteEvent`
27 + :::
28 + ::::
29 + ::::row
30 + :::col width="40%"
31 + **Властивості**
32 + :::
33 + :::col
34 + `RpcRequest $rpcRequest`, `Service $service`, `array $params`
35 + :::
36 + ::::
37 + :::::
38 + ::::::
39 +
40 + Найважливіша подія: на ній бандл валідує й конвертує параметри, захоплює блокування і **виконує сам метод**. Ваш лістенер стає частиною цього конвеєра — де саме, визначає пріоритет.
41 +
42 + ## Коли генерується
43 +
44 + Один раз на кожне виконання методу: після промаху кешу й після того, як метод знайдено. Для будь-якого джерела запиту — HTTP, консоль, batch, Messenger, відкладене виконання notifications/callback, прогрів кешу.
45 +
46 + **Не генерується** для кешованої відповіді, невідомого методу і прийому notifications/callback (метод виконується пізніше, і тоді подія буде).
47 +
48 + ## Вбудовані лістенери
49 +
50 + | Пріоритет | Лістенер | Що робить |
51 + | --- | --- | --- |
52 + | 1002 | `ValidateParamsListener` | Перетворює параметри на список аргументів методу (іменовані й позиційні), підставляє значення за замовчуванням і сервіси з `#[Autowire]`. Перезаписує `$event->params`. Відсутні обов'язкові параметри — `rpc.error` |
53 + | 1001 | `ValidateParamsListener::constraintValidation` | Перевіряє [`#[RPC\Assertions]`](/docs/api/json-rpc-bundle/add_rpc_service/assertions); порушення — `rpc.error` |
54 + | 1000 | `ParamsConverterEventListener` | [Конвертує](/docs/api/json-rpc-bundle/add_rpc_service/params) скаляри в об'єкти й масиви в DTO |
55 + | -100 | `LockEventListener` | Якщо запит уже має помилку — `rpc.error`; інакше захоплює [`#[RPC\Lock]`](/docs/api/json-rpc-bundle/add_rpc_service/lock) |
56 + | -1000 | `RpcServerListener` | Викликає метод з `$event->params`, формує відповідь (`rpc.pre_response`), генерує `rpc.post_execute` і зупиняє поширення |
57 +
58 + ## Що можна змінити
59 +
60 + **`$event->params` — це саме ті аргументи, з якими буде викликано метод.** Ефект залежить від пріоритету:
61 +
62 + | Пріоритет лістенера | Що ви бачите і що змінюєте |
63 + | --- | --- |
64 + | понад 1002 | Сирі параметри запиту; ваші зміни буде перезаписано на 1002 |
65 + | між 1002 і 1000 | Список аргументів зі скалярами; зміни пройдуть валідацію і конвертацію |
66 + | між 1000 і -1000 | Готові аргументи (об'єкти, DTO); зміни передаються в метод як є |
67 + | нижче -1000 | Викликається лише на шляху помилки виконання |
68 +
69 + * Ключ кешу рахується з сирих параметрів запиту, тож зміни `$params` на нього не впливають.
70 + * Замінювати `$event->rpcRequest` не варто — це розсинхронізує сервер.
71 +
72 + **Зупинка поширення** пропускає виклик методу. Підтримується лише сценарій помилки: згенеруйте `rpc.error` і зупиніть подію — так роблять вбудовані лістенери. Повернути успішний результат без виклику методу штатного способу немає.
73 +
74 + ```php
75 + public function __construct(private RpcEventFactory $events) {}
76 +
77 + // ...
78 + $this->events->fireError($event->rpcRequest, new RpcLogicException('Not allowed'));
79 + $event->stopPropagation();
80 + ```
81 +
82 + ## Приклади використання
83 +
84 + ### Значення параметра з контексту
85 +
86 + Підставити ідентифікатор поточного клієнта замість того, що передав клієнт (захист від підміни):
87 +
88 + ```php
89 + #[AsEventListener(RpcEvent::PRE_EXECUTE, priority: 1001)]
90 + final class TenantParamListener
91 + {
92 + public function __construct(private TenantResolver $tenants) {}
93 +
94 + public function __invoke(RpcPreExecuteEvent $event): void
95 + {
96 + if (array_key_exists('tenantId', $event->params)) {
97 + $event->params['tenantId'] = $this->tenants->current();
98 + }
99 + }
100 + }
101 + ```
102 +
103 + Пріоритет 1001: після формування списку аргументів (1002), але до конвертації (1000).
104 +
105 + ### Перевірка доступу до методу
106 +
107 + ```php
108 + #[AsEventListener(RpcEvent::PRE_EXECUTE, priority: 999)]
109 + final class AccessListener
110 + {
111 + public function __construct(private RpcEventFactory $events, private Acl $acl) {}
112 +
113 + public function __invoke(RpcPreExecuteEvent $event): void
114 + {
115 + if (!$this->acl->canCall($event->service->name)) {
116 + $this->events->fireError($event->rpcRequest, new RpcLogicException('Access denied'));
117 + $event->stopPropagation();
118 + }
119 + }
120 + }
121 + ```
122 +
123 + ### Інші ідеї
124 +
125 + * вимірювання часу виконання (старт тут, фініш на `rpc.post_execute` / `rpc.error`);
126 + * відкриття транзакції Doctrine перед виконанням методу;
127 + * аудит змін: збереження параметрів методів, що змінюють дані.