From 105fa2c9ce5a9b5175aaf54c6d6c66ce9e18c073 Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Tue, 27 Oct 2026 23:07:55 +0100 Subject: [PATCH] fix(core): missing signal availability callback --- core/src/Providers/ArtisanServiceProvider.php | 4 ++ .../tests/Unit/Console/ConsoleSignalsTest.php | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 core/tests/Unit/Console/ConsoleSignalsTest.php diff --git a/core/src/Providers/ArtisanServiceProvider.php b/core/src/Providers/ArtisanServiceProvider.php index 57f7637751..4040ea8087 100644 --- a/core/src/Providers/ArtisanServiceProvider.php +++ b/core/src/Providers/ArtisanServiceProvider.php @@ -9,6 +9,7 @@ use Illuminate\Console\Scheduling\ScheduleFinishCommand; use Illuminate\Console\Scheduling\ScheduleTestCommand; use Illuminate\Console\Scheduling\ScheduleWorkCommand; +use Illuminate\Console\Signals; use Illuminate\Events\Dispatcher; use Illuminate\Support\ServiceProvider; use Illuminate\Database\Console\Seeds\SeedCommand; @@ -22,6 +23,7 @@ use Illuminate\Database\Console\Migrations\RefreshCommand as MigrateRefreshCommand; use Illuminate\Database\Console\Migrations\RollbackCommand as MigrateRollbackCommand; use Illuminate\Database\Console\Migrations\MigrateMakeCommand; +use Symfony\Component\Console\SignalRegistry\SignalRegistry; use EvolutionCMS\Console\ClearCompiledCommand; use EvolutionCMS\Console\VendorPublishCommand; @@ -104,6 +106,8 @@ class ArtisanServiceProvider extends ServiceProvider */ public function register() { + Signals::resolveAvailabilityUsing(static fn () => SignalRegistry::isSupported()); + $hereCommands = array_merge( $this->commands, $this->devCommands ); diff --git a/core/tests/Unit/Console/ConsoleSignalsTest.php b/core/tests/Unit/Console/ConsoleSignalsTest.php new file mode 100644 index 0000000000..4cf969938e --- /dev/null +++ b/core/tests/Unit/Console/ConsoleSignalsTest.php @@ -0,0 +1,55 @@ +resolver = new ReflectionProperty(Signals::class, 'availabilityResolver'); + $this->previousResolver = $this->resolver->getValue(); + $this->resolver->setValue(null, null); + + (new ArtisanServiceProvider(new Container()))->register(); +}); + +afterEach(function () { + $this->resolver->setValue(null, $this->previousResolver); +}); + +test('console boot registers signal availability for the current runtime', function () { + $called = false; + + Signals::whenAvailable(function () use (&$called) { + $called = true; + }); + + expect($called)->toBe(function_exists('pcntl_signal')); +}); + +test('commands can trap and release signals after console boot', function () { + $command = new Command(); + $command->setApplication(new Application()); + $resolved = false; + $signal = defined('SIGUSR1') ? SIGUSR1 : 10; + $previousHandler = function_exists('pcntl_signal_get_handler') + ? pcntl_signal_get_handler($signal) + : null; + + try { + $command->trap(function () use (&$resolved, $signal) { + $resolved = true; + + return $signal; + }, static function () {}); + + expect($resolved)->toBe(function_exists('pcntl_signal')); + } finally { + $command->untrap(); + } + + if ($previousHandler !== null) { + expect(pcntl_signal_get_handler($signal))->toBe($previousHandler); + } +});