Your message has been sent.
This article has been saved to your account.
Go to my account
This article has been emailed to your Kindle.
Send this article
Complete the form below to send this article, AJAX Chat Implementation: Part 1, to a friend (or to yourself). We will never share your details (or your friend's) with anyone. For more information, read our Privacy Policy.
In this article by Bogdan Brinzarea and Cristian Darie, we will discuss about AJAX Chat implementation in detail.
Lets get started. We'll keep the application simple, modular, and extensible. We won't implement a login module, support for chat rooms, the online users list, and so on. By keeping it simple we'll focus on what the goal of this article is: posting and retrieving messages without causing any page reloads. We'll also let the user pick a color for her or his messages, because this involves an AJAX mechanism that is another good exercise.
The chat application can be tested online at http://ajaxphp.packtpub.com, and should look like Figure 8-1:

Using jQuery as a framework will simplify things: we won't need to worry about constructing XmlHttpRequest by ourselves and implementing design patterns and best practices.
Technically, the application is split into two smaller applications that build the final solution:
- The chat application: Here we use a MySql database and AJAX to store and retrieve the users' messages and pass them between the client and the server.
- The code for choosing a text color: Here we use AJAX to call the PHP script that can tell us which text color was chosen by the user from the color palette. We use an image containing the entire spectrum of colors and allow the user choose any color for the text he or she writes. When the user clicks on the palette, the mouse coordinates are sent to the server, which obtain the color code, store it in the user's DB entry, and set the user's test to that color.
The chat application
Here we use a MySql database and AJAX to store and retrieve the users' messages and pass them between the client and the server. The chat window contacts the server periodically to send and retrieve the newest posted messages from the server to each user. Our DB will also hold username and text color information used in the application.
Implementing this functionality involves creating the files and structures shown in the following figure:

The application functions following our usual coding pattern as follows:
- The user interface is generated by index.html, which displays the chat box and the color picker. This file loads the other client-side files, which in our case are chat.js (our JavaScript chat class), jQuery-1.3.2.js (the jQuery framework), chat.css, and palette.png (the color picker image).
- On the server side, the main player is chat.php, which is designed to take requests from the client. In our case, client-server communication will happen between chat.js (on the client) and chat.php (on the server). The chat.php script uses three other files—config.php, error_handler.php and chat.class.php; we'll pay special attention to the latter, which is more complex and more interesting than the others.
- The other server-side file that listens to client requests is color.php, which is called whenever the user clicks the color palette image. When that happens, the client script calls color.php, tells it the location the user clicked on the palette, and color.php replies by telling the color at that location.
- You'll also need to create a new data table named chat (refer to the following figure), which holds the chat messages exchanged by the chatters.
The files to which we're paying a little attention before starting to code are chat.js and chat.class.php. The chat.class.php file contains a server-side class named Chat which includes all the server-side functionality required to manipulate chat messages, as you can see in its diagram in Figure 8-3. This class contains methods for adding, deleting, and retrieving chat messages to and from the chat database table.

Then we have the Chat class in the chat.js file. This is a JavaScript class that contains the client-side functionality required for our chatting application, which include functions for retrieving the list of messages from the server, sending new messages, deleting messages, displaying error messages, and so on. Most of the features are backed up by the server-side components, which are called to perform the necessary work.

Time for action – implementing AJAX chat with JSON
Follow these steps to implement your AJAX Chat application:
- First we need the database that will hold our user information. Connect to the ajax database, and create a table named chat with the following code:
CREATE TABLE chat
(
chat_id int(11) NOT NULL auto_increment,
posted_on datetime NOT NULL,
user_name varchar(255) NOT NULL,
message text NOT NULL,
color char(7) default '#000000',
PRIMARY KEY (chat_id)
); - In your ajax folder, create a new folder named chat.
- We will start creating the application with the server functionality. In the chat folder, create a file named config.php, and add the database configuration code to it (you may need to change these values to match your configuration):
<?php
// defines database connection data
define('DB_HOST', 'localhost');
define('DB_USER', 'ajaxuser');
define('DB_PASSWORD', 'practical');
define('DB_DATABASE', 'ajax');
?> - Now create and add the standard error handling file, error_handler.php:
<?php
// set the user error handler method to be error_handler
set_error_handler('error_handler', E_ALL);
// error handler function
function error_handler($errNo, $errStr, $errFile, $errLine)
{
// clear any output that has already been generated
if(ob_get_length()) ob_clean();
// output the error message
$error_message = 'ERRNO: ' . $errNo . chr(10) .
'TEXT: ' . $errStr . chr(10) .
'LOCATION: ' . $errFile .
', line ' . $errLine;
echo $error_message;
// prevent processing any more PHP scripts
exit;
}
?> - Our top-level php file, chat.php, will be calling functions that we will later define in chat.class.php (protecting our modularity). Create another file named chat.php and add the following code to it:
<?php
// reference the file containing the Chat class
require_once("chat.class.php");
// retrieve the operation to be performed
$mode = $_POST['mode'];
// default the last message id to 0
$id = 0;
// create a new Chat instance
$chat = new Chat();
// if the operation is SendAndRetrieve
if($mode == 'SendAndRetrieveNew')
{
// retrieve the action parameters used to add a new message
$name = $_POST['name'];
$message = $_POST['message'];
$color = $_POST['color'];
$id = $_POST['id'];
// check if we have valid values
if ($name != '' && $message != '' && $color != '')
{
// post the message to the database
$chat->postMessage($name, $message, $color);
}
}
// if the operation is DeleteAndRetrieve
elseif($mode == 'DeleteAndRetrieveNew')
{
// delete all existing messages
$chat->deleteMessages();
}
// if the operation is Retrieve
elseif($mode == 'RetrieveNew')
{
// get the id of the last message retrieved by the client
$id = $_POST['id'];
}
// Clear the output
if(ob_get_length()) ob_clean();
// Headers are sent to prevent browsers from caching
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
header('Content-Type: application/json');
// retrieve new messages from the server
echo json_encode($chat->retrieveNewMessages($id));
?> - Now we need to create the functionality behind chat.php. Create another file named chat.class.php, and add the following code to it:
<?php
// load configuration file
require_once('config.php');
// load error handling module
require_once('error_handler.php');
// Chat class that contains server-side chat functionality
class Chat
{
// database handler
private $mMysqli;
// constructor opens database connection
function __construct()
{
// connect to the database
$this->mMysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE);
}
// destructor closes database connection
public function __destruct()
{
$this->mMysqli->close();
}
// truncates the table containing the messages
public function deleteMessages()
{
// build the SQL query that adds a new message to the server
$query = 'TRUNCATE TABLE chat';
// execute the SQL query
$result = $this->mMysqli->query($query);
}
/*
The postMessages method inserts a message into the database
- $name represents the name of the user that posted the message
- $message is the posted message
- $color contains the color chosen by the user
*/
public function postMessage($name, $message, $color)
{
// escape the variable data for safely adding them to the database
$name = $this->mMysqli->real_escape_string($name);
$message = $this->mMysqli->real_escape_string($message);
$color = $this->mMysqli->real_escape_string($color);
// build the SQL query that adds a new message to the server
$query = 'INSERT INTO chat(posted_on, user_name, message, color) ' .
'VALUES (NOW(), "' . $name . '" , "' . $message .
'","' . $color . '")';
// execute the SQL query
$result = $this->mMysqli->query($query);
}
/*
The retrieveNewMessages method retrieves the new messages that have
been posted to the server.
- the $id parameter is sent by the client and it
represents the id of the last message received by the client. Messages
more recent by $id will be fetched from the database and returned to
the client in JSON format.
*/
public function retrieveNewMessages($id=0)
{
// escape the variable data
$id = $this->mMysqli->real_escape_string($id);
// compose the SQL query that retrieves new messages
if($id>0)
{
// retrieve messages newer than $id
$query =
'SELECT chat_id, user_name, message, color, ' .
' DATE_FORMAT(posted_on, "%Y-%m-%d %H:%i:%s") ' .
' AS posted_on ' .
' FROM chat WHERE chat_id > ' . $id .
' ORDER BY chat_id ASC';
}
else
{
// on the first load only retrieve the last 50 messages from server
$query =
' SELECT chat_id, user_name, message, color, posted_on FROM ' .
' (SELECT chat_id, user_name, message, color, ' .
' DATE_FORMAT(posted_on, "%Y-%m-%d %H:%i:%s") AS posted_on ' .
' FROM chat ' .
' ORDER BY chat_id DESC ' .
' LIMIT 50) AS Last50' .
' ORDER BY chat_id ASC';
}
// execute the query
$result = $this->mMysqli->query($query);
// build the JSON response
$response = array();
// output the clear flag
$response['clear']= $this->isDatabaseCleared($id);
$response['messages']= array();
// check to see if we have any results
if($result->num_rows)
{
// loop through all the fetched messages to build the result message
while ($row = $result->fetch_array(MYSQLI_ASSOC))
{
$message = array();
$message['id'] = $row['chat_id'];
$message['color'] = $row['color'];
$message['name'] = $row['user_name'];
$message['time'] = $row['posted_on'];
$message['message'] = $row['message'];
array_push($response['messages'],$message);
}
// close the database connection as soon as possible
$result->close();
}
// return the JSON response
return $response;
}
/*
The isDatabaseCleared method checks to see if the database has been cleared
since last call to the server-the $id
parameter contains the id of the last message received by the client*/
private function isDatabaseCleared($id)
{
if($id>0)
{
// by checking the number of rows with ids smaller than the client's
// last id we check to see if a truncate operation was performed in
// the meantime
$check_clear = 'SELECT count(*) old FROM chat where chat_id<=' . $id;
$result = $this->mMysqli->query($check_clear);
$row = $result->fetch_array(MYSQLI_ASSOC);
// if a truncate operation occurred the whiteboard needs to be reset
if($row['old']==0)
return 'true';
return 'false';
}
return 'true';
}
}
?> - Now it's time to make a first test, executing the code you've written so far. This step is optional. Write a simple test.html file, with the following code:
<html>
<head>
<title>Testing chat.php</title>
</head>
<body>
<form action="chat.php" method="post">
Mode:
<input type="text" name="mode" size="50" value="SendAndRetrieveNew" />
<br />
Name:
<input type="text" name="name" size="50"/>
<br />
Message:
<input type="text" name="message" size="50" />
<br />
Color:
<input type="text" name="color" size="50" value="#000000" />
<br />
ID:
<input type="text" name="id" size="50" />
<br />
<input type="submit" />
</form>
</body>
</html> - Load test.html in your browser. Type some sample data as shown in Figure 8-5, and click on the Submit Query button. As a result, you should be able to find the data you've just posted in your chat data table, as shown in Figure 8-6.
- We continue our little journey by creating the real client or your application, plus the server-side part of the color picker component (a file named color.php). Let's start by copying the palette.png file from the code download to the chat folder. This is the image we use for the color picker.
- Copy the jQuery-1.3.2.js file from the code download to the chat folder. As you already know, this is the jQuery component, which we'll use as a base for your client-side chat component.
- Now create a file named chat.js and add the following code to it:
/* chatURL - URL for updating chat messages */
var chatURL = "chat.php";
/* colorURL - URL for retrieving the chosen RGB color */
var colorURL = "color.php";
/* variables that establish how often to access the server */
var updateInterval = 2000; // how many milliseconds to wait to get new message
// when set to true, display detailed error messages
var debugMode = true;
/* lastMessageID - the ID of the most recent chat message */
var lastMessageID = -1;
// function that displays an error message
function displayError(message)
{
// display error message, with more technical details if debugMode is true
alert("Error accessing the server! " +
(debugMode ? message : ""));
}
// function that displays a PHP error message
function displayPHPError(error)
{
displayError ("Error number :" + error.errno + "rn" +
"Text :"+ error.text + "rn" +
"Location :" + error.location + "rn" +
"Line :" + error.line + + "rn");
}
function retrieveNewMessages()
{
$.ajax({
url: chatURL,
type: 'POST',
data: $.param({
mode: 'RetrieveNew',
id: lastMessageID
}),
dataType: 'json',
error: function(xhr, textStatus, errorThrown) {
displayError(textStatus);
},
success: function(data, textStatus) {
if(data.errno != null)
displayPHPError(data);
else
readMessages(data);
// restart sequence
setTimeout("retrieveNewMessages();", updateInterval);
}
});
}
function sendMessage()
{
var message = $.trim($('#messageBox').val());
var color = $.trim($('#color').val());
var username = $.trim($('#userName').val());
// if we need to send and retrieve messages
if (message != '' && color != '' & username != '') {
var params = {
mode: 'SendAndRetrieveNew',
id: lastMessageID,
color: color,
name: username,
message: message
};
$.ajax({
url: 'chat.php',
type: 'POST',
data: $.param(params),
dataType: 'json',
error: function(xhr, textStatus, errorThrown) {
displayError(textStatus);
},
success: function(data, textStatus) {
if(data.errno != null)
displayPHPError(data);
else
readMessages(data);
// restart sequence
setTimeout("retrieveNewMessages();", updateInterval);
}
});
}
}
function deleteMessages()
{
$.ajax({
url: chatURL,
type: 'POST',
success: function(data, textStatus) {
if(data.errno != null)
displayPHPError(data);
else
readMessages(data);
// restart sequence
setTimeout("retrieveNewMessages();", updateInterval);
},
data: $.param({
mode: 'DeleteAndRetrieveNew'
}),
dataType: 'json',
error: function(xhr, textStatus, errorThrown) {
displayError(textStatus);
}
});
}
function readMessages(data, textStatus)
{
// retrieve the flag that says if the chat window has been cleared or not
clearChat = data.clear;
// if the flag is set to true, we need to clear the chat window
if (clearChat == 'true') {
// clear chat window and reset the id
$('#scroll')[0].innerHTML = "";
lastMessageID = -1;
}
if (data.messages.length > 0)
{
// check to see if the first message
// has been already received and if so
// ignore the rest of the messages
if(lastMessageID > data.messages[0].id)
return;
// the ID of the last received message is stored locally
lastMessageID = data.messages[data.messages.length - 1].id;
}
// display the messages retrieved from server
$.each(data.messages, function(i, message) {
// compose the HTML code that displays the message
var htmlMessage = "";
htmlMessage += "<div class="item" style="color:" + message.color + "">";
htmlMessage += "[" + message.time + "] " + message.name + " said: <br/>";
htmlMessage += message.message;
htmlMessage += "</div>";
// check if the scroll is down
var isScrolledDown = ($('#scroll')[0].scrollHeight - $('#scroll')[0].scrollTop <=
$('#scroll')[0].offsetHeight);
// display the message
$('#scroll')[0].innerHTML += htmlMessage;
// scroll down the scrollbar
$('#scroll')[0].scrollTop = isScrolledDown ?
$('#scroll')[0].scrollHeight : $('#scroll')[0].scrollTop;
});
}
$(document).ready(function()
{
// hook to the blur event
$('#userName').blur(
// function that ensures that the username is never empty and if so
// a random name is generated
function(e) {
// ensures our user has a default random name when the form loads
if (this.value == "")
this.value = "Guest" + Math.floor(Math.random() * 1000);
}
);
// populate the username field with
// the default value
$('#userName').triggerHandler('blur');
// handle the click event on the image
$('#palette').click(
function(e) {
// http://docs.jquery.com/Tutorials:Mouse_Position
// retrieve the relative mouse position inside the image
var x = e.pageX - $('#palette').position().left;
var y = e.pageY - $('#palette').position().top;
// make the ajax request to get the RGB code
$.ajax({
url: colorURL,
success: function(data, textStatus) {
if(data.errno != null)
displayPHPError(data);
else
{
$('#color')[0].value = data.color;
$('#sampleText').css('color', data.color);
}
},
data: $.param({
offsetx: x,
offsety: y
}),
dataType: 'json',
error: function(xhr, textStatus, errorThrown) {
displayError(textStatus);
}
}
);
}
);
// set the default color to black
$('#sampleText').css('color', 'black');
$('#send').click(
function(e) {
sendMessage();
}
);
$('#delete').click(
function(e) {
deleteMessages();
}
);
// set autocomplete off
$('#messageBox').attr('autocomplete', 'off');
// handle the enter key event
$('#messageBox').keydown(
function(e) {
if (e.keyCode == 13) {
sendMessage();
}
}
);
retrieveNewMessages();
});
>> Continue Reading AJAX Chat: Part 2
About the Author :
Bogdan Brinzarea
Bogdan has a strong background in Computer Science holding a Master and Bachelor Degree at the Automatic Control and Computers Faculty of the Politehnica University of Bucharest, Romania and also an Auditor diploma at the Computer Science department at Ecole Polytechnique, Paris, France.
His main interests cover a wide area from embedded programming, distributed and mobile computing and new web technologies. Currently, he is employed as an Alternative Channels Specialist at Banca Romaneasca, Member of National Bank of Greece where he is responsible for the Internet Banking project and coordinates other projects related to security applications and new technologies to be implemented in the banking area.
Cristian Darie
Cristian Darie is a software engineer with experience in a wide range of modern technologies, and the author of numerous books, including his popular AJAX and PHP tutorial by Packt, his ASP.NET E-Commerce tutorial, by APress and his forthcoming SEO tutorial for PHP developers by Wrox Press. Cristian is studying distributed application architectures for his PhD, and is getting involved with various commercial and research projects. When not planning to buy Google, he enjoys his bit of social life. If you want to say "hi", you can reach Cristian through his personal website at http://www.cristiandarie.ro.



Post new comment