A Super Lean Longpolling Javascript/PHP Chat
Using a text file and cleverly evaluating byte counts, we can create a fairly smart base for a chat powered by a simple JS front end and PHP back end. Using jQuery and bootstrap, the actual code is less than 200 lines!
First, try out the demo:
Viewing source on the demo page will reveal the front end secrets, but the chat program loop is actually relatively simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | var chat = function() { var username=""; var currentBytes = 0; var channel = ""; var timer = {}; function listenToChannel(ch, cb) { var timer = setInterval(function(){ $.get("chat.php?channel",{name:ch},function(resp) { if(resp.size > currentBytes) { if(typeof(cb) == "function") { cb(currentBytes); } currentBytes = resp.size; } }); },1000); return timer; } function loadNewMessages(ch, start, cb) { $.get("chat.php?messages",{channel:ch,bytes:start},function(resp) { $.each(resp, function(i, msg){ $("#room-messages").append($(" <div></div> ") .append($(" <div></div> ") .addClass("chat-user") .html(msg.user)) .append($(" <div></div> ") .addClass("chat-text") .html(msg.text) ).addClass("chat-line")); }); $("#room-messages").scrollTop($("#room-messages")[0].scrollHeight); if(typeof(cb) == "function") { cb(); } }); } $(document).ready(function(){ var channelSizes = {}; // Always get rooms when page loads $.get("chat.php?channels",function(resp) { $("#channel-select").html(" <option>Choose room...</option>"); $.each(resp,function(i, channel) { channelSizes[channel.value]=channel.size; $("#channel-select").append(" <option value='" +channel.value+"'>"+channel.name+"</option>"); }); $("#channel-select").append(" <option value='new'>New...</option>"); }); // When a channel is selected, load chat $("#start-chat").click(function(){ channel = $("#channel-select").val(); username = $("#username").val(); currentBytes = channelSizes[channel]; timer = listenToChannel(channel, function(bytes){ loadNewMessages(channel, bytes); }); $("#choose-room-div").hide(); $("#chat-room-div").slideDown(); }); $("#send-chat").click(function(){ if($("#message-text").val()=="") return; var data = { "message": $("#message-text").val(), "user": username, "channel":channel }; $.post("chat.php?sendmessage",data,function(resp){ loadNewMessages(currentBytes); $("#message-text").val(""); }); }); $("#leave-chat").click(function(){ clearInterval(timer); $("#choose-room-div").slideDown(); $("#chat-room-div").hide(); }); $('#message-text').keydown(function (e){ if(e.keyCode == 13){ $("#send-chat").trigger("click"); } }) }); }(); |
And here’s the backend (chat.php).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | <?php // Get transcript size function getChannel($name) { $ret = []; $file = __DIR__ . "/transcripts/".$name; $ret['size'] = filesize($file); return json_encode($ret); } // Return a list of channels function getChannels() { $ret = []; $dir = __DIR__ . "/transcripts/"; if ($handle = opendir($dir)) { while (false !== ($entry = readdir($handle))) { //echo $entry; if(is_file($dir.$entry)) { list($name, $date) = explode("|",$entry); $ret[] = [ 'name' => $name, 'date' => $date, 'value' => $entry, 'size' => filesize($dir.$entry) ]; } } closedir($handle); } return json_encode($ret); } // Create a new channel function setChannel($name) { } // Get the messages in a channel starting at a buffer function getChannelMessages($channel, $bytes=0) { $ret = []; $file = __DIR__ . "/transcripts/".$channel; if($handle = fopen($file, 'r')) { fseek($handle, $bytes); while (($line = fgets($handle)) !== false) { // process the line read. list($msg,$user,$time) = explode("|",$line); $ret[] = [ 'text' => $msg, 'user' => $user, 'time' => $time ]; } fclose($handle); } return json_encode($ret); } // Add a message to a channel file function setChannelMessage($channel, $user, $message) { $file = __DIR__ . "/transcripts/".$channel; $message = str_replace("|","",$message); $message = htmlentities($message); $line = $message."|".$user."|".time().PHP_EOL; $handle = fopen($file, 'a'); fwrite($handle, $line); fclose($handle); $ret['size'] = filesize($file); return json_encode($ret); } /* * Output to the browser */ header('Content-Type: application/json'); // Post routes if(isset($_POST)) { if(isset($_GET['setChannel'])) { setChannel($_POST['name']); } if(isset($_GET['sendmessage'])) { echo setChannelMessage($_POST['channel'],$_POST['user'],$_POST['message']); } } // Get routes if(isset($_GET)) { if(isset($_GET['messages'])) { echo getChannelMessages($_GET['channel'],$_GET['bytes']); die(); } if(isset($_GET['channel'])) { echo getChannel($_GET['name']); } if(isset($_GET['channels'])) { echo getChannels(); } } ?> |
0 Comments