rpc.pre_execute

rpc.pre_execute

Назва події

rpc.pre_execute

Константа

RpcEvent::PRE_EXECUTE

Клас

RpcPreExecuteEvent

Властивості

RpcRequest $rpcRequest, Service $service, array $params

Найважливіша подія: на ній бандл валідує й конвертує параметри, захоплює блокування і виконує сам метод. Ваш лістенер стає частиною цього конвеєра — де саме, визначає пріоритет.

Коли генерується

Один раз на кожне виконання методу: після промаху кешу й після того, як метод знайдено. Для будь-якого джерела запиту — HTTP, консоль, batch, Messenger, відкладене виконання notifications/callback, прогрів кешу.

Не генерується для кешованої відповіді, невідомого методу і прийому notifications/callback (метод виконується пізніше, і тоді подія буде).

Вбудовані лістенери

Пріоритет Лістенер Що робить
1002 ValidateParamsListener Перетворює параметри на список аргументів методу (іменовані й позиційні), підставляє значення за замовчуванням і сервіси з #[Autowire]. Перезаписує $event->params. Відсутні обов'язкові параметри — rpc.error
1001 ValidateParamsListener::constraintValidation Перевіряє #[RPC\Assertions]; порушення — rpc.error
1000 ParamsConverterEventListener Конвертує скаляри в об'єкти й масиви в DTO
-100 LockEventListener Якщо запит уже має помилку — rpc.error; інакше захоплює #[RPC\Lock]
-1000 RpcServerListener Викликає метод з $event->params, формує відповідь (rpc.pre_response), генерує rpc.post_execute і зупиняє поширення

Що можна змінити

$event->params — це саме ті аргументи, з якими буде викликано метод. Ефект залежить від пріоритету:

Пріоритет лістенера Що ви бачите і що змінюєте
понад 1002 Сирі параметри запиту; ваші зміни буде перезаписано на 1002
між 1002 і 1000 Список аргументів зі скалярами; зміни пройдуть валідацію і конвертацію
між 1000 і -1000 Готові аргументи (об'єкти, DTO); зміни передаються в метод як є
нижче -1000 Викликається лише на шляху помилки виконання
  • Ключ кешу рахується з сирих параметрів запиту, тож зміни $params на нього не впливають.
  • Замінювати $event->rpcRequest не варто — це розсинхронізує сервер.

Зупинка поширення пропускає виклик методу. Підтримується лише сценарій помилки: згенеруйте rpc.error і зупиніть подію — так роблять вбудовані лістенери. Повернути успішний результат без виклику методу штатного способу немає.

public function __construct(private RpcEventFactory $events) {}

// ...
$this->events->fireError($event->rpcRequest, new RpcLogicException('Not allowed'));
$event->stopPropagation();

Приклади використання

Значення параметра з контексту

Підставити ідентифікатор поточного клієнта замість того, що передав клієнт (захист від підміни):

#[AsEventListener(RpcEvent::PRE_EXECUTE, priority: 1001)]
final class TenantParamListener
{
    public function __construct(private TenantResolver $tenants) {}

    public function __invoke(RpcPreExecuteEvent $event): void
    {
        if (array_key_exists('tenantId', $event->params)) {
            $event->params['tenantId'] = $this->tenants->current();
        }
    }
}

Пріоритет 1001: після формування списку аргументів (1002), але до конвертації (1000).

Перевірка доступу до методу

#[AsEventListener(RpcEvent::PRE_EXECUTE, priority: 999)]
final class AccessListener
{
    public function __construct(private RpcEventFactory $events, private Acl $acl) {}

    public function __invoke(RpcPreExecuteEvent $event): void
    {
        if (!$this->acl->canCall($event->service->name)) {
            $this->events->fireError($event->rpcRequest, new RpcLogicException('Access denied'));
            $event->stopPropagation();
        }
    }
}

Інші ідеї

  • вимірювання часу виконання (старт тут, фініш на rpc.post_execute / rpc.error);
  • відкриття транзакції Doctrine перед виконанням методу;
  • аудит змін: збереження параметрів методів, що змінюють дані.