37

PHP Web Socket Chat Application

Posted (Updated ) in Javascript, PHP

UPDATE 15 Feb 2012: This post is woefully out of date. See my new PHP WebSocket Chat Application 2.0 post for a working tutorial compatible with the latest WebSocket spec as of Feb 15, 2012.

Today I’ll be doing an in-depth tutorial on how to create a simple, real-time chat application with HTML5 web sockets.

Disclaimer: Due to the relative infancy of web socket technology, this tutorial will currently only work on Google Chrome and the spec isn’t finalized yet so it may break in the future (specifically during the handshake phase).

Rather than reinventing the wheel I’ll be using two open source scripts freely available on the internet to take care of most of the hard work for us. These are PHPWebSocket by georgenava and ServerEventsDispatcher by Ismael. This tutorial has the following prerequisites:

Let’s begin.

Firstly set up a folder for your webapp – I called mine chat. This is the file structure I used:

/chat
/chat/assets/javascript/ServerEventDispatcher.js
/chat/includes/websocket.class.php
/chat/index.php
/chat/server.php

Now for the index.php file that our visitors will see:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>HTML5 Chat</title>
<style>
	body {width:960px;margin:auto}
	#conversation {height:500px;border:1px solid #000}
	input {width:100%}
</style>
<script src='assets/javascript/ServerEventDispatcher.js'></script>
</head>
<body>
	<div id='conversation'>&nbsp;</div>
	<input type='text' name='say'>
</body>
</html>

We’ll make one relatively minor change to ServerEventDispatcher.js to get it accepting our web socket’s address in its constructor. Here’s the modification:

//change
var ServerEventsDispatcher = function(){
	var conn = new WebSocket('ws://socket.server.com');
 
//to
var ServerEventsDispatcher = function( host ) {
	var conn = new WebSocket('ws://' + host);

While we’re here, there’s a weird bug involving the FIRST message sent from the server starting with a NULL character – if anyone here knows why or how to fix this properly please let me know. In the mean time here’s the code to fix it client-side:

// dispatch to the right handlers
conn.onmessage = function(evt) {
	data = evt.data;
	if ( !data.charCodeAt(0) )
		data = data.substr(1);
 
	var data = JSON.parse( data ),
		event_name = data[0],

Now to add in the client-side events. When the visitor loads our page, we want it to connect to the web socket server, print a ‘Connected’ message and also to allow the user to send and receive messages. ServerEventDispatcher makes all these actions very quick and simple to implement. Add the following script to your page:

<script>
	//Connect to the server
	var server = new ServerEventsDispatcher("127.0.0.1:12345/chat/server.php");
 
	//Let the user know we're connected
	server.bind('open', function() {
		log( "Connected" );
	});
 
	//OH NOES! Disconnection occurred.
	server.bind('close', function( data ) {
		log( "Disconnected" );
	});
 
	//Log any messages sent from server
	server.bind('message', function( payload ) {
		log( payload.data );
	});
 
	//Log a message to the conversation window
	function log( msg ) {
		document.getElementById('conversation').innerHTML += msg + '<br>';
	}
 
	//Does the actual sending of a message
	function send( msg ) {
		server.trigger( 'message', { data: msg } );
	}
 
	//Sends a message when user presses the 'enter' key
	function onkey( event ) {
		if ( event.keyCode == 13 )
		send( document.getElementById('message').value );
	};
</script>

…and remember to add the ‘onkey’ event to the text input field:

<input type='text' name='say' onkeypress='onkey(event)'>

That should be just about it for the client side. So what’s going on?

ServerEventDispatcher uses a very similar syntax to JQuery – so if you’re a JQuery user you should feel right at home with this syntax. We created 3 events – one for both connection and disconnection messages and one for displaying anything sent back from the server. Also created was a trigger – for sending data entered to the server. These are all pretty self explanatory and you should get the gist of things from the comments.

OK…so can we run it? Nope. Not yet.

The client side and the easy part is over but now moving on to the server script. We’ll be using PHP in a way you probably haven’t used it before. Generally when you load a webpage, your PHP scripts execute and close in the blink of an eye and that’s it – however today we’ll be running a script as a server in itself – it won’t close until it either crashes (hopefully not) or we close it. Let’s get going. I’m calling the server script server.php.

From PHPWebSockets, copy websocket.class.php to the includes folder as per my file structure above. You’ll notice that in this file there is a function called process (Around line 47). This function is given the message the visitor entered, and returns it straight back to them – in our chat application that’s a little helpful, however we want the message to be sent back to all connected users in the conversation – not just the person who sent the message. Here’s the modified function.

function process($user,$msg) {
	//$this->send($user->socket,$msg);
 
	//Send message to all connected usersusers
	foreach ( $this->sockets as $socket )
		if ( $socket != $this->master )
			$this->send( $socket, $msg );
}

We’re done! Now to fire up this bad boy

Open a terminal window (Command line on windows) and navigate to your project directory. Execute the script with PHP and if all went to plan you should get the following message:

$ php -f server.php
Server Started : 2010-05-26 22:40:03
Listening on   : localhost port 12345
Master socket  : Resource id #5

If you did then – congratulations! You may now visit your site:
http://localhost/chat

Type into the text field and your message should be echoed back to you in the conversation window. Open a second browser and connect there too. Type something and both windows should get the message. Welcome to the future.

Download the finished web application here.

UPDATE Nov 14, 2010: Since the writing of this post, the handshake spec has changed in such a way that browsers requiring the new spec will return the error code INVALID_STATE_ERR upon connection. I checked the PHPWebSocket page for an updated version but it looks like the developer hasn’t released one yet so instead I now recommend using Bohuco’s excellent websocket script instead.

  • Download Bohuco’s script from here and place into /includes/WebSocketServer.php
  • Change server.phpto the following:
    <?php
    	require dirname(__FILE__).'/includes/WebSocketServer.php';
     
    	function process(WebsocketUser $user, $msg, WebsocketServer $server)
    	{
    		$server->send( $user->socket, $msg );
    	}
     
    	$server = new WebsocketServer('localhost', 12345, 'process');
    	$server->run();

That should be all there is to it. I made one other slight modification to index.php, adding a quick and simple local echo to illustrate when a user has sent a message to the server which I’ve provided below:

//Sends a message when user presses the 'enter' key
function onkey( event ) {
	if ( event.keyCode == 13 )
	{
		var message = document.getElementById('message').value;
		log( '> ' + message );
		send( message );
	}
};

The updated download can be found here.