Tutorials‎ > ‎

Web Chat Application using Long-Polling Technology with PHP and AJAX

posted Jul 20, 2013, 12:40 AM by Arden Sagiterry Setiawan   [ updated Aug 15, 2016, 11:39 PM by Surya Wang ]

Introduction
     AJAX allows programmer to create web applications which can interact with database in real time. Nevertheless, for applications which requires constant updates in small interval, like chatting application, programmer would need to make multiple AJAX connection for each update. It can result in decreased performance for the server and introduce delay time between updates.
     Long-Polling is one of the push technology which allows client to receive data from server when it is, rather than asking for update by each interval. As a result, the number of connection needed is reduced because the connection is terminated only when server has something new to give.
     This tutorial will introduce the usage of Long-Polling technology with PHP and AJAX. In the end of this tutorial, we will end up with a simple, yet functional web chat application.

Requirements
  • PHP-compatible web server, for example: Apache webserver
  • PHP engine
  • MySQL database engine
  • Latest JQuery plugin - Get it here
  • Web Browser
  • Text Editor, I recommend Notepad++
For development purpose, you can use XAMPP for ease of installation. The package includes Apache webserver, PHP engine, and MySQL database engine.

Chat Page
     First we are going to create the web  page for our chatting application. Use your preferred text editor and create a web page called chatterFront.html. The structure is as follows.

<!DOCTYPE HTML>
<html>
<head>
     <title>Chatter: Long-Polling AJAX Chatting System</title>
     <link href="chatterStyle.css" rel="stylesheet" type="text/css" />
     <script type="text/javascript" src="jquery-1.10.2.min.js"></script>
     <script type="text/javascript" src="chatterScript.js"></script>
</head>
<body>
     <ul id="chatMessageList" class="chatMessageList"></ul>
     <form action="chatterEngine.php" method="post" id="formPostChat">
          <fieldset>
                <label for="postUsername">Username</label>
                     <input type="text" id="postUsername" />
                </fieldset>
                <fieldset>
                      <textarea id="postText"></textarea>
                </fieldset>
                <fieldset>
                     <input type="submit" value="Reply" />
                     <span class="errorMessage" id="postError"></span>
                </fieldset>
      </form>
</body>
</html>

This is the place where we are going to do the chat. Notice that some elements have id and class associated with them. Class is used for styling, while id is used to fetch the element in our script later on.

Chat Page Styling
     The chat page is still a bare HTML. To give more appeal, we will give it styling using css. Note that in above code I already put a reference link to a css. Create a new file in your text editor and save it as chatterStyle.css. Put the code below.

body{
        font-family:Arial, Helvetica, Sans-serif;
        font-size:11px;
}

ul.chatMessageList{
        display:block;
        padding:10px;
        margin:0 auto;
        margin-bottom:20px;
        list-style:none;
        width:500px;
        height:320px;
        border:1px solid #808080;
        overflow-y: auto;
}

ul.chatMessageList li.chatMessage{
        display:block;
        padding:6px;
        background-color:#dadaff;
        margin-bottom:10px;
}

ul.chatMessageList li.chatMessage span{
        font-weight:bold;
        display:block;
}

ul.chatMessageList li.chatMessage p{
        margin:0px;
}

form{
        display:block;
        width:500px;
        margin:0 auto;
        padding:10px;
        border:1px solid #808080;
}

fieldset{
        margin:0px;
        border:0px;
        padding:5px 0px;
}

fieldset textarea{
        width:400px;
        height:100px;
}

fieldset input[type=submit]{
        padding:5px;
        cursor:pointer;
}

.errorMessage{
        color:#f21212;
}

The style only provides something to make our web beautiful, it is unnecessary to the application. But at least it made our application more acceptable to users.

Chat Message Database
     To store our chat messages so our application can fetch it, we need to create a database.
  1. Access MySQL. You can do it easily using PHPMyAdmin if it is installed in your system.
  2. Create a database. You can use any name, but for this tutorial I will use chatter as its name.
  3. Create a table in the database. Use this SQL script.
CREATE TABLE cht_chat(
        messageId BINARY(32) PRIMARY KEY NOT NULL,
        username VARCHAR(300) NOT NULL DEFAULT 'Anonymous',
        text VARCHAR(300) NOT NULL DEFAULT '',
        insertDate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)

Back End PHP
    This is our PHP back end which will be called by our AJAX script. It is responsible to post new message and retrieve messages. To make it, create a new file and save it as chatterEngine.php. Put this code in your file,

<?php
        class Chatter{
                //change this according to your database setup
                protected $server = 'localhost';
                protected $username = 'root';
                protected $password = '';
                protected $database = 'chatter';

                //leave this as our database connection later
                protected $connection = null;
                public function __construct(){
  $this->connection = @mysql_connect($this->server, $this->username, $this->password);
  if($this->connection){
  if(!mysql_select_db($this->database)) die('database not found');
  }
  else die('database connection failed. Check your setup');
  $mode = $this->fetch('mode');
  switch($mode){
  case 'get':
  $this->getMessage();
  break;
  case 'post':
  $this->postMessage();
  break;
  default:
  $this->output(false, 'Wrong mode.');
  break;
  }
  return;
                }
                protected function getMessage(){
  $endtime = time() + 20;
  $lasttime = $this->fetch('lastTime');
  $curtime = null;
  while(time() <= $endtime){
  $rs = mysql_query("
  SELECT *
  FROM cht_chat
  ORDER BY insertDate desc
  LIMIT 0, 30
  ");
  if($rs){
  $messages = array();
  while($row = mysql_fetch_array($rs)){
  $messages[] = array(
  'user' => $row['username'],
  'text' => $row['text'],
  'time' => $row['insertDate']
  );
  }
  $curtime = strtotime($messages[0]['time']);
  }
  if(!empty($messages) && $curtime != $lasttime){
  $this->output(true, '', array_reverse($messages), $curtime);
  break;
  }
  else{
  sleep(1);
  }
  }
                }
                protected function postMessage(){
  $user = $this->fetch('user');
  $text = $this->fetch('text');
  if(empty($user) || empty($text)){
  $this->output(false, 'Username and Chat Text must be inputted.');
  }
  else{
  $rs = mysql_query("
  INSERT INTO cht_chat(
  messageId,
  username,
  text,
  insertDate
  )
  VALUES(
  uuid(),
  '$user',
  '$text',
  CURRENT_TIMESTAMP
  )
  ");
  if($rs){
  $this->output(true, '');
  }
  else{
  $this->output(false, 'Chat posting failed. Please try again.');
  }
  }
                }
                protected function fetch($name){
  $val = isset($_POST[$name]) ? $_POST[$name] : '';
  return mysql_real_escape_string($val, $this->connection);
                }
                protected function output($result, $output, $message = null, $latest = null){
  echo json_encode(array(
'result' => $result,
'message' => $message,
'output' => $output,
'latest' => $latest
  ));
                }
  }
  new Chatter();

Explanation:
  • We use class to make sure that our function will not interfere with other PHP functions.
  • First we initialize basic parameter to call for connecting to database
  • For constructor, first we connect to the database. If failed then we terminate the script.
    • We also take $mode variable from POST request to determine whether we should get messages or post new message to database
  • getMessage() is used for getting message using Long-Polling method.
    • First we determine the amount of time before we put the script on timeout and restart connection. For this example we use time() + 20, which means 20 seconds after the current time when the call is made. We also take $lasttime variable from POST request given by our script later on. Because it is our first call, set $curtime to null.
    • While there is still time before timeout, we make the query to fetch newest 30 messages from database, ordered by the latest message first. We then took the latest time into $curtime variable.
    • If $curtime and $lasttime is not same, then there is new messages to show. We output the data and break the looping, making our script to initiate new connection.
    • Else we will put the script to sleep for 1 second and redo the query until new data is fetch or timeout reached. This is the essence of Long-Polling
  • postMessage() is used to post new message to database. We just use INSERT query to put our data to database.
  • fetch() is an utility function to get value from POST request.
  • output() is an utility function to display json data so our script can recognize them as result.
AJAX Script
     This is the main part which connect all components above. For the code to work you need JQuery plugin. At the current time when the tutorial is made, the latest stable version is 1.10.2. You can change the link in html page above if you take newer or older JQuery version.
     For our script, open new file and save it as chatterScript.js. Put the code here.

function Chatter(){
  this.getMessage = function(callback, lastTime){
  var t = this;
  var latest = null;
  $.ajax({
  'url': 'chatterEngine.php',
  'type': 'post',
  'dataType': 'json',
  'data': {
  'mode': 'get',
  'lastTime': lastTime
  },
  'timeout': 30000,
  'cache': false,
  'success': function(result){
  if(result.result){
  callback(result.message);
  latest = result.latest;
  }
  },
  'error': function(e){
  console.log(e);
  },
  'complete': function(){
  t.getMessage(callback, latest);
  }
  });
  };
  this.postMessage = function(user, text, callback){
  $.ajax({
  'url': 'chatterEngine.php',
  'type': 'post',
  'dataType': 'json',
  'data': {
  'mode': 'post',
  'user': user,
  'text': text
  },
  'success': function(result){
  callback(result);
  },
  'error': function(e){
  console.log(e);
  }
  });
  };
};

var c = new Chatter();

$(document).ready(function(){
  $('#formPostChat').submit(function(e){
  e.preventDefault();
  var user = $('#postUsername');
  var text = $('#postText');
  var err = $('#postError');
  c.postMessage(user.val(), text.val(), function(result){
  if(result){
  text.val('');
  }
  err.html(result.output);
  });
  return false;
  });
  c.getMessage(function(message){
  var chat = $('#chatMessageList').empty();
  for(var i = 0; i < message.length; i++){
  chat.append(
  '<li class="chatMessage">' +
  ' <span class="chatUsername">' + message[i].user + '</span>' +
  ' <p class="chatText">' + message[i].text + '</p>' +
  '</li>'
  );
  }
  $('#chatMessageList').scrollTop($('#chatMessageList')[0].scrollHeight);
  });
});

Explanation:
  • We create a new javascript class Chatter as the namespace so our function will not interfere with other javascript libraries (if present).
  • getMessage() is used to fetch new messages. It is simply an AJAX call to our chatterEngine.php using 'get' as the mode in POST request. We also put lastTime inside the POST request, which we get from our previous call.
  • postMessage() is used to post new message to chatterEngine.php using 'post' as the mode in POST request. From there, the PHP script will put the data to database.
  • We initialize the class as an object so we can use it in our script.
  • After the document is ready, we put event handler on our form so when it is submitted it will use AJAX to post the value rather than the usual html post routine.
  • Finally, we fetch new messages immediately with getMessage. We put a callback function inside it so that the result will be displayed by appending them to our list element.

In Action
     This is an example screenshot of the application in action. Several web pages can access it at the same time so it can function as a chat room.


Conclusion
     This tutorial showed a simple way to utilize Long-Polling for real-time applications. There are many rooms to improve upon the code. The concept is to hold the connection as long as it can until a new data arrives, thus minimizing the number of connections needed. This technique is heavy for the web server, and I recommend to use a dedicated server for this kind of application rather than general-purpose one like Apache.
ċ
Chatter.zip
(35k)
Arden Sagiterry Setiawan,
Jul 20, 2013, 12:46 AM