Skip to content

[Console] ConsoleEvents::SIGNAL subscriber not called #45332

Closed
@GwendolenLynch

Description

@GwendolenLynch

Symfony version(s) affected

5.2+

Description

The (awesome) console signal handling introduced in #33729 and #37827 seems to have some issues (or maybe it's just me 😺 ) with subscriber events:

  • Subscriber ConsoleEvents::SIGNAL events do not get called, but events such as ConsoleEvents::TERMINATE do
  • Listener events (command implementing SignalableCommandInterface) work, with one caveat;
    • if a subscriber exists, it will be called and the command's discrete handler ignored

How to reproduce

  1. Create a Symfony project symfony new --webapp symfony-pcntl-test

  2. Build the following classes

// src/Command/AbstractSignalCommand.php

abstract class AbstractSignalCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $io->title("Starting {$this->getName()} command execution");

        for ($i = 0; $i <10; ++$i) {
            $io->writeln('Tick ' . $i);
            sleep(1);
        }

        $io->success('Normal exit from command.');

        return Command::SUCCESS;
    }
}
// src/Command/SignalListenerCommand.php

#[AsCommand(
    name: 'app:signal:listener',
)]
class SignalListenerCommand extends AbstractSignalCommand implements SignalableCommandInterface
{
    public function getSubscribedSignals(): array
    {
        return [SIGINT];
    }

    public function handleSignal(int $signal): void
    {
        if (SIGINT === $signal) {
            dump('Caught a listener SIGINT');
            exit;
        }
    }
}
// src/Command/SignalSubscriberCommand.php

#[AsCommand(
    name: 'app:signal:subscriber',
)]
class SignalSubscriberCommand extends AbstractSignalCommand
{
}
// src/EventSubscriber/CommandSignalSubscriber.php

class CommandSignalSubscriber implements EventSubscriberInterface
{
    public function onConsoleSignal(ConsoleSignalEvent $event): void
    {
        if ($event->getHandlingSignal() === SIGINT) {
            dump('Caught a !!!subscriber!!! SIGINT');
            exit;
        }
    }

    public function onConsoleTerminate(ConsoleTerminateEvent $event)
    {
        dump('Handling normal termination event');
    }

    public static function getSubscribedEvents(): array
    {
        return [
            ConsoleEvents::SIGNAL => 'onConsoleSignal',
            ConsoleEvents::TERMINATE => 'onConsoleTerminate',
        ];
    }
}
  1. Check event dispatcher
$ bin/console debug:event-dispatcher console.signal
# ...
 ------- ---------------------------------------------------------------- ---------- 
  Order   Callable                                                         Priority  
 ------- ---------------------------------------------------------------- ---------- 
  #1      App\EventSubscriber\CommandSignalSubscriber::onConsoleSignal()   0         
 ------- ---------------------------------------------------------------- -----------
  1. Run the listener enabled command and emit a CTRL+c
$ bin/console app:signal:listener

Starting app:signal:listener command execution
==============================================

Tick 0
Tick 1
^C"Caught a !!!subscriber!!! SIGINT"

NOTE: The handling of the signal is done in the subscriber here, but removing ConsoleEvents::SIGNAL from CommandSignalSubscriber::getSubscribedEvents will cause the handling to be done by the listener.

  1. Run the subscriber enabled command and emit a CTRL+c
$ bin/console app:signal:subscriber

Starting app:signal:subscriber command execution
================================================

Tick 0
Tick 1
^C

NOTE: Nothing handles the signal.

Possible Solution

No response

Additional Context

  • Tested on Symfony v6.0.4

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