如何异步运行PHP代码

15
如何在不等待的情况下异步运行PHP代码?我有一个几乎无限长的任务,应该在服务器启动时运行,并且应该异步处理而不要等待。
可能的选项包括:
1. 在网页中运行代码并保持打开状态以执行该任务。 2. 从某个命令行实用工具(我不确定如何)调用脚本,在后台进行处理。
我在本地服务器上运行PHP脚本,当某些事件发生时,例如生日提醒,将发送电子邮件。
请建议我如何在不打开浏览器的情况下实现这一点。

3
最好的选择可能是cron任务或守护进程,但如果没有更多细节很难确定。 - deceze
听起来你不是想创建一个网页脚本。 - Lightness Races in Orbit
3
“几乎”和“无限”的两个词你不能真正地放在一起使用。它要么是无限的,要么不是,不存在“几乎无限”的说法。 - netcoder
2
"就快了,再来一次!" 无限循环。 ;o) - deceze
你的服务器运行的是哪个操作系统? - Cole
抱歉,如果我没有解释清楚。我正在使用带有Apache/MYSQL/PHP的Windows操作系统。@netcoder,这个任务是无限的,正如我所描述的那样,它的目的是通过检查任务来发送电子邮件。 - AjayR
5个回答

26
如果您想要在浏览器中运行它(也许您不熟悉命令行),您仍然可以这样做。我几个月前研究了许多解决方案,最可靠且最简单的实现方式是从如何在PHP中发布异步HTTP请求中得到的以下方法。
<?php


$params['my_param'] = $a_value;
post_async('http:://localhost/batch/myjob.php', $params);

/*
 * Executes a PHP page asynchronously so the current page does not have to wait for it to     finish running.
 *  
 */
function post_async($url, array $params)
{
    foreach ($params as $key => &$val) {
      if (is_array($val)) $val = implode(',', $val);
        $post_params[] = $key.'='.urlencode($val);  
    }
    $post_string = implode('&', $post_params);

    $parts=parse_url($url);

    $fp = fsockopen($parts['host'],
        isset($parts['port'])?$parts['port']:80,
        $errno, $errstr, 30);

    $out = "POST ".$parts['path']." HTTP/1.1\r\n";
    $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";
    if (isset($post_string)) $out.= $post_string;

    fwrite($fp, $out);
    fclose($fp);
}

假设上述文件位于你的Web根目录(例如/var/www)中,名为runjobs.php。通过访问http://localhost/runjobs.php,你的myjob.php文件将开始运行。你可能想要在浏览器中添加一些输出以让你知道它已经成功提交,并且如果你的Web服务器对外开放,则最好添加一些安全性。这个解决方案的一个不错的特点是,如果你添加了一些安全性,你可以在任何你能找到浏览器的地方启动作业。


2
这并不是异步的操作。你只是向套接字写入数据,然后关闭了该套接字而没有等待响应。异步操作应该是创建一个新线程或者其他等待来自curl请求的响应,并确保请求已被接收的机制。 - Trevor
13
在某些情况下,未得到回应可能并非理想选择,但这绝对是异步的:“异步操作在非阻塞方案下执行,允许主程序流继续处理。” - Todd Chaffee
如果您需要使用HTTP_GET,请将$out = "POST ".$parts['path']." HTTP/1.1\r\n";替换为以下内容:if (!empty($parts['query'])) { $query = "?".$parts['query']; } $out = "GET ".$parts['path']." HTTP/1.1\r\n"; - allofmex

4

这绝对是一个cron任务。您可以设置一个php脚本,执行一次您的任务,并且可以根据需要定期运行cron。 这里有一篇很好的文章,介绍如何将php脚本设置为cron任务;非常容易实现。


1
这是一个非常常见的解决方案,用于解决非常常见的问题。大多数具有重复事件(软件更新检查、生日提醒等)的PHP程序都有一个“cron.php”脚本,该脚本通过crontab运行,可以通过类似“wget”或“curl”的程序,或直接通过命令行执行来运行。这样,您就不需要一直保持脚本运行,并且可以在任务完成后立即让它退出。它将在一定时间间隔后再次被调用。 - SplinterReality

0

这并不是 PHP 的设计初衷。您需要使用 PECL 线程库来启动异步运行的线程,但我不建议这样做。在异步部分的新热点是 node.js - 我建议您研究一下并看看是否可以利用它。它专为轻量级、异步网络操作而设计,并可用于触发 PHP 脚本。


这个问题似乎更适合于普通的 shell 环境,而不是 Web 服务器,因此可能不需要使用 NodeJS 和异步 Web 请求处理。 - Cole

0
如何异步运行PHP代码而无需等待?我有一个长时间的任务(几乎是无限的),它应该在服务器启动时运行,并且应该以异步方式进行处理,而无需等待。
假设您使用典型的LAMP系统,您可以从命令行启动一个PHP守护进程:
root# php yourscript.php &

其中yourscript.php包含类似于以下内容

<?php

$log = fopen('/var/log/yourscript.log', 'a+');
// ### check if we are running already omitted
while(true) {
    // do interesting stuff and log it.

    // don't be a CPU hog
    sleep(1);
}
?>

装饰:

为了使您的脚本可以直接执行:chmod +x yourscript.php,并在yourscript的开头添加#!/usr/bin/php

要开始使用apache,您应该将该命令添加到您的apache启动脚本(通常是apachectl),并确保在apache停止时添加代码来终止它。

检查是否已经运行涉及到一个带有/var/locks/中PID文件的内容,以及类似system('/bin/ps '.$thePID)的东西。这也使得kill指令更容易编写。


0

感谢Todd Chaffee,但这对我没有用,所以我编辑了你的代码,希望你不介意,也许它还能帮助其他人使用这种技术。

cornjobpage.php //主页面

     <?php
//if you want to call page for multiples time w.r.t array 
//then uncomment loop start & end)
?>

<?php
//foreach ($inputkeywordsArr as $singleKeyword) {
    $url="http://localhost/projectname/testpage.php";
        $params['Keywordname'] = "testValue";//$singleKeyword 
        post_async($url, $params);

        //}//foreach ($inputkeywordsArr end
        ?>
        <?php

        /*
         * Executes a PHP page asynchronously so the current page does not have to wait for it to     finish running.
         *  
         */
        function post_async($url, array $params)
        {
            foreach ($params as $key => &$val) {
              if (is_array($val)) $val = implode(',', $val);
                $post_params[] = $key.'='.urlencode($val);  
            }
            $post_string = implode('&', $post_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"];//Output > testValue
    ?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接