Facebook Like Chat Application in PHP
Reading Time:
Reading Time:
There are two tables in this design, users, and chat. This schema design does not implement a real authentication system. It just simulates a situation where both the users are logged in by passing the user id through the browser address bar i.e as a parameter.
The chat table has a foreign key reference to the users table id field.
The Chat Class (FbChatMock) contains the core methods for adding and retrieving the chat messages from the database. This class also connects to the database when the object is first created.
The methods in this class are easy to understand. The addMessage()
method adds the user-provided message by escaping it through real_escape_string
and adds the message into the database. The user id is cast into an integer so that nothing gets inserted other than an integer value.
The getMessages
method makes a JOIN statement with both the users and the chat table on the user_id field.
/**
* @author Tamil selvan k
*/
class FbChatMock {
// Holds the database connection
private $dbConnection;
//----- Database connection details --/
//-- Change these to your database values
private $_dbHost = 'localhost';
private $_dbUsername = 'root';
private $_dbPassword = '';
public $_databaseName = 'fbchat';
//----- ----/
/**
* Create's the connection to the database and stores it in the dbConnection
*/
public function __construct() {
$this->dbConnection = new mysqli($this->_dbHost, $this->_dbUsername,
$this->_dbPassword, $this->_databaseName);
if ($this->dbConnection->connect_error) {
die('Connection error.');
}
}
/**
* Get the list of messages from the chat
*
* @return array
*/
public function getMessages() {
$messages = array();
$query = "
SELECT
`chat`.`message`,
`chat`.`sent_on`,
`users`.`id`,
`users`.`username`
FROM `users`
JOIN `chat`
ON `chat`.`user_id` = `users`.`id`
ORDER BY `sent_on`";
// Execute the query
$resultObj = $this->dbConnection->query($query);
// Fetch all the rows at once.
while ($row = $resultObj->fetch_assoc()) {
$messages[] = $row;
}
return $messages;
}
/**
* Add a new message to the chat table
*
* @param Integer $userId The user who sent the message
* @param String $message The Actual message
* @return bool|Integer The last inserted id of success and false on faileur
*/
public function addMessage($userId, $message) {
$addResult = false;
$cUserId = (int) $userId;
// Escape the message with mysqli real escape
$cMessage = $this->dbConnection->real_escape_string($message);
$query = "
INSERT INTO `chat`(`user_id`, `message`, `sent_on`)
VALUES ({$cUserId}, '{$cMessage}', UNIX_TIMESTAMP())";
$result = $this->dbConnection->query($query);
if ($result !== false) {
// Get the last inserted row id.
$addResult = $this->dbConnection->insert_id;
} else {
echo $this->dbConnection->error;
}
return $addResult;
}
}
The chat.php page initially fetches the previous chat messages if there is one and displays it. This page is the main chat page. Here is where the user is stored in the session which is got from the query parameter
<?php
@session_start();
$_SESSION['user_id'] = isset($_GET['user_id']) ? (int) $_GET['user_id'] : 0;
if ($_SESSION['user_id'] === 0) {
echo '<h1>
Provide your user id</h1>
';
exit;
}
// Load the messages initially
require_once __DIR__ . '/../FbChatMock.php';
$chat = new FbChatMock();
$messages = $chat->getMessages();
?>
<div class="container" style="border: 1px solid lightgray;">
<div class="msg-wgt-header">
<a href="#">John</a>
</div>
<div class="msg-wgt-body">
<table>
<?php
if (!empty($messages)) {
foreach ($messages as $message) {
$msg = htmlentities($message['message'], ENT_NOQUOTES);
$user_name = ucfirst($message['username']);
$sent = date('F j, Y, g:i a', $message['sent_on']);
echo <<<MSG
<tr class="msg-row-container">
<td>
<div class="msg-row">
<div class="avatar"></div>
<div class="message">
<span class="user-label"><a href="#" style="color: #6D84B4;">{$user_name}</a> <span class="msg-time">{$sent}</span></span><br/>{$msg}
</div>
</div>
</td>
</tr>
MSG;
}
} else {
echo '<span style="margin-left: 25px;">No chat messages available!</span>';
}
?>
</table>
</div>
<div class="msg-wgt-footer">
<textarea id="chatMsg" placeholder="Type your message. Press Shift + Enter for newline"></textarea>
</div>
</div>
There are two main ajax pages,
session_start();
if (isset($_POST['msg'])) {
require_once __DIR__ . '/../../core/FbChatMock.php';
$userId = (int) $_SESSION['user_id'];
// Escape the message string
$msg = htmlentities($_POST['msg'], ENT_NOQUOTES);
$chat = new FbChatMock();
$result = $chat->addMessage($userId, $msg);
}
require_once __DIR__ . '/../../core/FbChatMock.php';
$chat = new FbChatMock();
$messages = $chat->getMessages();
$chat_converstaion = array();
if (!empty($messages)) {
$chat_converstaion[] = '<table>';
foreach ($messages as $message) {
$msg = htmlentities($message['message'], ENT_NOQUOTES);
$user_name = ucfirst($message['username']);
$sent = date('F j, Y, g:i a', $message['sent_on']);
$chat_converstaion[] = <<<MSG
<tr class="msg-row-container">
<td>
<div class="msg-row">
<div class="avatar"></div>
<div class="message">
<span class="user-label"><a href="#" style="color: #6D84B4;">{$user_name}</a> <span class="msg-time">{$sent}</span></span><br/>{$msg}
</div>
</div>
</td>
</tr>
MSG;
}
$chat_converstaion[] = '</table>';
} else {
echo '<span style="margin-left: 25px;">No chat messages available!</span>';
}
echo implode('', $chat_converstaion);
These scripts helps in getting the messages with an interval of 20 seconds for each message fetching. The binding of the users keyboard events is also done in this script. jQuery is needed for these scripts to work properly.
/**
* Add a new chat message
*
* @param {string} message
*/
function send_message(message) {
$.ajax({
url: '/ajax/add_msg.php',
method: 'post',
data: {msg: message},
success: function(data) {
$('#chatMsg').val('');
get_messages();
}
});
}
/**
* Get's the chat messages.
*/
function get_messages() {
$.ajax({
url: '/ajax/get_messages.php',
method: 'GET',
success: function(data) {
$('.msg-wgt-body').html(data);
}
});
}
/**
* Initializes the chat application
*/
function boot_chat() {
var chatArea = $('#chatMsg');
// Load the messages every 5 seconds
setInterval(get_messages, 20000);s
// Bind the keyboard event
chatArea.bind('keydown', function(event) {
// Check if enter is pressed without pressing the shiftKey
if (event.keyCode === 13 && event.shiftKey === false) {
var message = chatArea.val();
// Check if the message is not empty
if (message.length !== 0) {
send_message(message);
event.preventDefault();
} else {
alert('Provide a message to send!');
chatArea.val('');
}
}
});
}
// Initialize the chat
boot_chat();
/* message box header styles*/
.msg-wgt-header {
background-color: #6D84B4;
border: 1px solid rgba(0, 39, 121, 0.76);
color: white;
text-align: center;
height: 28px;
}
.msg-wgt-header a {
text-decoration: none;
font-weight: bold;
color: white;
vertical-align: middle;
}
/* message box body styles*/
.msg-wgt-body {
height: 300px;
overflow-y: scroll;
}
.msg-wgt-body table {
width: 100%;
}
.msg-row-container {
border-bottom: 1px solid lightgray;
}
.msg-row-container td {
border-bottom: 1px solid lightgray;
padding: 3px 0 3px 2px;
}
.msg-row {
width: 100%;
}
.message {
margin-left: 40px;
}
/* Message box footer styles*/
.msg-wgt-footer {
height: 50px;
}
.msg-wgt-footer textarea {
width: 95%;
font-family: 'tahoma';
font-size: 13px;
padding: 5px;
}
.user-label {
font-size: 11px;
}
.msg-time {
font-size: 10px;
float: right;
color: gray;
}
.avatar {
width: 24px;
height: 24px;
float: left;
background: url('/assets/chat_avatar.jpg');
border: 1px solid lightgray;
}