Как запустить PHP-скрипт в фоновом режиме после отправки формы?

проблема
У меня есть форма, которая при отправке запускает базовый код для обработки представленной информации и вставляет ее в базу данных для отображения на веб-сайте уведомления. Кроме того, у меня есть список людей, которые подписались на получение этих уведомлений по электронной почте и SMS-сообщению. Этот список тривиально, как момент (всего лишь около 150), однако этого достаточно, чтобы потребовать больше минуты, чтобы перебирать всю таблицу подписчиков и отправлять более 150 писем. (Письма отправляются индивидуально по запросу системных администраторов нашего почтового сервера из-за массовых политик электронной почты.)

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

  1. Во-первых, плакат может подумать, что сервер отстает, и снова нажмите кнопку «Отправить», заставив скрипт начать работу или запустить дважды. Я мог бы решить это, используя JavaScript, чтобы отключить кнопку и заменить текст, чтобы сказать что-то вроде «Обработка …», однако это не так идеально, потому что пользователь все равно будет застревать на странице для длины выполнения скрипта. (Кроме того, если JavaScript отключен, эта проблема все еще существует.)

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

(Возможное решение
Я решил, что хочу разбить часть «email» скрипта в отдельный файл, который я могу позвонить после того, как уведомление было опубликовано. Первоначально я думал о том, чтобы поместить это на страницу подтверждения после успешного уведомления. Однако пользователь не будет знать, что этот скрипт запущен, и любые аномалии не будут очевидны для них; Этот скрипт не может потерпеть неудачу.

Но что, если я могу запустить этот скрипт в качестве фонового процесса? Итак, мой вопрос заключается в следующем: как я могу выполнить скрипт PHP для запуска в качестве фоновой службы и выполнить полностью независимую от того, что пользователь сделал на уровне формы?

EDIT: Это невозможно сделать. Он должен запускать момент отправки формы. Это высокоприоритетные уведомления. Кроме того, системные администраторы, работающие на наших серверах, запрещают работу кронов не чаще, чем 5 минут.

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

    Я использую следующую строку для вызова сценария электронной почты:

     shell_exec("/path/to/php /path/to/send_notifications.php '".$post_id."' 'alert' >> /path/to/alert_log/paging.log &"); 

    Важно отметить & в конце команды (как указано @netcoder). Эта команда UNIX запускает процесс в фоновом режиме.

    Дополнительные переменные, окруженные одинарными кавычками после пути к скрипту, устанавливаются как переменные $_SERVER['argv'] которые я могу вызвать в моем сценарии.

    Затем скрипт электронной почты выводит в мой файл журнала с помощью >> и выводит что-то вроде этого:

     [2011-01-07 11:01:26] Alert Notifications Sent for http://alerts.illinoisstate.edu/2049 (SCRIPT: 38.71 seconds) [2011-01-07 11:01:34] CRITICAL ERROR: Alert Notifications NOT sent for http://alerts.illinoisstate.edu/2049 (SCRIPT: 23.12 seconds) 

    На серверах Linux / Unix вы можете выполнить задание в фоновом режиме с помощью proc_open :

     $descriptorspec = array( array('pipe', 'r'), // stdin array('file', 'myfile.txt', 'a'), // stdout array('pipe', 'w'), // stderr ); $proc = proc_open('php email_script.php &', $descriptorspec, $pipes); 

    Здесь & есть важный бит. Сценарий будет продолжен, даже если исходный скрипт закончился.

    PHP exec("php script.php") может это сделать.

    Из руководства :

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

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

    И почему бы не сделать HTTP-запрос на скрипт и игнорировать ответ?

    http://php.net/manual/en/function.httprequest-send.php

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

    Фоновая работа cron звучит как хорошая идея для этого.

    Для запуска скрипта в качестве cron вам понадобится ssh-доступ к компьютеру.

    $ php scriptname.php для запуска.

    Как насчет этого?

    1. Ваш PHP-скрипт, содержащий форму, сохраняет флаг или какое-то значение в базе данных или файле.
    2. Второй скрипт PHP периодически проверяет это значение, и если он был установлен, он запускает скрипт электронной почты синхронно.

    Этот второй скрипт PHP должен быть запущен как cron.

    Как я знаю, вы не можете сделать это простым способом (см. Fork exec и т. Д. (Не работают под окнами)), возможно, вы можете отменить подход, используйте фон браузера, разместив форму в ajax, так что если сообщение все еще работа у вас нет времени ожидания.
    Это может помочь, даже если вам нужно сделать долгую разработку.

    О отправке почты всегда предлагается использовать спулер, может быть локальным и быстрым smtp-сервером, который принимает ваши запросы и связывает их с реальным MTA или помещает все в БД, чем использует cron, который запускает очередь.
    Cron может находиться на другой машине, вызывающей спулер в качестве внешнего URL:

     * * * * * wget -O /dev/null http://www.example.com/spooler.php 

    Если вы можете получить доступ к серверу через ssh и можете запускать свои собственные скрипты, вы можете создать простой сервер fifo, используя php (хотя вам придется перекомпилировать php с поддержкой posix fork ).

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

    Или самое простое решение будет отправлять HttpRequest и не читать возвращаемые данные, но сервер может уничтожить скрипт до его завершения.

    Пример сервера:

     <?php define('FIFO_PATH', '/home/user/input.queue'); define('FORK_COUNT', 10); if(file_exists(FIFO_PATH)) { die(FIFO_PATH . ' exists, please delete it and try again.' . "\n"); } if(!file_exists(FIFO_PATH) && !posix_mkfifo(FIFO_PATH, 0666)){ die('Couldn\'t create the listening fifo.' . "\n"); } $pids = array(); $fp = fopen(FIFO_PATH, 'r+'); for($i = 0; $i < FORK_COUNT; ++$i) { $pids[$i] = pcntl_fork(); if(!$pids[$i]) { echo "process(" . posix_getpid() . ", id=$i)\n"; while(true) { $line = chop(fgets($fp)); if($line == 'quit' || $line === false) break; echo "processing (" . posix_getpid() . ", id=$i) :: $line\n"; // $data = json_decode($line); // processData($data); } exit(); } } fclose($fp); foreach($pids as $pid){ pcntl_waitpid($pid, $status); } unlink(FIFO_PATH); ?> 

    Пример клиента:

     <?php define('FIFO_PATH', '/home/user/input.queue'); if(!file_exists(FIFO_PATH)) { die(FIFO_PATH . ' doesn\'t exist, please make sure the fifo server is running.' . "\n"); } function postToQueue($data) { $fp = fopen(FIFO_PATH, 'w+'); stream_set_blocking($fp, false); //don't block $data = json_encode($data) . "\n"; if(fwrite($fp, $data) != strlen($data)) { echo "Couldn't the server might be dead or there's a bug somewhere\n"; } fclose($fp); } $i = 1000; while(--$i) { postToQueue(array('xx'=>21, 'yy' => array(1,2,3))); } ?> 

    Более простой способ запуска php-скрипта в фоновом режиме – php script.php> / null / dev &

    Php будет работать в фоновом режиме, и страница также будет быстрее доходить до страницы действия.

    Предполагая, что вы работаете на платформе * nix, используйте cron и исполняемый файл php .

    РЕДАКТИРОВАТЬ:

    Есть целый ряд вопросов, которые требуют «запускать php без cron» на SO уже. Вот один из них:

    Расписание скриптов без использования CRON

    Тем не менее, ответ exec () выше звучит очень многообещающе 🙂

    Если вы работаете в Windows, proc_open или popen

    Но если мы на том же сервере «Linux» запускаем cpanel, то это правильный подход:

     #!/usr/bin/php <?php $pid = shell_exec("nohup nice php -f 'path/to/your/script.php' /dev/null 2>&1 & echo $!"); While(exec("ps $pid")) { //you can also have a streamer here like fprintf, // or fgets } ?> 

    Не используйте fork() или curl если вы сомневаетесь, что можете справиться с ними, это просто как злоупотребление вашим сервером

    Наконец, в файле script.php который вызывается выше, обратите внимание на это, убедитесь, что вы написали:

     <?php ignore_user_abort(TRUE); set_time_limit(0); ob_start(); // <-- really optional but this is pure php //Code to be tested on background ob_flush(); flush(); //this two do the output process if you need some. //then to make all the logic possible str_repeat(" ",1500); //.for progress bars or loading images sleep(2); //standard limit ?> 

    В моем случае у меня есть 3 параметра, один из них – строка (mensaje):

     exec("C:\wamp\bin\php\php5.5.12\php.exe C:/test/N/trunk/api/v1/Process.php $idTest2 $idTest3 \"$mensaje\" >> c:/log.log &"); 

    В моем Process.php у меня есть этот код:

     if (!isset($argv[1]) || !isset($argv[2]) || !isset($argv[3])) { die("Error."); } $idCurso = $argv[1]; $idDestino = $argv[2]; $mensaje = $argv[3]; 

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

    form_action_page.php

      <?php post_async("http://localhost/projectname/testpage.php", "Keywordname=testValue"); //post_async("http://localhost/projectname/testpage.php", "Keywordname=testValue2"); //post_async("http://localhost/projectname/otherpage.php", "Keywordname=anyValue"); //call as many as pages you like all pages will run at once //independently without waiting for each page response as asynchronous. //your form db insertion or other code goes here do what ever you want //above code will work as background job this line will direct hit before //above lines response ?> <?php /* * Executes a PHP page asynchronously so the current page does not have to wait for it to finish running. * */ function post_async($url,$params) { $post_string = $params; $parts=parse_url($url); $fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 30); $out = "GET ".$parts['path']."?$post_string"." HTTP/1.1\r\n";//you can use POST instead of GET if you like $out.= "Host: ".$parts['host']."\r\n"; $out.= "Content-Type: application/x-www-form-urlencoded\r\n"; $out.= "Content-Length: ".strlen($post_string)."\r\n"; $out.= "Connection: Close\r\n\r\n"; fwrite($fp, $out); fclose($fp); } ?> 

    testpage.php

      <? echo $_REQUEST["Keywordname"];//case1 Output > testValue //here do your background operations it will not halt main page ?> 

    PS: если вы хотите отправить параметры URL-адреса в качестве цикла, выполните следующий ответ: https://stackoverflow.com/a/41225209/6295712

    Из всех ответов никто не рассматривал смехотворно легкую функцию fastcgi_finish_request , которая при вызове сбрасывает все оставшиеся данные в браузер и закрывает сеанс Fastcgi и HTTP-соединение, позволяя сценарию работать в фоновом режиме.

    Пример:

     <?php header('Content-Type: application/json'); echo json_encode(['ok' => true]); fastcgi_finish_request(); // The user is now disconnected from the script // do stuff with received data,