I try to make some kind of progress indicator for a simple php loop.
There are a lot of ajax / php questions here regarding progress and php loops, but after reading a lot of them, I still can not implement (nor understand exactly ) the logic..
Most of them are either talking about -upload- progress bar or are too localized to a specific case .( either that - or I do not know how to choose the right keywords to search.)
My final goal is to make a progress bar, but to start, I tried a little echo loop.
I tried the following :
HTML form :
<p class="submit">
<input type="submit" class="button-primary" id="o99_sudi_do_now" value="<?php _e('DO IT NOW !', 'o99_sudi_domain'); ?>" />
<img src="<?php echo admin_url('/images/wpspin_light.gif'); ?>" class="waiting" id="o99_sudi_loading" style="display:none;"/>
</p>
<div id="o99_sudi_results"></div>
jQuery
jQuery(document).ready(function($){
jQuery('#o99_sudi_do_now_form').click(function(e){
e.preventDefault();
jQuery.post(ajaxurl,{action:'o99_random_loop'}, function(data){
jQuery('#o99_sudi_results').empty().append( data );
});
});
});
PHP
function o99_random_loop_cb(){
for ($i = 0; $i < 10; $i++) {
//ob_end_clean(); // [TESING]
echo '-- step' .$i . ' of 10</br>';
//flush();// [TESING]
//usleep(100000000000*0.1); // total time 10 sec.// [TESING]
//ob_flush();// [TESING]
usleep(20000);
}
die();
and since it is all in a wordpress admin area -
add_action( 'wp_ajax_o99_random_loop', 'o99_random_loop_cb' );
the result (or output) is :
-- Step 0 of 10
-- Step 1 of 10
-- Step 2 of 10
-- Step 3 of 10
-- Step 4 of 10
-- Step 5 of 10
-- Step 6 of 10
-- Step 7 of 10
-- Step 8 of 10
-- Step 9 of 10
which is kind of ok , but the problem is that the output arrives and the div is updated only when the execution is finished - and I want it to be updated on-the-fly ,step by step... (my real planned loop is quite long and will take about 2-3 seconds for iteration. )
Like you can see with the code, i have tried flush(); , ob_flush(); and any other suggestion that I have seen , but I suspect that the LOGIC is wrong here (because it is wordpress?? or flushing in wrong place ?) and actually , no matter how much I flush(); - the result arrive only when the function is ENDING ..
My goal is to make a progress bar - but for now even help with a simple echoing progress will be great ..
You can't achieve what you want with a single POST to PHP. Even if you flush your PHP output, the jQuery side won't process the result handler until the entire PHP process is complete. Long ago you could get something like this behavior with only some browsers (Netscape, etc) that would start displaying content before the document was complete, however, with AJAX you won't see any of the resulting content from PHP until the PHP script is finished.
Hence the reason you are getting all your output at once.
There are a few possible ways to accomplish what you want to do. One method is to break your PHP loop into stages so that only a portion of the loop is executed each time. You could then execute a few iterations (or even one iteration) and return that result to the browser. The browser would then display the result and call to PHP again for the next iteration (or set of iterations). While this would give you your progress indicator it would dramatically slow down your overall operation and is not recommended.
EDITED to remove use of SESSION which won't work due to concurrency issues:
Another option is to have PHP save the progress of your loop to a database and have the browser side regularly poll another PHP script that checks the database to determine the progress. On a quick loop this won't be all that helpful, but on a longer running loop this could be workable. So along with your $.post call you'll start a series of timed $.get calls to check the progress.
EDIT:
Here are a few snippets to illustrate:
// variable to control polling logic
var pollInterval;
// Your current post logic here with the addition of a variable to stop execution of the polling process
jQuery.post(ajaxurl,{action:'o99_random_loop'}, function(data){
// post is complete so stop execution
window.clearInterval(pollInterval);
});
// Now start a get process to get the progress every 5 seconds
pollInterval = window.setInterval(function () {
// I'm assuming pollingurl is the URL to your PHP script that checks the progress
jQuery.get(pollingurl, function(data){
jQuery('#o99_sudi_results').empty().append( data );
});
}, 5000);
I'll assume for now that on the PHP side you are comfortable saving your progress to a database table and retrieving said progress from the table.
Related
I am writing a script that gets an eight digit number then reads it out loud. I want a delay of two seconds between the numbers as they are being read out. The problem is that even after setting the delay with sleep(), all my numbers are read out at once which leads to a summon-like audio that is getting me closer to being the antichrist with every debug. Here is my code. How do i get my loop to execute so that the numbers are read as e.g 2 .... 1 .... 3 .... 4 .... (where .... = 2 seconds.)
for($counter=0; $counter<count($dbId); $counter++)
{
$fileName = array_search($dbId[$counter], $letterArray);
echo "<audio src='../resources/audio/mp3/'.$fileName.'.mp3' autoplay></audio>";
echo time() . "<br />";
sleep(2);
}
You have an incorrect understanding of sleep. Sleep delays the php script, which delays the
response from the server, but the resulting webpage that you serve will be exactly the same
with or without sleep. It will just take longer to load. You can not control this from php
you have to do it on the client somehow. Look at this question for example.
PHP is a hypertext pre processor. Meaning it's output cannot be send intermittently to the browser.
You have two options.
Create all audio elements without autoplay and handle your desired functionality with javascript using setInterval
Make a series of AJAX requests in order to get the next audio element enabling the autoplay feature. These requests can be fired by either listening for the audio to finish, or again using setInterval
My php script uses php simplehtmldom to parse html and get all the links and images that I want and this can run for a duration depending on the amount of images to download.
I thought it would be good idea to allow cancelling in this case. Currently I call my php using Jquery-Ajax, the closest thing I could find is php register_shutdown_function but not sure if it can work for my case. Any ideas?
So once php is launched, it cant be disturbed? like fire ajax again to call an exit to the same php file?
This is good only in case you are processing really massive data loads through AJAX. For other cases, just handle it in JS to not display result if canceled.
But as I said If you are processing huge loads of data, then you can add a interrupt condition in every nth step of running script and fulfill that condition using another script. For example you can use a file to store a interrupt data, or MySQL MEMORY table.
Example.
1, process.php (ajax script processing loads of data)
// clean up previous potential interrupt flag
$fileHandler = fopen('interrupt_condition.txt', 'w+');
fwrite($fileHandler, '0');
fclose($fileHandler);
function interrupt_check() {
$interruptfile = file('interrupt_condition.txt');
if (trim($interruptfile[0]) == "1") { // read first line, trim it and parse value - if value == 1 interrupt script
echo json_encode("interrupted" => 1);
die();
}
}
$i = 0;
foreach ($huge_load_of_data as $object) {
$i++;
if ($i % 10 == 0) { // check for interrupt condition every 10th record
interrupt_check();
}
// your processing code
}
interrupt_check(); // check for last time (if something changed while processing the last 10 entries)
2, interrupt_process.php (ajax script to propagate cancel event to file)
$fileHandler = fopen('interrupt_condition.txt', 'w+');
fwrite($fileHandler, '1');
fclose($fileHandler);
This will definitely affect performance of your script, but makes you a backdoor to close execution. This is very simple example - you need to make it more complex to make it work for more users simultaneously, etc.
You can also use MySQL MEMORY Table, MEMCACHE - Non-persistent Caching Server or whatever non-persistent storage you could find.
I have a foreach loop that calls a function to set values to an array. Sometimes it takes hours to complete depending on how many times it has to run thru the function to complete.
What I would like to have is a progress bar or at least a 1/1000 completed type progress indicator.
Is this possible? If so how could I implement this into my code? Would it be in the function or in the foreach loop? Been researching and found some examples using for and $i++ but I am not really sure how to implement that since I am already using a foreach loop.
Thanks much.
function scrape_amazon($links) {
//my code runs here to set all values in $ret array.
}
foreach($links as $link) {
$ret = scrape_amazon($link);
}
PHP probably isn't really the right tool for this task, however what you could do is:
Launch the slow code as a background process, and output progress to a file.
Have a PHP script that polls that file for progress information (either by page refresh or AJAX)
Launching the background process can be done in several ways, including:
Launch via cron every 60 seconds, and poll for new jobs spooled in some readable area
Launch via a fork/exec mechanism from a web page
Launch as a daemon at system startup
It will take some effort to avoid problems with multiple executions and/or overlap.
I use this, which well, not an ajax, do only flushing, but not so ugly.
I place an image
<img src='progress.gif' height=18 width=0 name=probar>
Then set on every event done on server a echo a line, then flush:
echo "<script language='JavaScript'>\ndocument.probar.width=".(($sys["probar_width"]/$task_all)*$task_i).";\n</script>\n";
flush();
If your server (eg. apache) use caching (eg. gzip is enabled) it won't work well.
Ok here is my problem.
I have a file which outputs an XML based on an input X
I have another file which calls the above(1) file with 10000 (i mean many) times with different numbers for X
When an user clicks "Go" It should go through all those 10000 Xs and simultaneously show him a progress of how many are done. (hmm may be updated once every 10sec).
How do i do it? I need ideas. I know how to AJAX and stuff, but whats the structure my program should take?
EDIT
So according to the answer given below i did store my output in a session variable. It then outputs the answer. What is happening is:
When i execute a loong script. It gets executed say within 1min. But in the mean time if i open (in a new window) just the file which outputs my SESSION variable, then it doesnt output will the first script has run. Which is completely opposite to what i want. Whats the problem here? Is it my syste/server which doesnt handle multiple requests or what?
EDIT 2
I use the files approach:
To read what i want
> <?php include_once '../includeTop.php'; echo
> util::readFromLog("../../Files/progressData.tmp"); ?>
and in another script
$processed ++;
util::writeToLog($dir.'/progressData.tmp', "Files processed: $processed");
where the functions are:
public static function writeToLog($file,$data) {
$f = fopen($file,"w");
fwrite($f, $data);
fclose($f);
}
public static function readFromLog($file) {
return file_get_contents($file);
}
But still the same problem persist :(. I can manually see the file gettin updated like 1, 2, 3 etc. But when i run my script to do from php it just waits till my original script is output.
EDIT 3
Ok i finally found the solution. Instead of seeking the output from the php file i directly goto the log now and seek it.
Put the progress (i.e. how far are you into the 2nd file) into a memcached directly from the background job, then deliver that value if requested by the javascript application (triggered by a timer, as long as you did not reach a 100%). The only thing you need to figure out is how to pass some sort of "transaction ID" to both the background job and the javascript side, so they access the same key in memcached.
Edit: I was wrong about $_SESSION. It doesn't update asynchronously, i.e. the values you store in it are not accessible until the script has finished. Whoops.
So the progress needs to be stored in something that does update asynchronously: Memory (like pyroscope suggests, and which is still the best solution), a file, or the database.
In other words, instead of using $_SESSION to store the value, it should be stored by memcached, in a file or in the database.
I.e. using the database
$progress = 0;
mysql_query("INSERT INTO `progress` (`id`, `progress`) VALUES ($uid, $progress)");
# loop starts
# processing...
$progress += $some_increment;
mysql_query("UPDATE `progress` SET `progress`=$progress WHERE `id`=$uid");
# loop ends
Or using a file
$progress = 0;
file_put_contents("/path/to/progress_files/$uid", $progress);
# loop starts
# processing...
$progress += $some_increment;
file_put_contents("/path/to/progress_files/$uid", $progress);
# loop ends
And then read the file/select from the database, when requesting progress via ajax. But it's not a pretty solution compared to memcached.
Also, remember to remove the file/database row once it's all done.
You could put the progress in a $_SESSION variable (you'll need a unique name for it), and update it while the process runs. Meanwhile your ajax request simply gets that variable at a specific interval
function heavy_process($input, $uid) {
$_SESSION[$uid] = 0;
# loop begins
# processing...
$_SESSION[$uid] += $some_increment;
# loop ends
}
Then have a url that simply spits out the $_SESSION[$uid] value when it's requested via ajax. Then use the returned value to update the progress bar. Use something like sha1(microtime()) to create the $uid
Edit: pyroscope's solution is technically better, but if you don't have a server with memcached or the ability to run background processes, you can use $_SESSION instead
i have a script which output the percentage of completeness : 10% 20%...100% done.
Can i bring that output to the web?
I call it using jquery ajax:
$.post('dojob.php?id=jobid');
thanks Arman
If I'm understanding what you want, you're looking to call dojob.php and have it return it's progress at certain intervals. This can't be done with one ajax call.
If you need to do this, you will have to call dojob.php but then every x seconds make a separate call to somehow monitor it's progress. For instance, lets say you have checkStatus.php which can give you the progress of dojob. So you call dojob.php, then every 5 seconds call checkStatus.php which will return your % complete of dojob.
Simiarly, you could call dojob and have it kick off a separate thread and then return. THen every x seconds call checkStatus.php, which will either return the % complete, or upon completion return the result of dojob.
to simply write it, try the following code
html:
<div id="progress"></div>
js code:
$('#progress').load('dojob.php?id=jobid');