I'm sure similar questions have bee answered over and over again. If yes then I googled in the wrong direction and apologize for that.
My problem:
I'm writing a web page with a process running in the background. The process I'm talking about is a R script which runs quite long maybe several days. When the progress is started, its started like that.
exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));
Id' like to track whether the process is still running. This way, when the user checks he gets either the message that it is finished or not. The tracking starts and stops when the user chooses the input file he uploaded on to the server after for example he logs in again or did something else on the page. I also want it to update the page when the process finishes so the page changes in case he is just looking at it.
I have two possibilities. I can either check it via the process id or whether an output file is generated or not.
I tried
while(is_process_running($ps)){
ob_flush();
flush();
sleep(1);
}
this works kind of, except that all other functionality on the page freezes.
That's my is_process_running($ps) function.
function is_process_running($PID){
exec("ps $PID", $ProcessState);
return(count($ProcessState) >= 2);
}
What I really need is another process running in the background checking whether the first process is still running and if not, refreshes the page when the first process finishes.
How would you do that? Please let me know if you need additional information. All help is much appreciated.
with symcbean's answer I was able to solve it. My javascript code is below maybe its of use to someone who faces the same problem. I'm also open for improvement. I still consider myself a beginner.
function loadXMLDoc(){
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
return xmlhttp;
}
function start(file,time){
var xmlhttp = loadXMLDoc();
if(lines == 0){
timer = setInterval(function(){sendRequest(xmlhttp,file)},time);
}
}
function sendRequest(xmlhttp,file){
xmlhttp.open("POST",file,true);
xmlhttp.send()
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4 && xmlhttp.status==200 && xmlhttp.responseText != ""){
var text = xmlhttp.responseText;
var splitted = text.split("\n");
var length = splitted.length
if(splitted[length-2]=="finished"){
refresh_page();
clearInterval(timer);
lines = 0;
}
}
}
}
The start method is called with file path and the time interval its supposed to check for changes. Note the refresh page method I did not post but that just whatever you want to refresh on your page. The page is refreshed when the last line in the file says finished.
I included the line variable to check whether the process is already started or not. I have not fully tested the code but so far its doing what I want it to do.
First off, there are a number of issues with the way you are starting the process - it really needs to be a in a separate process group from the PHP which launches it.
As to checking its status, again, you don't want PHP stuff hanging around for a long time at the end of an http connection. Also getting the webserver to flush content to the browser on demand AND getting the browser to render it progressively is very difficult - and fragile. Even if you get it working on one browser/webserver combination it's unlikely to work in another.
Use Ajax to poll a simple script which reports back on the process status / progress.
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Best way to manage long-running php script?
I have to built a big email list.Everything works perfectly,but when i submit the form page is loading untill every email is send.So i want this email sending script run in background.and notice the user that script is runnign in background.
I cant use Ajax.
i want something like.. proc_open,exec,shell_exec..
You can have cron job which would run php script which will get queue from db and send email
On main script you just need to add emails to queue
I would use ajax only if you need progress bar. With ajax solution you would need to keep window open until it's ended.
You could build an AJAX call that calls the php script. This way, your site will still be operational while the request is fulfilled. And when it's finished, your AJAX will return and you can show a messagebox to the user.
For more information, check at least this and if you understand what AJAX is and what it does, use it with this
Ajax request would be the best choice for this. You can send a request using javascript and even report progress to user (which might require some additional work)
If you find ajax too difficult - run script in an iframe. This is not the most elegant, but the most simple method.
Submit the form with AJAX and update the progress in a Div
For example - write to some place "A"(db or file) current state of your script runtime: "complete"/"incomplete". After start script in background send to your user waiting page which using AJAX handling changes at "A".
This Ajax script will execute a PHP file on the background. It could also send the response to a HTML element if you want.
<script type="text/javascript" language="javascript">
function execute(filename,var1,var2,var3)
{
var xmlhttp;
if(window.XMLHttpRequest)
{
//Code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else if(window.ActiveXObject)
{
//Code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else
{
alert("Your browser does not support AJAX!");
}
var url = filename+"?";
var params = "var1="+var1+"&var2="+var2+"&var3="+var3;
xmlhttp.open("POST", url, true);
xmlhttp.onreadystatechange=function()
{
if(xmlhttp.readyState==4)
{
//Below line will fill a DIV with ID 'response'
//with the reply from the server. You can use this to troubleshoot
//document.getElementById('response').innerHTML=xmlhttp.responseText;
xmlhttp.close;
}
}
//Send the proper header information along with the request
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("Content-length", params.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(params);
}
</script>
You can try to run the script through an ajax function as well if you don't want to set cron script.
PHP has an function that can keep an process running even if the user that requested the page leaves the page : ignore_user_abort if you check the comments there you can see this example :
<?php
ignore_user_abort(1); // run script in background
set_time_limit(0); // run script forever
$interval=60*15; // do every 15 minutes...
do{
// add the script that has to be ran every 15 minutes here
// ...
sleep($interval); // wait 15 minutes
}while(true);
?>
It IS an pure php cron job BUT, the risk with this script is that it continues indefinitely or atleast untill you reset/kill php.
Setting the set_time_limit(0); to set_time_limit(86400); would kill the script after an day.
This should point you in the right direction/.
IMPORTANT
After the problem by the OP, it is advisable to only run this script if you have SSH access to the server so you can KILL/RESTART php apache in case the server keeps hanging.
Also do not run the script on a LIVE server.
I'm writing some PHP that does a fair amount of processing and then generates reports of the results. Previously it would do a periodic flush() but we're moving to Zend Framework and can't do that anymore. Instead, I would like to have some kind of status display that updates while the report is generated. So I made a progress bar that loads in an iframe, added shared memory to the progress bar update action and the report generation action, and caused the output to load via xmlhttprequest. This all works fine. My issue is that the browser wants to do the two requests serially instead of in parallel, so it will request the progress bar and then BLOCK until the progress bar completes BEFORE it requests the actual output. This means that the process will never end since the real work never starts.
I've searched all morning for some way around this and came up empty-handed.
Is there some way to cause two connections, or am I just screwed?
My next action will be to break the processing apart some more and make the status updating action do the actual work, save the result, and then use the other action to dump it. This will be really painful and I'd like to avoid it.
Edit: Here is the javascript, as requested:
function startProgress()
{
var iFrame = document.createElement('iframe');
document.getElementsByTagName('body')[0].appendChild(iFrame);
iFrame.id = 'progressframe';
iFrame.src = '/report/progress';
}
function Zend_ProgressBar_Update(data)
{
document.getElementById('pg-percent').style.width = data.percent + '%';
document.getElementById('pg-text-1').innerHTML = data.text;
document.getElementById('pg-text-2').innerHTML = data.text;
}
function Zend_ProgressBar_Finish()
{
document.getElementById('pg-percent').style.width = '100%';
document.getElementById('pg-text-1').innerHTML = 'Report Completed';
document.getElementById('pg-text-2').innerHTML = 'Report Completed';
document.getElementById('progressbar').style.display = 'none'; // Hide it
}
function ajaxTimeout(){
xmlhttp.abort();
alert('Request timed out');
}
var xmlhttp;
var xmlhttpTimeout;
function loadResults(){
if (window.XMLHttpRequest){
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}else{
// code for IE6, IE5
xmlhttp=new ActiveXObject(\"Microsoft.XMLHTTP\");
}
xmlhttp.open(\"POST\",\"/report/output\",true);
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
clearTimeout(xmlhttpTimeout);
document.getElementById('report-output').innerHTML=xmlhttp.responseText;
}
}
var xmlhttpTimeout=setTimeout(ajaxTimeout,600000); // Ten minutes
xmlhttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xmlhttp.send('".file_get_contents("php://input")."');
}
This gets called from the following onload script:
onload="startProgress(); setTimeout(loadResults,1000);"
The issue is not in Javascript. If you put an alert() in there, the alert will be triggered at the right time, but the browser is delaying the second http transaction until the first completes.
Thank you everyone for your input.
I didn't come up with a satisfactory answer for this within the timeframe permitted by our development schedule. It appears that every common browser wants to re-use an existing connection to a site when doing multiple transactions with that site. Nothing I could come up with would cause the browser to initiate a parallel connection on demand. Any time there are two requests from the same server the client wants to do them in a serial fashion.
I ended up breaking the processing into parts and moving it into the status bar update action, saving the report output into a temporary file on the server, then causing the status bar finish function to initiate the xmlhttprequest to load the results. The output action simply spits out the contents of the temporary file and then deletes it.
Using two async ajaxes could do the trick. With the first ajax request you should start the process by calling the php-cli to do the actual work deep in the background (so it doesn't expire or cancel) and return the id of the process (task). Now when you have the process id, you can start the periodical ajax to display the process made.
Making a db table containing process_id, state, user would not be a bad thing. In this case even if the user would close the browser while the process is running, the process would continue until done. The user could revisit the page and see the percentage done, because the process running in cli would save the progress into the db table.
Make a system call to the php file and detach it?
ex:
exec('nohup php test.php > test.out 2> test.err < /dev/null &');
echo 'I am totally printing here';
test.php contains a sleep for 2 seconds and prints, but echo returns immediately.
Have it store the results in a file/database/whatever. It will act like a very dirty fork.
You could also do something similar with a CURL call I bet if you have issues executing.
Credit here for the code example from bmellink (mine was way worse than his).
If you are able to load the report in the iFrame, you can kind of reverse your logic (I have done this to track file uploads to PHP).
Load Report in iFrame (can be hidden or whatever you like).
Make ajax call to get progress (step 1 will have to log progress as others have mentioned).
When the progress reports loading complete, you may show the iframe or whatever is needed to complete.
Hope that helps. Just did a whole lot with iFrames, CORS, and Ajax calls to API's.
I have checked the suggestions that came up before posting, hope I didn't miss anything now.
I have a piece of code that I use to get txt-files for my website but now I need to redo the code so it gets both txt and php-files, but it just won't read the php-script. I'm a bit afraid to mess up the code at this moment so I'm walking on the safe side of the road and ask here if anyone knows a good add to the code. It's quite embarrasing that I still have codes for IE 5&6 in it, so if you wish to remove that at the same time, go ahead. I won't hate you for it, I promise.
UPDATE:
I have four files:
html - Calling the .js-file with the ajax-script.
js - With all my javascript(and simular)-codes.
php - That contains... Well, you get the point.
I have to call the php-code somehow, like I call my txt-files, but of course so the php works as it should. I am very new to AJAX, so I don't dare to mess around with this code at the moment, but I figured that I might be able to add some kind of if-statement that calles the php-file as it is intended to be.
But I have no clue what that code might be and where to put it for things to work accordingly. Any help would be appritiated and credited in the code, of course.
Heres the AJAX-code that is contained within the .js-file:
/*Load the link.*/
function loadXMLDoc(url)
{
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("leftCol").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
/*Highly unnecessary, but I wanted to see if it worked and it looks better on the .html-page.*/
function get_link(url)
{
loadXMLDoc(url);
}
As the above commenter said, it is best to use a 3rd party tool for such things - if for no other reason than to greatly increase cross-browser compatibility.
if you were to use jQuery, the code would be reduced to.
function get_link(url)
{
$.ajax({url: url, success: success:function(result){
//the code / resulting string will be in the result variable.
}});
}
jQuery CDN Hosted: http://code.jquery.com/jquery-1.5.min.js
Let me ask this... if you change your code to
function get_link(url)
{
window.location=url;
}
Does your web browser successfully navigate to the page you are trying to retrieve via AJAX? If not, there is likely a problem with your PHP syntax.
it just won't read the php-script
It's a rather vague statement, but here are few pointers that could be the solution :
PHP file are interpreted on the server so when you do an Ajax call to that page what you receive on the client side is the result of that php script, not his content.
You are assigning the result of the query directly in the HTML, if the result contains data that does not render anything, you won't see anything. For example the content <script>Text here bla bla bla</script> will just show nothing.
If you want to make sure you get some data back from a file, you can just alert the content when you receive it.
Make sure your path to your PHP page is correct. To detect if the file is not giving a 404 error code or any other error code, you can use this :
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
document.getElementById("leftCol").innerHTML = xmlhttp.responseText;
} else {
alert("Error " + xmlhttp.status);
}
}
}
as a fix to a previous problem of mine, i have thought to run a cURL script using JavaScript to prevent my site waiting for a response of each one.
at the moment I have this script given by a friend and i have noticed something.
Link to JS file
My question is if I were to remove the following lines, would it mean that the page would not wait for a response?
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
setTimeout("location.reload(true)",1500);
}
}
Thanks.
If you remove the lines, it will not go anywhere.
If you want to speed it up, get rid of the setTimeout line, that is not nessecary.
If you were to remove the if statement, and just do the window.locaiton line, it will fire before the request is made since that onreadystatechange fires to tell you when the request is opened, started, transferring, and done.
Ajax 101 Article
I am creating a web game for learning new words aimed at children.
I have a set of four links each displaying a specific word retrieved from my database and a clue, I need to check that the word which has been selected matches the correct word for that clue.
I know that I need to use javascript because of the onClick function and I can successfully check whether the word selected matches the correct word. However, I then need to update a score held in the database if the word is matched correctly, therefore I would need to use php.
From what I can gather this means I must use AJAX but I can't find a good example of anyone using AJAX onClick of a link to then update a database.
I have attempted to do this...but its probably completely wrong as I couldn't get it to work properly:
//This is my link that I need to use in my game.php file where $newarray[0] is that answer I want to check against $newarray[$rand_keys]
<a onClick=originalUpdateScore('$newarray[0]','$newarray[$rand_keys]')>$newarray[0]</a>
//my attempt at ajax in a score.js file
var xmlHttp;
function originalUpdateScore(obj,corr){
xmlHttp=GetXmlHttpObject();
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request");
return;
}
if(corr == obj){
var url="getscore.php";
//url=url+"?q="+str;
//url=url+"&sid="+Math.random();
xmlHttp.onreadystatechange=stateChanged;
//xmlHttp.open("GET",url,true);
xmlHttp.open(url,true);
xmlHttp.send(null);
alert('Correct');
}
else
{
alert('AHHHHH!!!');
}
window.location.reload(true);
}
function stateChanged()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
document.getElementById("txtHint").innerHTML=xmlHttp.responseText;
}
}
function GetXmlHttpObject()
{
var xmlHttp=null;
try
{
// Firefox, Opera 8.0+, Safari
xmlHttp=new XMLHttpRequest();
}
catch (e)
{
//Internet Explorer
try
{
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xmlHttp;
}
//attempting to update the database in a getscore.php file
<?php
//$q=$_GET["q"];
include("dbstuff.inc");
$con = mysqli_connect($host, $user, $passwd, $dbname)
or die ("Query died: connection");
$sql= "UPDATE `temp` SET `tempScore`= `tempScore`+1 WHERE (temp.Username='$_SESSION[logname]')";
$showsql = "SELECT `tempScore` FROM `temp` WHERE (temp.Username='$_SESSION[logname]')";
$result = mysqli_query($con, $showsql);
echo "$result";
mysqli_close($con);
?>
I would highly recommend learning AJAX properly - it won't take you ages but will help you understand what you can and can't do with it.
Updating a DB from a web page via AJAX is very common. I would suggest simplifying your JavaScript development using jQuery (a JavaScript library). There is a good introduction to jQuery and AJAX here.
Basically what jQuery will do is write a lot of the boilerplate code for you. What you will end up writing is something like this:
function updateScore(answer, correct) {
if (answer == correct) {
$.post('updatescore.php');
}
}
...
<a onclick="updateScore(this, correct)" ...> </a>
What you're doing here is sending a POST request to updatescore.php when the answer is correct.
Then, in your updatescore.php, you just need to have PHP code like you already do which will update the score in the database.
You can obviously do many more complicate things than this, but hopefully that will be enough to get you started.
I noticed you have "window.location.reload(true);" in your code. Why? That seems like it would make things not work.
You should try to analyze your program to find out where the problem is happening. Then you will be able to ask us a very specific question like "why does Firefox not fire the onClick handler when I click on this link" instead of just posting three pages of code. When you paste so much code, it's pretty hard for us to find your bug.
So here are the questions you should ask:
Is my HTML being parsed correctly? To me, it looks like it might not be parsed correctly because you did not put quotes around the value of onClick. You should use quotes, like: onClick="..." To find out if your HTML is being parsed nicely, you can use Firefox's View->Source feature and look at the colors it prints.
Is my onClick handler getting called? It looks like you are using alert()'s effectively so that's good.
Does the request actually get sent to my server? To determine this, you should use Firefox, and install the Firebug extension. In the "Net" panel, it will show you all the AJAX requests that are being made by your page, and it will show you the results that were returned from the server.
Is the script on my server doing the right thing? So on the server side, you can now add lines like "echo 'hello world';" and you will see that output in the Firebug Net panel, which will help you debug the behavior of your server-side script.
Is my stateChanged function getting called? Once again, use alert() statements, or write to Firebug's debug console.
Once you've narrowed your problem down, try to reduce your code to the simplest possible code that still fails. Then show us the code and tell us exactly what the symptoms of the error are.
On another note, I recommend getting this book: Javascript: The Deinitive Guide, 5th Edition by O'Reilly. It covers lots of cool stuff like AJAX and closures. It costs $50 but it's definitely a good investment because it explains things in a much more coherent way then you'll ever get from free websites.