Reputation: 84
To set some context, I'm working on a Symfony 4.4 API, which use a vendor named EkinoNewRelicBundle
to communicate data to the New Relic API.
This vendor uses a subscriber named RequestListener
to subscribe on the kernel.request
event of Symonfy to define data to send to the New Relic API.
I've got an issue in a specific case, when there is an authentication issue resulting in a 498, another subscriber from the SecurityBundle
throw an exception, stopping the request processing.
Unfortunately, a method of the EkinoNewRelicBundle
subscriber, named setTransactionName
, has a lower priority than the SecurityBundle subscriber, resulting in data not properly set by Ekino as the process is stop.
By editing by hand the vendor, I found that a priority 10
on the setTransactionName
would be enough to be executed before the SecurityBundle
.
I'm looking for a way to edit the priority of the RequestListener
priority at runtime.
So far, I've tried to :
Isn't there an easy way to change a priority of a subscribed event in a vendor's subscriber?
The maintener did talk about this seven years ago, when the configuration was still accessible with the compiler pass in this EkinoNewRelicBundle issue.
Upvotes: 1
Views: 1332
Reputation: 18426
As per the How to Override any Part of a Bundle Symfony Documentation.
If you want to modify the services created by a bundle, you can use service decoration.
As the Ekino\NewRelicBundle\Listener\RequestListener
is a service that is registered in the configs and autoconfigured by Symfony, you can create a decorator and add a custom getSubscribedEvents()
method to override the priorities.
// /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
Resulting kernel.request
Event Dispatcher
Before
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
------- ------------------------------------------------------------------------------------------------------- ----------
After
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
------- ------------------------------------------------------------------------------------------------------- ----------
Resulting Container Event Listeners
$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()));
}
Upvotes: 2