How-to: Basic Chat App w/ PHP, jQuery & Ajax

Posted on July 24, 2012 by Jimmy K. in Projects, Tutorials.

One of my friends recently asked for assistance with a chat application he’s developing and, while his is a bit more robust than the one I set out to create, I decided to build one from the ground up to wrap my head around it. The code below took a few hours to compile, but is simple enough that it should work right out of the box.

This project is a very basic chat application that allows users to specify a chat handle (likely their name) and a message. When the user clicks the “Post Message” button or clicks enter, the message is sent using jQuery and Ajax to the PHP processor where the message is validated, stored, and then displayed to the users. For simplicity, I opted to use a flat file storage system (a simple text file) rather than using a MySQL database. If your project demands a database-driven solution, you will want to adjust any relevant code in the PHP back-end.

A working example of this project can be found here.

I’ve separated this project into three pieces: The HTML front-end, the JavaScript back-end, and the PHP back-end.

The HTML Front-end

The contents of this file are located in /index.php. This file includes a reference to the PHP back-end (/inc/ajaxchat.inc.php) and the JavaScript back-end (/js/ajaxchat.js). This file contains all the elements required for the chat to display properly.


	<?php require_once("./inc/ajaxchat.inc.php"); ?>

	<!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>
	<title>Ajax Chat Example</title>

	<!-- Mobile-Friendly Tags -->
	<meta name="viewport" content="width=440,initial-scale=1.0,user-scalable=yes" />
	<!-- Mobile-Friendly Tags -->

	<link href="http://cloud.endseven.net/css/default/1.1/style.css" media="all" rel="stylesheet" />

	<style type="text/css">

		#Ajax-Chat-Window { width: 390px; height: 200px; background: #f0f0f0; color: #333; font-size: 11px; line-height: 16px; overflow: scroll; overflow: -moz-scrollbars-vertical; padding: 5px; }

	</style>

	<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
	<script type="text/javascript" src="http://cloud.endseven.net/jquery/ajaxrequest/1.1/ajaxrequest.js"></script>
	<script type="text/javascript" src="./js/ajaxchat.js"></script>
	</head><body style="background: #333;">

	<div style="background: #fff; display: inline-block; margin: 10px; padding: 10px;">
	<table style="width: 400px; height: auto;">
	<tr style="width: 400px; height: auto;">
	<td style="width: 400px; height: auto; padding-bottom: 20px;"><div id="Ajax-Chat-Window"></div></td>
	</tr>
	<tr style="width: 400px; height: auto;">
	<td style="width: 400px; height: auto;">

		<form name="PostMessage" action="" method="post" onsubmit="ePostMessage(); return false;">
		<input type="hidden" name="FormType" value="PostMessage" />

		<div class="input-field">Handle:</div>
		<input type="text" name="Handle" />

		<div class="input-field">Message:</div>
		<input type="text" name="Message" autocomplete="off" />

		<div><input type="submit" value="Post Message" /></div>

		</form>

	</td>
	</tr>
	</table>
	</div>

	</body>
	</html>

JavaScript Back-end

The contents of this file are located in /js/ajaxchat.js. This file contains all the JavaScript, jQuery, and Ajax functionality including asynchronous Ajax requests, automatic scrolling, form validation, etc.

	var $bUseAutoScroll = true; // helps control auto-scrolling..
	var $iAutoScrollTimeout; // helps control auto-scrolling..
	var $sCachedChatWindowText = ""; // helps control auto-scrolling..

	var $iUpdateChatWindowTimeout; // used to update the chat window..
	var $oAjaxRequest_UpdateChatWindow = new AjaxRequest(); // used to update the chat window..
	var $oUpdateChatWindowTarget; // used to update the chat window..

	/* Execute an ajax request. */
	function jAjaxRequestHandler($sFlag, $oArguments) {

		if ($sFlag == "UPDATE-CHAT-WINDOW") {

			$oAjaxRequest_UpdateChatWindow.$sLoadingHTML = $oUpdateChatWindowTarget.innerHTML;
			$oAjaxRequest_UpdateChatWindow.$sErrorHTML = "Unable to load content. :(";
			$oAjaxRequest_UpdateChatWindow.$sRequestURL = "./index.php";
			$oAjaxRequest_UpdateChatWindow.$oRequestVars = { Request: "UpdateChatWindow" };
			$oAjaxRequest_UpdateChatWindow.jExecuteRequest($oUpdateChatWindowTarget, $oUpdateChatWindowTarget, function() { eAjaxRequestCallback($sFlag, $oArguments); });

		} else if ($sFlag == "POST-MESSAGE") {

			var $oAjaxRequest_PostMessage = new AjaxRequest();
			$oAjaxRequest_PostMessage.$sRequestURL = "./index.php";
			$oAjaxRequest_PostMessage.$oRequestVars = { Request: "PostMessage", Handle: escape(document.forms['PostMessage']['Handle'].value), Message: escape(document.forms['PostMessage']['Message'].value) };
			$oAjaxRequest_PostMessage.jExecuteRequest(null, null, function() { eAjaxRequestCallback($sFlag, $oArguments); });

		}

	}

	/* Handle ajax request callbacks. */
	function eAjaxRequestCallback($sFlag, $oArguments) {

		if ($sFlag == "POST-MESSAGE") {

			// update the chat window..
			jAjaxRequestHandler("UPDATE-CHAT-WINDOW", {});

			// enable the auto-scroll..
			$bUseAutoScroll = true;

			// reset the input fields..
			document.forms['PostMessage']['Message'].value = "";
			document.forms['PostMessage']['Message'].focus();

		} else if ($sFlag == "UPDATE-CHAT-WINDOW") {

			// set a timeout to update the chat window..
			$iUpdateChatWindowTarget = setTimeout('jAjaxRequestHandler("UPDATE-CHAT-WINDOW", {});', 10000);

			if ($bUseAutoScroll && $sCachedChatWindowText != $("#Ajax-Chat-Window").html()) {

				// automatically scroll to the bottom..
				$("#Ajax-Chat-Window").animate({ scrollTop: $("#Ajax-Chat-Window").prop("scrollHeight") }, 1000);

			}

			// update the cached chat window text..
			$sCachedChatWindowText = $("#Ajax-Chat-Window").html();

		}

	}

	/* Document is ready. */
	$(document).ready(function() {

		// get the chat window target..
		$oUpdateChatWindowTarget = document.getElementById("Ajax-Chat-Window");

		// update the chat window..
		jAjaxRequestHandler("UPDATE-CHAT-WINDOW", {});

		$("#Ajax-Chat-Window").scroll(function() {

			if ($bUseAutoScroll) {

				// set a timeout to reset the auto-scroll flag..
				$iAutoScrollTimeout = setTimeout("$bUseAutoScroll = true;", 30000);

				// disable the auto-scroll..
				$bUseAutoScroll = false;

			}

		});

	});

	/* Triggered when the form is submitted. */
	function ePostMessage() {

		var $oForm = document.forms['PostMessage'];

		if ($oForm['Handle'].value == "") {

			alert('Please specify a chat handle and try again.');
			return false;

		} else if ($oForm['Message'].value == "") {

			return false;

		}

		jAjaxRequestHandler("POST-MESSAGE", {});

	}

PHP Back-end

The contents of this file are located in /inc/ajaxchat.inc.php. This file contains all the PHP functionality for validating, storing, and serving chat text.

<?php

	define("AC_LOG_FILENAME", "chat.txt"); // the filename of the chat log..
	define("AC_NUM_LINES_CLEANUP", 500); // the number of lines to trigger a chat log cleanup..
	define("AC_NUM_LINES_DISPLAYED", 30); // the number of lines displayed..
	define("AC_ALLOWED_HTML_TAGS", "<b><u><i><strong>"); // the allowed html tags..

	/* Make chat text safe. */
	function jMakeChatSafe($sValue) {

		$sValue = stripslashes($sValue); // $_POST values sometimes add slashes..
		$sValue = urldecode($sValue); // decode the value because of the JavaScript escape() function..
		$sValue = strip_tags($sValue, AC_ALLOWED_HTML_TAGS); // only allow specific html tags..
		$sValue = str_replace("|", "", $sValue); // we use the pipe as a delimiter..
		return $sValue;

	}

	/* Close any open tags. */
	function jCloseOpenTags($sValue) {

		preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $sValue, $aResults);
		$aOpenTags = $aResults[1];
		$aOpenTags = array_reverse($aOpenTags); // reverse the open tags..

		preg_match_all('#</([a-z]+)>#iU', $sValue, $aResults);
		$aClosedTags = $aResults[1];

		if (count($aClosedTags) == count($aOpenTags)) {
			return $sValue; // all tags are already closed..
		}

		for ($i = 0; $i < count($aOpenTags); $i++) {

			if (!in_array($aOpenTags[$i], $aClosedTags)){
				$sValue .= "</" . $aOpenTags[$i] . ">"; // append the closed tag..
			} else {
				unset($aClosedTags[array_search($aOpenTags[$i], $aClosedTags)]);
			}

		}

		return $sValue;

	}

	if ($_POST['Request'] == "UpdateChatWindow") {

		if (file_exists(AC_LOG_FILENAME)) {

			$sChatText = file_get_contents(AC_LOG_FILENAME); // load the chat text..
			$aChatText = explode("\n", $sChatText); // explode by lines..

			for ($i = sizeof($aChatText) - AC_NUM_LINES_DISPLAYED; $i < sizeof($aChatText); $i++) {

				if (!empty($aChatText[$i])) {

					list($iStamp, $sHandle, $sMessage) = explode("|", $aChatText[$i]); // extract the parts..
					echo "[" . @date("h:i:s A", $iStamp) . "] <strong>" . jCloseOpenTags($sHandle) . "</strong>: " . jCloseOpenTags($sMessage) . "<br />";

				}

			}

			if (sizeof($aChatText) > AC_NUM_LINES_CLEANUP) {

				$oHandle = fopen(AC_LOG_FILENAME, "w+");

				for ($i = sizeof($aChatText) - AC_NUM_LINES_DISPLAYED; $i < sizeof($aChatText); $i++) {

					if (!empty($aChatText[$i])) {
						fwrite($oHandle, $aChatText[$i] . "\n");
					}

				}

				fclose($oHandle);

			}

		}

		exit;

	} else if ($_POST['Request'] == "PostMessage") {

		$sMessage = jMakeChatSafe($_POST['Message']);
		$iStamp = date("U");

		$sHandle = jMakeChatSafe($_POST['Handle']);
		$sHandle = strip_tags($sHandle); // don't allow any html tags..
		$sHandle = preg_replace("/[^A-Za-z0-9 ]/", "", $sHandle); // only allow alpha-numeric characters..

		$oHandle = fopen(AC_LOG_FILENAME, "a+");
		fwrite($oHandle, "$iStamp|$sHandle|$sMessage\n");
		fclose($oHandle);

		exit;

	}

	?>

And that’s it! This project utilizes portions of pre-existing projects including:

  • [jQuery] AjaxRequest v1.1
  • [CSS] Default v1.1

And possibly [JavaScript] Form Validator v1.1 in the future. All of the aforementioned files are available for download or hot-linking from the END[SEVEN] Cloud.

Click here to download the source files for this project.

Tags: , , , , ,

 

Jimmy K. is a Chicago-based web developer who actively posts tutorials, articles and insights on his web development blog to help other programmers and developers.

You can find Jimmy on Google+ and Twitter.

 
 
 
 
 

If you like this, please leave a comment.

Name (required)
Email Address (required)
Website
Comments: