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

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. 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.
  • 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 after rpc.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.response event was removed in 7.6 — use rpc.pre_response or rpc.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.