Асинхронная оболочка exec в PHP

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

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

Ответ 1

Если он "не заботится о выходе", не мог ли вызов exec в script с & выполнить фоновый процесс?

РЕДАКТИРОВАТЬ - добавив, что @AdamTheHut прокомментировал это сообщение, вы можете добавить это к вызову exec:

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

Это перенаправит как stdio (сначала >), так и stderr (2>) в /dev/null и запустится в фоновом режиме.

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


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

" &> /dev/null &"

Ответ 2

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

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

Ответ 3

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

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

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

Этот вопрос аналогичен: Есть ли у PHP потоковая передача?

Ответ 4

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

Ответ 5

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

Он основан на командах 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

Ответ 6

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

mycommand -someparam somevalue &

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

start mycommand -someparam somevalue

Ответ 7

правильный путь (!) для этого -

  • вилка()
  • setsid()
  • 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 stuff
 exit();

Ответ 8

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

/** 
 * 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 является константой, определенной как define('PHP_PATH', '/opt/bin/php5') или аналогичной)

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

Ответ 9

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

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

Ответ 10

Вы также можете запустить PHP script как daemon или cronjob: #!/usr/bin/php -q

Ответ 11

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

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

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

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

Ответ 12

без использования очереди, вы можете использовать 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);

Ответ 13

Я также нашел компонент Symfony Process, полезный для этого.

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.