Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions src/Terminus.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ class Terminus implements

private Application $application;

/**
* @var callable[]
*/
private $cleanup_handlers = [];

/**
* @var static
*/
private static $instance;

/**
* Object constructor
*
Expand All @@ -91,6 +101,7 @@ class Terminus implements
*/
public function __construct(Config $config, InputInterface $input, OutputInterface $output)
{
self::$instance = $this;
$this->setConfig($config);
$this->setInput($input);
$this->setOutput($output);
Expand Down Expand Up @@ -514,6 +525,10 @@ public function run(?InputInterface $input = null, ?OutputInterface $output = nu
if ($output === null) {
$output = $this->output();
}

// Set up signal handlers for graceful shutdown
$this->setupSignalHandlers();

$config = $this->getConfig();
if (!empty($cassette = $config->get('vcr_cassette')) && !empty($mode = $config->get('vcr_mode'))) {
$this->startVCR(array_merge(compact('cassette'), compact('mode')));
Expand All @@ -525,6 +540,97 @@ public function run(?InputInterface $input = null, ?OutputInterface $output = nu
return $status_code;
}

/**
* Set up signal handlers for graceful shutdown
*/
private function setupSignalHandlers(): void
{
if (!function_exists('pcntl_signal')) {
// pcntl extension not available, signal handling not possible
return;
}

// Handle SIGINT (Ctrl+C) and SIGTERM
pcntl_signal(SIGINT, [$this, 'handleSignal']);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);

// Enable signal handling
pcntl_async_signals(true);
}

/**
* Handle signals for graceful shutdown
*
* @param int $signal The signal number
*/
public function handleSignal(int $signal): void
{
$this->logger->notice('Received signal {signal}, performing cleanup...', ['signal' => $signal]);
$this->logger->debug(
'Signal handler called with {count} cleanup handlers registered',
['count' => count($this->cleanup_handlers)]
);

$this->cleanup();

// Restore default signal handler and re-raise the signal
pcntl_signal($signal, SIG_DFL);
posix_kill(posix_getpid(), $signal);
}

/**
* Get the current Terminus instance
*
* @return static|null
*/
public static function getInstance(): ?self
{
return self::$instance;
}

/**
* Register a cleanup handler that will be called during signal handling
*
* @param callable $handler The cleanup handler function
*/
public function registerCleanupHandler(callable $handler): void
{
$this->cleanup_handlers[] = $handler;
$this->logger->debug(
'Cleanup handler registered. Total handlers: {count}',
['count' => count($this->cleanup_handlers)]
);
}

/**
* Perform cleanup operations
*/
private function cleanup(): void
{
try {
$this->logger->debug('Starting cleanup with {count} handlers', ['count' => count($this->cleanup_handlers)]);

// Call all registered cleanup handlers
foreach ($this->cleanup_handlers as $index => $handler) {
try {
$this->logger->debug('Calling cleanup handler {index}', ['index' => $index]);
call_user_func($handler);
$this->logger->debug('Cleanup handler {index} completed successfully', ['index' => $index]);
} catch (\Exception $e) {
$this->logger->error(
'Error in cleanup handler {index}: {message}',
['index' => $index, 'message' => $e->getMessage()]
);
}
}

// Clear any active locks or temporary files
$this->logger->notice('Cleanup completed.');
} catch (\Exception $e) {
$this->logger->error('Error during cleanup: {message}', ['message' => $e->getMessage()]);
}
}

/**
* Starts and configures PHP-VCR
*
Expand Down
Loading