Creating a Real-Time Chat App with PHP and Node.js

About a year ago, I wrote about AJAX Long Polling in PHP which is one of the ways to create a real-time app in PHP. But, it was a nightmare to host an app built using it. (I don't know how Facebook does it) So, I found a new solution, which I will be describing here. To host this, you will need the full control of a server (probably a VPS server).

I described my idea for connecting PHP and Node.js for real-time communication at medium earlier. In this article, I will show you how to use PHP and Node.js to create a real-world application. (Chat App)

Why Node.JS? PHP isn't enough?

PHP is enough for everything. But, when talking about real-time applications, PHP has no built-in capabilities to create and manipulate them easily. True, there are third-party libraries. I haven't used them (until now), so I cannot tell you anything about those. But, this technique works for me very well. Need a prove? Check out Hyvor Talk where real-time communication is built on this technique.

Prerequisites

How Does Our Chat App Work?

I hope you got the basic idea. If not, don't worry. You will get everything after we finish our chat app. So, let's get into the code.

Creating the Database

Create a table called chat_messages with 4 columns id (int), name(varchar), time (int), message(text). Here's the SQL.


CREATE TABLE `your_db`.`chat_messages` ( 
	`id` INT NOT NULL AUTO_INCREMENT , 
	`name` VARCHAR(50) NOT NULL , 
	`time` INT NOT NULL , 
	`message` TEXT NOT NULL , 
	PRIMARY KEY (`id`)
) ENGINE = InnoDB;


Creating the project structure

Here's the project structure...


/
	/node
		server.js
	index.php
	js-index.js
	php-send-message.php
	php-db.php
	.htaccess


Note that I prepend the file extensions to file name (php-) to make it easy to find each type of files in the editor when sorted in the alphabetical order. Do it only if you like.

Creating the MYSQLI Connection

The php-db.php file will have the MYSQLi database connection. Replace the credential with your ones.


<?php
$mysqli = new mysqli('HOST', 'USERNAME', 'PASSWORD', 'DATABASE');


index.php (Step-By-Step)

First, we will select the last 20 messages in the ascending order from the database.


<?php
include_once 'php-db.php';
$result = $mysqli -> query('
	SELECT * FROM (
		SELECT name, message, time FROM chat_messages ORDER BY time DESC LIMIT 20
	) tmp ORDER BY time ASC
');
?>


See this to learn more about selecting the last few rows in the ascending order.

Next thing: the HTML and CSS Content. I won't be focusing much on HTML and CSS. Here's a very basic template that would work. We will be echoing the fetched last 20 messages in our message box using a simple PHP loop.


<!DOCTYPE html>
<html>
<head>
	<title>Chat App by Hyvor Developer</title>

<style type="text/css">
	* {
		box-sizing:border-box;
	}
	#content {
		width:600px;
		max-width:100%;
		margin:30px auto;
		background-color:#fafafa;
		padding:20px;
	}
	#message-box {
		min-height:400px;
		overflow:auto;
	}
	.author {
		margin-right:5px;
		font-weight:600;
	}
	.text-box {
		width:100%;
		border:1px solid #eee;
		padding:10px;
		margin-bottom:10px;
	}
</style>

</head>
<body>
<div id="content">
	<div id="message-box">
		<?php foreach ($result as $row) : ?>
			<div>
				<span class="author"><?= $row['name'] ?>:</span>
				<span class="messsage-text"><?= $row['message'] ?></span>
			</div>	
		<?php endforeach; ?>	
	</div>
	<div id="connecting">Connecting to web sockets server...</div>
	<input type="text" class="text-box" id="name-input" placeholder="Your Name">
	<input type="text" class="text-box" id="message-input" placeholder="Your Message" onkeyup="handleKeyUp(event)">
	<p>Press enter to send message</p>
</div>
<script type="text/javascript" src="js-index.js"></script>
</body>
</html>


Full index.php Code

Now we have the template. We need some javascript and PHP backend to save the message in the database.

Writing Javascript (js-index.js)

Let's save our two inputs in variables for ease.

js-index.js


var nameInput = document.getElementById("name-input"),
	messageInput = document.getElementById("message-input");


The keyup handler and send message function.


function handleKeyUp(e) {
	if (e.keyCode === 13) {
		sendMessage();
	}
}
function sendMessage() {
	var name = nameInput.value.trim(),
		message = messageInput.value.trim();

	if (!name)
		return alert("Please fill in the name");

	if (!message)
		return alert("Please write a message");

	var ajax = new XMLHttpRequest();
	ajax.open("POST", "php-send-message.php", true);
	ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	ajax.send("name=" + name + "&message=" + message);

	messageInput.value = "";
}


handleKeyUp() function gets executed when user types on the message input. If he hits enter, the sendMessage() function will be executed.

Then, sendMessage() function sends the name and message to php-send-message.php script which we will be creating in the next steps. Here we use the POST method. If you like you can use a library like jQuery to do this easily.

WebSocket Handling - Client

In the same file (js-index.js), we will add functions to handle Web sockets.


window.WebSocket = window.WebSocket || window.MozWebSocket;

var connection = new WebSocket('ws://localhost:8080');
var connectingSpan = document.getElementById("connecting");
connection.onopen = function () {
	connectingSpan.style.display = "none";
};
connection.onerror = function (error) {
	connectingSpan.innerHTML = "Error occured";
};
connection.onmessage = function (message) {
	var data = JSON.parse(message.data);

	var div = document.createElement("div");
	var author = document.createElement("span");
		author.className = "author";
		author.innerHTML = data.name;
	var message = document.createElement("span");
		message.className = "messsage-text";
		message.innerHTML = data.message;

	div.appendChild(author);
	div.appendChild(message);

	document.getElementById("message-box").appendChild(div);

}


Full js-index.js code

HTTP and Websockets Server in Node.JS

HTTP Server

This is the most interesting part. In this step, we will be creating a HTTP and Websockets server in Node.JS.

Before you go, you will need to install websockets package. Run the following command in your terminal (or cmd) to install it.


npm install websockets


Let's create our server file. I'll break the server file into parts for ease.

node/server.js


"use strict";
var http = require('http');
var url = require('url');

var WebsocketServer = require('websocket').server;


Here's the code for our HTTP server.


var server = http.createServer(function(request,response) {
	function getPostParams(request, callback) {
	    var qs = require('querystring');
	    if (request.method == 'POST') {
	        var body = '';

	        request.on('data', function (data) {
	            body += data;
	            if (body.length > 1e6)
	                request.connection.destroy();
	        });

	        request.on('end', function () {
	            var POST = qs.parse(body);
	            callback(POST);
	        });
	    }
	}
    // in-server request from PHP
    if (request.method === "POST") {
    	getPostParams(request, function(POST) {	
			messageClients(POST.data);
			response.writeHead(200);
			response.end();
		});
		return;
	}
});
server.listen(8080);


First, we create a HTTP server in Node.JS (If you like to learn more about HTTP servers in Node.JS visit here). The server will listen to the port 8080.

When someone tries to access the HTTP server, the callback function in http.createServer() will be executed.

Let's see what the function does.

Unlike PHP, Node.JS is fully async. In PHP we can directly access $_POST when PHP runs on a server. But, in Node.JS we have to read the POST data and then run a callback after the reading. So, we create the getPostParams() function.

Reminder: PHP will send a HTTP POST request to the Node.JS server. That's what I told earlier. So, if the request.method is "POST", it should be a request from PHP. Then, we will send the message from PHP (POST.data) to all the clients. We will create the messageClients() function in the next step.

WebSocket Server

In the same server.js file, we will create our websocket server.


var websocketServer = new WebsocketServer({
	httpServer: server
});
websocketServer.on("request", websocketRequest);
// websockets storage
global.clients = {}; // store the connections
var connectionId = 0; // incremental unique ID for each connection (this does not decrement on close)
function websocketRequest(request) {
	// start the connection
	var connection = request.accept(null, request.origin); 
	connectionId++;
	// save the connection for future reference
	clients[connectionId] = connection;
}
// sends message to all the clients
function messageClients(message) {
	for (var i in clients) {
		clients[i].sendUTF(message);
	}
}

Full Source code of server.js

PHP Ajax Request Handler

The last coding task is to create the AJAX handler in PHP for sending messages.


<?php
include_once 'php-db.php';

// get the input
$name = trim(htmlspecialchars($_POST['name'] ?? ''));
$message = trim(htmlspecialchars($_POST['message'] ?? ''));

if (!$name || !$message)
	die;

// insert data into the database
$stmt = $mysqli -> prepare('INSERT INTO chat_messages (name, time, message) VALUES (?,?,?)');
$time = time();
$stmt -> bind_param('sis', $name, $time, $message);
$stmt -> execute();
	
// Send the HTTP request to the websockets server (it runs both  HTTP and Websockets)
// (change the URL accordingly)
$ch = curl_init('http://localhost:8080');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");

// we send JSON encoded data to the Node.JS server
$jsonData = json_encode([
	'name' => $name,
	'message' => $message
]);
$query = http_build_query(['data' => $jsonData]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);


In this PHP script, first, we add the name and message into the database (with a timestamp). Then, we send a HTTP POST request to our Node.js server with a POST param data which is a JSON encoded string which contains the name and the message. This data param will be sent to the client directly.

If you don't have cURL installed, you can use any other method to make the HTTP request. (Check the internet)

Starting the Node.JS server

Node.JS server is a long-running server. Assume that you have uploaded this chat app online. And, your server run into problems and stops running. So, do your users need to stay until you get into the server and re-run the Node.js script?

There's an awesome manager for Node.JS called PM2. If you need to learn more about it just visit their website. They have nice descriptions on its features.

Let's install PM2. Run the following code in the terminal to install it.


npm install pm2 -g


Final step: Start the Node.js server.

Yay! you have just created a chat app using PHP and Node.JS. Visit your chat app from our browser and check if everything works fine. If something went wrong, read the debugging section. Otherwise, skip it.

Debugging

If you did everything correctly, it should work fine. But, you know, any program can have bugs. Here are some tips that might help.

Conclusion

In my point of view, this approach is a really good one for connecting PHP and Node.JS especially for adding real-time support for PHP web apps.

In this tutorial we created a simple chat app which works in real-time. There are some points that you can improve.

But, there's a one thing: You will need to do some hard work to check if the user is logged in from Node.JS (like sharing sessions). But, this can lead to security risks if you do not do it correctly. At the moment, I'm experimenting on this. I'll update this article I find a good solution for that. If you have an idea, please let me know.

All the code of this tutorial is available on Github. (Note that source code is very well commented)

Finally, thank you for reading. If you like this tutorial, don't forget to share it.

Tagged: PHP Node.JS
Liked the article? Follow me on Twitter, or help Hyvor grow by funding Hyvor Talk.
Latest on Hyvor Developer
PHP Beginner's Tutorial
Beginner's PHP Tutorial
Image for All About MYSQLI Prepared Statements in PHP
All About MYSQLI Prepared Statements in PHP
Image for Image Upload with AJAX, PHP, and MYSQL - The Beginner's Guide
Image Upload with AJAX, PHP, and MYSQL - The Beginner's Guide
Image for PHP Contact Form - The Email Method
PHP Contact Form - The Email Method
Image for The Best Way to Perform MYSQLI Prepared Statements in PHP
The Best Way to Perform MYSQLI Prepared Statements in PHP
Image for Live Search with AJAX, PHP, and MYSQL
Live Search with AJAX, PHP, and MYSQL
Related Articles
335