Turn a classic LAMP stack into a real‑time experience. Learn how to implement PHP long polling, when to use it instead of WebSockets and how to keep it fast, stable and business‑ready.
This guide is designed to capture long‑tail searches like “php long polling example”, “php ajax long polling tutorial” and “slim php long polling”, while giving you a clear path from concept to implementation.
- What is long polling?
- When does PHP long polling make sense?
- How PHP long polling works internally
- PHP long polling example with Ajax
- Implementing long polling in Slim
- Performance and scalability best practices
- Handling errors, timeouts and client disconnects
- PHP long polling vs WebSockets vs Server‑Sent Events
- Recommended PHP libraries and tools
- How PHPTrends can help with your real‑time stack
- PHP long polling FAQs
What is long polling?
In the traditional HTTP request–response model, the browser sends a request, the server responds immediately and the connection is closed. This works perfectly for static pages and simple APIs, but it feels sluggish when users expect real‑time updates: live chat messages, instant notifications, streaming dashboards or “user is typing” indicators.
The simplest solution is short polling: the browser sends a request every few seconds to ask, “Any new data?”. For many applications, that quickly becomes wasteful. Most requests return nothing, your logs explode and your infrastructure spends a lot of time answering “no”.
Long polling is an HTTP‑based strategy to get near real‑time behaviour without switching to WebSockets:
- The client sends a request and is prepared to wait.
- The server holds the connection open until new data is available or a timeout is reached.
- As soon as something changes, the server responds and the connection closes.
- The client immediately opens a new long‑poll request.
When does PHP long polling make sense?
PHP long polling is not a silver bullet, but for many teams it is the most pragmatic way to deliver real‑time experiences without rewriting the entire stack or negotiating new infrastructure.
Scenarios where PHP long polling shines
- Shared hosting or classic hosting plans where you cannot run a dedicated WebSocket or Node.js server, but you can deploy PHP scripts.
- Legacy PHP applications that need modern features such as notifications, activity feeds or live dashboards without a structural rewrite.
- Corporate or restricted environments where proxies and firewalls block or interfere with WebSocket traffic, while HTTP(S) requests still pass without friction.
- Moderate traffic use cases where you expect hundreds or low thousands of concurrent users, not tens of thousands.
Typical business use cases
- Real‑time notifications (orders, support tickets, comments, approvals).
- Admin dashboards that show statuses, KPIs or background job progress.
- Lightweight chat or “ask an agent” widgets embedded into an existing PHP app.
- Presence indicators such as “user is online” or “support agent is typing…”.
Once you have validated the product and traffic grows, you can decide whether you keep long polling, replace it with WebSockets/SSE or combine approaches. This page gives you the technical and strategic foundation to make that decision.
How PHP long polling works internally
A PHP long‑poll endpoint behaves more like a small, stateful worker than a conventional controller. Instead of calculating a response and returning it immediately, it enters a controlled loop that keeps the connection open for a short period.
High‑level flow
- The client sends a request, often with a
lastIdor timestamp indicating the last event it has seen. - The server checks whether there are new records after that identifier.
- If there is new data, the server responds immediately and closes the connection.
- If there is no new data yet, the script sleeps for a small interval (
sleep(1)for example) and checks again. - Once a maximum wait time is reached (e.g. 25–30 seconds) without changes, the server sends a “timeout” response and the client opens a new long‑poll request.
Key PHP considerations
- Execution time limits: increase the limit for the long‑polling script (for example with
set_time_limit()) so that PHP does not terminate it prematurely. - Worker usage: each open connection occupies an Apache or PHP‑FPM worker. Proper timeouts and efficient loops are essential to protect the rest of your site.
- Efficient queries: use indexed primary keys, timestamps or queues to detect new events cheaply instead of scanning large tables inside the loop.
- Connection monitoring: use
connection_aborted()orconnection_status()to stop work once the client has gone away.
Think of your long‑poll endpoint as a specialised real‑time microservice built with the tools you already have: PHP, MySQL and a standard web server.
PHP long polling example with Ajax
The following example demonstrates a robust long‑polling pattern for user‑specific notifications. It is intentionally simple, but it already includes best practices for timeouts, resource usage and client reconnection.
Example table structure
CREATE TABLE notifications (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
message VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_notifications_user_created
ON notifications (user_id, created_at);
Server-side PHP: poll.php
<?php
// poll.php
declare(strict_types=1);
session_start();
// Example current user id from your auth layer
$currentUserId = 123;
// Last received notification ID from the client (0 = first time)
$lastId = isset($_GET['lastId']) ? (int) $_GET['lastId'] : 0;
// Max time (seconds) to keep this request open
$timeout = 25;
$pollInterval = 1;
$start = time();
header('Content-Type: application/json');
header('Cache-Control: no-cache, must-revalidate');
header('Connection: keep-alive');
// Basic PDO connection (replace with your own)
$pdo = new PDO(
'mysql:host=localhost;dbname=app;charset=utf8mb4',
'user',
'password',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
// Allow this script to run long enough
set_time_limit($timeout + 5);
do {
// 1) Check for new notifications
$stmt = $pdo->prepare(
'SELECT id, message, created_at
FROM notifications
WHERE user_id = :userId AND id > :lastId
ORDER BY id ASC'
);
$stmt->execute([
':userId' => $currentUserId,
':lastId' => $lastId,
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 2) If we have new data, return it immediately
if ($rows) {
echo json_encode([
'status' => 'ok',
'events' => $rows,
]);
flush();
exit;
}
// 3) No new data yet - short sleep, then check again
sleep($pollInterval);
// Optional: break if client disconnected
if (connection_aborted()) {
exit;
}
} while ((time() - $start) < $timeout);
// 4) Timeout without new data
echo json_encode([
'status' => 'timeout',
'events' => [],
]);
flush();
Client-side JavaScript using Fetch
let lastId = 0;
let isPolling = false;
async function startLongPolling() {
if (isPolling) return;
isPolling = true;
while (true) {
try {
const response = await fetch(
'/dig_in/php-long-polling/poll.php?lastId=' + lastId,
{ cache: 'no-store' }
);
if (!response.ok) {
console.warn('Long polling error:', response.status);
await new Promise(r => setTimeout(r, 3000));
continue;
}
const data = await response.json();
if (data.status === 'ok' && data.events.length) {
data.events.forEach(event => {
lastId = Math.max(lastId, parseInt(event.id, 10));
renderNotification(event);
});
}
// Loop continues automatically: the while(true) keeps us going
} catch (err) {
console.error('Long polling failed:', err);
// Backoff before retry
await new Promise(r => setTimeout(r, 5000));
}
}
}
function renderNotification(event) {
const list = document.querySelector('#notifications');
if (!list) return;
const li = document.createElement('li');
li.textContent = `[${event.created_at}] ${event.message}`;
list.prepend(li);
}
document.addEventListener('DOMContentLoaded', startLongPolling);
This pattern is flexible enough for notification feeds, dashboards or lightweight chat. From here, you can plug in authentication, choose a better storage layer (Redis, queues, topic‑based channels) and tune the timeout and intervals for your traffic profile.
Implementing long polling in Slim
One of the backlinks pointing to this URL comes from a discussion about using long‑running connections in the Slim framework. Slim can absolutely host long‑poll endpoints, as long as you respect PHP’s resource limits and keep each request under control.
Below is a simplified example for Slim 4 that uses the same notification pattern as above.
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Psr7\Response;
$app->get('/api/notifications/long-poll', function (Request $request, Response $response): Response {
$queryParams = $request->getQueryParams();
$lastId = isset($queryParams['lastId']) ? (int) $queryParams['lastId'] : 0;
$userId = 123; // Replace with your auth logic
$timeout = 25;
$pollInterval = 1;
$start = time();
/** @var PDO $pdo */
$pdo = $this->get(PDO::class);
$response = $response
->withHeader('Content-Type', 'application/json')
->withHeader('Cache-Control', 'no-cache, must-revalidate')
->withHeader('Connection', 'keep-alive');
set_time_limit($timeout + 5);
do {
$stmt = $pdo->prepare(
'SELECT id, message, created_at
FROM notifications
WHERE user_id = :userId AND id > :lastId
ORDER BY id ASC'
);
$stmt->execute([
':userId' => $userId,
':lastId' => $lastId,
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($rows) {
$response->getBody()->write(json_encode([
'status' => 'ok',
'events' => $rows,
]));
return $response;
}
sleep($pollInterval);
if (connection_aborted()) {
return $response;
}
} while ((time() - $start) < $timeout);
$response->getBody()->write(json_encode([
'status' => 'timeout',
'events' => [],
]));
return $response;
});
You can mount this route under /dig_in/php-long-polling or expose it as part of a broader real‑time API namespace such as /api/events. The important part is to keep the behaviour predictable and well documented so that frontend developers can build on top of it.
Performance and scalability best practices
Long polling has a cost: every open request occupies server resources. The goal is not to avoid that cost entirely (that is impossible), but to keep it predictable and manageable as your application grows.
Designing sane limits
- Timeout window: 20–30 seconds of maximum wait time is usually enough for near real‑time behaviour.
- Polling interval: sleeping 0.5–1 second between checks prevents busy loops while keeping latency low.
- Dedicated pool: consider a dedicated PHP‑FPM pool or Apache worker group for long‑poll endpoints so they cannot starve regular traffic.
Efficient data access
- Use indexed fields (IDs, timestamps, status columns) to detect new events cheaply.
- Move heavy processing out of the long‑poll loop into background jobs or queues.
- Cache pre‑processed responses (for example, per user or per channel) to reduce database pressure.
Monitoring and observability
- Track the number of open long‑poll requests over time.
- Monitor worker utilisation and queue depth (PHP‑FPM, Apache, Nginx).
- Log timeout responses to ensure clients are reconnecting as expected rather than silently failing.
If you are approaching the limits of what long polling can sustainably handle, that is usually a good sign that the product is delivering value. At that point, migrating the most demanding features to WebSockets or an async PHP stack becomes a strategic investment instead of a premature optimisation.
Handling errors, timeouts and client disconnects
The most subtle bugs in long polling do not come from the happy path. They come from network blips, browser tabs being closed unexpectedly and edge cases where the server keeps working long after the client has disappeared.
Detecting client disconnects
PHP provides connection_aborted() and connection_status() helpers that you can check inside your long‑polling loop. If the client leaves, you can safely stop work and free the worker:
if (connection_aborted()) {
// Optional: log or clean up
exit;
}
Graceful timeouts
Instead of leaving timeouts ambiguous, always return a structured response such as:
{ "status": "timeout", "events": [] }
The client can detect this state, wait briefly and then open a fresh long‑poll request. This keeps both sides in sync and avoids spooky intermittent bugs.
Backoff strategies on the client
- On network errors, wait a few seconds before retrying and increase the delay if failures continue.
- On server errors (5xx), log the incident, show a subtle warning and retry less aggressively.
- On success with new events, restart the long poll immediately to keep latency small.
PHP long polling vs WebSockets vs Server‑Sent Events
When you design real‑time features, you are not just choosing an API pattern. You are choosing how much complexity you are willing to absorb in your infrastructure and your team.
Long polling (PHP)
- Runs on standard PHP hosting without additional daemons.
- Works with normal HTTP and HTTPS traffic; easy to debug.
- Simple scaling story for small and medium projects.
- Each open request uses a worker: poor tuning can slow down the rest of the site.
WebSockets
- Full duplex communication between client and server.
- Very efficient at high scale when implemented with event‑driven servers (Node.js, Go, async PHP, etc.).
- Requires additional services, new deploy pipelines and new monitoring.
- In some corporate environments, proxies and firewalls restrict WebSocket traffic.
Server‑Sent Events (SSE)
- One‑way streaming from server to client over HTTP.
- Great for notifications, dashboards and logs when you do not need bidirectional messaging.
- Browser support is good but not universal; some legacy environments still require fallbacks.
Recommended PHP libraries and tools
You can build long polling from scratch, but you do not have to. The PHP ecosystem offers a rich set of libraries that help you experiment, harden and evolve your real‑time architecture.
Libraries and frameworks to explore
- Simple long‑polling demos: small open‑source examples that show the core pattern with Ajax and vanilla PHP.
- ReactPHP: an event‑driven, non‑blocking toolkit for PHP that can host long‑lived connections more efficiently than classic FPM.
- Ratchet: a WebSocket server library for PHP built on top of ReactPHP for when you are ready to add bidirectional messaging.
- Workerman: a high‑performance PHP socket server suitable for chat, notification and streaming workloads.
- Laravel broadcasting stack: if you are in the Laravel world, broadcasting events to Pusher or your own WebSocket server can complement long polling or replace it over time.
At PHPTrends, we track which PHP libraries are actively maintained and growing. When you choose your stack based on real usage data instead of hype, you reduce the risk of committing to a dead‑end technology.
Need a clear path to real‑time PHP without overcomplicating your stack?
Choosing between PHP long polling, WebSockets and other real‑time options is not just a technical decision. It affects hosting costs, development speed, monitoring, security and future maintainability. PHPTrends focuses entirely on the PHP ecosystem, helping teams choose modern tools that fit their current constraints and growth plans.
Whether you are validating the first version of a product or scaling an established platform, we can help you design a pragmatic real‑time architecture based on proven PHP libraries and frameworks.
