运行 PHP 代码而无需等待 Python 返回值



我有一个运行python脚本的PHP脚本

exec("C:/Python26/python C:/xampp/htdocs/timeout.py");

它工作得很好。但是 Python 代码在 10 分钟内返回一个值。我想在 5 分钟内显示在浏览器echo "Time out";上,而无需等待返回值。

第一页.php

<form action="spg.php" method="POST" enctype="multipart/form-data">
<div align="center"><input type="submit"name="submit" value="INSERT"> 
<input type="submit" name="show" value="Show">
</div>
</form>

第二页.php

<?php
$start_time = time();
$timeVar=true;
while(true) {
if ((time() - $start_time) < 300) {
$read= exec("start C:/Python26/python C:/xampp/htdocs/test/timeout.py");
}
else
{
echo "Time out";
}
}   
?>

timeout.py

from multiprocessing 
import Process
import time 
def do_actions():    
i = 0
while True:
i += 1
# print(i)
time.sleep(1) 
if __name__ == '__main__':
# We create a Process
action_process = Process(target=do_actions)
# We start the process and we block for 5 seconds.
action_process.start()
action_process.join(timeout=600)
# We terminate the process.
action_process.terminate()
print("Hey there! I timed out! You can do things after me!")

简介

你的代码有很多问题,你用来做到这一点的方法。我会尽量简单地回答它,但请记住,这不是一个容易的问题 - 可能需要一些解释。


代码的问题

首先,为什么您现有的解决方案不起作用?我立即注意到的一些事情是:

  • while(true)循环是无限的。由于您永远不会break它,因此此代码将永远不会完成执行。您可能打算使用$timeVar作为while循环的条件,并在代码中更改它,但从未这样做过。

  • 在每个循环中,如果过去的时间少于 300 秒,则尝试通过再次执行 Python 脚本来再次读取该脚本的输出。你在这里忘记的是exec()是一个阻塞函数 - 这意味着在它完成之前不会执行任何内容 - 所以你只是在这里等待脚本的整个执行 - 没有什么真正改变通过把它放在循环中。

当然,这些都是可以理解的错误。这是一个难题。


exec()函数

很多这个问题似乎可以归结为exec(),用于执行命令行语句的函数,在执行任何其他代码之前等待语句完成。这样做的原因是由于命令的实际工作方式 - 如果它们需要打印出错误或输出,则需要分别打印到stderrstdout在PHP中,exec()充当stderrstdout,因此命令需要能够在那里发送输出。这意味着在命令完成之前,PHP 无法继续前进。

如果您使用的是 *nix 系统,这将容易得多。但是,在Windows上,我们将不得不放弃使用exec(),而是使用两种不同的功能:popen()pclose()

首先,我们需要使命令在后台运行。这是一个非常简单的任务:在命令的开头,你写了start,我们将改为写start b(在后台开始)。

但是等等!如果命令始终在后台运行,我们将如何获得命令的结果?这就是重定向运算符>>的用武之地。它将 shell 函数的所有输出发送到文件中。因为我们使用了两个>字符,所以它将覆盖那里已经存在的任何文件。这将在以后变得很重要。

因此,在您的test目录中创建一个文件夹(其中包含 PHP 和 Python 脚本),并将其命名为output。然后,我们将在 PHP 中为文件生成一个名称,以便多人可以同时使用您的网站,而无需获得彼此的输出。我们可以为此使用time()函数 - 它们不太可能在同一秒内完成。现在,您应该能够将输出发送到output目录中的文件,该文件将以当前time()值命名。

现在是困难的一点:执行它。popen()运行该命令。它的第一个参数是命令,第二个参数是它的模式。它返回一个资源指针。您可以在下面链接的 PHP 手册上阅读有关此内容的内容。我们需要知道的是,使用"r"模式 ,如果出错(包含任何 shell 错误消息),它也将返回一个资源,因此它运行命令并始终返回我们需要关闭的指针。

因此:

$filename = time().".txt";
pclose(popen("start b C:/Python26/python C:/xampp/htdocs/test/timeout.py >> "C:/xampp/htdocs/test/output/$filename"", "r"));

应该在后台运行我们的命令。

有关此主题的进一步阅读:

  • PHP exec 命令(或类似命令)不等待结果
  • 如何将命令提示符输出重定向到文件
  • execPHP 手册
  • popenPHP 手册

获得第二名.php

现在,我们让你的 Python 脚本在后台运行。我们的PHP文件可以结束并向用户显示一个页面,并且Python仍将在服务器上运行。但是我们将如何获得输出?

首先,我们需要记住文件名 - 将其存储在会话cookie中应该可以很好地工作。在secondpg.php的最顶部,我们需要添加代码session_start(),以便我们可以使用会话 cookie。然后,我们将通过使用会话超全局:$_SESSION["filename"] = $filename;来设置文件名,将文件名存储在 cookie 中。

我们需要做的另一件事是确保文件存在,以便我们稍后可以检查它是否为空(>>会用 Python 脚本的输出覆盖它,但只有在脚本完成后)。我们可以使用file_put_contents().

我们还需要包含一个 JavaScript 文件(我们还没有写过)。因此,这里是第二个pg.php

<?php
session_start(); //Allows us to use sessions
$time = time(); //Creates a filename using the current time past the epoc as a unique identifier
file_put_contents("output/$time",""); //Ensure that the file exists, and is empty
$_SESSION["time"] = $time; //Sets the filename in a session cookie, so we can use it later
pclose(popen("start b C:/Python26/python C:/xampp/htdocs/test/timeout.py >> "C:/xampp/htdocs/test/output/$time.txt"", "r")); //Runs our Python script
?>
<script src="check.js"></script>

延伸阅读:

  • PHP会话

获取输出

现在,您需要编写一个新的 PHP 文件。称之为get_output.php。代码非常基本:

<?php
session_start(); //Allows us to use sessions
if (time()-$_SESSION["time"]>300) { //If the file was made more than 300 seconds (five minutes ago) we just echo timeout
echo "Timeout";
}
else {
echo file_get_contents("output/".$_SESSION["time"].".txt"); //Echo out the contents of the file. Pulls the filename out of our session cookie.
}
?>

希望该文件是不言自明的:它只是echo输出文件的内容。

现在,我们需要编写一个 JavaScript 文件。这将在用户计算机上运行,并且每五秒钟运行一次我们刚刚编写的PHP文件,以便我们可以检查该文件是否有内容。它会将内容写入页面正文。

调用此文件检查.js

function do_check() {
var http = new XMLHttpRequest(); //Open a request to another page
http.onreadystatechange = function() {
if (http.readyState==4&&http.status==200) { //If we have loaded the page successfully
document.body.innerHTML = http.responseText; //Set the body content to be the response text
}
}
http.open("GET","get_output.php",true); //Request the file
http.send(); //Send the request
}
setInterval("do_check()", 5000); //Call the function every five seconds.

延伸阅读:

  • JavaScript XMLHttpRequest

结论

这应该是你所需要的。每五秒页面内容将更改为文件的内容,您可以通过自己更改文件来检查这一点 - 页面应更新以显示更改。

最新更新