Skip to content

[EntityListener] with multiple listeners per class only the first priority is used #1884

Open
@acran

Description

@acran

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"

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions