2

在构建一个返回给用户的页面时,我想提交一个后台脚本来对数据库中的数据进行一些数值分析,并将结果通过电子邮件发送给用户。这个过程可能需要一分钟左右,所以我不想在页面运行时延迟页面服务。

有没有办法从构建页面的脚本中触发另一个 PHP 脚本,以便它可以发送页面并在其他脚本在后台运行时完成?

对于测试,这个 TEST.PHP:

<?php
set_time_limit(0);
mail ('myemail@myemail.com','Test Email from ClockPie', 'foobar');
?>

然后我把它放在构建页面服务的脚本中:

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

我在 Windoze 7 Home Premium 下运行。这有什么明显的问题吗?

是的,我知道这本质上是一个重复的问题,并且还有其他关于同一件事的现有问题,但是我在 StackOverflow 上太过分了,无法简单地添加评论并参与讨论:-(

4

5 回答 5

-1

shell_exec()就像坐在命令提示符下输入字符串值并按回车一样。您不能只键入“test.php”并让它触发,您需要运行 PHP,并将其指向您的 php 文件。

根据您安装 php 的方式,您可能可以简单地将其更改为,shell_exec('php test.php');或者您可能必须提供 PHP 的路径,例如shell_exec('C:\php\bin\php.exe test.php');. 路径将取决于您的环境。

在您显示的代码中,如果您只是发送一封电子邮件,那不应该超过几秒钟,所以我什至不会走这条路。但我不认为这是您的代码的最终结果,而只是一个示例。

于 2013-08-03T20:16:22.187 回答
-1

您可以做的是输出页面,然后flush()将所有数据发送给用户。然后你发了邮件。用户将看到该页面,但在后台它仍在加载。这不需要任何类似的东西shell_exec();

这种方法也被 phpBB 之类的软件用来处理需要相当长的时间的 cron 作业。

flush()文档: http: //php.net/manual/en/function.flush.php

基本程序架构:

  1. 构建和回显页面
  2. 冲洗()
  3. 发送电子邮件
于 2013-08-03T20:18:32.557 回答
-1

以下代码适用于我的系统。请注意,我只是一个业余爱好者,而不是专家,所以这可能不属于“最佳实践”的范畴,我不知道安全隐患可能是什么,但这绝对有效,创建多个运行的线程同时。不要介意文件夹名称“卡路里”。当我把这个示例代码放在一起时,这恰好是我正在工作的文件夹。

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!
于 2013-08-03T20:21:09.263 回答
-1

如果你想 fork 另一个 PHP 进程,你需要使用pcntl_fork。但是我不认为这是最好的方法。相反,我会让浏览器访问的脚本将要处理的数据添加到队列中。然后设置一个计划任务以每“x”分钟运行一次,该任务将处理队列并在完成后向用户发送电子邮件。

当然,我假设如果队列中实际上没有任何内容(即,它应该需要几毫秒才能完成),您将使这个预定的脚本非常轻量级。如果没有,你每隔几分钟就会让你的服务器陷入困境,在这种情况下,寻找类似的东西pcntl_fork会是更好的解决方案。

这样做的一个缺点是,如果您将其设置为例如每 5 分钟运行一次,但处理数据只需要 2 分钟,则用户最多需要等待 3 分钟才能收到电子邮件。如果这是一个问题,请调整它以更频繁地运行。

于 2013-08-03T20:27:45.560 回答
-1

您可以使用 shell_exec() 来运行您的 'sub php script' asynchronously,这意味着它将在您的主 php 脚本继续运行的同时在后台运行,方法是附加带有 & 符号的触发您的 'sub php script' 的命令。所以,它会是这样的:

$cmd="php /path/to/you/php/sub/script.php &";
shell_exec($cmd);
//main script continues running here, while sub script runs in the background...
于 2013-08-03T20:34:40.743 回答