Lifecycle events

← Lifecycle events

Ashterix

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

en/docs/api/json-rpc-bundle/functionality/events/README.md new file +87 āˆ’0

1 + # Lifecycle events
2 +
3 + Processing of every RPC request goes through [Symfony EventDispatcher](https://symfony.com/doc/current/event_dispatcher.html) events. By subscribing to them, you can add auditing, metrics and tracing, change method arguments or replace an error — without changing the procedures.
4 +
5 + :::warning
6 + Listeners interfere with the server's work. Before changing data or stopping event propagation, read the page of the corresponding event: each one describes what is safe to change and what is not.
7 + :::
8 +
9 + ## Event order
10 +
11 + For a regular request with an `id`:
12 +
13 + ```
14 + rpc.request
15 + └─ (cache: if #[RPC\Cache] has a response — nothing else is dispatched)
16 + rpc.pre_execute ← validation, parameter conversion, #[RPC\Lock], method call
17 + ā”œā”€ success: rpc.pre_response → rpc.post_execute
18 + └─ error: rpc.error → rpc.pre_response
19 + ── response sent to the client ──
20 + rpc.post_response ← CacheRelation, notifications, $rpc.callback
21 + ```
22 +
23 + For [notifications](/docs/api/json-rpc-bundle/functionality/notifications) and [`$rpc.callback`](/docs/api/json-rpc-bundle/functionality/callback), only `rpc.request` and `rpc.pre_response` with a placeholder response are dispatched on receipt, and the method itself runs in `rpc.post_response` — with its own `rpc.pre_execute`, `rpc.pre_response`, `rpc.post_execute`. Full description — on the [Lifecycle](/docs/api/json-rpc-bundle/developers/lifecycle) page.
24 +
25 + ## Event list
26 +
27 + Names are constants of `Ufo\JsonRpcBundle\EventDrivenModel\Events\RpcEvent`, classes are in the same namespace.
28 +
29 + | Event | Class | When |
30 + | --- | --- | --- |
31 + | [`rpc.request`](/docs/api/json-rpc-bundle/functionality/events/request) | `RpcRequestEvent` | Request received, before the cache check |
32 + | [`rpc.pre_execute`](/docs/api/json-rpc-bundle/functionality/events/pre_execute) | `RpcPreExecuteEvent` | Before the method call; the method itself also runs here |
33 + | [`rpc.post_execute`](/docs/api/json-rpc-bundle/functionality/events/post_execute) | `RpcPostExecuteEvent` | Method executed successfully |
34 + | [`rpc.pre_response`](/docs/api/json-rpc-bundle/functionality/events/pre_response) | `RpcPreResponseEvent` | Response built |
35 + | [`rpc.post_response`](/docs/api/json-rpc-bundle/functionality/events/post_response) | `RpcPostResponseEvent` | Response sent to the client |
36 + | [`rpc.error`](/docs/api/json-rpc-bundle/functionality/events/error) | `RpcErrorEvent` | Error during execution |
37 + | [`rpc.async_output`](/docs/api/json-rpc-bundle/functionality/events/async_output) | `RpcAsyncOutputEvent` | A batch child process returned a result |
38 + | [`rpc.async_request`](/docs/api/json-rpc-bundle/functionality/events/async_request) | `RpcAsyncRequestEvent` | Declared, but the bundle does not dispatch it |
39 +
40 + ## Subscribing
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 + The priority is set in the attribute: `#[AsEventListener(RpcEvent::PRE_EXECUTE, priority: 500)]`. Higher priority runs earlier. The priorities of built-in listeners are listed on each event's page: they determine what your listener sees and whether it is called at all.
58 +
59 + ## General rules
60 +
61 + * **Event properties are public and not readonly**, but assigning a new value does not always take effect — see the event's page.
62 + * **`stopPropagation()`** stops not only your neighbors but also built-in listeners with a lower priority. On `rpc.pre_execute`, `rpc.pre_response` and `rpc.error` this can leave the request without a response — do not stop these events unless you build the response yourself.
63 + * **An exception from a listener** is turned into an error response. In a single HTTP request it does not go through `rpc.error` (see [Open issues](/docs/api/json-rpc-bundle/developers/known_issues/open)).
64 + * **A cached response** (`#[RPC\Cache]`) is returned without execution events: nothing is dispatched after `rpc.request`.
65 +
66 + ## Context inside a listener
67 +
68 + | What you need | Where to get it |
69 + | --- | --- |
70 + | Method, parameters, `id` | `$event->rpcRequest->getMethod()`, `getParams()` (without `$rpc`), `getId()` |
71 + | Special `$rpc` parameters | `$event->rpcRequest->getRpcParams()` — `getTimeout()`, `hasCallback()`, `getRayId()`, `ignoreCache` |
72 + | Notification or not | `$event->rpcRequest instanceof Ufo\RpcObject\RpcNotificationRequest` |
73 + | API version | `$event->rpcRequest->apiVersion` |
74 + | API group | service `Ufo\JsonRpcBundle\Server\ApiGroupContext`: `getGroup()` |
75 + | HTTP request | service `Ufo\JsonRpcBundle\Server\RequestPrepare\RequestCarrier`: `getCarrier()`, if it is `IRpcHttpCarrier` — `getHttpRequest()` |
76 + | Token | service `IRpcSecurity`: `getTokenHolder()->getToken()` |
77 + | Earlier events of this request | service `RpcEventFactory`: `getEvent(Class::class)`, `getEvents(Class::class)` |
78 +
79 + ## Custom events
80 +
81 + `RpcEventFactory::fire()` accepts any name. An unknown name is dispatched as `rpc.custom_event` (class `RpcEvent` with an `$eventData` array); the bundle itself neither dispatches nor listens to such events.
82 +
83 + ## Migrating from older versions
84 +
85 + * Since 8.0, event classes live in `Ufo\JsonRpcBundle\EventDrivenModel\Events\*` (previously — `Ufo\RpcObject\Events\*`).
86 + * The `rpc.response` event was removed in 7.6 — use `rpc.pre_response` or `rpc.post_response`.
87 + * Since 10.3, the bundle does not stop propagation of `kernel.terminate` / `console.terminate`, so the application's terminate listeners also run after RPC requests.