AJAX Chat Implementation: Part 1

Exclusive offer: get 50% off this eBook here
AJAX and PHP: Building Modern Web Applications 2nd Edition

AJAX and PHP: Building Modern Web Applications 2nd Edition — Save 50%

Build user friendly Web 2.0 Applications with JavaScript and PHP

£14.99    £7.50
by Bogdan Brinzarea Cristian Darie | December 2009 | AJAX

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:

Figure 8-1: Online chat application built with AJAX and jQuery

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:

Figure 8-2: The components of the AJAX Chat application

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.

Figure 8-3: Server-side Chat class

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.

Figure 8-4: Client-side Chat class
AJAX and PHP: Building Modern Web Applications 2nd Edition Build user friendly Web 2.0 Applications with JavaScript and PHP
Published: December 2009
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:

Time for action – implementing AJAX chat with JSON

Follow these steps to implement your AJAX Chat application:

  1. 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)
    );
  2. In your ajax folder, create a new folder named chat.
  3. 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');
    ?>
  4. 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;
    }
    ?>
  5. 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));
    ?>
  6. 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';

    }
    }
    ?>

  7. 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>
  8. 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.
  9. 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.
  10. 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.
  11. 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

AJAX and PHP: Building Modern Web Applications 2nd Edition Build user friendly Web 2.0 Applications with JavaScript and PHP
Published: December 2009
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:

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.

Books From Packt

Apache MyFaces 1.2 Web Application Development
Apache MyFaces 1.2 Web Application Development

WordPress Plugin Development: Beginner's Guide
WordPress Plugin Development: Beginner's Guide

Apache MyFaces Trinidad 1.2: A Practical Guide
Apache MyFaces Trinidad 1.2: A Practical Guide

jQuery UI 1.7: The User Interface Library for jQuery
jQuery UI 1.7: The User Interface Library for jQuery

JBoss RichFaces 3.3
JBoss RichFaces 3.3

jQuery 1.3 with PHP
jQuery 1.3 with PHP

Joomla! with Flash
Joomla! with Flash

jQuery 1.3 with PHP
jQuery 1.3 with PHP

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software