Learn how to use Asynchronous JavaScript + XML (Ajax) and PHP to build a chat system in a web application. Your customers can discuss website content with you and other customers without the need to download or install any specialized instant messaging software.
|
Visit the Ajax technical resource center, an all-in-one center for Ajax programming model information, including many documents, tutorials, forums, blogs, wikis, and news. Any new Ajax information can be found here.
|
|
|
Subscribe to RSS feeds for Ajax-related articles and tutorials |
|
|
|
Web 2.0Since its appearance, developers have been talking about communities. Whether or not you think this is a bit exaggerated, but it is very attractive to allow users or readers to conveniently discuss page topics or products sold in real time. But what should we do? Can I add a chat on the product promotion page without having the customer install any special software, including Adobe Flash Player? Of course! Practice has proved that PHP, MySQL, dynamic HTML (DHTML), Ajax, and prototype. js libraries can be used with free ready-made tools.
Let's get started now.
Login
A chat must first have an identity. This requires a simple logon page, as shown in Listing 1.
Listing 1. index.html
<html>
<head> <title> Chat Login </ title> </ head>
<body>
<form action = "chat.php" method = "post">
Username: <input type = "text" name = "username">
<input type = "submit" value = "Login">
</ form>
</ body>
</ html>
The display result of this page is shown in Figure 1.
Figure 1. Chat login window
Note: The login window is required in this example because I want to know who is talking. For your application, there may already be a login page, just use your existing username.
Back to top
Basic chat system
The chat system is essentially a string table, and each string belongs to a speaker. The simplest model is shown in Listing 2.
Listing 2. chat.sql
DROP TABLE IF EXISTS messages;
CREATE TABLE messages (
message_id INTEGER NOT NULL AUTO_INCREMENT,
username VARCHAR (255) NOT NULL,
message TEXT,
PRIMARY KEY (message_id)
);
The script contains the automatically added message ID, user name, and message itself. If necessary, you can also add a time stamp to each message to record the time it was sent.
If you need to manage multiple conversations on different topics, you also need to create a table to record different topics, and add the relevant topic_id in the message table. In order to simplify the example as much as possible, I used the simplest model.
The following commands were used to establish the database and load mode:
% mysqladmin create chat
% mysql chat <chat.sql
Depending on the settings of the MySQL server and its security settings and passwords, the commands may be slightly different.
The most basic chat user interface (UI) is shown in Listing 3.
Listing 3. chat.php
<? php
if (array_key_exists ('username', $ _POST)) {
$ _SESSION ['user'] = $ _POST ['username'];
}
$ user = $ _SESSION ['user'];
?>
<html>
<head> <title> <? php echo ($ user)?>-Chatting </ title>
<script src = "prototype.js"> </ script>
</ head>
<body>
<div id = "chat" style = "height: 400px; overflow: auto;">
</ div>
<script>
function addmessage ()
{
new Ajax.Updater ('chat', 'add.php',
{
method: 'post',
parameters: $ ('chatmessage'). serialize (),
onSuccess: function () {
$ ('messagetext'). value = '';
}
});
}
</ script>
<form id = "chatmessage">
<textarea name = "message" id = "messagetext">
</ textarea>
</ form>
<button onclick = "addmessage ()"> Add </ button>
<script>
function getMessages ()
{
new Ajax.Updater ('chat', 'messages.php', {
onSuccess: function () {window.setTimeout (getMessages, 1000);}
});
}
getMessages ();
</ script>
</ body>
</ html>
In the beginning of the script, you can get the username from the parameters submitted on the login page and store it in the session. Then load the Prototype.js JavaScript library, which can complete all Ajax processing.
Then the page provides a place to store the message. This area is filled in by the getMessages () JavaScript function at the back of the file.
Below the message area is a form and textarea where the user enters the message text. There is also a button Add to add a chat message.
The page is shown in Figure 2.
Figure 2. Simple chat window
Note the getMessages () function, the page actually polls the server every 1000 milliseconds (1 second), checks for new messages, and outputs the results to the message area at the top of the page. Later in this article, we will introduce polling in detail. I want to complete the basic implementation first. The messages.php page returns the current message list. The page is shown in Listing 4.
Listing 4. messages.php
<table>
<? php
// Install the DB module using 'pear install DB'
require_once 'DB.php';
$ db = & DB :: Connect ('mysql: // root @ localhost / chat', array ());
if (PEAR :: isError ($ db)) {die ($ db-> getMessage ());}
$ res = $ db-> query ('SELECT * FROM messages');
while ($ res-> fetchInto ($ row))
{
?>
<tr> <td> <? php echo ($ row [1])?> </ td>
<td> <? php echo ($ row [2])?> </ td> </ tr>
<? php
}
?>
</ table>
The beginning of the script connects to the database with a DB library, which can be downloaded from PEAR (see Resources). If you have not installed this library, you can complete it with the following command:
% pear install DB
After PEAR is installed, the script can query the current message, retrieve each line, and output the user name and message text.
Finally, there is the add.php script, which is called from the Prototype.js Ajax code of the addmessage () function on the page. The script gets the message text and user name from the conversation, and then inserts a new row in the message table. The code is shown in Listing 5.
Listing 5. add.php
<? php
require_once ("DB.php");
$ db = & DB :: Connect ('mysql: // root @ localhost / chat', array ());
if (PEAR :: isError ($ db)) {die ($ db-> getMessage ());}
$ sth = $ db-> prepare ('INSERT INTO messages VALUES (null,?,?)');
$ db-> execute ($ sth, array ($ _SESSION ['user'], $ _POST ['message']));
?>
<table>
<? php
$ res = $ db-> query ('SELECT * FROM messages');
while ($ res-> fetchInto ($ row))
{
?>
<tr> <td> <? php echo ($ row [1])?> </ td>
<td> <? php echo ($ row [2])?> </ td> </ tr>
<? php
}
?>
</ table>
The add.php script also returns the current message list because the Ajax code in the original page updates the chat history from the returned HTML code. So users can immediately see the text added to the conversation.
This is the basic structure of the chat system. The next section explains how to improve the efficiency of polling.
Back to top
A little improvement
In this original chat system, the page requests all chat records of a conversation every second. Although it has little effect on shorter conversations, if the conversation is very long, performance problems will become apparent. Fortunately, the solution is simple. Each message has a message_id, which is automatically incremented. Therefore, if you know that you already have a message that belongs to an ID, you only need to request the message that appears after this ID. This can greatly reduce the number of message transfers. Most requests are likely to have no new messages, and the packet delivered will become smaller.
A more efficient design requires a slight modification of the chat.php page, as shown in Listing 6.
Listing 6. chat.php (modified)
<? php
if (array_key_exists ('username', $ _POST)) {
$ _SESSION ['user'] = $ _POST ['username'];
}
$ user = $ _SESSION ['user'];
?>
<html>
<head> <title> <? php echo ($ user)?>-Chatting </ title>
<script src = "prototype.js"> </ script>
</ head>
<body>
<div style = "height: 400px; overflow: auto;">
<table id = "chat">
</ table>
</ div>
<script>
function addmessage ()
{
new Ajax.Request ('add.php', {
method: 'post',
parameters: $ ('chatmessage'). serialize (),
onSuccess: function (transport) {
$ ('messagetext'). value = '';
}
});
}
</ script>
<form id = "chatmessage">
<textarea name = "message" id = "messagetext">
</ textarea>
</ form>
<button onclick = "addmessage ()"> Add </ button>
<script>
var lastid = 0;
function getMessages ()
{
new Ajax.Request ('messages.php? id =' + lastid, {
onSuccess: function (transport) {
var messages = transport.responseXML.getElementsByTagName ('message');
for (var i = 0; i <messages.length; i ++)
{
var message = messages [i] .firstChild.nodeValue;
var user = messages [i] .getAttribute ('user');
var id = parseInt (messages [i] .getAttribute ('id'));
if (id> lastid)
{
var elTR = $ ('chat'). insertRow (-1);
var elTD1 = elTR.insertCell (-1);
elTD1.appendChild (document.createTextNode (user));
var elTD2 = elTR.insertCell (-1);
elTD2.appendChild (document.createTextNode (message));
lastid = id;
}
}
window.setTimeout (getMessages, 1000);
}
});
}
getMessages ();
</ script>
</ body>
</ html>
Instead of using "chat" <div> tags to contain all messages, it is now changed to <table> tags, and a new line is dynamically added when new messages are received. You can see that the corresponding changes in the getMessages () function are longer than the first version.
The new version of getMessages () expects the result of the messages.php page to be an XML block containing new messages. messages.php adds a parameter id, which is the message_id of the last message displayed on the page. Initially the ID is 0, so the messages.php page returns all messages. After that, the ID of the last message displayed so far is sent.
The XML response is decomposed into elements using the onSuccess handler, and each element is added to the table using standard DHTML Document Object Model (DOM) functions, such as insertRow (), insertCell (), and appendChild ().
The modified messages.php file returns XML instead of HTML, as shown in Listing 7.
Listing 7. messages.php
<? php
require_once ("DB.php");
header ('Content-type: text / xml');
$ id = 0;
if (array_key_exists ('id', $ _GET)) {$ id = $ _GET ['id'];}
$ db = & DB :: Connect ('mysql: // root @ localhost / chat', array ());
if (PEAR :: isError ($ db)) {die ($ db-> getMessage ());}
?>
<messages>
<? php
$ res = $ db-> query ('SELECT * FROM messages WHERE message_id>?', $ id);
while ($ res-> fetchInto ($ row))
{
?>
<message id = "<? php echo ($ row [0])?>" user = "<? php echo ($ row [1])?>">
<? php echo ($ row [2])?>
</ message>
<? php
}
?>
</ messages>
Figure 3 shows the new and improved version.
Figure 3. The optimized chat window
Nothing has changed in appearance. But compared with the original efficiency is much higher.
Back to top
"Real Time" Secret
If you are new to Ajax or only have knowledge of the field, the concept of "polling" may scare you. Unfortunately, polling is the only way. To establish a continuous pipeline between the client and the server without the need to install specific software on both ends, there is no cross-platform, cross-browser method that can achieve this. Even so, it may require special configuration of the firewall to work. Therefore, if you need a simple method that everyone can use, Ajax and polling are the only possibilities.
But where is the "real time" that is constantly being promoted and advertised? Polling cannot be real-time. Is this really the case? I think it depends on your definition of real time. When I used to write electrophysiology data retrieval code, real-time meant milliseconds. I believe that geologists see minutes, days, and even years as real-time in some cases.
If you check Wikipedia, you will find that the average human response time is between 200 and 270 milliseconds. That is the time to hit a ball. It takes much longer to read a message and form a response, even if you are very committed. Therefore, when waiting for a chat message, about 200 milliseconds (probably longer) should be sufficient. I set it to 1 second and I did n’t feel uncomfortable.
As the host of the developerWorks Ajax forum (see Resources), polling and real-time problems are encountered at least once a month. I hope that for Ajax, the polling and so-called real-time masks have been exposed. It is recommended to try polling before considering an extremely complex real-time solution. This way you can at least know what you can do with ready-made tools before trying a custom solution.
Back to top
Work after
Hope this article provides a good starting point for you to use this as a basis to implement your own chat system in your application. Here are some suggestions:
Record users: List the people currently participating in the conversation next to the chat window. This tells people who participated in the conversation, when they came, and when they quit.
Allow multiple talks: Allow multiple talks on different topics to take place simultaneously.
Support emoji characters: translate character combinations such as :-) into appropriate smiley images.
Use URL parsing: Use regular expressions in the client-side JavaScript code to discover the URL and convert it into a hyperlink.
Handle the Enter key: cancel the Add button, and check whether the user pressed the Enter or Return key by checking the onkeydown event of textarea.
Display user input time: notify the server when the user starts typing, and other people in the conversation can see someone replying. In this way, if someone types slowly, the feeling of ending the conversation can be minimized.
Limit the size of messages: Another way to keep conversations smooth is to avoid messages that are too long. Limiting the maximum number of characters in textarea—also by capturing onkeydown—helps increase the speed of the conversation.
This is just part of the idea of modifying the above code for improvement. If you do this and want to share your results in the community, please let me know and I can put it in the downloaded source code.
Back to top
Conclusion
I admit that I don't like chatting very much. I never opened my chat client. Text messages have only been used once in a long time. My chat identifier is idratheryouemail. Serious enough. However, I find that chatting with the current environment, such as the situation described in this article, is very attractive. why? Because it mainly focuses on the related topics of the website, it can avoid the rambling about the recent "TomKat" news to the greatest extent.
Try this code in your web application. See if you can let your readers and customers talk in real time and tell me how effective it is through the developerWorks Ajax forum. Hope to surprise you.