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
Lifecycle events
Processing of every RPC request goes through Symfony EventDispatcher events. By subscribing to them, you can add auditing, metrics and tracing, change method arguments or replace an error — without changing the procedures.
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.
Event order
For a regular request with an id:
rpc.request
└─ (cache: if #[RPC\Cache] has a response — nothing else is dispatched)
rpc.pre_execute ← validation, parameter conversion, #[RPC\Lock], method call
├─ success: rpc.pre_response → rpc.post_execute
└─ error: rpc.error → rpc.pre_response
── response sent to the client ──
rpc.post_response ← CacheRelation, notifications, $rpc.callback
For notifications and $rpc.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 page.
Event list
Names are constants of Ufo\JsonRpcBundle\EventDrivenModel\Events\RpcEvent, classes are in the same namespace.
| Event | Class | When |
|---|---|---|
rpc.request |
RpcRequestEvent |
Request received, before the cache check |
rpc.pre_execute |
RpcPreExecuteEvent |
Before the method call; the method itself also runs here |
rpc.post_execute |
RpcPostExecuteEvent |
Method executed successfully |
rpc.pre_response |
RpcPreResponseEvent |
Response built |
rpc.post_response |
RpcPostResponseEvent |
Response sent to the client |
rpc.error |
RpcErrorEvent |
Error during execution |
rpc.async_output |
RpcAsyncOutputEvent |
A batch child process returned a result |
rpc.async_request |
RpcAsyncRequestEvent |
Declared, but the bundle does not dispatch it |
Subscribing
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Ufo\JsonRpcBundle\EventDrivenModel\Events\RpcEvent;
use Ufo\JsonRpcBundle\EventDrivenModel\Events\RpcPostResponseEvent;
#[AsEventListener(RpcEvent::POST_RESPONSE)]
final class AuditListener
{
public function __invoke(RpcPostResponseEvent $event): void
{
// $event->rpcRequest->getMethod(), $event->response ...
}
}
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.
General rules
- Event properties are public and not readonly, but assigning a new value does not always take effect — see the event's page.
stopPropagation()stops not only your neighbors but also built-in listeners with a lower priority. Onrpc.pre_execute,rpc.pre_responseandrpc.errorthis can leave the request without a response — do not stop these events unless you build the response yourself.- 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). - A cached response (
#[RPC\Cache]) is returned without execution events: nothing is dispatched afterrpc.request.
Context inside a listener
| What you need | Where to get it |
|---|---|
Method, parameters, id |
$event->rpcRequest->getMethod(), getParams() (without $rpc), getId() |
Special $rpc parameters |
$event->rpcRequest->getRpcParams() — getTimeout(), hasCallback(), getRayId(), ignoreCache |
| Notification or not | $event->rpcRequest instanceof Ufo\RpcObject\RpcNotificationRequest |
| API version | $event->rpcRequest->apiVersion |
| API group | service Ufo\JsonRpcBundle\Server\ApiGroupContext: getGroup() |
| HTTP request | service Ufo\JsonRpcBundle\Server\RequestPrepare\RequestCarrier: getCarrier(), if it is IRpcHttpCarrier — getHttpRequest() |
| Token | service IRpcSecurity: getTokenHolder()->getToken() |
| Earlier events of this request | service RpcEventFactory: getEvent(Class::class), getEvents(Class::class) |
Custom events
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.
Migrating from older versions
- Since 8.0, event classes live in
Ufo\JsonRpcBundle\EventDrivenModel\Events\*(previously —Ufo\RpcObject\Events\*). - The
rpc.responseevent was removed in 7.6 — userpc.pre_responseorrpc.post_response. - 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.