更改来自供应商捆绑包的订户的优先级

Changing priority of a subscriber coming from a vendor's bundle

为了设置 某些上下文 ,我正在开发 Symfony 4.4 API,它使用名为 EkinoNewRelicBundle 的供应商将数据传送到新的遗物API。 该供应商使用名为 RequestListener 的订阅者订阅 Symonfy 的 kernel.request 事件,以定义要发送到 New Relic API.

的数据

在特定情况下,我遇到了一个问题,当存在导致 498 的身份验证问题时,来自 SecurityBundle 的另一个订阅者抛出异常,停止请求处理。 不幸的是,EkinoNewRelicBundle 订阅者的一个名为 setTransactionName 的方法的优先级低于 SecurityBundle 订阅者,导致进程停止时 Ekino 无法正确设置数据。

通过手动编辑供应商,我发现 setTransactionName 上的优先级 10 足以在 SecurityBundle.

之前执行

我正在寻找一种在运行时编辑 RequestListener 优先级的方法。 到目前为止,我已经尝试 :

没有一种简单的方法可以更改供应商订阅者中订阅事件的优先级吗?

七年前,维护者确实谈到过这个问题,当时配置仍然可以通过编译器通道访问 in this EkinoNewRelicBundle issue

根据 How to Override any Part of a Bundle Symfony 文档。

If you want to modify the services created by a bundle, you can use service decoration.

由于 Ekino\NewRelicBundle\Listener\RequestListener 是在 configs 中注册并由 Symfony 自动配置的服务,您可以创建装饰器并添加自定义 getSubscribedEvents() 方法来覆盖优先级。

创建装饰器

// /src/Decorator/EkinoRequestListenerDecorator.php

namespace App\Decorator;

use Ekino\NewRelicBundle\Listener\KernelRequestEvent;
use Ekino\NewRelicBundle\Listener\RequestListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;

class EkinoRequestListenerDecorator implements EventSubscriberInterface
{

    /** 
     * @var RequestListener
     */
    private $decorated;

    public function __construct(RequestListener $decorated)
    {
        $this->decorated = $decorated;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => [
                ['setApplicationName', 255],
                ['setIgnoreTransaction', 31],
                ['setTransactionName', 10],
            ],
        ];
    }

    public function setApplicationName(KernelRequestEvent $event): void
    {
        $this->decorated->setApplicationName($event);
    }

    public function setIgnoreTransaction(KernelRequestEvent $event): void
    {
        $this->decorated->setIgnoreTransaction($event);
    }

    public function setTransactionName(KernelRequestEvent $event): void
    {
        $this->decorated->setTransactionName($event);
    }
}

配置装饰器

The decorates option tells the container that the App\Decorator\EkinoRequestListenerDecorator service replaces the Ekino\NewRelicBundle\Listener\RequestListener service.

This configuration replaces Ekino\NewRelicBundle\Listener\RequestListener with a new one, but keeps a reference of the old one as App\Decorator\EkinoRequestListenerDecorator.inner

# /config/services.yaml

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

    # ...

    App\Decorator\EkinoRequestListenerDecorator:
        decorates: Ekino\NewRelicBundle\Listener\RequestListener
        arguments: ['@App\Decorator\EkinoRequestListenerDecorator.inner']

调试事件调度程序

php bin/console debug:event-dispatcher kernel.request

生成 kernel.request 事件调度程序

之前

Registered Listeners for "kernel.request" Event
===============================================

 ------- ------------------------------------------------------------------------------------------------------- ---------- 
  Order   Callable                                                                                                Priority  
 ------- ------------------------------------------------------------------------------------------------------- ---------- 
  #3      Ekino\NewRelicBundle\Listener\RequestListener::setApplicationName()                                     255       
  #8      Ekino\NewRelicBundle\Listener\RequestListener::setIgnoreTransaction()                                   31            
  #21     Ekino\NewRelicBundle\Listener\RequestListener::setTransactionName()                                     -10       
 ------- ------------------------------------------------------------------------------------------------------- ---------- 

之后

Registered Listeners for "kernel.request" Event
===============================================

 ------- ------------------------------------------------------------------------------------------------------- ---------- 
  Order   Callable                                                                                                Priority  
 ------- ------------------------------------------------------------------------------------------------------- ----------     
  #3      App\Decorator\EkinoRequestListenerDecorator::setApplicationName()                                       255       
  #8      App\Decorator\EkinoRequestListenerDecorator::setIgnoreTransaction()                                     31
  #13     App\Decorator\EkinoRequestListenerDecorator::setTransactionName()                                       10        
 ------- ------------------------------------------------------------------------------------------------------- ---------- 

生成的容器事件侦听器

        $instance->addListener('kernel.request', [0 => function () {
            return ($this->privates['App\Decorator\EkinoRequestListenerDecorator'] ?? $this->getEkinoRequestListenerDecoratorService());
        }, 1 => 'setApplicationName'], 255);
        $instance->addListener('kernel.request', [0 => function () {
            return ($this->privates['App\Decorator\EkinoRequestListenerDecorator'] ?? $this->getEkinoRequestListenerDecoratorService());
        }, 1 => 'setIgnoreTransaction'], 31);
        $instance->addListener('kernel.request', [0 => function () {
            return ($this->privates['App\Decorator\EkinoRequestListenerDecorator'] ?? $this->getEkinoRequestListenerDecoratorService());
        }, 1 => 'setTransactionName'], 10);


//...

    protected function getEkinoRequestListenerDecoratorService()
    {
        return $this->privates['App\Decorator\EkinoRequestListenerDecorator'] = new \App\Decorator\EkinoRequestListenerDecorator(new \Ekino\NewRelicBundle\Listener\RequestListener(($this->privates['Ekino\NewRelicBundle\NewRelic\Config'] ?? $this->getConfigService()), ($this->privates['Ekino\NewRelicBundle\NewRelic\BlackholeInteractor'] ?? ($this->privates['Ekino\NewRelicBundle\NewRelic\BlackholeInteractor'] = new \Ekino\NewRelicBundle\NewRelic\BlackholeInteractor())), [], [], new \Ekino\NewRelicBundle\TransactionNamingStrategy\RouteNamingStrategy()));
    }