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 | ||
| 20 | final 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 | } |