Wednesday 21 December 2016

php - How to display loading percentage and how to do it without javascript?



I want to make something similar to loaders in PHP
so I used this code:




$x=1;
while($x<=100) {
echo "Loading: $x %
";
$x++;
}
?>


So that it would display from "Loading 1%" to "Loading 100%".

But this will result in all appearing one at a time without disappearing after the new line appears. So I want to know how to make the new line appear and the old disappear and this starts after the page loads, so the user will be able to watch a loader actually loading from 1% to 100%.



UPDATE: I know I should use JS and/or Ajax to achieve it, I just wanted to know if there's a way to also do it in PHP :)


Answer



To follow long-running tasks is common but not really easy to implement the first time. Here is a complete example.



a sample of long-running task
(a sample of long-running task in a Sellermania's product)






The task



Imagine you currently have the following task, and want to display a progress bar to your visitors.



PHP task.php



  
$total_stuffs = 200;

$current_stuff = 0;
while ($current_stuff < $total_stuffs) {
$progress = round($current_stuff * 100 / $total_stuffs, 2);

// ... some stuff
sleep(1);

$current_stuff++;
}



The UI



Your beautiful UI looks like this:



a beautiful ui



HTML ui.html








My Task!



Run task

Progression: XX%










The launcher



We need to launch the task in background, in order to make this demonstration relevant. So, this php script will be called asynchronously when clicking "run", and will execute the task above in background.




PHP task-launch.php




// launches the task in background
// see https://stackoverflow.com/a/12341511/731138 for details about the arguments
exec("/usr/bin/php task.php > /dev/null 2>&1 &");








problems



There are 3 problems here:




  1. you can run the task more than once by clicking several times the run button, how to avoid several tasks in the background at the same time?


  2. the task is ran at server side, so we need to do something to access the server and ask for progression information.



  3. and when we will be connected to the server side, the $progress variable is unavailable for reading, because it is stored within the context of the task.php running instance.









solutions



Store progression information into something readable from outside




Usually, progression information are stored into a database, or a file, or whatever that can be writtable by a program (your task actaully), and readable by another (your ui where progression should be displayed).



I developped a class for sharing data within several php applications (available on github here), it works about the same way as stdClass but always securely synchronize its contents into a file.



Just download src/Sync.php and change the task.php above by:



PHP task.php





require("Sync.php");

$total_stuffs = 200;
$current_stuff = 0;

$shared = new Sync("some_file.txt");
$shared->progress = 0;

while ($current_stuff < $total_stuffs) {

$shared->progress = round($current_stuff * 100 / $total_stuffs, 2);

// ... some stuff
sleep(1);

$current_stuff++;
}

// to tell the follower that the task ended
$shared->progress = null;



Important note: here, some_file.txt is where are stored your task's shared data, so don't hesitate to use "task_[user_id].txt" for example if each user has its own task. And look at the readme on github to optimize file access.



Use the synchronized variable to protect the task launcher




  • The progression is set to 0 at the beginning of the task, so the first thing to do is to check, before running the task, that this progression is not set to 0.




PHP task-launch.php




require("Sync.php");
$shared = new Sync("some_file.txt");

if (is_null($shared->progress)) {
exec("/usr/bin/php task.php > /dev/null 2>&1 &");
}




  • If the run button is clicked twice very quickly, we can still have 2 instances of the task. To handle this case, we need to simulate a mutex, in a word, make the variable only available to the current application to do some stuff - other applications will stay sleeping until the shared variable is unlocked.



PHP task.php





require("Sync.php");

$total_stuffs = 200;
$current_stuff = 0;

$shared = new Sync("some_file.txt");

// here is the magic: impossible to set the progression to 0 if an instance is running
// ------------------
$shared->lock();

if (!is_null($shared->progress))
{
$shared->unlock();
exit ;
}
$shared->progress = 0;
$shared->unlock();
// ------------------

while ($current_stuff < $total_stuffs) {

$shared->progress = round($current_stuff * 100 / $total_stuffs, 2);

// ... some stuff
sleep(1);

$current_stuff++;
}

// the task ended, no more progression
$shared->progress = null;



Warning: if your task crashes and never reach the end, you'll never be able to launch it anymore. To avoid such cases, you can also store the child's getmypid() and some time() stuffs inside your shared variable, and add a timeout logic in your task.



Use polling to ask the server progression information



Polling stands for asking for progression information to the server every lapse of time (such as, 1 sec, 5 secs or whatever). In a word, client asks progression information to the server every N secs.




  • at server-side, we need to code the handler to answer the progression information.




PHP task-follow.php




require("Sync.php");

$shared = new Sync("some_file.txt");


if ($shared->progress !== null) {
echo $shared->progress;
} else {
echo "--"; // invalid value that will break polling
}



  • at client-side, we need to code the "asking progression information to the server" business




HTML ui-polling.html







My Task!




Run task

Progression: XX%










it works!









I also developed a jquery plugin, domajax, intended to do "ajax without javascript" (in fact, the plugin itself is in jQuery, but using it do not require JavaScript code), and by combining options you can do polling.



In our demonstration:




  • the follower becomes:



PHP task-follow.php





require("Sync.php");

$shared = new Sync("some_file.txt");

if ($shared->progress !== null) {
echo $shared->progress;
}




  • the UI source becomes:



HTML ui-domajax.html








My Task!



href="#"
id="run-task"
class="domajax click"

data-endpoint="task-launch.php"
data-domajax-complete="#polling"
>Run task

id="polling"
data-endpoint="task-follow.php"
data-delay="1000"
data-output-not-empty="#task-progress-pct"
data-domajax-not-empty=""

>


Progression: --%









As you can see, there is no visible javascript at all in this code. Clean isn't it?



Other examples are available on domajax's website, look at the "Manage progress bars easily" tab in the demo pane. All options are heavily documented for details.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...