From 93e83b9688956fafbf687ae7ae5b2893bee90e5a Mon Sep 17 00:00:00 2001 From: Hafiz Muhammad Moaz Date: Fri, 28 Aug 2026 03:21:17 +0500 Subject: [PATCH 1/3] feat: trap SIGINT and other signals via pcntl (fixes #12) --- src/CLI.php | 46 ++++++++++++++++++++++++++++++++++++++++++++++ tests/CLITest.php | 10 ++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/CLI.php b/src/CLI.php index 16ffe4a..4a27e11 100644 --- a/src/CLI.php +++ b/src/CLI.php @@ -361,6 +361,52 @@ public static function spinner(int $frame = 0, bool $finalize = false) : void static::liveLine($frames[$frame % \count($frames)]); } + /** + * Register a handler for the given POSIX signal when pcntl is available. + * + * @param int $signal The signal number + * @param callable $handler The signal handler + * + * @return bool True if the handler was registered, false if pcntl is unavailable + */ + public static function onSignal(int $signal, callable $handler) : bool + { + if (!\function_exists('pcntl_signal')) { + return false; + } + return pcntl_signal($signal, $handler, true); + } + + /** + * Register a cleanup handler for the interrupt signal (Ctrl+C) when available. + * + * @param callable $handler The signal handler + * + * @return bool True if the handler was registered + */ + public static function onSigint(callable $handler) : bool + { + if (!\defined('SIGINT')) { + return false; + } + return static::onSignal(\SIGINT, $handler); + } + + /** + * Restore the default handler for a POSIX signal when pcntl is available. + * + * @param int $signal The signal number + * + * @return bool True if the handler was restored + */ + public static function restoreSignal(int $signal) : bool + { + if (!\function_exists('pcntl_signal')) { + return false; + } + return pcntl_signal($signal, \SIG_DFL); + } + /** * Performs audible beep alarms. * diff --git a/tests/CLITest.php b/tests/CLITest.php index 97678d7..61ee51b 100644 --- a/tests/CLITest.php +++ b/tests/CLITest.php @@ -266,4 +266,14 @@ public function testSpinner() : void self::assertStringContainsString('|', Stdout::getContents()); CLI::setAnsi(true); } + + public function testSignals() : void + { + $term = \defined('SIGTERM') ? \SIGTERM : 15; + self::assertIsBool(CLI::onSignal($term, static function () : void { + })); + self::assertIsBool(CLI::onSigint(static function () : void { + })); + self::assertIsBool(CLI::restoreSignal($term)); + } } From 52195cf1b513cdf0e6ca7543eb5b3fceaa33553a Mon Sep 17 00:00:00 2001 From: Hafiz Muhammad Moaz Date: Fri, 28 Aug 2026 03:31:07 +0500 Subject: [PATCH 2/3] style: use fully qualified global pcntl functions --- src/CLI.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CLI.php b/src/CLI.php index 4a27e11..ec4684d 100644 --- a/src/CLI.php +++ b/src/CLI.php @@ -65,7 +65,7 @@ public static function supportsAnsi() : bool || \getenv('WT_SESSION') !== false || \getenv('TERM_PROGRAM') === 'vscode' || (\function_exists('sapi_windows_vt100_support') - && sapi_windows_vt100_support(\STDOUT)); + && \sapi_windows_vt100_support(\STDOUT)); } return true; @@ -389,7 +389,7 @@ public static function onSigint(callable $handler) : bool if (!\defined('SIGINT')) { return false; } - return static::onSignal(\SIGINT, $handler); + return static::onSignal(SIGINT, $handler); } /** @@ -404,7 +404,7 @@ public static function restoreSignal(int $signal) : bool if (!\function_exists('pcntl_signal')) { return false; } - return pcntl_signal($signal, \SIG_DFL); + return pcntl_signal($signal, SIG_DFL); } /** From 379069a693b9fa9d218a022bb5e928b71bec18ed Mon Sep 17 00:00:00 2001 From: Hafiz Muhammad Moaz Date: Fri, 28 Aug 2026 03:35:06 +0500 Subject: [PATCH 3/3] style: match Linux coding standard on platform specific functions --- src/CLI.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CLI.php b/src/CLI.php index ec4684d..a4618ab 100644 --- a/src/CLI.php +++ b/src/CLI.php @@ -65,7 +65,7 @@ public static function supportsAnsi() : bool || \getenv('WT_SESSION') !== false || \getenv('TERM_PROGRAM') === 'vscode' || (\function_exists('sapi_windows_vt100_support') - && \sapi_windows_vt100_support(\STDOUT)); + && sapi_windows_vt100_support(\STDOUT)); } return true; @@ -374,7 +374,7 @@ public static function onSignal(int $signal, callable $handler) : bool if (!\function_exists('pcntl_signal')) { return false; } - return pcntl_signal($signal, $handler, true); + return \pcntl_signal($signal, $handler, true); } /** @@ -389,7 +389,7 @@ public static function onSigint(callable $handler) : bool if (!\defined('SIGINT')) { return false; } - return static::onSignal(SIGINT, $handler); + return static::onSignal(\SIGINT, $handler); } /** @@ -404,7 +404,7 @@ public static function restoreSignal(int $signal) : bool if (!\function_exists('pcntl_signal')) { return false; } - return pcntl_signal($signal, SIG_DFL); + return \pcntl_signal($signal, \SIG_DFL); } /**