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
+
* аудит змін: збереження параметрів методів, що змінюють дані.