Lines 96.00% 24 / 25
Methods 80.00% 4 / 5
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 __destruct 100.00% 6 / 6 100.00% 1 / 1 3
 install 91.66% 11 / 12 0.00% 0 / 1 3.01
 received 100.00% 3 / 3 100.00% 1 / 1 2
 describe 100.00% 3 / 3 100.00% 1 / 1 4
20final class SignalStop
21{
22    private static ?int $signal = null;
23
24    private static bool $listening = false;
25
26    /** @var array<int, callable|int> */
27    private array $previous = [];
28
29    private bool $previousAsync = false;
30
31    private function __construct() {}
32
33    public function __destruct()
34    {
35        // a signal stops only the handle() call it arrived in; drivers used afterwards run normally
36        self::$signal = null;
37        foreach ($this->previous as $signal => $handler) {
38            pcntl_signal($signal, $handler);
39        }
40        if ($this->previous !== []) {
41            pcntl_async_signals($this->previousAsync);
42            self::$listening = false;
43        }
44    }
45
46    public static function install(): self
47    {
48        self::$signal = null;
49        $guard = new self();
50        if (!function_exists('pcntl_signal')) {
51            return $guard;
52        }
53
54        // Synchronous delivery: received() dispatches at the safe points. With async delivery a
55        // SIGTERM that arrives inside php-amqplib's socket wait was sometimes never handled.
56        $guard->previousAsync = pcntl_async_signals(false);
57        foreach ([SIGTERM, SIGINT] as $signal) {
58            $guard->previous[$signal] = pcntl_signal_get_handler($signal);
59            pcntl_signal($signal, static function (int $received): void {
60                self::$signal ??= $received;
61            });
62        }
63        self::$listening = true;
64
65        return $guard;
66    }
67
68    public static function received(): ?int
69    {
70        if (self::$listening) {
71            pcntl_signal_dispatch();
72        }
73
74        return self::$signal;
75    }
76
77    public static function describe(int $signal): string
78    {
79        return match ($signal) {
80            SIGTERM => 'SIGTERM',
81            SIGINT => 'SIGINT',
82            default => "signal {$signal}",
83        };
84    }
85}