PHP запускает фоновый процесс с помощью nohup equiv

#php #windows

#php #Windows

Вопрос:

Я просматривал различные обсуждения и статьи о том, как заставить PHP запускать процесс в фоновом режиме, используя Windows-эквивалент nohup; мой код должен работать как с Unix, так и с Windows, но в данный момент я сосредоточен на Windows.

Моя проблема в том, что все приведенные примеры, похоже, предполагают использование exec или pclose / popen или system, но когда я пробую их, я вижу, что фоновый процесс запускается при вызове, но, похоже, он задерживает вызывающего до завершения…

… то, что я ищу, — это быстрое завершение вызывающей программы, пока фоновый процесс продолжает выполнять то, что ему нужно (в данном случае несколько секунд обработки, но в окончательной версии это может занять несколько минут).

Я включил пример программы, в которой, если вы измените значение $option [находится внизу скрипта] на 210, 220, 230 или 240, вы сможете увидеть результаты. Посмотрите полные результаты в файле журнала, заданном с помощью $this->logfn.

 <?php

error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);

class Test  {
    var $logfn="C:/test.log";
    // ------------------------------------------------
    public function doTrace(string $msgTx) {
        $now = date('Y-m-d H:i:s', gmdate(time()));
        error_log($now."::".$msgTx."n", 3, $this->logfn);
        echo "<LI>$now:$msgTx";
    }
    // ------------------------------------------------
    public function doTest(int $option, array $args = null) {
        $this->doTrace("0 ".php_sapi_name());

        if ( strcmp(php_sapi_name(), "cli") == 0 ) {
            // Command line execution
            if ( isset($args) and count($args) > 0 ) $x = $args[1];
            else $x="-";
            $this->doTrace("   900---------------- background cli starting ($x)");
            sleep(1);
            $this->doTrace("   901---------------- tick");
            sleep(1);
            $this->doTrace("   902---------------- tick");
            sleep(1);
            $this->doTrace("   903---------------- tick");
            sleep(1);
            $this->doTrace("   904---------------- tick");
            sleep(1);
            $this->doTrace("   905---------------- tick");
            $this->doTrace("   910 background sleep complete");

        }
        else {
            // Web execution
            unlink($this->logfn);
            $this->doTrace("100---------------- www starting option $option - " . __FILE__);

            $script = __FILE__;

            if ( !file_exists($script) ) echo "<LI>$script NOT FOUND</LI>";

            $php = "php ";
            $cmd = "-f $script ARG1";


            $this->doTrace("200 Starting background process...");
            switch($option){
            case 210:$this->execInBackground210($php, $cmd); break;
            case 220:$this->execInBackground220($php, $cmd); break;
            case 230:$this->execInBackground230($php, $cmd); break;
            case 240:$this->execInBackground240($php, $cmd); break;
            }
            $this->doTrace("300 cmd ran in background");
        }
    }

    // ------------------------------------------------
    // exec
    public function execInBackground210($php, $cmd) {
        if ( substr(php_uname(), 0, 7) == "Windows" ) {
            $this->doTrace("210a - Spawning $php $cmd");
            exec("start /B $php $cmd", $output, $return);
            echo "<HR>nn";
            echo "<UL>";
            echo "<LI>o:" . nl2br(print_r($output, true)) . "</LI>";
            echo "<LI>r:$return</LI>";
            echo "</UL>";
            $this->doTrace("210b - Spawn finished");
        }
        else {
            exec("nohup $cmd > /dev/null amp;");
        }
    }

    // ------------------------------------------------
    // popen
    public function execInBackground220($php, $cmd) {
        if ( substr(php_uname(), 0, 7) == "Windows" ) {
            echo "<LI>$cmd";
            $this->doTrace("220a - Spawning $php $cmd");
            pclose(popen("start /B " . "$php $cmd", "r"));
            $this->doTrace("220b - Spawn finished");
        }
        else {
            exec("$php $cmd" . " > /dev/null amp;");
        }
    }

    // -------------------------------------------------------------------------------------------------------
    // COM/run
    public function execInBackground230($php, $cmd) {
        if ( substr(php_uname(), 0, 7) == "Windows" ) {
            $this->doTrace("230a - Spawning $php $cmd");
            $shell = new COM("WScript.Shell");
            $shell->run("$php $cmd", 0, false);
            $this->doTrace("230b - Spawn finished");
        }
        else {
            exec($cmd . " > /dev/null amp;");
        }
    }

    // -------------------------------------------------------------------------------------------------------
    // system
    public function execInBackground240($php, $cmd) {
        if ( substr(php_uname(), 0, 7) == "Windows" ) {
            $this->doTrace("240a - Spawning $php $cmd");
            system("start /B $php $cmd"." 2>amp;1", $return);
            $this->doTrace("240b - Spawn done [$return]");
        }
        else {
            exec($cmd . " > /dev/null amp;");
        }
    }
}

//$option=210;
//$option=220;
//$option=230;
$option=240;

$try = new Test();
echo "<html>";
echo "<body>";
if ( isset($argv) ) {
    echo $try->doTest($option,$argv);
}
else {
    echo $try->doTest($option,null);
}
echo "</body>";
echo "</html>";
?>
  

Я надеюсь увидеть в файле журнала, что журналы серии 900 запускаются после завершения других (из вызовов веб-страницы), но то, что я вижу, это то, что веб-страница ожидает завершения фонового процесса, прежде чем управление будет передано обратно интерфейсу, в результате чего журналы серии 900 находятся в середине файла журнала, а не в конце.

Файл журнала в настоящее время выглядит следующим образом (при запуске $ option = 240)

 2019-03-31 16:53:37::100---------------- www starting option 240 - C:.....testSpawn.php
2019-03-31 16:53:37::200 Starting background process...
2019-03-31 16:53:37::240a - Spawning php  -f C:.... testSpawn.php ARG1
2019-03-31 16:53:37::0 cli
2019-03-31 16:53:37::   900---------------- background cli starting (ARG1)
2019-03-31 16:53:38::   901---------------- tick
2019-03-31 16:53:39::   902---------------- tick
2019-03-31 16:53:40::   903---------------- tick
2019-03-31 16:53:42::   904---------------- tick
2019-03-31 16:53:43::   905---------------- tick
2019-03-31 16:53:43::   910 background sleep complete
2019-03-31 16:53:43::240b - Spawn done [0]
2019-03-31 16:53:43::300 cmd ran in background
  

Итак, что нужно сделать, чтобы среда Windows PHP запускала порожденные процессы в фоновом режиме асинхронно?

(Я на PHP5.7)

Ответ №1:

start /B нормально. Однако, как правило, php этого нет в %PATH% Windows. Вы должны определить полный путь. Попробуйте PHP_BINARY константу, которая должна содержать абсолютный путь к исполняемому файлу PHP.

Если вы хотите прочитать stdout поток процесса, вы можете сделать его неблокирующим:

 $cmd = PHP_BINARY;
if(is_resource($proc = proc_open("start /B $cmd script.php", [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipes)))
{
  echo '<h1>Process Stream</h1>', PHP_EOL;
  stream_set_blocking($pipes[1], false);
  stream_set_blocking($pipes[2], false);
  

Затем вы можете получить доступ к потокам, не дожидаясь EOF / EOL .

Комментарии:

1. Благодаря Quasi, я добавил версию этого предложения в качестве execInBackground250, и действительно, кажется, что это ближе к тому, что я ищу, но я замечаю, что фоновые сообщения 900 прекращаются, если вызывающий останавливается — т. Е. Кажется, что фон умирает, если вызывающий умирает — что не соответствует требованию nohup, поскольку я хочу, чтобы веб-интерфейс продолжал выполнять другие действия, пока выполняется фоновый режим.