Automatically Restart PHP Script on Exit Automatically Restart PHP Script on Exit bash bash

Automatically Restart PHP Script on Exit


A PHP Script can also restart itself with PCNTL.

Disclaimer: the point of this exercise is only to prove that PHP is perfectly capable of restarting itself and to answer the question:

Is there a way I can automatically restart a PHP script whenever it exits, regardless of whether it has been exited properly, or has terminated due to an error?

It is therefor beyond our scope to go into any detail about unix processes so I would suggest you start with the PCNTL book or refer to for more details.

In the examples we will assume it is a PHP CLI script launched in a *nix environment from a terminal with a semi-decent shell using the command:

$ php i-can-restart-myself.php 0

We pass a restart count attribute as indicator that the process was restarted.

Can I automatically restart a PHP Script?

Yes I can!

<?php    echo ++$argv[1];     // count every restart    $_ = $_SERVER['_'];  // or full path to php binary    echo "\n======== start =========\n";    // do a lot of stuff    $cnt = 0;    while( $cnt++ < 10000000 ){}    echo "\n========== end =========\n";    // restart myself    pcntl_exec($_, $argv);

Regardless of whether it has been terminated properly?

Yes I can restart if terminated!

<?php    echo ++$argv[1];        $_ = $_SERVER['_'];     register_shutdown_function(function () {        global $_, $argv; // note we need to reference globals inside a function        // restart myself        pcntl_exec($_, $argv);    });    echo "\n======== start =========\n";    // do a lot of stuff    $cnt = 0;    while( $cnt++ < 10000000 ){}    echo "\n========== end =========\n";    die; // exited properly    // we can't reach here     pcntl_exec($_, $argv);

Or terminated due to an error?

Ditto!

<?php    echo ++$argv[1];        $_ = $_SERVER['_'];     register_shutdown_function(function () {        global $_, $argv;         // restart myself        pcntl_exec($_, $argv);    });    echo "\n======== start =========\n";    // do a lot of stuff    $cnt = 0;    while( $cnt++ < 10000000 ){}    echo "\n===== what if? =========\n";    require 'OOPS! I dont exist.'; // FATAL Error:    // we can't reach here    echo "\n========== end =========\n";            die; // exited properly    // we can't reach here     pcntl_exec($_, $argv);

But you know I am going to want more than that right?

Certainly! I can restart on kill, hub even Ctrl-C!

<?php    echo ++$argv[1];         $_ = $_SERVER['_'];      $restartMyself = function () {        global $_, $argv;         pcntl_exec($_, $argv);    };    register_shutdown_function($restartMyself);    pcntl_signal(SIGTERM, $restartMyself); // kill    pcntl_signal(SIGHUP,  $restartMyself); // kill -s HUP or kill -1    pcntl_signal(SIGINT,  $restartMyself); // Ctrl-C    echo "\n======== start =========\n";    // do a lot of stuff    $cnt = 0;    while( $cnt++ < 10000000 ){}    echo "\n===== what if? =========\n";    require 'OOPS! I dont exist.'; // FATAL Error:    // we can't reach here    echo "\n========== end =========\n";            die; // exited properly    // we can't reach here     pcntl_exec($_, $argv);

How do I terminate it now?

If you flood the process by holding down Ctrl-C you might just catch it somewhere in the shutdown.

I don't want to see all these errors can I restart on those too?

No problem I can handle errors too!

<?php    echo ++$argv[1];         $_ = $_SERVER['_'];      $restartMyself = function () {        global $_, $argv;         pcntl_exec($_, $argv);    };    register_shutdown_function($restartMyself);    pcntl_signal(SIGTERM, $restartMyself);       pcntl_signal(SIGHUP,  $restartMyself);       pcntl_signal(SIGINT,  $restartMyself);       set_error_handler($restartMyself , E_ALL); // Catch all errors    echo "\n======== start =========\n";    // do a lot of stuff    $cnt = 0;    while( $cnt++ < 10000000 ){}    echo $CAREFUL_NOW; // NOTICE: will also be caught    // we would normally still go here    echo "\n===== what if? =========\n";    require 'OOPS! I dont exist.'; // FATAL Error:    // we can't reach here    echo "\n========== end =========\n";            die; // exited properly    // we can't reach here     pcntl_exec($_, $argv);

Although this appears to be working fine at first glance, because pcntl_exec runs in the same process we do not notice that things are terribly wrong. If you wanted to spawn a new process and letting the old one die instead, which is a perfectly viable alternative see next post, you will notice 2 processes are started each iteration, because we trigger a PHP NOTICE and an ERROR due to a common oversight. Which of course can easily be rectified by ensuring we die() or exit() after the call to pcntl_exec in the error handler, otherwise PHP assumes tolerance were accepted and continues.

The point I am trying to make is that even if you are able to relaunch a script that failed this is not an excuse to allow broken code. Although there may exist viable use cases for this practice, I strongly disagree that restart on failure should be employed as a solution for scripts failing due to errors! As we can see from these examples there is no way of knowing exactly where it failed so we cant be sure where it will start from again. Going for the "quick fix" solution which could appear to be working fine may have more problems now then before.

I would instead prefer to see the defect addressed with some proper unit tests which will help flush out the culprit so we may rectify the problem. If PHP runs out of memory it can be avoided through conservative use of resources by unsetting the variables after use. (btw. you will find assigning null is much quicker than using unset for the same result) I am afraid though, if left unresolved, restarting would most definitely serve as suitable opener to the can of worms you already have.


Here be dragons.

Advanced usage and not for the faint of heart.

To launch a separate process change the restartMyself function to:

<?php    $restartMyself = function () {        global $_, $argv;         if(!pcntl_fork()) // We only care about the child fork            pcntl_exec($_, $argv);        die; // insist that we don't tolerate errors      }

This can certainly be an adventure, chasing processes that restart immune to your kill if you manage to catch them in process. =)

Ensure that the script has enough to do so you may have enough time to kill it.

Try:

$ kill -9

or:

$ kill -s KILL

to insist the undead stay dead. This is no idle reference as you would be wise to consider zombies and orphans but ultimately even the toughest processes usually still need a parent so killing the shell session should see them to rest.

All standard disclaimers apply, good luck! =)


Assuming:

  • Call the same script file x number of times
  • Re-call if it gets interrupted/ended/stopped
  • Automated process for infinite loop

Some options come to mind to achieve your goal [Unix/Linux based solutions]:

  1. Use a BASH script, so that the PHP script after stopped/ended/interrupted can be resumed:

    #!/bin/bashcleardatephp -f my_php_file.phpsleep 100# rerun myselfexec $0
  2. The Fat Controller execution handler, this are the key features you're looking for:

    • Used to repeatedly run other programs
    • Can run many instances of any script/program in parallel
    • any script can be rerun once it finishes executing

    very similar to CRON but this key features are just what you need

  3. Using a CRON, you can have it call your PHP script at intervals of e.g., one minute, then with this code at the very beggining of you PHP script, you will be able to control the number of threads running, exiting if 8 are already running:

    exec('ps -A | grep ' . escapeshellarg(basename(__FILE__)) , $results);if (count($results) > 7) {  echo "8 Already Running\n"  die(0);}

    search for processes running with the same path of the current file, and returns the number of processes found. Then, if bigger them 7, exits.