Description
Bug Report
Q | A |
---|---|
Version | 2.14.0 |
Summary
Using the AsEntityListener
attribute to listen to entity events one can set a priority for each event listener. The documentation suggests that then all listeners for an entity event are sorted by priority and strictly executed in that order.
But when having multiple event listeners in a single class with differing priorities this actually produces unexpected results. Because only the priority of the first defined listener is actually used and also applied to the other listeners.
This is due to EntityListenerPass::process()
using PriorityTaggedServiceTrait::findAndSortTaggedServices()
which effectively sorts all tagged services by the priority of the first attribute.
Current behavior
The defined entity listeners are not correctly executed in order of their individual priorities.
Expected behavior
Each defined entity listener should be executed according to its specific priority.
How to reproduce
Given these two classes with entity listeners
#[AsEntityListener(entity: User::class, event: Events::prePersist, priority: 0)]
#[AsEntityListener(entity: User::class, event: Events::postPersist, priority: 0)]
final class FirstListener {
public function prePersist(User $user, PrePersistEventArgs $event) {
dump('this is ' . __METHOD__);
}
public function postPersist(User $user, PostPersistEventArgs $event) {
dump('this is ' . __METHOD__);
}
}
#[AsEntityListener(entity: User::class, event: Events::prePersist, priority: 1)]
#[AsEntityListener(entity: User::class, event: Events::postPersist, priority: -1)]
final class SecondListener {
public function prePersist(User $user, PrePersistEventArgs $event) {
dump('this is ' . __METHOD__);
}
public function postPersist(User $user, PostPersistEventArgs $event) {
dump('this is ' . __METHOD__);
}
}
The expected output order of the dump()
statements would be
"this is App\EventListener\SecondListener::prePersist"
"this is App\EventListener\FirstListener::prePersist"
"this is App\EventListener\FirstListener::postPersist"
"this is App\EventListener\SecondListener::postPersist"
but actually is
"this is App\EventListener\SecondListener::prePersist"
"this is App\EventListener\FirstListener::prePersist"
"this is App\EventListener\SecondListener::postPersist"
"this is App\EventListener\FirstListener::postPersist"
Switching just the order of the attributes on the second class
#[AsEntityListener(entity: User::class, event: Events::postPersist, priority: -1)]
#[AsEntityListener(entity: User::class, event: Events::prePersist, priority: 1)]
final class SecondListener {
public function prePersist(User $user, PrePersistEventArgs $event) {
dump('this is ' . __METHOD__);
}
public function postPersist(User $user, PostPersistEventArgs $event) {
dump('this is ' . __METHOD__);
}
}
the output becomes
"this is App\EventListener\FirstListener::prePersist"
"this is App\EventListener\SecondListener::prePersist"
"this is App\EventListener\FirstListener::postPersist"
"this is App\EventListener\SecondListener::postPersist"