Do you need to develop a real-time application using JavaScript and PHP? There are several methods for client-side interactions with the server in real-time, including short polling, long polling, Web Sockets, and Server-Sent Events (SSE).
Short polling and SSE may not be the best choices for a website. Instead, you can consider using long polling or Web Sockets. Long polling is supported by most web browsers, making it a reliable option, while Web Sockets may not be compatible with older browsers. However, it’s important to note that long polling can consume more CPU capacity than Web Sockets. Many popular websites, such as Facebook, utilize long polling for real-time updates. In this article, we will walk you through the process of creating a long polling script with PHP. Additionally, we will provide advanced long-polling examples towards the end of the article.
Ajax Long Polling is a technique used for real-time data updates in web applications, and understanding it can complement your knowledge of PHP array shorthand. If you found this article interesting, you may also like to explore related topics in web development and PHP best practices.
Here’s how it works:
- An AJAX request is sent from the browser (using JavaScript) to the web server;
- The web server continuously checks for updates;
- When an update is detected, the server stops looping and sends a response to the browser;
- JavaScript processes the response and updates the webpage’s DOM;
- All of these operations occur asynchronously in real time.
By the end of this article, you’ll have a clear understanding of how to implement long polling in your real-time web applications using PHP.
Creating JavaScript Code for AJAX Requests
To begin, we need to send an AJAX request from the browser to the server to achieve our desired outcome. It is assumed that you have a basic understanding of using AJAX with JavaScript. If you need a more comprehensive guide, you can follow my tutorial on this topic.
Here’s the JavaScript code for sending the AJAX request:
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// when everything goes fine...
}
};
xhttp.open("GET", "long-polling.php", true);
xhttp.send();
Here’s what the code above accomplishes:
- It creates a new XMLHttpRequest object and assigns it to the variable xhttp;
- It defines the onreadystatechange function, which will be executed each time the ready state of the AJAX request changes. We’ll explain more about creating this function to handle the response in the final examples;
- Next, it opens the request using the GET method. The GET method is preferred because it is fast and works well with long-polling. However, be cautious not to send sensitive data with the request;
- “long-polling.php” is the file that we will create in the next step;
- Finally, it sends the request to the server.
This code serves as the foundation for interacting with the server using AJAX in your web applications. In the upcoming examples, we will delve into handling responses and implementing long-polling effectively.
Backend Concepts for Effective Web Development
In the realm of backend web development, there are crucial concepts and techniques that can significantly impact your web applications. Let’s explore some fundamental elements that are essential for building robust and efficient backend systems.
The Infinite Loop
Understanding how infinite loops work is fundamental to long-polling and other real-time processes. Here’s an example of an infinite loop in PHP:
<?php
while(true) {
echo 'yes'; // this will be executed unlimited times
}
Long-polling relies on infinite loops, which continuously run as long as the condition remains true. It’s important to note that an uncontrolled infinite loop can freeze your browser, so use caution.
Ending the Execution
Breaking out of an infinite loop and sending a response to the client is crucial. In the following script, we demonstrate three methods to break out of loops:
<?php
while(true) {
// escape from the infinite loop
break;
// escape from multiple loops
for ($i = 0; $i < 10000; $i ) {
if ($foundAnUpdate) {
break 2; // escapes from the for loop & the infinite loop
}
}
// output a message and terminate the script
if ($foundAnUpdate) {
exit(json_encode[
'status' => 'success'
]);
}
}
- The first type of break statement is used to exit one loop, and the code immediately after the loop is executed;
- Adding a number after the break statement allows you to break out of a specified number of loops (as seen in the example, “2” breaks both loops);
- The exit function is used to output a message and terminate the script, with the JSON-encoded string as the output.
Other Useful Functions
Several PHP functions play a crucial role in backend development:
- session_write_close(): Cancels the usage of sessions in the script, as using session data in long polling can freeze the browser;
- ignore_user_abort(): Defines whether the script should stop when a browser request is aborted;
- set_time_limit(): Sets the maximum execution time for the script. A value of zero allows the script to run indefinitely, so consider setting a time limit to maintain server health;
- sleep() and usleep(): Useful for introducing pauses in the script to prevent it from affecting server performance. It’s advisable to use these functions judiciously.
Here’s an example of incorporating some of these functions into an infinite loop:
<?php
session_write_close();
ignore_user_abort(true);
set_time_limit(0);
while(true) {
// here goes the loop
sleep(1);
}
Code Explanation
In this section, we’ll break down the code’s functionality in a straightforward manner, explaining the purpose of each function call without poetic embellishments.
- Managing Sessions with session_write_close(): We start by using session_write_close() to stop any further writing to sessions within the script. This is important for efficient session management, especially in long-polling and similar processes. Using session data in such scenarios can cause browser tab freezing and hinder your application’s responsiveness;
- Handling Browser Aborts with ignore_user_abort(): Next, we use ignore_user_abort(true) to tell the script to gracefully terminate if the user cancels the request in their browser. Setting it to “true” ensures that the script stops when the user cancels, which can be a more efficient use of server resources. However, if your application needs to continue running even when the browser cancels the request, you can set this parameter to “false.”;
- Managing Execution Time with set_time_limit(): To control script execution time precisely, we use set_time_limit(0), effectively allowing the script to run indefinitely. While this can be suitable for some scenarios, it’s essential to consider server load and user traffic. If your website has a lot of users, it’s wise to set a reasonable execution time limit, usually between 30 to 40 seconds. This helps prevent server overload and ensures smoother performance;
- Regulating Execution with sleep() and usleep(): As a practical measure, we introduce the sleep(1) function, which adds a one-second pause in the script’s execution. This pause helps control the script’s pace and align it with your application’s needs. For more precise control, you can also use the usleep() function to set sleep times in milliseconds.
Illustrative Examples
Below are two examples for your reference. You can download a .zip file containing both examples at the end of this section.
Example 1: Monitoring File Updates Using the Cookie Method
In this example, an “index.html” file serves as the client-side component. It is responsible for sending an AJAX request to “long-polling.php,” which functions as an AJAX Request Handler on the server side.
<html>
<head>
<title>Listen to File Update</title>
</head>
<body>
<script type="text/javascript">
function poll() {
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
if (this.status === 200) {
try {
var json = JSON.parse(this.responseText);
} catch {
poll();return;
}
if (json.status !== true) {
alert(json.error);return;
}
var div = document.createElement("DIV");
document.body.appendChild(div);
div.innerHTML = 'time: ' json.time ', content: ' json.content;
poll();
} else {
poll();
}
}
}
ajax.open('GET', 'long-polling.php', true);
ajax.send();
}
poll();
</script>
</body>
</html>
<?php
session_write_close();
ignore_user_abort(false);
set_time_limit(40);
try {
// lastUpdate cookie saves the file update time which was sent to the browser
if (!isset($_COOKIE['lastUpdate'])) {
setcookie('lastUpdate', 0);
$_COOKIE['lastUpdate'] = 0;
}
$lastUpdate = $_COOKIE['lastUpdate'];
$file = 'file.txt';
if (!file_exists($file)) {
throw new Exception('file.txt Does not exist');
}
while (true) {
$fileModifyTime = filemtime($file);
if ($fileModifyTime === false) {
throw new Exception('Could not read last modification time');
}
// if the last modification time of the file is greater than the last update sent to the browser...
if ($fileModifyTime > $lastUpdate) {
setcookie('lastUpdate', $fileModifyTime);
// get file contents
$fileRead = file_get_contents($file);
exit(json_encode([
'status' => true,
'time' => $fileModifyTime,
'content' => $fileRead
]));
}
// to clear cache
clearstatcache();
// to sleep
sleep(1);
}
} catch (Exception $e) {
exit(
json_encode(
array (
'status' => false,
'error' => $e -> getMessage()
)
)
);
}
In this illustration, the methodology relies on utilizing a cookie. During the initial request, a cookie named “lastUpdate” is generated and transmitted to the client’s browser. This cookie serves as a repository for the most recent file modification time of “file.txt,” which was delivered to the browser.
In subsequent requests, the script continually iterates until the current modification time of “file.txt” surpasses the value stored in the “lastUpdate” cookie. When this condition is met, a JSON response is dispatched to the client’s browser. The JavaScript code embedded within the AJAX request dynamically generates a new <div> element and populates it with relevant content.
Example 2: Real-Time Database Updates
In this scenario, we implement a solution for monitoring database updates in real-time. Rather than relying on cookies for data storage, we utilize cookies solely to save user IDs. All timestamps and other relevant data are stored in the database.
- Our database comprises two essential tables: “db_user_data” for user-specific information and “db_updating_table” for housing new messages;
- During the initial request, a user cookie is established for user identification purposes;
- In the subsequent loop, the system continually checks for the presence of new rows within the “db_updating_table.” If any such rows exist, the column values of these rows are transmitted to the user’s browser in real-time;
- To facilitate real-time updates to the “db_updating_table,” we’ve incorporated the “update-other-table.php” script. When a user accesses “index.html” and another user requests “update-other-table.php” to create a new message (or a new row), the first user is instantly able to view the new message in real-time.
We recommend downloading the accompanying .zip file and experimenting with the code firsthand. Detailed explanations, line-by-line comments, and code modifications for “index.html” are provided for your convenience.
function poll() {
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
if (this.status === 200) {
try {
var json = JSON.parse(this.responseText);
} catch {
poll();return;
}
if (json.status !== true) {
alert(json.error);return;
}
var data = json.data;
for (var i = 0, len = data.length; i < len; i ) {
var x = data[i];
var div = document.createElement("DIV");
document.body.appendChild(div);
div.innerHTML = 'time: ' x.time ', content: ' x.content;
}
poll();
} else {
poll();
}
}
}
ajax.open('GET', 'long-polling.php', true);
ajax.send();
}
poll();
<?php
session_write_close();
ignore_user_abort(false);
set_time_limit(40);
try {
// connect to the db ($mysqli)
include_once '../config.php';
if (empty($_COOKIE['user'])) {
$user = rand(0,10000000);
// send to the browser
setcookie('user', $user);
// save in global variable
$_COOKIE['user'] = $user;
// add user to the database
$mysqli -> query("INSERT INTO db_user_data VALUES ($user, 0)");
// first request does not do anything than creating the cookie
exit();
}
// get the user value
$user = $_COOKIE['user'];
while (true) {
// select new rows
$result = $mysqli -> query("SELECT t.id, t.content, t.time FROM db_updating_table t INNER JOIN db_user_data ud ON ud.last_sent_id < t.id WHERE ud.user = $user ORDER BY t.id");
if ($result && $result -> num_rows) {
$output = [];
$lastId = 0;
foreach ($result as $row) {
$output[] = [
'content' => $row['content'],
'time' => $row['time']];
$lastId = $row['id'];
}
$mysqli -> query("UPDATE db_user_data SET last_sent_id = $lastId WHERE user = $user");
echo json_encode([
'status' => true,
'data' => $output
]);
exit;
}
// db queries are heavy. So 2 seconds
sleep(2);
}
} catch (Exception $e) {
exit(
json_encode(
array (
'status' => false,
'error' => $e -> getMessage()
)
)
);
}
<?php
include_once '../config.php';
$content = !empty($_GET['content']) ? $_GET['content'] : 'No Message Defined';
$time = time();
$mysqli -> query("INSERT INTO db_updating_table (content,time) VALUES ('$content', $time)");
Continuously querying the database demands a robust server infrastructure. If you possess such a server, there's no issue with pursuing this approach.
Continuously querying the database demands a robust server infrastructure. If you possess such a server, there’s no issue with pursuing this approach.
Conclusion
This guide offers an in-depth understanding of how to use AJAX long polling with different techniques. It aims to provide a comprehensive resource for developers striving to create interactive real-time applications. With its detailed examples, it demonstrates the practical application of the concepts discussed. The guide encourages you to leverage these insights to create an optimal real-time web experience for your users.