CLI: Catching Ctrl+C, kill commands and fatal errors

Permanent Link: CLI: Catching Ctrl+C, kill commands and fatal errors 31. Mai 2009 Comment No Comment

Sometimes, when you write a Command Line (CLI) script, you want to catch Ctrl+C. Simple Example: Your script does some stuff on the database and you don't want to leave it in the state it had in the moment the script was killed, Ctrl+C'd or quit by a fatal error.

First off, we have to tell PHP to use ticks, in order for the catches to work (for more information on ticks check the pcntl_signal() documentation):

declare(ticks = 1);

Then, we add shutdown functions to the classes that have to rollback the state:

/**
* Cleanup if process has been killed unexpectedly
*
*/
private function shutdown()
{
$this->getTargetDb()->rollBack();
print('Script quit unexpectedly. Doing rollback' . PHP_EOL);
exit();
}

/**
* Method that is executed, when a Fatal Error occurs
*
*/
public function fatalErrorShutdown()
{
$lastError = error_get_last();
if (!is_null($lastError) && $lastError['type'] === E_ERROR) {
$this->shutdown();
}
}

/**
* Method, that is executed, if script has been killed by
* SIGINT: Ctrl+C
* SIGTERM: kill
*
* @param int $signal
*
*/
public function sigintShutdown($signal)
{
if ($signal === SIGINT || $signal === SIGTERM) {
$this->shutdown();
}
}

You shouldn't forget to include an exit() at some point in the shutdown methods or your script won't quit at all. In the example the shutdown() method (which is called by both shutdown methods, that will be registered afterwards) quits the script as soon as it's done.

As a last step, you only have to register your shutdown methods and you're done:

$sync = new Process_Sync();
// Catch Fatal Error (Rollback)
register_shutdown_function(array($sync, 'fatalErrorShutdown'));
// Catch Ctrl+C, kill and SIGTERM (Rollback)
pcntl_signal(SIGTERM, array($sync, 'sigintShutdown'));
pcntl_signal(SIGINT, array($sync, 'sigintShutdown'));