Хотел бы запустить фоновый PHP-скрипт из основного PHP-скрипта


При создании страницы для обратной отправки пользователю я хотел бы отправить фоновый скрипт для проведения численного анализа данных в базе данных и отправки результата пользователю по электронной почте. Этот процесс может занять минуту или около того, поэтому я не хочу задерживать показ страницы во время ее запуска.

Есть ли способ запустить другой PHP-скрипт из скрипта, который создает страницу, чтобы он мог отправить страницу и выполнить ее, пока другой скрипт работает в фоновом режиме?

Для тестирование, это TEST.PHP :

<?php
set_time_limit(0);
mail ('[email protected]','Test Email from ClockPie', 'foobar');
?>

Затем я вставляю это в скрипт, который создает страницу обслуживания:

...
shell_exec ('test.php'); 
...

Я работаю под Windoze 7 Home Premium. Есть ли в этом что-то явно неправильное?

И да, я знаю, что это, по сути, дублирующий вопрос, и есть другие существующие вопросы по этому же вопросу, но я слишком большой поклонник здесь, в StackOverflow, чтобы просто добавлять комментарии и участвовать в обсуждениях :-(

Author: Fredashay, 2013-08-04

5 answers

Если вы хотите разветвить другой процесс PHP, вам нужно будет использовать pcntl_fork. Однако я не думаю, что это лучший метод здесь. Вместо этого я бы сделал так, чтобы скрипт, к которому обращается браузер, добавлял данные, подлежащие обработке, в очередь. Затем настройте запланированную задачу для запуска каждые "x" минут, которые будут обрабатывать очередь и отправлять пользователю электронное письмо по завершении.

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

Одним из недостатков такого способа является то, что если вы настроите его на запуск, например, каждые 5 минут, но для обработки данных потребуется всего 2 минуты, пользователю потребуется подождать до 3 дополнительных минут, чтобы получить электронное письмо. Если это проблема, настройте ее для запуска чаще.

 -1
Author: Mike, 2013-08-03 20:46:37

shell_exec() это все равно, что сидеть в командной строке, вводить строковое значение и нажимать клавишу return. Ты не можешь просто печатать"test.php "и пусть он сработает, вам нужно запустить PHP и указать его на свой php-файл.

В зависимости от того, как у вас установлен php, вы, возможно, сможете просто изменить его на shell_exec('php test.php'); или вам, возможно, придется указать путь к PHP, например shell_exec('C:\php\bin\php.exe test.php');. Путь будет зависеть от вашего окружения.

В коде, который вы показываете, если вы просто отправляете одно электронное письмо, это не должно занять больше времени чем на несколько секунд, так что я бы даже не пошел по этому пути. Но я не думаю, что это конечный результат вашего кода, а просто образец.

 -1
Author: Steven V, 2013-08-03 20:16:22

Что вы можете сделать, это вывести страницу и flush(), чтобы все данные были отправлены пользователю. Затем вы отправили электронное письмо. Пользователь увидит страницу, но в фоновом режиме она все еще загружается. Для этого не требуется ничего подобного shell_exec();

Этот метод также используется программным обеспечением, таким как phpBB, для обработки заданий cron, занимающих довольно много времени.

flush() документация: http://php.net/manual/en/function .flush.php

Базовая архитектура программы:

  1. Сборка и эхо Страница
  2. промыть()
  3. Отправить электронное письмо
 -1
Author: Jeroen Bollen, 2013-08-03 20:18:32

Вы можете использовать shell_exec() для запуска вашего "суб-php-скрипта" asynchronously, что означает, что он будет выполняться в фоновом режиме, пока ваш основной php-скрипт продолжается, добавив команду, которая запускает ваш "суб-php-скрипт", с символом &. Итак, это было бы что-то вроде этого:

$cmd="php /path/to/you/php/sub/script.php &";
shell_exec($cmd);
//main script continues running here, while sub script runs in the background...
 -1
Author: mti2935, 2013-08-03 20:34:40

Следующий код работает в моей системе. Обратите внимание, что я просто любитель, а не эксперт, так что это может не подпадать под категорию "лучших практик", и я понятия не имею, каковы могут быть последствия для безопасности, но это абсолютно работает, создавая несколько потоков, которые все работают одновременно. Не обращайте внимания на название папки "Калории". Просто так получилось, что это та папка, в которой я работал, когда собирал этот пример кода.

main.php:

error_log('Hello, world, from main!');

$numberOfThreadsToCreate = 3;

for($i = 0; $i < $numberOfThreadsToCreate; ++$i) {
    error_log("Main starting child {$i}");

    $fp = fsockopen('localhost', 8888);
    if(!$fp) {
        error_log("$errstr ($errno)");
        exit;
    }

    $firstSleep = $numberOfThreadsToCreate - $i;
    $header = "GET /Calories/thread.php?threadID={$i}&firstSleep={$firstSleep}"
                . " HTTP/1.1\r\n"
                . "Host: localhost\r\n"
                . "Connection: Close\r\n\r\n";

    $r = fputs($fp, $header); 
    fclose($fp);

    sleep(1);
}

for($i = 0; $i < 5; ++$i) {
    sleep(1);
    error_log('Main is still running');
}

error_log("Goodbye, cruel world, from main!");


thread.php

$myThreadID = $_GET['threadID'];
$sleep = $_GET['firstSleep'];

error_log("Hello, world, from child thread, ID={$myThreadID}!");
for($i = 0; $i < 5; ++$i) {
    error_log("Child {$myThreadID} sleeping for {$sleep} seconds");
    sleep($sleep);
    $sleep = 1;
}

error_log("Goodbye, cruel world, from child thread, ID={$myThreadID}!");

И результаты файла журнала:

Hello, world, from main!
Main starting child 0
Hello, world, from child thread, ID=0!
Child 0 sleeping for 3 seconds
Main starting child 1
Hello, world, from child thread, ID=1!
Child 1 sleeping for 2 seconds
Main starting child 2
Hello, world, from child thread, ID=2!
Child 2 sleeping for 1 seconds
Child 1 sleeping for 1 seconds
Child 2 sleeping for 1 seconds
Child 0 sleeping for 1 seconds
Child 1 sleeping for 1 seconds
Child 2 sleeping for 1 seconds
Child 0 sleeping for 1 seconds
Main is still running
Child 1 sleeping for 1 seconds
Child 2 sleeping for 1 seconds
Child 0 sleeping for 1 seconds
Main is still running
Child 1 sleeping for 1 seconds
Child 2 sleeping for 1 seconds
Child 0 sleeping for 1 seconds
Main is still running
Goodbye, cruel world, from child thread, ID=1!
Goodbye, cruel world, from child thread, ID=2!
Main is still running
Goodbye, cruel world, from child thread, ID=0!
Main is still running
Goodbye, cruel world, from main!
 -1
Author: GreatBigBore, 2013-08-04 19:02:42