设置一些上下文我正在研究Symfony 4.4 API,它使用名为EkinoNewRelicBundle
的供应商将数据通信到New Relic API。该供应商使用名为RequestListener
的订阅者订阅symmonfy的kernel.request
事件,以定义发送到New Relic API的数据。
我有一个问题在特定情况下,当存在导致498的身份验证问题时,来自SecurityBundle
的另一个订阅者抛出异常,停止请求处理。不幸的是,EkinoNewRelicBundle
订阅者的一个名为setTransactionName
的方法的优先级低于SecurityBundle订阅者,导致Ekino在进程停止时无法正确设置数据。
通过手工编辑供应商,我发现setTransactionName
上的10
优先级足以在SecurityBundle
之前执行。
我正在寻找一种在运行时编辑RequestListener
优先级的优先级的方法。到目前为止,我已经尝试了:
- 覆盖但由于优先级是在类的公共静态方法中定义的,并且似乎是直接在EventDispatcher中加载的,因此没有配置可以重写;
- 使用编译器传递来操作定义,但与上面一样,没有要编辑的定义,因为它似乎直接加载在EventDispatcher中;
- 允许Ekino订阅者编辑优先级,禁用Ekino配置中的侦听器,但这会导致我新定义的订阅者的配置问题,因为它无法自动连接。
是否有一种简单的方法来更改供应商订阅者中订阅事件的优先级?
维护人员确实在七年前讨论过这个问题,当时配置仍然可以通过编译器传递到EkinoNewRelicBundle问题。
根据如何覆盖包的任何部分Symfony文档
如果你想修改由bundle创建的服务,你可以使用装饰服务。
由于EkinoNewRelicBundleListenerRequestListener
是一个在配置中注册并由Symfony自动配置的服务,您可以创建一个装饰器并添加一个自定义getSubscribedEvents()
方法来覆盖优先级。
创建装饰器
// /src/Decorator/EkinoRequestListenerDecorator.php
namespace AppDecorator;
use EkinoNewRelicBundleListenerKernelRequestEvent;
use EkinoNewRelicBundleListenerRequestListener;
use SymfonyComponentEventDispatcherEventSubscriberInterface;
use SymfonyComponentHttpKernelKernelEvents;
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);
}
}
配置装饰器
decorates选项告诉容器
AppDecoratorEkinoRequestListenerDecorator
此配置将
EkinoNewRelicBundleListenerRequestListener
替换为新的CC_15,但保留a引用旧的为AppDecoratorEkinoRequestListenerDecorator.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.
# ...
AppDecoratorEkinoRequestListenerDecorator:
decorates: EkinoNewRelicBundleListenerRequestListener
arguments: ['@AppDecoratorEkinoRequestListenerDecorator.inner']
调试事件调度程序
php bin/console debug:event-dispatcher kernel.request
结果kernel.request
事件调度程序
Registered Listeners for "kernel.request" Event
===============================================
------- ------------------------------------------------------------------------------------------------------- ----------
Order Callable Priority
------- ------------------------------------------------------------------------------------------------------- ----------
#3 EkinoNewRelicBundleListenerRequestListener::setApplicationName() 255
#8 EkinoNewRelicBundleListenerRequestListener::setIgnoreTransaction() 31
#21 EkinoNewRelicBundleListenerRequestListener::setTransactionName() -10
------- ------------------------------------------------------------------------------------------------------- ----------
后Registered Listeners for "kernel.request" Event
===============================================
------- ------------------------------------------------------------------------------------------------------- ----------
Order Callable Priority
------- ------------------------------------------------------------------------------------------------------- ----------
#3 AppDecoratorEkinoRequestListenerDecorator::setApplicationName() 255
#8 AppDecoratorEkinoRequestListenerDecorator::setIgnoreTransaction() 31
#13 AppDecoratorEkinoRequestListenerDecorator::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 AppDecoratorEkinoRequestListenerDecorator(new EkinoNewRelicBundleListenerRequestListener(($this->privates['Ekino\NewRelicBundle\NewRelic\Config'] ?? $this->getConfigService()), ($this->privates['Ekino\NewRelicBundle\NewRelic\BlackholeInteractor'] ?? ($this->privates['Ekino\NewRelicBundle\NewRelic\BlackholeInteractor'] = new EkinoNewRelicBundleNewRelicBlackholeInteractor())), [], [], new EkinoNewRelicBundleTransactionNamingStrategyRouteNamingStrategy()));
}