Command Signals
friendsofhyperf/command-signals adds coroutine-based POSIX signal handling to Hyperf commands.
Installation
composer require friendsofhyperf/command-signalsThe package supports Hyperf 3.2 and is discovered automatically. Its ConfigProvider does not publish any configuration. The command example below assumes that the application already has hyperf/command; this package does not declare it as a dependency.
The implementation uses Hyperf Engine signals and calls posix_getpid() and posix_kill(). Run it in a coroutine-capable Swoole or Swow environment that supports POSIX signals. The PHP POSIX extension must provide those two functions. Composer suggests ext-swoole >= 4.6.0 or ext-swow >= 0.1.0, but does not declare the POSIX extension as a dependency.
Usage
Add InteractsWithSignals to a command and call trap() with one signal number or an array of signal numbers. The callback receives the signal number.
namespace App\Command;
use FriendsOfHyperf\CommandSignals\Traits\InteractsWithSignals;
use Hyperf\Command\Annotation\Command;
use Hyperf\Command\Command as HyperfCommand;
use Psr\Container\ContainerInterface;
#[Command]
class FooCommand extends HyperfCommand
{
use InteractsWithSignals;
public function __construct(protected ContainerInterface $container)
{
parent::__construct('foo');
}
public function configure()
{
parent::configure();
$this->setDescription('Hyperf Demo Command');
}
public function handle()
{
$this->trap([SIGINT, SIGTERM], function (int $signo): void {
$this->warn(sprintf('Received signal %d, exiting...', $signo));
});
sleep(10);
$this->info('Bye!');
}
}trap() creates a SignalRegistry through the container on its first call and automatically clears its callbacks when the current coroutine ends. Multiple callbacks may be registered for the same signal; they run concurrently when that signal is received.
Each registered signal is handled once. After its callbacks finish, the registry stops waiting and sends the same signal to the current process again, so the operating system will normally apply the signal's default action.
Use untrap() to clear callbacks for one signal, several signals, or all signals. It does not cancel a waiter that has already started; if that signal later arrives, the registry still sends it to the process again after running no callbacks.
$this->untrap(SIGINT);
$this->untrap([SIGINT, SIGTERM]);
$this->untrap();API
The trait exposes these protected methods to the command:
trap(array|int $signo, callable $callback): voiduntrap(null|array|int $signo = null): void
SignalRegistry is also public. Its constructor is __construct(int $timeout = 1, int $concurrentLimit = 0), where timeout is the timeout in seconds for each wait attempt and concurrentLimit limits concurrently executing callbacks. A limit of 0 means unlimited concurrency. It provides:
register(int|array $signo, callable $signalHandler): voidunregister(null|int|array $signo = null): void
unregister() clears the callbacks for the selected signals; passing null clears all callbacks.
Execution
- Press
Ctrl + Cto sendSIGINT.
$ hyperf foo
^CReceived signal 2, exiting...- Send
SIGTERM, for example withkillall php.
$ hyperf foo
Received signal 15, exiting...
[1] 51936 terminated php bin/hyperf.php foo