更改来自供应商捆绑包的订户的优先级
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
优先级的方法。
到目前为止,我已经尝试 :
- override the configuration,但由于优先级是在 class 的 public 静态方法中定义的,并且似乎是直接在 EventDispatcher 中加载的,因此没有要覆盖的配置;
- use a compiler pass 来操作定义,但是和上面一样,没有要编辑的定义,因为它似乎是直接在 EventDispatcher 中加载的;
- 扩展 Ekino 订阅者以编辑优先级,从 Ekino 的配置中禁用监听器,但这会导致我新定义的订阅者的配置问题,因为它无法自动连接.
没有一种简单的方法可以更改供应商订阅者中订阅事件的优先级吗?
七年前,维护者确实谈到过这个问题,当时配置仍然可以通过编译器通道访问 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()));
}
为了设置 某些上下文 ,我正在开发 Symfony 4.4 API,它使用名为 EkinoNewRelicBundle
的供应商将数据传送到新的遗物API。
该供应商使用名为 RequestListener
的订阅者订阅 Symonfy 的 kernel.request
事件,以定义要发送到 New Relic API.
在特定情况下,我遇到了一个问题,当存在导致 498 的身份验证问题时,来自 SecurityBundle
的另一个订阅者抛出异常,停止请求处理。
不幸的是,EkinoNewRelicBundle
订阅者的一个名为 setTransactionName
的方法的优先级低于 SecurityBundle 订阅者,导致进程停止时 Ekino 无法正确设置数据。
通过手动编辑供应商,我发现 setTransactionName
上的优先级 10
足以在 SecurityBundle
.
我正在寻找一种在运行时编辑 RequestListener
优先级的方法。
到目前为止,我已经尝试 :
- override the configuration,但由于优先级是在 class 的 public 静态方法中定义的,并且似乎是直接在 EventDispatcher 中加载的,因此没有要覆盖的配置;
- use a compiler pass 来操作定义,但是和上面一样,没有要编辑的定义,因为它似乎是直接在 EventDispatcher 中加载的;
- 扩展 Ekino 订阅者以编辑优先级,从 Ekino 的配置中禁用监听器,但这会导致我新定义的订阅者的配置问题,因为它无法自动连接.
没有一种简单的方法可以更改供应商订阅者中订阅事件的优先级吗?
七年前,维护者确实谈到过这个问题,当时配置仍然可以通过编译器通道访问 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 theEkino\NewRelicBundle\Listener\RequestListener
service.
This configuration replaces
Ekino\NewRelicBundle\Listener\RequestListener
with a new one, but keeps a reference of the old one asApp\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()));
}