Асинхронный Shell exec в PHP

у меня есть php-скрипт, который должен вызывать сценарий оболочки, но не заботится о выходе. Сценарий оболочки делает несколько вызовов SOAP и медленно завершается, поэтому я не хочу замедлять запрос PHP, пока он ждет ответа. Фактически, запрос PHP должен иметь возможность выйти без завершения процесса оболочки.

Я просмотрел различные exec(), shell_exec(), pcntl_fork(), etc. функции, но ни одна из них не предлагает именно то, что я хочу. (Или, если они это делают, мне непонятно как. Есть предложения?

13 ответов


если он "не заботится о выходе", не может ли exec скрипту вызываться с помощью & для фона процесса?

редактировать - применив @AdamTheHut прокомментировал этот пост, вы можете добавить к вызову exec:

" > /dev/null 2>/dev/null &"

это перенаправит оба stdio ("первый"!--5-->) и stderr (2>) к /dev/null и работать в фоновом режиме.

есть и другие способы сделать то же самое, но это самое простое для чтения.


альтернатива вышеуказанному двойному перенаправлению:

" &> /dev/null &"

Я at для этого, так как это действительно начало независимого процесса.

<?php
    `echo "the command"|at now`;
?>

в linux вы можете сделать следующее:

$cmd = 'nohup nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = shell_exec($cmd);

Это выполнит команду в командной строке, а затем просто вернет PID, который вы можете проверить > 0, чтобы убедиться, что он работал.

этот вопрос сродни: имеет ли PHP поток?


для всех пользователей Windows: я нашел хороший способ запустить асинхронный PHP-скрипт (на самом деле он работает почти со всем).

Он основан на командах popen() и pclose (). И хорошо работает как на Windows, так и на Unix.

function execInBackground($cmd) {
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r")); 
    }
    else {
        exec($cmd . " > /dev/null &");  
    }
} 

исходный код от:http://php.net/manual/en/function.exec.php#86329


php-execute-a-background-process есть несколько хороших предложений. Я думаю, что мой довольно хорош, но я предвзят:)


в Linux вы можете запустить процесс в новом независимом потоке, добавив амперсанд в конце команды

mycommand -someparam somevalue &

в Windows вы можете использовать команду" Пуск " DOS

start mycommand -someparam somevalue

правильный путь(!) сделать это значит

  1. вызов Fork()
  2. setsid()
  3. execve ()

fork forks, setsid говорят текущему процессу стать ведущим (без родителя), execve говорит вызывающему процессу быть замененным вызываемым. чтобы родитель мог уйти, не затрагивая ребенка.

 $pid=pcntl_fork();
 if($pid==0)
 {
   posix_setsid();
   pcntl_exec($cmd,$args,$_ENV);
   // child becomes the standalone detached process
 }

 // parent's stuff
 exit();

я использовал это...

/** 
 * Asynchronously execute/include a PHP file. Does not record the output of the file anywhere.  
 * Relies on the PHP_PATH config constant.
 *
 * @param string $filename  file to execute
 * @param string $options   (optional) arguments to pass to file via the command line
 */ 
function asyncInclude($filename, $options = '') {
    exec(PHP_PATH . " -f {$filename} {$options} >> /dev/null &");
}

(где PHP_PATH является const определяется как define('PHP_PATH', '/opt/bin/php5') или аналогичные)

он передает Аргументы через командную строку. Чтобы прочитать их в PHP, см. argv.


единственный способ, который я нашел, что действительно работал для меня:

shell_exec('./myscript.php | at now & disown')

Я нашел Компонент Процесса Symfony полезное для этого.

use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
// ... run process in background
$process->start();

// ... do other things

// ... if you need to wait
$process->wait();

// ... do things after the process has finished

посмотреть, как это работает в его GitHub repo.


используйте имя fifo.

#!/bin/sh
mkfifo trigger
while true; do
    read < trigger
    long_running_task
done

тогда, когда вы хотите, чтобы начать длительные задачи, просто напишите строку (незаблокированном на курок файл.

пока ваш вход меньше, чем PIPE_BUF и один write() операция, вы можете записать аргументы в fifo и показать их как $REPLY в скрипте.


вы также можете запустить PHP-скрипт как демон или cronjob: #!/usr/bin/php -q


без очереди использования вы можете использовать proc_open() такой:

    $descriptorspec = array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w"),
        2 => array("pipe", "w")    //here curaengine log all the info into stderror
    );
    $command = 'ping stackoverflow.com';
    $process = proc_open($command, $descriptorspec, $pipes);