Introduction
There's many things wrong with your code, and the method which you are using to go about doing this. I will try to answer it as simply as possible, but do bear in mind that this is not an easy problem - it will probably take some explanation.
The Problems With Your Code
Firstly, why does your existing solution not work? Some of the things that I notice right away are:
A while(true)
loop is infinite. Since you never break
out of it, this code will never finish executing. You probably intended to use $timeVar
as the conditional for your while
loop and change it within your code, but never did.
In every loop, if less than 300 seconds have gone past, you try to read the output of the Python script again, by executing it again. What you have forgotten here is that exec()
is a blocking function - that means that nothing after it will execute until it has finished - so you'll just be waiting for the entire execution of the script here - nothing has really changed by putting it in the loop.
Of course, these are understandable mistakes. This is a hard problem.
The exec()
Function
A lot of this problem seems to boil down to exec()
, the function used to execute command-line statements, waiting for the statement to finish before any other code is executed. The reason for this is due to how commands actually work - if they need to print out errors or output, they need to print to stderr
and stdout
respectively. In PHP, exec()
acts as stderr
and stdout
, and thus commands need to be able to send output there. This means that PHP can't move on until the command has finished.
If you were using a *nix system, this would be a lot easier. However, on Windows, we will have to move away from using exec()
and instead, use two different functions: popen()
and pclose()
.
Firstly, we need to make the command run in the background, though. This is a simple enough task: at the beginning of your command, where you have written start
, we will instead write start \b
(start in background).
But wait! How are we going to get the result of the command, if it always runs in the background? This is where the redirection operator, >>
, comes in. It sends all output from a shell function into a file instead. Because we used two >
characters, it will overwrite any file that already exists there. This will become important later on.
So, make a folder in your test
directory (where the PHP and Python scripts are contained), and call it output
. Then, we'll generate a name for the file in PHP, so that multiple people can use your website at once without getting each-others output. We can just use the time()
function for this - it is unlikely that they would both do it at the exact same second. Now you should be able to send output to a file in the output
directory, which will be named after the current time()
value.
Now for the hard bit: executing it. popen()
runs the command. Its first parameter is the command, the second is its mode. It returns a resource pointer. You can read about this on the PHP manual, linked below. All we need to know is that with a mode of "r"
, it will also return a resource if it errors (containing any shell error messages), so it runs the command and always returns a pointer that we need to close.
Thus:
$filename = time().".txt";
pclose(popen("start \b C:/Python26/python C:/xampp/htdocs/test/timeout.py >> \"C:/xampp/htdocs/test/output/$filename\"", "r"));
should run our command in the background.
Further reading on this topic:
Finishing secondpg.php
Now, we have your Python script running in the background. Our PHP file can end and display a page to the user, and the Python will still be running on the server. But how will we ever get the output?
Firstly, we'll need to remember the filename - storing it in a session cookie should work quite nicely. At the very top of secondpg.php, we'll need to add the code session_start()
, so that we can use session cookies. Then, we'll store the filename in a cookie by setting it using the session superglobal: $_SESSION["filename"] = $filename;
.
Another thing we need to do is ensure that the file exists, so that we can check if it is empty later (>>
will overwrite it with the output of the Python script, but only after the script is finished). We can create the file using file_put_contents()
.
We'll also need to include a JavaScript file (which we have not written yet). Thus, here is 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>
Further reading:
Getting The Output
Now, you need to write a new PHP file. Call it get_output.php. The code is pretty basic:
<?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.
}
?>
Hopefully that file is pretty self-explanatory: it simply echo
s out the contents of our output file.
Now, we need to write a JavaScript file. This will run on the users computer, and every five seconds, will run the PHP file we just wrote, so that we can check if the file has contents. It will write the contents to the body of the page.
Call this file 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.
Further reading:
Conclusion
That should be all that you need. Every five seconds the page content will be changed to the contents of the file, and you can check this by changing the file yourself - the page should update to show the change.