Skip to content

Handling process signals

Any process can receive signals either through user interaction (in a terminal), from another process or via the kill command. PHP processes can handle them and perform actions to safely free resources or prevent the process from being terminated.

Examples below only use one listener per signal but you can add as many as you wish (which is complicated when dealing manually with PHP builtin functions).

Free resources before stopping

This is a reuse of the socket example.

use Innmind\Url\Url;
use Innmind\IO\Readable\Frame;
use Innmind\Socket\Internet\Transport;
use Innmind\TimeContinuum\Earth\ElapsedPeriod;
use Innmind\Signals\Signal;
use Innmind\Immutable\{
    Sequence,
    Str,
};

$client = $os
    ->remote()
    ->socket(Transport::tcp(), Url::of('tcp://127.0.0.1:8080')->authority())
    ->match(
        static fn($client) => $client,
        static fn() => throw new \RuntimeException('Unable to connect to the server'),
    );
$watch = $os->sockets()->watch(new ElapsedPeriod(1000))->forRead($client);
$signaled = true;
$os
    ->process()
    ->signals()
    ->listen(Signal::terminate, function() use (&$signaled) {
        $signaled = false;
    });

$receivedData = $client
    ->timeoutAfter(ElapsedPeriod::of(1_000))
    // it sends this every second to keep the connection alive
    ->heartbeatWith(static fn() => Sequence::of(Str::of('foo')))
    ->abortWhen(function() use (&$signaled) {
        return $signaled;
    })
    ->frames(Frame\Chunk::of(1))
    ->one()
    ->match(
        static fn() => true,
        static fn() => false,
    );

if ($receivedData) {
    echo 'Server has responded'.
}

$client->unwrap()->close();

When the process receive the SIGTERM signal it will be paused then the anonymous function will be called and the process will then be resumed.

Signal handling is already performed when using innmind/ipc or innmind/amqp so you don't have to think about it.

Prevent process from being stopped

$prevent = function() {
    echo 'Process cannot be interrupted in the middle of a backup';
};

$os->process()->signals()->listen(Signal::terminate, $prevent);
$os->process()->signals()->listen(Signal::interrupt, $prevent);

// perform the backup here that can't be stopped to prevent data corruption

$os->process()->signals()->remove($prevent);

This example will prevent the process from being terminated by a SIGTERM or SIGINT while in the middle of a backup, but if the signals comes before or after the backup then the process will be terminated as expected.