limi.eu

A progress bar in browser showing progress of long-lasting PHP function

A progress bar in browser showing progress of long-lasting PHP function

It uses the HTML5 EventSource interface to receive server-sent events (push notifications) from a PHP script running on the web server. Also the CSS styling of a HTML progress bar is shown.

How it looks like:

Press "Start" to watch the progress bar in action.

How it is done:

test.html:

<!doctype html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script src="script.js"></script>
</head>
<body>
    <div>Press "Start" to watch the progress bar in action.</div>

    <div class="button_left">
        <button onclick="start_task();">Start</button>
    </div>

    <div>
        <progress id="progress1" value="0"></progress>
        <div id="status1" style="min-height:20px;"></div>
    </div>
</body>
</html>

script.js:

var source = "";

function start_task() {
    // define the EventSource object and the
    // push notifications sending server side script
    var php_file = "test.php";
    source = new EventSource(php_file);

    // reset the progress bar to zero
    document.getElementById("progress1").value = 0;
    // write a status message
    document.getElementById("status1").innerHTML(" Please wait ...");

    // a message is received
    source.addEventListener("message", function(e) {
        // data is JSON encoded on the server
        var result = JSON.parse(e.data);

        // set progress bar and status message according to received value
        document.getElementById("progress1").value = result.progress/100;
        document.getElementById("status1").innerHTML(result.progress+" %");

        if(result.message == "TERMINATE") {
            source.close();
            document.getElementById("progress1").value = 1;
            document.getElementById("status1").innerHTML("100 % - Ready!");
        }
    }, false);

    // an error is received
    source.addEventListener("error", function(e) {
        document.getElementById("status1").innerHTML("UUps: There was an error!");
        source.close();
    }, false);
}

test.php:

<?php
// try to prevent php timeout
ini_set("max_execution_time", "1000");

// make sure apache does not gzip this type, else it would get buffered
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache');

function sendMsg($id, $message, $progress) {
    $d = array("message"=>$message, "progress"=>$progress);
    echo "id: $id" . PHP_EOL;
    echo "data: " . json_encode($d) . PHP_EOL;
    echo PHP_EOL;

    // push the data out by all force
    ob_flush();
    flush();
}

$i=0;
// usleep simulates a long-lasting preparing function (i.e. SELECT JOIN etc.)
usleep(1000000); // wait 1s

// the long lasting function we want to watch the progress
$big_number = 10000;
while ($i < $big_number) {
    // usleep simulates a long-lasting function (i.e. INSERT/UPDATE, big calculation etc.)
    usleep(200);
    sendMsg(++$i, "", $i/$big_number*100);
}

// ready message
sendMsg(++$i, "TERMINATE", 100);

?>

style.css:

/* Reset the default appearance */
progress {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
}
/* webkit-browser: z.B. Chrome, Opera (>12) */
progress::-webkit-progress-bar {
    background: #EFEFEF;
    border-radius: 5px;
}
progress::-webkit-progress-value {
    background-color: #C5A000;
    border-radius: 5px;
}
/* Firefox */
progress::-moz-progress-bar {
    background-color: #C5A000;
}
/* all browsers */
progress {
    background: #EFEFEF;
    border-radius: 5px;
    border: 1px solid #C0C0C0;
    width: 300px;
    height: 10px;
    color: #C5A000 /* only IE10+ needs this */;
}
.button_left {
    float: left;
    padding-right: 20px;
}