Tuesday, December 11, 2007

Ajax Chat - Using Prototype & PHP

In the past years chat rooms were so rare in the websites and only those website which needed it were using chat facilities. Most of those chat rooms were developed by means of Java Applet technology hence the client browser had to be Java-enabled in order to use the chat rooms. After a while flash chat rooms came in the picture but they had the same drawback since the client needs to have the Flash player plugin installed in order to use the chat rooms.
Recently the Ajax-enabled chat rooms have come to the websites even the personal websites. They are so light, easy to develop and don't need any special software or prerequisites on the clients' browsers.

In this tutorial we will learn how to develop a very simple Ajax-enabled chat room based on PHP 5 server-side scripting language and Prototype JavaScript framework.

You can develop this chat room following the steps bellow or download it here.

1. JavaScript Message class and one utility function
Create 2 folders called php and js. Copy the prototype.js file in the js folder.
Create a new javascript file called chat.js which contains the following code and put it in the js folder.

var Message = Class.create();
Message.prototype = {
initialize: function(id, timestamp, serverTimestamp, nickname, text) {
this.id = id;
this.timestamp = timestamp;
this.serverTimestamp = serverTimestamp;
this.nickname = nickname;
this.text = text;
},
};


function removeChildrenOf(s) {
while (s.hasChildNodes())
s.removeChild(s.childNodes[0]);
}


2. PHP Message class
Create a new php file called message.class.php which contains the following code and put it in the php folder.


<?php
class Message {

const MAX_TEXT_LENGTH = 100;
const MAX_NICKNAME_LENGTH = 20;

// ...........................................

private $id;
private $timestamp;
private $serverTimestamp;
private $nickname;
private $text;

// ...........................................

public function __construct($id = 0, $timestamp = 0, $serverTimestamp, $nickname = "", $text = "") {
$this->id = $id;
$this->timestamp = $timestamp;
$this->serverTimestamp = $serverTimestamp;
$this->nickname = $nickname;
$this->text = $text;
}

// ...........................................

public function setId($id) {
$this->id = $id;
}

public function getId() {
return $this->id;
}

public function setTimestamp($timestamp) {
$this->timestamp = $timestamp;
}

public function getTimestamp() {
return $this->timestamp;
}

public function setServerTimestamp($serverTimestamp) {
$this->serverTimestamp= $serverTimestamp;
}

public function getServerTimestamp() {
return $this->serverTimestamp;
}

public function setText($text) {
if(strlen($text) > self::MAX_TEXT_LENGTH)
echo "Error: Text is too long! Maximum is ".self::MAX_TEXT_LENGTH;
else
$this->text = $text;
}

public function getText() {
return $this->text;
}

public function setNickname($nickname) {
if(strlen($text) > self::MAX_NICKNAME_LENGTH)
echo "Error: Nickname is too long! Maximum is ".self::MAX_NICKNAME_LENGTH;
else
$this->nickname = $nickname;
}

public function getNickname() {
return $this->nickname;
}
}
?>


3. Database, Tables and Configuration
Create a database in your mysql DBMS and run the following query on it in order to create the message table.


CREATE TABLE `message` (
`id` int(10) unsigned NOT NULL auto_increment,
`timestamp` bigint(20) unsigned default NULL,
`text` varchar(100) default NULL,
`serverTimestamp` bigint(20) unsigned default NULL,
`nickname` varchar(20) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;


Create a php file called config.inc.php which contains the following code and put it in the php folder.
Don't forget to write database name, username and password of your mySql server instead of corresponding parameters in this file.

<?php
define("DB_HOST", "localhost");
define("DB_USERNAME", "root");
define("DB_PASSWORD", "ebic");
define("DB_NAME", "chat");
?>


4. Send Message server-side script

Create a php file called send.php which contains the following code, don't put it in any folder, let it be in the root folder of your website.


<?php
include_once('./php/config.inc.php');

$mysqli = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);

if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

$text = isset($_POST['text']) ? $_POST['text'] : "";
$timestamp = isset($_POST['timestamp']) ? $_POST['timestamp'] : 0;
$serverTimestamp = isset($_POST['serverTimestamp']) ? $_POST['serverTimestamp'] : 0;
$nickname = isset($_POST['nickname']) ? $_POST['nickname'] : "";

$sql = "INSERT INTO message (text, nickname, timestamp, serverTimestamp)
VALUES('$text', '$nickname', '$timestamp', '$serverTimestamp')";

if (!$mysqli->query($sql)) {
echo "Error in query";
}

?>


5. Receive Messages server-side script
Create a php file called get.php which contains the following code, don't put it in any folder, let it be in the root folder of your website.


<?php
include_once('./php/config.inc.php');

header('Content-type: text/xml');

$mysqli = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);

if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

echo "<messages>";

$timestamp = time();
$sql = "DELETE FROM message WHERE $timestamp - serverTimestamp > 1000 ";

if (!$mysqli->query($sql)) {
echo "Error in query";
} else {
$sql = "SELECT * FROM message";
if (!$result = $mysqli->query($sql)) {
echo "Error in query";
} else {
while($record = $result->fetch_assoc()) {
echo "<message>";

echo "<id>";
echo $record['id'];
echo "</id>";

echo "<timestamp>";
echo $record['timestamp'];
echo "</timestamp>";

echo "<serverTimestamp>";
echo $record['serverTimestamp'];
echo "</serverTimestamp>";

echo "<text>";
echo $record['text'];
echo "</text>";

echo "<nickname>";
echo $record['nickname'];
echo "</nickname>";

echo "</message>";
}
}
}

echo"</messages>";

?>


6. Utility server-side script for getting the server timestamp
Create a php file called timestamp.php which contains the following code, don't put it in any folder, let it be in the root folder of your website.


<?php
echo mktime();
?>


7. Client script for sending and getting (main html page)
Create a html file called index.html which contains the following code, don't put it in any folder, let it be in the root folder of your website.


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Chat Room (Shout Box)</title>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/chat.js"></script>

<script type="text/javascript"><!--
<!--

var serverTimestamp = 0;
var messages = new Array(10000);
var msgCount = 0;

function existInArray(msg) {
for(i = 0; i < msgCount; i++) {
if(msg.timestamp == messages[i].timestamp)
if(msg.serverTimestamp == messages[i].serverTimestamp)
return true;
}
return false;
}

function getMessages() {
new Ajax.Request('get.php?ts=' + new Date().getTime(), {
onSuccess: function(transport) {
var xml = transport.responseXML;
var msgNodes = xml.documentElement.childNodes;
for(i = 0; i < msgNodes.length; i++) {
var id = msgNodes[i].getElementsByTagName("id")[0].firstChild.nodeValue;
var nickname = msgNodes[i].getElementsByTagName("nickname")[0].firstChild.nodeValue;
var text = msgNodes[i].getElementsByTagName("text")[0].firstChild.nodeValue;
var timestamp = msgNodes[i].getElementsByTagName("timestamp")[0].firstChild.nodeValue;
var serverTimestamp = msgNodes[i].getElementsByTagName("serverTimestamp")[0].firstChild.nodeValue;
var msg = new Message(id, timestamp, serverTimestamp, nickname, text);
//alert(i);
if(!existInArray(msg)) {
messages[msgCount++] = msg;
}
}
}
});

showMessages();
setTimeout("getMessages()", 4000);
}

function updateServerTimestamp() {
new Ajax.Request('timestamp.php?ts=' + new Date().getTime(), {
onSuccess: function(transport) {
serverTimestamp = transport.responseText;
}
});
}

function showMessages() {
var board = $('board');
removeChildrenOf(board);
for(i = 0; i < msgCount; i++) {
var msgView = document.createElement("div");
var msg = messages[i];
var date = new Date(parseInt(msg.timestamp, 10));
msgView.innerHTML = msg.nickname + " <small>(" + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + ")</small>: " + msg.text;
board.appendChild(msgView);
}
board.scrollTop = board.scrollHeight;
}

function sendMessage(msg) {
new Ajax.Request('send.php', {
method: 'post',
parameters: {
text: msg.text,
nickname: msg.nickname,
timestamp: msg.timestamp,
serverTimestamp: msg.serverTimestamp
},
onFailure: function() {
sendMessage(msg);
}
});

}

function doMessage() {
if ($F("message").length > 0) {
var board = $('board');
var msg = new Message(0, new Date().getTime(), serverTimestamp, $F('name'), $F("message"));
//alert(msg.timestamp);
messages[msgCount++] = msg;
showMessages();
sendMessage(msg);
board.scrollTop = board.scrollHeight;
}
$('message').clear();
$('message').focus();
updateServerTimestamp();
}

Event.observe(window, 'load', function() {
updateServerTimestamp();
getMessages();
});


//-->
</script>


<style>
<!--

#board {
width: 400px;
border: inset 2px;
height: 300px;
padding: 2px;
overflow: auto;
}

-->
</style>
</head>
<body>

<h1>Chat Room</h1>

<form action="" onsubmit="return false;">
<div id="board"></div>
<br />
<label>Nickname:</label>
<input type="text" name="name" id="name" maxlength="20" />
<br />
<label>Message:</label>
<input type="text" id="message" name="message" maxlength="100" size="45"/>
<input type="submit" value="Send" name="send" onclick="doMessage()"/>
</form>
<br />
<br />
<a href="http://ehsun7b.blogspot.com">ehsun7b.blogspot.com</a>
</body>
</html>



Enjoy chatting in your own website!

3 comments:

AtticShow said...

Hey, I've followed your tutorial and I've got a nice working chatroom, but I'm trying to convert it to a shout box, and I don't want it to delete the messages, I've tried removing this line

"$sql = "DELETE FROM message WHERE $timestamp - serverTimestamp > 1000 ";"

but that just breaks it, any suggestions?

Seven said...

Hi,
What do you mean exactly by shout box? Do you need to keep all messages of all time in the chatroom???

AtticShow said...

Well right now the code deletes the oldist comment made from the database, I would like to keep all messages and use it as a shoutboard for my website