Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Symfony Workflow Component - in some cases event "workflow.[workflow name].entered.[place name]" is being dispatched multiple times for a place even though marking was already in that place #57924

Closed
@m4y4-dev

Description

@m4y4-dev

Symfony version(s) affected

from 5.2 until 7.2

Description

When working with a workflow that uses two or more places at the same time the "workflow.[workflow name].entered.[place name]" events are being dispatched more than once (when the workflow actually entered a place). At the moment, this events are always dispatched (for all the places, that workflow is currently at) whenany place is entered, which in my opinion is wrong and causes problems in workflow executions because listeners for "workflow.[workflow name].entered.[place name]" events get executed more than once.

For example
The marking of workflow "reproduce_bug" just got to ['A' => 1, 'B' => 1] via a transition.
Then the events "workflow.reproduce_bug.entered.A" and "workflow.reproduce_bug.entered.B" where dispatched.
Everything is fine so far.
In the next trasition the marking gets changed from ['A' => 1, 'B' => 1] to ['A' => 1, 'D' => 1] via a transition.
Now the events "workflow.reproduce_bug.entered.D" and "workflow.reproduce_bug.entered.A" are being dispatched.
The event "workflow.reproduce_bug.entered.A" was dispatched again although the marking of the workflow was already at that place and it wasn't changed by the transition (as you can see in the example config below).

This behavior occures since version 5.2 of the Symfony Workflow Component up to current version 7.2.

How to reproduce

Ensure symfony/workflow component is installed
Could be any version >= 5.2 up to 7.2
composer require symfony/workflow:7.2

Example workflow configuration

framework:  workflows:    reproduce_bug:      type: workflow      marking_store:        type: method        property: state      supports:        - App\WorkflowState      initial_marking: initial_place      places:        - initial_place        - A        - B        - C        - D        - E      transitions:        from_initial_place_to_a_and_b:          from: initial_place          to:             - A            - B        from_a_to_c:          from: A          to: C        from_b_to_d:          from: B          to: D

The App\WorkflowState entity

<?phpnamespace App;class WorkflowState{    protected $state;    public function setState($state)    {        $this->state = $state;        return $this;    }    public function getState()    {        return $this->state;    }}

Simple symfony command to execute the workflows transition

<?phpnamespace App\Command;use Symfony\Component\Console\Command\Command;use Symfony\Component\Console\Input\InputInterface;use Symfony\Component\Console\Output\OutputInterface;use App\WorkflowState;use Symfony\Component\Workflow\Registry as WorkflowRegistry;class ReproduceWorkflowBugCommand extends Command{    /**     * @return void     */    protected function configure()    {        $this            ->setName('ReproduceWorkflowBugCommand')            ->setDescription('')        ;    }    /**     * @var Symfony\Component\Workflow\Registry     */    protected $workflowRegistry;    /**     * @param Symfony\Component\Workflow\Registry $workflowRegistry     */    public function __construct(WorkflowRegistry $workflowRegistry)    {        parent::__construct();        $this->workflowRegistry = $workflowRegistry;    }    /**     * @param InputInterface  $input     * @param OutputInterface $output     *      * @return void     */    protected function execute(InputInterface $input, OutputInterface $output): int    {        $workflowState = new WorkflowState();        $workflow = $this->workflowRegistry->get($workflowState, 'reproduce_bug');        $workflow->apply($workflowState, 'from_initial_place_to_a_and_b');        $workflow->apply($workflowState, 'from_b_to_d');        $workflow->apply($workflowState, 'from_a_to_c');        return 0;    }}

A simple event subscriber for debugging

<?phpnamespace App\EventSubscriber;use Symfony\Component\EventDispatcher\EventSubscriberInterface;class TestEventSubscriber implements EventSubscriberInterface{    public static function getSubscribedEvents(): array    {        return [            'workflow.reproduce_bug.entered.A' => 'triggerOnWorkflowReproduceBugEnteredAEvent',        ];    }        public function triggerOnWorkflowReproduceBugEnteredAEvent($event)    {        $transition = $event->getTransition();        $marking = $event->getMarking();        dump([            'name' => $transition->getName(),            'froms' => $transition->getFroms(),            'tos' => $transition->getTos(),            'marking' => $marking->getPlaces(),        ]);    }}

When executing the command
bin/console ReproduceWorkflowBugCommand

Following output is shown

array:4 [  "name" => "from_initial_place_to_a_and_b"  "froms" => array:1 [    0 => "initial_place"  ]  "tos" => array:2 [    0 => "A"    1 => "B"  ]  "marking" => array:2 [    "A" => 1    "B" => 1  ]]array:4 [  "name" => "from_b_to_d"  "froms" => array:1 [    0 => "B"  ]  "tos" => array:1 [    0 => "D"  ]  "marking" => array:2 [    "A" => 1    "D" => 1  ]]

This output shows that the "workflow.reproduce_bug.entered.A" event was dispatched twice.
Once for transition "from_initial_place_to_a_and_b" which is expected and once for transition "from_b_to_d" which wasn't expected.

Possible Solution

The issue is caused within theentered method of theSymfony\Component\Workflow\Workflow class as it dispatches the "workflow.[workflow name].entered.[place name]" events for the current marking places of the workflow.

Code, wich causes the issue
vendor/symfony/workflow/Workflow.php

private function entered(...){    ...    foreach ($marking->getPlaces() as $placeName => $nbToken) {        $this->dispatcher->dispatch($event, sprintf('workflow.%s.entered.%s', $this->name, $placeName));    }}

Fix
Instead of dispatching the events for each place of the marking, dispatch the events only for the "tos" of the transition. It was actually done this way until version 5.1 of the symfony workflow component.

if (null !== $transition) {    foreach ($transition->getTos() as $placeName) {        $this->dispatcher->dispatch($event, sprintf('workflow.%s.entered.%s', $this->name, $placeName));    }}

Additional Context

No response

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


      [8]ページ先頭

      ©2009-2025 Movatter.jp