php 提交表单后如何在后台运行PHP脚本?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4626860/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 13:45:06  来源:igfitidea点击:

How can I run a PHP script in the background after a form is submitted?

phpscriptingbackground

提问by Michael Irigoyen

Problem
I have a form that, when submitted, will run basic code to process the information submitted and insert it into a database for display on a notification website. In addition, I have a list of people who have signed up to receive these notifications via email and SMS message. This list is trivial as the moment (only pushing about 150), however it's enough to cause it takes upwards of a minute to cycle through the entire table of subscribers and send out 150+ emails. (The emails are being sent individually as requested by the system administrators of our email server because of mass email policies.)

问题
我有一个表单,当提交时,它将运行基本代码来处理提交的信息并将其插入到数据库中以在通知网站上显示。此外,我有一份已注册通过电子邮件和 SMS 消息接收这些通知的人员列表。这个列表暂时是微不足道的(仅推送大约 150 个),但是它足以导致循环浏览整个订阅者表并发送 150 多封电子邮件需要一分钟以上的时间。(由于群发电子邮件政策,电子邮件是按照我们电子邮件服务器系统管理员的要求单独发送的。)

During this time, the individual who posted the alert will sit on the last page of the form for almost a minute without any positive reinforcement that their notification is being posted. This leads to other potential problems, all that have possible solutions that I feel are less than ideal.

在此期间,发布警报的个人将在表单的最后一页上停留近一分钟,而没有任何正面强化他们的通知正在发布。这会导致其他潜在问题,所有可能的解决方案我都觉得不太理想。

  1. First, the poster might think the server is lagging and click the 'Submit' button again, causing the script to start over or run twice. I could solve this by using JavaScript to disable the button and replace the text to say something like 'Processing...', however this is less than ideal because the user will still be stuck on the page for the length of the script execution. (Also, if JavaScript is disabled, this problem still exists.)

  2. Second, the poster might close the tab or the browser prematurely after submitting the form. The script will keeping running on the server until it tries to write back to the browser, however if the user then browses to any page within our domain (while the script is still running), the browser hangs loading the page until the script has ended. (This only happens when a tab or window of the browser is closed and not the entire browser application.) Still, this is less than ideal.

  1. 首先,发布者可能认为服务器滞后并再次单击“提交”按钮,导致脚本重新开始或运行两次。我可以通过使用 JavaScript 禁用按钮并替换文本以说出诸如“正在处理...”之类的内容来解决此问题,但是这不太理想,因为用户在脚本执行期间仍会停留在页面上。(另外,如果 JavaScript 被禁用,这个问题仍然存在。)

  2. 其次,发布者可能会在提交表单后过早关闭选项卡或浏览器。该脚本将继续在服务器上运行,直到它尝试写回浏览器,但是如果用户随后浏览到我们域中的任何页面(当脚本仍在运行时),浏览器将挂起加载页面,直到脚本结束. (这仅在浏览器的选项卡或窗口关闭而不是整个浏览器应用程序关闭时发生。)不过,这并不理想。

(Possible) Solution
I've decided I want to break out the "email" part of the script into a separate file I can call after the notification has been posted. I originally thought of putting this on the confirmation page after the notification has been successfully posted. However, the user will not know this script is running and any anomalies will not be apparent to them; This script cannot fail.

(可能)解决方案
我决定将脚本的“电子邮件”部分分解到一个单独的文件中,我可以在发布通知后调用。本来是想在通知成功发布后把这个放在确认页面上的。然而,用户不会知道这个脚本正在运行,任何异常对他们来说都不会明显;这个脚本不会失败。

But, what if I can run this script as a background process? So, my question is this: How can I execute a PHP script to trigger as a background service and run completely independent of what the user has done at the form level?

但是,如果我可以将此脚本作为后台进程运行呢?所以,我的问题是:如何执行 PHP 脚本以作为后台服务触发并完全独立于用户在表单级别执行的操作?

EDIT:This cannotbe cron'ed. It must run the instant the form is submitted. These are high-priority notifications. In addition, the system administrators running our servers disallow crons from running any more frequently than 5 minutes.

编辑:不能被 cron'ed。它必须在提交表单的瞬间运行。这些是高优先级通知。此外,运行我们服务器的系统管理员不允许 cron 运行频率超过 5 分钟。

采纳答案by Michael Irigoyen

Doing some experimentation with execand shell_execI have uncovered a solution that worked perfectly! I choose to use shell_execso I can log every notification process that happens (or doesn't). (shell_execreturns as a string and this was easier than using exec, assigning the output to a variable and then opening a file to write to.)

做了一些实验用execshell_exec我已经揭露了完美工作的解决方案!我选择使用,shell_exec以便我可以记录发生(或不发生)的每个通知过程。(shell_exec作为字符串返回,这比使用更容易exec,将输出分配给变量,然后打开要写入的文件。)

I'm using the following line to invoke the email script:

我正在使用以下行调用电子邮件脚本:

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

It is important to notice the &at the end of the command (as pointed out by @netcoder). This UNIX command runs a process in the background.

注意&命令末尾的很重要(正如@netcoder 所指出的)。这个 UNIX 命令在后台运行一个进程。

The extra variables surrounded in single quotes after the path to the script are set as $_SERVER['argv']variables that I can call within my script.

脚本路径后用单引号括起来的额外变量被设置为$_SERVER['argv']我可以在脚本中调用的变量。

The email script then outputs to my log file using the >>and will output something like this:

然后电子邮件脚本使用 输出到我的日志文件>>,并将输出如下内容:

[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)

回答by netcoder

On Linux/Unix servers, you can execute a job in the background by using proc_open:

在 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);

The &being the important bit here. The script will continue even if the original script has ended.

这里&是重要的一点。即使原始脚本已结束,脚本仍将继续。

回答by Danogentili

Of all the answers, none considered the ridiculously easy fastcgi_finish_requestfunction, that when called, flushes all remaining output to the browser and closes the Fastcgi session and the HTTP connection, while letting the script run in the background.

在所有答案中,没有人考虑过简单得可笑的fastcgi_finish_request函数,当调用该函数时,会将所有剩余输出刷新到浏览器并关闭 Fastcgi 会话和 HTTP 连接,同时让脚本在后台运行。

An example:

一个例子:

<?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,

回答by Simon

PHP exec("php script.php")can do it.

PHPexec("php script.php")可以做到。

From the Manual:

手册

If a program is started with this function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.

如果一个程序使用这个函数启动,为了让它在后台继续运行,程序的输出必须被重定向到一个文件或另一个输出流。如果不这样做,将导致 PHP 挂起,直到程序执行结束。

So if you redirect the output to a log file (what is a good idea anyways), your calling script will not hang and your email script will run in bg.

因此,如果您将输出重定向到日志文件(无论如何这是个好主意),您的调用脚本将不会挂起,您的电子邮件脚本将在 bg.txt 中运行。

回答by Twister

And why not making a HTTP Request on the script and ignoring the response ?

为什么不在脚本上发出 HTTP 请求并忽略响应?

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

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

If you make your request on the script you need to call your webserver will run it in background and you can (in your main script) show a message telling the user that the script is running.

如果您对脚本提出请求,您需要调用您的网络服务器将在后台运行它,并且您可以(在您的主脚本中)显示一条消息,告诉用户该脚本正在运行。

回答by Sandeep Dhanda

The simpler way to run a PHP script in background is

在后台运行 PHP 脚本的更简单方法是

php script.php >/dev/null &

The script will run in background and the page will also reach the action page faster.

该脚本将在后台运行,页面也将更快地到达操作页面。

回答by Ivan Buttinoni

As I know you cannot do this in easy way (see fork exec etc (don't work under windows)), may be you can reverse the approach, use the background of the browser posting the form in ajax, so if the post still work you've no wait time.
This can help even if you have to do some long elaboration.

About sending mail it's always suggest to use a spooler, may be a local & quick smtp server that accept your requests and the spool them to the real MTA or put all in a DB, than use a cron that spool the queue.
The cron may be on another machine calling the spooler as external url:

据我所知,您无法以简单的方式执行此操作(请参阅 fork exec 等(不在 Windows 下工作)),可能您可以颠倒这种方法,使用浏览器的背景在 ajax 中发布表单,因此如果帖子仍然工作你没有等待时间。
即使您必须进行一些冗长的阐述,这也会有所帮助。

关于发送邮件,总是建议使用假脱机程序,它可能是一个本地和快速的 smtp 服务器,它接受您的请求并将它们假脱机到真正的 MTA 或将所有内容放入数据库中,而不是使用假脱机队列的 cron。
cron 可能在另一台机器上调用假脱机程序作为外部 url:

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

回答by adarshr

How about this?

这个怎么样?

  1. Your PHP script that holds the form saves a flag or some value into a database or file.
  2. A second PHP script polls for this value periodically and if it's been set, it triggers the Email script in a synchronous manner.
  1. 保存表单的 PHP 脚本将标志或某些值保存到数据库或文件中。
  2. 第二个 PHP 脚本会定期轮询此值,如果已设置,则会以同步方式触发电子邮件脚本。

This second PHP script should be set to run as a cron.

第二个 PHP 脚本应设置为作为 cron 运行。

回答by OneOfOne

If you can access the server over ssh and can run your own scripts you can make a simple fifo server using php (although you will have to recompile php with posixsupport for fork).

如果您可以通过 ssh 访问服务器并且可以运行您自己的脚本,您可以使用 php 制作一个简单的 fifo 服务器(尽管您必须重新编译 php 并posix支持fork)。

The server can be written in anything really, you probably can easily do it in python.

服务器真的可以用任何东西来编写,你可能可以很容易地用 python 来完成。

Or the simplest solution would be sending an HttpRequestand not reading the return data but the server might destroy the script before it finish processing.

或者最简单的解决方案是发送HttpRequest而不读取返回数据,但服务器可能会在完成处理之前销毁脚本。

Example server :

示例服务器:

<?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);
?>

Example client :

示例客户端:

<?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)));
}
?>

回答by Ian

Background cron job sounds like a good idea for this.

后台 cron 工作听起来是个好主意。

You'll need ssh access to the machine to run the script as a cron.

您需要 ssh 访问机器才能将脚本作为 cron 运行。

$ php scriptname.phpto run it.

$ php scriptname.php运行它。