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

时间:2017-07-17 03:26:13

标签: php python

我有一个运行带有

的python脚本的PHP脚本
exec("C:/Python26/python C:/xampp/htdocs/timeout.py");

很好用。但Python代码在10分钟内返回一个值。我想在5分钟内在浏览器echo "Time out";上显示而不等待返回值。

firstpg.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>

secondpg.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!")

1 个答案:

答案 0 :(得分:2)

简介

你的代码有很多问题,以及你用来执行此操作的方法。我将尝试尽可能简单地回答它,但要记住这不是一个容易的问题 - 它可能需要一些解释。

您的代码存在的问题

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

  • 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"));

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

关于这个主题的进一步阅读:

完成secondpg.php

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

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

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

我们还需要包含一个JavaScript文件(我们尚未编写)。因此,这里是 secondpg.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文件。称之为 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文件,以便我们可以检查文件是否包含内容。它会将内容写入页面正文。

将此文件称为 check.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.

进一步阅读:

结论

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