PHP & JavaScript World Domination Series: Storing data in the client.

One of the things that all programmers love is to write a program which writes another program. On the web we have two different programming environments: the client (browser) and the server. Due to the HTTP protocol definition we can write a program on the server which writes another program to be executed on the client. Let's pick PHP (of course) for the server and JavaScript for the client. We'll show you in this article how you can use this scheme to store data in the client and then minimize the data transfered between the server and the browser for interactive applications like a chat room, a news system or whatever you want.

Ingredients:

The Idea:

We have been trying to develop an HTTP chat room in PHP for a while. HTTP is not a good protocol for chatting but it is firewall/proxy immune, can use the potential of PHP and there's no need for Java Applets. We have had two main problems with the chat room: first since IE doesn't support the PUSH method, we needed to make it a "pull" application, (the client refreshes itself) which is unnatural for a chat application. We decided to make the client refresh time variable, the server will generate the refresh time as a function of the number of messages received in the server in the last x minutes. The second problem was deeper: since the idea is to refresh the client we would need the server to send all the messages each time, we observed that this implied heavy transfers and our benchmarks in a simulated simplified chat room model showed that this was the main cause of delays in the chat. This article deals with that second and deeper problem.

PHP & JavaScript World Domination Series: Storing data in the client.

The generic model:

Using frames you can refresh a given frame without reloading the others, this is good for minimizing (c/s) transfers. Our model is based on the following scheme:

In our scheme, the loader frame refreshes itself each "x" seconds - the idea is to store data in the "master" file allowing the "loader" frame to ask the server only for data that the client has not received yet. We use timestamps to mark messages, news or transferable items and know which ones need to be transfered to the client and which ones not, we use PHP4 sessions to store the "last timestamp" in the client so it is visible in the server. When the "loader" receives data it is stored in the "master" file (note that "master" is heavy but it is transfered only once) then it makes the display frame refresh. To be even more optimum we make the display frame as short as possible, the frame only calls a "display" JavaScript function which is obviously stored in the "master" file, this function uses the data stored in "master" to dynamically draw the frame. Let's review the method:

  1. A "browser" requests the "master" file. (frameset) The master file is transfered from the server, it defines the frameset and then the frames ("loader" and "display") are transfered.
  2. The loader frame is parsed in the server, if the client has no "timestamp" session var it gets all the data from the server and generates JavaScript code to store the data in "master". Then we set the "timestamp" session variable.
  3. The loader frame then generates JavaScript code to make the client refresh the "display" frame.
  4. The refresh causes the "display" frame to call a "display" function which generates the frame from the data.
  5. Each "x" seconds we go back to (2)

We can analyze the method as following:

We have 3 files:

"master"(very heavy, with display function and storage variables and initial values)
"loader"(light, php code to retrieve data from the server and write JS code)
"display"(VERY light, just a call to a display function in "master")

Master is transfered only once.
Loader and display are transfered each "x" seconds.
Loader may be big the first time it is called, then it is a very short file since only the data that the client has not received is transfered.
Display is always the same.
The "nice" display generation is processed in the client so we reduce the server load.

Are you confused? Let's see an example:

In this example we build a chat room which is not usable yet, we just show how to implement this model, please don't say "why don't you do this and that to the chat room...". You can build a chat room as complex as you want using this model if you find it useful, and remember it is not only useful for chatrooms.

PHP & JavaScript World Domination Series: Storing data in the client.

Action!

Please create a test database with mysql (mysqladmin create testbase) and then create a table with:

create table testeable (
  timestamp datetime,
  message	text
);

This is the "master" file:

<script>
lines=new Array();
function displaymsgs() {
  for(i=0;i<lines.length;i++) {
	 display.document.write(lines[i]);
	 display.document.write('<BR>');
  }
}
</script>
<frameset cols="1" rows="20,60,20" border="0">
<frame name="loader" src="loader.php">
<frame name="display" src="display.php">
<frame name="form" src="form.php">
</frameset>

Note that we have the loader frame, the display frame and a new frame called "form", this is the form we use to send messages to the chat room. Note the simple "display" function, you can use whatever you want here, colors, dynamic colors, user preferences, html tables, images, etc etc.

The display frame is:

<script>
top.displaymsgs();
</script>

We promised it will be short :-)

PHP & JavaScript World Domination Series: Storing data in the client.

The loader frame is next:

<?php

session_start
(); // We use Sessions here!

if(!isset($timestamp)) {
    
//If no timestamp set we define it as 0
    
$timestamp=0;    
}

$dab=mysql_connect("localhost","user","password");  // Use your own values here
mysql_select_db("testbase",$dab);

// Query the messages not seen by the client
$query="select * from testeable where timestamp>'$timestamp'";
$result=mysql_query($query,$dab);
$msgs=array();

// In the loop we store the messages in an array and also get the 
// maximum timestamp
while($res=mysql_fetch_array($result)) {
    
$msgs[]=$res["message"];
    if(
$res["timestamp"]>$timestamp) {
        
$timestamp=$res["timestamp"];
    }
}
session_register("timestamp"); // Register the timestamp

echo '<script>';

// In this loop we generate JavaScipt Code to 
// Add the messages to the array in "master" (note the use of "top"
// to refer to the parent frame which is master)
for($i=0;$i<$count($msgs);$i++) {
    
?>
    top.lines[top.lines.length]="<?php print("$msgs[$i]"); ?>";
    <?php


// Now we'll generate JS code to make the display frame reload.

?>
top.display.location.reload();
</script>

<!-- Note the JS setInterval method to make this frame reload each 4 seconds -->
<body onLoad="window.setInterval('location.reload()',4000);">
</body>

PHP & JavaScript World Domination Series: Storing data in the client.

Ok, then and just for your "testing" purposes the "form" frame:

<?php

session_start
();

if (!isset(
$timestamp)) {
      
$timestamp=0;
}

// Display form, use JavaScript to get last timestamp.
if (isset($msg)) {
    
$dab=mysql_connect("localhost","root","seldon");
    
mysql_select_db("testbase",$dab);
    
$query="insert into testeable(timestamp,message) values(now(),'$msg')";
    
mysql_query($query,$dab);
    
// Now get all messages after last_time.
    
$query="select * from testeable where timestamp>'$tt'";
    
$result=mysql_query($query,$dab);
    
$msgs=array();$i=0;$timestamp=0;
    while(
$res=mysql_fetch_array($result)) {
        
$msgs[]=$res["message"];
        if(
$res["timestamp"]>$timestamp) {
            
$tt=$res["timestamp"];
        }
    }
    
session_register("timestamp");
    
// No I have the max timestamp...
    // Using JavaScript, we can set those values...
    
?>
      <script>
      <?php
         
for($i=0;$i<$count($msgs);$i++) {
             
?>
                top.lines[top.lines.length]="<?print("$msgs[$i]");?>";
             <?php
         
}
      
?>
      top.display.location.reload();
      </script>
    <?php
}

?>
<form name="foo" action="<?php print("$PHP_SELF"); ?>" method="post">
Message:<input type="text" name="msg">
<input type="submit" name="newmsg" value="send">
</form>

Note that we make the display refresh from the "form" frame this is nice to the chat user since it sees his message and the new ones as soon as he submits his text. Users like this since it adds some dynamic to the chat mechanism. The form frame is very similar to the loader as you can see.

We have presented you a powerful technique to minimize client-server transfers storing data in the client and to reduce server load making the client execute the complex display routines. You have the power, go and conquer the world.