How to Implement a Chat Room Using Jquery/Php

How to implement a chat room using Jquery/PHP?

Chat with PHP/AJAX/JSON

I used this book/tutorial to write my chat application:

AJAX and PHP: Building Responsive Web Applications: Chapter 5: AJAX chat and JSON.

It shows how to write a complete chat script from scratch.


Comet based chat

You can also use Comet with PHP.

From: zeitoun:

Comet enables web servers to send data to the client without having any need for the client to request it. Therefor, this technique will produce more responsive applications than classic AJAX. In classic AJAX applications, web browser (client) cannot be notified in real time that the server data model has changed. The user must create a request (for example by clicking on a link) or a periodic AJAX request must happen in order to get new data fro the server.

I'll show you two ways to implement Comet with PHP. For example:

  1. based on hidden <iframe> using server timestamp
  2. based on a classic AJAX non-returning request

The first shows the server date in real time on the clients, the displays a mini-chat.

Method 1: iframe + server timestamp

You need:

  • a backend PHP script to handle the persistent http request backend.php
  • a frondend HTML script load Javascript code index.html
  • the prototype JS library, but you can also use jQuery

The backend script (backend.php) will do an infinite loop and will return the server time as long as the client is connected.

<?php
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Sun, 5 Mar 2012 05:00:00 GMT");
flush();
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<title>Comet php backend</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>

<body>
<script type="text/javascript">
// KHTML browser don't share javascripts between iframes
var is_khtml = navigator.appName.match("Konqueror") || navigator.appVersion.match("KHTML");
if (is_khtml)
{
var prototypejs = document.createElement('script');
prototypejs.setAttribute('type','text/javascript');
prototypejs.setAttribute('src','prototype.js');
var head = document.getElementsByTagName('head');
head[0].appendChild(prototypejs);
}
// load the comet object
var comet = window.parent.comet;
</script>

<?php
while(1) {
echo '<script type="text/javascript">';
echo 'comet.printServerTime('.time().');';
echo '</script>';
flush(); // used to send the echoed data to the client
sleep(1); // a little break to unload the server CPU
}
?>
</body>
</html>

The frontend script (index.html) creates a "comet" javascript object that will connect the backend script to the time container tag.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Comet demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="prototype.js"></script>

</head>
<body>
<div id="content">The server time will be shown here</div>

<script type="text/javascript">
var comet = {
connection : false,
iframediv : false,

initialize: function() {
if (navigator.appVersion.indexOf("MSIE") != -1) {

// For IE browsers
comet.connection = new ActiveXObject("htmlfile");
comet.connection.open();
comet.connection.write("<html>");
comet.connection.write("<script>document.domain = '"+document.domain+"'");
comet.connection.write("</html>");
comet.connection.close();
comet.iframediv = comet.connection.createElement("div");
comet.connection.appendChild(comet.iframediv);
comet.connection.parentWindow.comet = comet;
comet.iframediv.innerHTML = "<iframe id='comet_iframe' src='./backend.php'></iframe>";

} else if (navigator.appVersion.indexOf("KHTML") != -1) {

// for KHTML browsers
comet.connection = document.createElement('iframe');
comet.connection.setAttribute('id', 'comet_iframe');
comet.connection.setAttribute('src', './backend.php');
with (comet.connection.style) {
position = "absolute";
left = top = "-100px";
height = width = "1px";
visibility = "hidden";
}
document.body.appendChild(comet.connection);

} else {

// For other browser (Firefox...)
comet.connection = document.createElement('iframe');
comet.connection.setAttribute('id', 'comet_iframe');
with (comet.connection.style) {
left = top = "-100px";
height = width = "1px";
visibility = "hidden";
display = 'none';
}
comet.iframediv = document.createElement('iframe');
comet.iframediv.setAttribute('src', './backend.php');
comet.connection.appendChild(comet.iframediv);
document.body.appendChild(comet.connection);

}
},

// this function will be called from backend.php
printServerTime: function (time) {
$('content').innerHTML = time;
},

onUnload: function() {
if (comet.connection) {
comet.connection = false; // release the iframe to prevent problems with IE when reloading the page
}
}
}
Event.observe(window, "load", comet.initialize);
Event.observe(window, "unload", comet.onUnload);

</script>

</body>
</html>

Method 2: AJAX non-returning request

You need the same as in method 1 + a file for dataexchange (data.txt)

Now, backend.php will do 2 things:

  1. Write into "data.txt" when new messages are sent
  2. Do an infinite loop as long as "data.txt" file is unchanged
<?php
$filename = dirname(__FILE__).'/data.txt';

// store new message in the file
$msg = isset($_GET['msg']) ? $_GET['msg'] : '';
if ($msg != '')
{
file_put_contents($filename,$msg);
die();
}

// infinite loop until the data file is not modified
$lastmodif = isset($_GET['timestamp']) ? $_GET['timestamp'] : 0;
$currentmodif = filemtime($filename);
while ($currentmodif <= $lastmodif) // check if the data file has been modified
{
usleep(10000); // sleep 10ms to unload the CPU
clearstatcache();
$currentmodif = filemtime($filename);
}

// return a json array
$response = array();
$response['msg'] = file_get_contents($filename);
$response['timestamp'] = $currentmodif;
echo json_encode($response);
flush();
?>

The frontend script (index.html) creates the <div id="content"></div> tags hat will contains the chat messages comming from "data.txt" file, and finally it create a "comet" javascript object that will call the backend script in order to watch for new chat messages.

The comet object will send AJAX requests each time a new message has been received and each time a new message is posted. The persistent connection is only used to watch for new messages. A timestamp url parameter is used to identify the last requested message, so that the server will return only when the "data.txt" timestamp is newer that the client timestamp.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Comet demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="prototype.js"></script>
</head>
<body>

<div id="content">
</div>

<p>
<form action="" method="get" onsubmit="comet.doRequest($('word').value);$('word').value='';return false;">
<input type="text" name="word" id="word" value="" />
<input type="submit" name="submit" value="Send" />
</form>
</p>

<script type="text/javascript">
var Comet = Class.create();
Comet.prototype = {

timestamp: 0,
url: './backend.php',
noerror: true,

initialize: function() { },

connect: function()
{
this.ajax = new Ajax.Request(this.url, {
method: 'get',
parameters: { 'timestamp' : this.timestamp },
onSuccess: function(transport) {
// handle the server response
var response = transport.responseText.evalJSON();
this.comet.timestamp = response['timestamp'];
this.comet.handleResponse(response);
this.comet.noerror = true;
},
onComplete: function(transport) {
// send a new ajax request when this request is finished
if (!this.comet.noerror)
// if a connection problem occurs, try to reconnect each 5 seconds
setTimeout(function(){ comet.connect() }, 5000);
else
this.comet.connect();
this.comet.noerror = false;
}
});
this.ajax.comet = this;
},

disconnect: function()
{
},

handleResponse: function(response)
{
$('content').innerHTML += '<div>' + response['msg'] + '</div>';
},

doRequest: function(request)
{
new Ajax.Request(this.url, {
method: 'get',
parameters: { 'msg' : request
});
}
}
var comet = new Comet();
comet.connect();
</script>

</body>
</html>

Alternatively

You can also have a look at other chat applications to see how they did it:

  • http://hot-things.net/?q=blite - BlaB! Lite is an AJAX based and best viewed with any browser chat system that supports MySQL, SQLite & PostgreSQL databases.

  • Gmail/Facebook Style jQuery Chat - This jQuery chat module enables you to seamlessly integrate Gmail/Facebook style chat into your existing website.

  • Writing a JavaScript/PHP Chat Server - A tutorial

  • CometChat - CometChat runs on standard shared servers. Only PHP + mySQL required.

How to implement chat using jQuery, PHP, and MySQL?

If the host you are using would "block it for sure" if it's making that many requests, then you may want to consider getting a different host or upgrading your hosting package before worrying about your code. Check out how Facebook implements their chat:

The method we chose to get text from
one user to another involves loading
an iframe on each Facebook page, and
having that iframe's Javascript make
an HTTP GET request over a persistent
connection that doesn't return until
the server has data for the client.
The request gets reestablished if it's
interrupted or times out. This isn't
by any means a new technique: it's a
variation of Comet, specifically XHR
long polling, and/or BOSH.

Chat App with php, Jquery

You can do this with WebSockets. There are some cool WebSockets tools out there like:

  • Ratchet - http://socketo.me/
  • Wrench - https://github.com/varspool/Wrench
  • phpwebsocket - https://code.google.com/p/phpwebsocket/
  • apache-websocket - https://github.com/disconnect/apache-websocket

Using WebSockets you can append received messages to the chat log instead of updating the whole thing like it seems you are doing.

In case you choose to (or have to) keep requesting new messages to the server, since not all hosting providers will allow WebSockets, here are some tips that you may find useful to improve your chat app:

  1. Store the last received message id on client-side, so that when you request new messages to the server you can send it this id and it will only send you messages you didn't receive yet, thus avoiding unnecessary traffic.

  2. On the server side, record the last time a client requested new messages, so that you can define a timeout in order to detect user disconnection.

  3. To avoid overloading your server or client with more requests than it can handle, take into consideration the time took by the server to answer your last request when you define the interval for the next request, like this:

    1. Client requests messages
    2. Server replies in 100ms
    3. Client waits 100ms before requesting again
    4. Server replies in 200ms
    5. Client waits 200ms before requesting again
    6. ...

Live chat system using jQuery and PHP

  1. Use AJAX to submit message and add to database.

    $.post("myscript.php", {userId: 123, message: "My message"});

  2. Every several seconds (i.e. 10 seconds) request newer messages by AJAX and prepend (or append) to discussion div.

    window.setInterval(function() {
    $.post("myscript.php", {action: "refresh", lastId: 1234}, function(response) {
    $("#discussion").prepend(response.html);
    lastId = response.lastId;
    }, "json");
    }, 10000);

AJAX, jQuery, javascript for a chatroom

After a lot of trial and error, we found out that the problem was a simple missing semicolon on chat.php:

require('../includes/database/connect.db.php');

:)

best way to update chat room quickly, and reliably?

I know this question has been answered, but I wanted to throw in another possible solution. It is, as these things go, a simple one with some catches and caveats, but for a class project it would suffice. The example I am proposing is not one that I have created myself, and the code is untested. And it would likely take you a fair bit of time to implement the changes and get them to work.

But it explores some basic data formats such as the XML and JSON capabilities in PHP and JavaScript. It keeps track of the most recent data grab and only gets the new chat information. This way once the data gets back to the browser, it's only loading the new stuff instead of the whole thing.

In your code above, it looks like you are writing the entire chat, each time. I am sure there is some browser caching issue going on. Rather than requesting the entire html page each time, you could request a PHP that returns JSON data (PHP has built-in JSON functions) that only includes the last entries, and you append those to the document.

There is a lot of information available on JSON. If you haven't learned about it yet, it is a way of expressing data that is native to JavaScript. It can natively convert JSON formatted text into arrays, objects, and properties. And it is not as verbose as XML.

In your code, create a time stamp called

var lastDataReceived = "";

This will keep a time stamp of the last time new chat data was received from the server. In the $.get() success function, change the URL to a PHP page and set it to get JSON data. Also, pass in the needed information such as the name of the room you need data from, and the last time chat data was received:

function update()
{
$.get({
url: "/rooms/room/getchatdata.php",
data: { "room": roomname, "lastDataReceived": lastDataReceived },
success: function(data){
//..............
},
dataType: "json"
});
}

This will create a request to

"/rooms/room/chatdata.php?room="+roomname+"&lastDataReceived="+lastDataReceived 

On the server side, you can get the querystring parameters like so

$roomname = $_GET["room"];
$lastDataReceived = date_create_from_format('d/M/Y H:i:s', $_GET["lastDataReceived"]);

It looks like you are writing chat information to an HTML file. In this example, using XML data would most likely be preferable. There are many, many PHP XML tutorials out there. Here is one at W3Schools.com for SimpleXML functions. (http://www.w3schools.com/php/php_ref_simplexml.asp)

With this you can load from a file:

$xml = simplexml_load_file($roomname.".xml");

You can also create a new XML file if it is a new room for example:

<?php
$chats = <<<XML
<chats>
</chats>
XML;

$xml = new SimpleXMLElement($chats);
$xml->saveXML($roomname.".xml");
?>

To determine whether to read or write, you can check if the room exists by checking if the file for that room exists:

$xml = NULL;
if (file_exists($roomname.".xml")) {
//Set to exsiting
$xml = simplexml_load_file($roomname.".xml");
} else {
//Create new file
$chats = <<<XML
<chats>
</chats>
XML;
$xml = new SimpleXMLElement($chats);
$xml->saveXML($roomname.".xml");
}

Either way you get a $xml variable that you can work with.

So now, say you've made your request back to the server to see if there is any new chat data. You're working with the variables $roomname and $lastDataReceived that you created earlier. And you've loaded the $xml object from the file. Now you need to find any new additions.

$chats = $xml->chats->children();
$newchats = array();

foreach($chats as $c) {
if ($c['date'] > $lastDataReceived) {
array_push($newchats, $c);
}
}

Now that you have an array of the new chat items, write the JSON data back to the browser:

$json = json_encode($newchats);
header('Content-Type: application/json');
echo $json;

Now back to your JavaScript. In the PHP sample above, $newchats is initialized as a new array. When json_encode() is called on it, if there are no new chats, the echo will return an empty array. You can test for this in JS.

In this case, you would only be adding the new items that came in. So you would need to add new HTML to the document. This is super easy with jQuery. Say the chats were all in a <div> tag:

<div id="chats">
<!--all chats here-->
</div>

And you have a template div that you want all chats to look like

<div class="chat_template" style="display:none;">
<div class="person_name"></div>
<div class="chat_text"></div>
<div class="chat_time"></div>
</div>

You can clone it, load the data into it, and append it to the document.

function update()
{
$.get({
url: "/rooms/room/chatdata.php",
data: { "room": roomname, "lastDataReceived": lastDataReceived },
success: function(data){
if (data.length > 0) {
for (var i = 0; i < data.length; i++) {[
var c = data[i];
//I do not know what the exact structure of the data will be.
// You may need to output the data to the console to see it's structure.
// But I am assuming you will have access to the date value, the person's name
// and the text.

//Clone the template and add the values
var div = $("div.chat_template").clone(true);
div.find("div.person_name").html(name);
div.find("div.chat_text").html(text);
div.find("div.chat_time").html(date_val);
div.show();

//Add the new div to the document
$("div#chats").append(div);

//Set the last received time for the next query
lastDataReceived = date_val;
}

playMessageSound();
}
},
dataType: "json"
});
}

When sending new chat info to the server, you can use that same data: { "chattext": text....} type structure in the jQuery $.post() function. You will need a different PHP file on the server like addchatdata.php. I already touched on an algorithm which gets or creates a chat room file based on whether it exists.

So if you want the child XML element to look like this

<chat date="04/Jun/2015 13:18:23" name="George">
Here is some of George's text.
</chat>

Once you get the $xml object, you can add new XML to it like so:

$chat = $xml->addChild("chat", "Here is some of George's text.");
$chat->addAttribute("date", date('d/M/Y H:i:s'));
$chat->addAttribute("name", "George"); //<--Or use a variable name for the name

//Then save the changes back out to the file:
$xml->saveXML($roomname.".xml");

In a general sense, this is doing what you were already doing: storing data from new chats, then loading that data into other clients connected to the chat. However, here it is only retrieving the new information, and will load much faster on the browser.

High traffic php ajax chat design options

A few suggestions to improve performance. Some of these have already been suggested, but I'll include them, so that you have a complete catalog. Here they are, in no particular order:

1) Serve JSON from PHP, and render the HTML for the query in the client. This should save you a considerable amount of processing time and bandwidth.

2) Minimize the number of fields that you query from MySQL, as each field has to be transferred and marshaled through PDO.

3) Consider using Comet to push messages to the Ajax clients over persistent connections. You can read more about Comet at http://www.webreference.com/programming/javascript/rg28/index.html and http://www.zeitoun.net/articles/comet_and_php/start

4) Consider caching your MySQL results in a Memcached or Redis. This will consume some additional memory, but will dramatically decrease the overall overhead, and take an enormous load off of MySQL. The biggest traffic sites in the world use these techniques to reduce overhead and significantly increase the number of concurrent users.

5) Find a way to use HTML5 websockets. Webchat is one of the driving applications of HTML5 websocket, so this problem is ideally suited to the technology.

6) Consider reducing the refresh time from 1 second to between 2 and 5 seconds. Users can't tell the difference between 1 and 2 seconds, and with a chat application, it takes considerably longer than 5 seconds to type most responses. At 5 seconds, half of the messages will be delivered in less than 2.5 seconds, and the other half between 2.5 and 5 seconds, on average. It's worth investigating, as a simple change to a 5 second polling interval would reduce your server overhead by 5X, which may be enough to support your current set of users with no code changes.



Related Topics



Leave a reply



Submit