PHP AJAX and JSON
Introduction to AJAX and JSON in PHP
AJAX (Asynchronous JavaScript and XML) combined with JSON (JavaScript Object Notation) enables the creation of dynamic, responsive web applications that can update content without requiring full page reloads. PHP serves as an excellent backend for AJAX requests, providing data processing and JSON responses for modern web applications.
Understanding AJAX and JSON integration with PHP is essential for building interactive user interfaces, real-time applications, and modern web APIs that provide seamless user experiences.
Why AJAX and JSON Matter
Enhanced User Experience: AJAX enables seamless interactions without page refreshes, creating fluid, desktop-like web applications.
Improved Performance: Only necessary data is transferred, reducing bandwidth usage and improving response times.
Real-time Updates: Applications can fetch and display new information without user intervention.
API Development: JSON responses make PHP applications ideal for serving mobile apps and single-page applications.
Progressive Enhancement: AJAX can enhance existing forms and interfaces while maintaining fallback functionality.
Key Concepts
Asynchronous Processing: JavaScript makes requests to PHP scripts without blocking the user interface.
JSON Data Exchange: Lightweight data format that's easy to parse in both JavaScript and PHP.
RESTful APIs: Standardized approach to creating web services using HTTP methods and JSON responses.
Error Handling: Proper handling of network failures, server errors, and validation issues.
Security Considerations: CSRF protection, input validation, and output sanitization for AJAX endpoints.
Basic AJAX Implementation
Simple AJAX with JSON Response
<?php
/**
* Basic AJAX JSON Response Handler
*
* Demonstrates fundamental AJAX request handling
* with JSON responses and error management.
*/
// Set JSON content type
header('Content-Type: application/json');
// Enable CORS for development (configure properly for production)
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
/**
* Simple API response helper
*/
function jsonResponse($data, $status = 200, $message = 'success') {
http_response_code($status);
$response = [
'status' => $status < 400 ? 'success' : 'error',
'message' => $message,
'data' => $data,
'timestamp' => date('c')
];
echo json_encode($response, JSON_PRETTY_PRINT);
exit;
}
/**
* Error response helper
*/
function errorResponse($message, $status = 400, $errors = []) {
http_response_code($status);
$response = [
'status' => 'error',
'message' => $message,
'errors' => $errors,
'timestamp' => date('c')
];
echo json_encode($response, JSON_PRETTY_PRINT);
exit;
}
// Handle different request methods
$method = $_SERVER['REQUEST_METHOD'];
$requestUri = $_SERVER['REQUEST_URI'];
// Parse request data
$input = json_decode(file_get_contents('php://input'), true) ?? [];
$data = array_merge($_GET, $_POST, $input);
try {
switch ($method) {
case 'GET':
handleGetRequest($data);
break;
case 'POST':
handlePostRequest($data);
break;
case 'PUT':
handlePutRequest($data);
break;
case 'DELETE':
handleDeleteRequest($data);
break;
default:
errorResponse('Method not allowed', 405);
}
} catch (Exception $e) {
errorResponse('Internal server error', 500, ['exception' => $e->getMessage()]);
}
/**
* Handle GET requests
*/
function handleGetRequest($data) {
// Example: Get user information
if (isset($data['action']) && $data['action'] === 'get_user') {
$userId = $data['user_id'] ?? null;
if (!$userId) {
errorResponse('User ID is required', 400);
}
// Simulate database lookup
$user = [
'id' => $userId,
'name' => 'John Doe',
'email' => '[email protected]',
'created_at' => '2024-01-15T10:00:00Z'
];
jsonResponse($user, 200, 'User retrieved successfully');
}
// Example: Search functionality
if (isset($data['action']) && $data['action'] === 'search') {
$query = $data['q'] ?? '';
if (strlen($query) < 2) {
errorResponse('Search query must be at least 2 characters', 400);
}
// Simulate search results
$results = [
['id' => 1, 'title' => 'First Result', 'snippet' => 'This matches your search...'],
['id' => 2, 'title' => 'Second Result', 'snippet' => 'Another matching result...']
];
jsonResponse($results, 200, 'Search completed');
}
// Default response
jsonResponse(['message' => 'GET endpoint ready'], 200);
}
/**
* Handle POST requests
*/
function handlePostRequest($data) {
// Example: Create new user
if (isset($data['action']) && $data['action'] === 'create_user') {
$name = $data['name'] ?? '';
$email = $data['email'] ?? '';
// Validation
$errors = [];
if (empty($name)) {
$errors['name'] = 'Name is required';
}
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Valid email is required';
}
if (!empty($errors)) {
errorResponse('Validation failed', 422, $errors);
}
// Simulate user creation
$newUser = [
'id' => rand(1000, 9999),
'name' => $name,
'email' => $email,
'created_at' => date('c')
];
jsonResponse($newUser, 201, 'User created successfully');
}
// Example: Contact form submission
if (isset($data['action']) && $data['action'] === 'contact_form') {
$name = $data['name'] ?? '';
$email = $data['email'] ?? '';
$message = $data['message'] ?? '';
// Validation
$errors = [];
if (empty($name)) $errors['name'] = 'Name is required';
if (empty($email)) $errors['email'] = 'Email is required';
if (empty($message)) $errors['message'] = 'Message is required';
if (!empty($errors)) {
errorResponse('Please fix the following errors', 422, $errors);
}
// Process form (send email, save to database, etc.)
$success = true; // Simulate processing
if ($success) {
jsonResponse(['contact_id' => uniqid()], 200, 'Thank you! Your message has been sent.');
} else {
errorResponse('Failed to send message', 500);
}
}
errorResponse('Invalid action', 400);
}
/**
* Handle PUT requests
*/
function handlePutRequest($data) {
if (isset($data['action']) && $data['action'] === 'update_user') {
$userId = $data['user_id'] ?? null;
$name = $data['name'] ?? '';
$email = $data['email'] ?? '';
if (!$userId) {
errorResponse('User ID is required', 400);
}
// Simulate user update
$updatedUser = [
'id' => $userId,
'name' => $name,
'email' => $email,
'updated_at' => date('c')
];
jsonResponse($updatedUser, 200, 'User updated successfully');
}
errorResponse('Invalid action', 400);
}
/**
* Handle DELETE requests
*/
function handleDeleteRequest($data) {
if (isset($data['action']) && $data['action'] === 'delete_user') {
$userId = $data['user_id'] ?? null;
if (!$userId) {
errorResponse('User ID is required', 400);
}
// Simulate user deletion
jsonResponse(['deleted_id' => $userId], 200, 'User deleted successfully');
}
errorResponse('Invalid action', 400);
}
?>
<!DOCTYPE html>
<html>
<head>
<title>AJAX JSON Example</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.section { margin-bottom: 30px; padding: 20px; border: 1px solid #ddd; }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 5px; font-weight: bold; }
.form-group input, .form-group textarea { width: 100%; padding: 8px; border: 1px solid #ccc; }
.button { background: #007cba; color: white; padding: 10px 20px; border: none; cursor: pointer; }
.button:hover { background: #005a87; }
.result { margin-top: 15px; padding: 10px; border-radius: 4px; }
.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.loading { background: #fff3cd; color: #856404; border: 1px solid #ffeaa7; }
</style>
</head>
<body>
<h1>AJAX JSON Examples</h1>
<!-- Search Example -->
<div class="section">
<h2>Search Example</h2>
<div class="form-group">
<label for="search-input">Search:</label>
<input type="text" id="search-input" placeholder="Enter search term...">
</div>
<button class="button" onclick="performSearch()">Search</button>
<div id="search-results" class="result" style="display: none;"></div>
</div>
<!-- Contact Form Example -->
<div class="section">
<h2>Contact Form</h2>
<form id="contact-form">
<div class="form-group">
<label for="contact-name">Name:</label>
<input type="text" id="contact-name" name="name" required>
</div>
<div class="form-group">
<label for="contact-email">Email:</label>
<input type="email" id="contact-email" name="email" required>
</div>
<div class="form-group">
<label for="contact-message">Message:</label>
<textarea id="contact-message" name="message" rows="4" required></textarea>
</div>
<button type="submit" class="button">Send Message</button>
</form>
<div id="contact-result" class="result" style="display: none;"></div>
</div>
<!-- User Management Example -->
<div class="section">
<h2>User Management</h2>
<button class="button" onclick="loadUser(123)">Load User 123</button>
<button class="button" onclick="createUser()">Create User</button>
<div id="user-result" class="result" style="display: none;"></div>
</div>
<script>
/**
* Generic AJAX function
*/
function makeAjaxRequest(method, data, callback) {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
try {
const response = JSON.parse(xhr.responseText);
callback(response, xhr.status);
} catch (e) {
callback({
status: 'error',
message: 'Invalid JSON response',
data: xhr.responseText
}, xhr.status);
}
}
};
xhr.open(method, window.location.href, true);
xhr.setRequestHeader('Content-Type', 'application/json');
if (method === 'GET') {
xhr.send();
} else {
xhr.send(JSON.stringify(data));
}
}
/**
* Display result helper
*/
function displayResult(elementId, response, status) {
const element = document.getElementById(elementId);
element.style.display = 'block';
if (response.status === 'success') {
element.className = 'result success';
element.innerHTML = '<strong>Success:</strong> ' + response.message +
'<pre>' + JSON.stringify(response.data, null, 2) + '</pre>';
} else {
element.className = 'result error';
element.innerHTML = '<strong>Error:</strong> ' + response.message;
if (response.errors) {
element.innerHTML += '<ul>';
for (const field in response.errors) {
element.innerHTML += '<li>' + field + ': ' + response.errors[field] + '</li>';
}
element.innerHTML += '</ul>';
}
}
}
/**
* Search functionality
*/
function performSearch() {
const query = document.getElementById('search-input').value;
const resultDiv = document.getElementById('search-results');
if (query.length < 2) {
resultDiv.className = 'result error';
resultDiv.innerHTML = 'Please enter at least 2 characters';
resultDiv.style.display = 'block';
return;
}
resultDiv.className = 'result loading';
resultDiv.innerHTML = 'Searching...';
resultDiv.style.display = 'block';
makeAjaxRequest('GET', null, function(response, status) {
displayResult('search-results', response, status);
});
// Note: In a real implementation, you'd pass the query parameter
}
/**
* Contact form submission
*/
document.getElementById('contact-form').addEventListener('submit', function(e) {
e.preventDefault();
const formData = {
action: 'contact_form',
name: document.getElementById('contact-name').value,
email: document.getElementById('contact-email').value,
message: document.getElementById('contact-message').value
};
const resultDiv = document.getElementById('contact-result');
resultDiv.className = 'result loading';
resultDiv.innerHTML = 'Sending message...';
resultDiv.style.display = 'block';
makeAjaxRequest('POST', formData, function(response, status) {
displayResult('contact-result', response, status);
if (response.status === 'success') {
document.getElementById('contact-form').reset();
}
});
});
/**
* Load user
*/
function loadUser(userId) {
const resultDiv = document.getElementById('user-result');
resultDiv.className = 'result loading';
resultDiv.innerHTML = 'Loading user...';
resultDiv.style.display = 'block';
makeAjaxRequest('GET', null, function(response, status) {
displayResult('user-result', response, status);
});
// Note: In a real implementation, you'd pass the user_id parameter
}
/**
* Create user
*/
function createUser() {
const userData = {
action: 'create_user',
name: 'Jane Smith',
email: '[email protected]'
};
const resultDiv = document.getElementById('user-result');
resultDiv.className = 'result loading';
resultDiv.innerHTML = 'Creating user...';
resultDiv.style.display = 'block';
makeAjaxRequest('POST', userData, function(response, status) {
displayResult('user-result', response, status);
});
}
/**
* Search on input change (with debouncing)
*/
let searchTimeout;
document.getElementById('search-input').addEventListener('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(function() {
const query = document.getElementById('search-input').value;
if (query.length >= 2) {
performSearch();
}
}, 500);
});
</script>
</body>
</html>
Advanced AJAX Patterns
Real-time Updates and WebSocket-like Functionality
<?php
/**
* Advanced AJAX Patterns
*
* Demonstrates polling, long-polling, and real-time
* update patterns using AJAX and PHP.
*/
session_start();
header('Content-Type: application/json');
/**
* Server-Sent Events (SSE) endpoint
*/
if (isset($_GET['action']) && $_GET['action'] === 'events') {
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
// Prevent timeout
ignore_user_abort(false);
set_time_limit(0);
$lastEventId = $_SERVER['HTTP_LAST_EVENT_ID'] ?? 0;
while (true) {
// Check if connection is still alive
if (connection_aborted()) {
break;
}
// Get new events (simulate with random data)
$events = getNewEvents($lastEventId);
foreach ($events as $event) {
echo "id: {$event['id']}\n";
echo "event: {$event['type']}\n";
echo "data: " . json_encode($event['data']) . "\n\n";
$lastEventId = $event['id'];
}
// Flush output
if (ob_get_level()) {
ob_end_flush();
}
flush();
// Wait before next check
sleep(2);
}
exit;
}
/**
* Long polling endpoint
*/
if (isset($_GET['action']) && $_GET['action'] === 'poll') {
$timeout = 30; // 30 seconds timeout
$startTime = time();
$lastUpdate = $_GET['last_update'] ?? 0;
while ((time() - $startTime) < $timeout) {
$data = checkForUpdates($lastUpdate);
if ($data) {
echo json_encode([
'status' => 'success',
'data' => $data,
'timestamp' => time()
]);
exit;
}
// Check if client disconnected
if (connection_aborted()) {
exit;
}
// Wait before checking again
usleep(500000); // 0.5 seconds
}
// Timeout reached
echo json_encode([
'status' => 'timeout',
'message' => 'No updates available',
'timestamp' => time()
]);
exit;
}
/**
* File upload with progress tracking
*/
if (isset($_POST['action']) && $_POST['action'] === 'upload') {
$uploadDir = 'uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
if (isset($_FILES['file'])) {
$file = $_FILES['file'];
$filename = uniqid() . '_' . basename($file['name']);
$filepath = $uploadDir . $filename;
if (move_uploaded_file($file['tmp_name'], $filepath)) {
echo json_encode([
'status' => 'success',
'message' => 'File uploaded successfully',
'data' => [
'filename' => $filename,
'size' => $file['size'],
'url' => $filepath
]
]);
} else {
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => 'Failed to upload file'
]);
}
} else {
http_response_code(400);
echo json_encode([
'status' => 'error',
'message' => 'No file provided'
]);
}
exit;
}
/**
* Simulate getting new events
*/
function getNewEvents($lastEventId) {
static $eventCounter = 0;
$events = [];
// Simulate random events
if (rand(1, 10) <= 3) { // 30% chance of new event
$eventCounter++;
$eventId = $lastEventId + $eventCounter;
$events[] = [
'id' => $eventId,
'type' => 'notification',
'data' => [
'title' => 'New Notification',
'message' => 'You have a new message at ' . date('H:i:s'),
'timestamp' => time()
]
];
}
return $events;
}
/**
* Simulate checking for updates
*/
function checkForUpdates($lastUpdate) {
// Simulate data changes
if (rand(1, 10) <= 2) { // 20% chance of update
return [
'type' => 'data_update',
'message' => 'New data available',
'count' => rand(1, 5),
'timestamp' => time()
];
}
return null;
}
/**
* Batch processing endpoint
*/
if (isset($_POST['action']) && $_POST['action'] === 'batch_process') {
$items = json_decode($_POST['items'], true) ?? [];
$batchId = uniqid();
// Store batch info in session for progress tracking
$_SESSION['batch_' . $batchId] = [
'total' => count($items),
'processed' => 0,
'status' => 'running',
'results' => []
];
echo json_encode([
'status' => 'success',
'batch_id' => $batchId,
'total_items' => count($items)
]);
// Process items in background (in real app, use queue)
processBatchItems($batchId, $items);
exit;
}
/**
* Batch progress endpoint
*/
if (isset($_GET['action']) && $_GET['action'] === 'batch_progress') {
$batchId = $_GET['batch_id'] ?? '';
if (isset($_SESSION['batch_' . $batchId])) {
echo json_encode([
'status' => 'success',
'data' => $_SESSION['batch_' . $batchId]
]);
} else {
http_response_code(404);
echo json_encode([
'status' => 'error',
'message' => 'Batch not found'
]);
}
exit;
}
/**
* Simulate batch processing
*/
function processBatchItems($batchId, $items) {
// In a real application, this would be handled by a background job
// For demo purposes, we'll simulate processing
foreach ($items as $index => $item) {
// Simulate processing time
usleep(100000); // 0.1 seconds
$_SESSION['batch_' . $batchId]['processed'] = $index + 1;
$_SESSION['batch_' . $batchId]['results'][] = [
'item' => $item,
'status' => 'completed',
'timestamp' => time()
];
}
$_SESSION['batch_' . $batchId]['status'] = 'completed';
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Advanced AJAX Patterns</title>
<style>
body { font-family: Arial, sans-serif; max-width: 1000px; margin: 0 auto; padding: 20px; }
.section { margin-bottom: 30px; padding: 20px; border: 1px solid #ddd; }
.progress { width: 100%; background: #f0f0f0; margin: 10px 0; }
.progress-bar { height: 20px; background: #4caf50; width: 0%; transition: width 0.3s; }
.log { background: #f5f5f5; padding: 10px; margin: 10px 0; max-height: 200px; overflow-y: auto; }
.notification { background: #e7f3ff; padding: 10px; margin: 5px 0; border-left: 4px solid #007cba; }
.button { background: #007cba; color: white; padding: 10px 20px; border: none; cursor: pointer; margin: 5px; }
</style>
</head>
<body>
<h1>Advanced AJAX Patterns</h1>
<!-- Server-Sent Events -->
<div class="section">
<h2>Server-Sent Events (Real-time Notifications)</h2>
<button class="button" onclick="startEventStream()">Start Event Stream</button>
<button class="button" onclick="stopEventStream()">Stop Event Stream</button>
<div id="notifications"></div>
</div>
<!-- Long Polling -->
<div class="section">
<h2>Long Polling</h2>
<button class="button" onclick="startPolling()">Start Polling</button>
<button class="button" onclick="stopPolling()">Stop Polling</button>
<div id="polling-log" class="log"></div>
</div>
<!-- File Upload with Progress -->
<div class="section">
<h2>File Upload with Progress</h2>
<input type="file" id="file-input" multiple>
<button class="button" onclick="uploadFiles()">Upload Files</button>
<div class="progress">
<div id="upload-progress" class="progress-bar"></div>
</div>
<div id="upload-log" class="log"></div>
</div>
<!-- Batch Processing -->
<div class="section">
<h2>Batch Processing</h2>
<button class="button" onclick="startBatchProcess()">Process 10 Items</button>
<div class="progress">
<div id="batch-progress" class="progress-bar"></div>
</div>
<div id="batch-status"></div>
</div>
<script>
let eventSource = null;
let pollingInterval = null;
/**
* Server-Sent Events
*/
function startEventStream() {
if (eventSource) {
eventSource.close();
}
eventSource = new EventSource('?action=events');
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
addNotification(data.title, data.message);
};
eventSource.onerror = function(event) {
console.error('EventSource error:', event);
addNotification('Error', 'Connection error occurred');
};
addNotification('Info', 'Event stream started');
}
function stopEventStream() {
if (eventSource) {
eventSource.close();
eventSource = null;
addNotification('Info', 'Event stream stopped');
}
}
function addNotification(title, message) {
const container = document.getElementById('notifications');
const notification = document.createElement('div');
notification.className = 'notification';
notification.innerHTML = '<strong>' + title + ':</strong> ' + message +
' <small>(' + new Date().toLocaleTimeString() + ')</small>';
container.insertBefore(notification, container.firstChild);
// Remove old notifications
while (container.children.length > 5) {
container.removeChild(container.lastChild);
}
}
/**
* Long Polling
*/
function startPolling() {
if (pollingInterval) {
clearInterval(pollingInterval);
}
let lastUpdate = Math.floor(Date.now() / 1000);
function poll() {
const xhr = new XMLHttpRequest();
xhr.open('GET', '?action=poll&last_update=' + lastUpdate, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
try {
const response = JSON.parse(xhr.responseText);
logPolling('Response: ' + JSON.stringify(response));
if (response.status === 'success') {
lastUpdate = response.timestamp;
}
// Start next poll
setTimeout(poll, 1000);
} catch (e) {
logPolling('Error: Invalid JSON response');
setTimeout(poll, 5000);
}
}
};
xhr.send();
logPolling('Polling started...');
}
poll();
}
function stopPolling() {
if (pollingInterval) {
clearInterval(pollingInterval);
pollingInterval = null;
logPolling('Polling stopped');
}
}
function logPolling(message) {
const log = document.getElementById('polling-log');
log.innerHTML = '[' + new Date().toLocaleTimeString() + '] ' + message + '\n' + log.innerHTML;
}
/**
* File Upload with Progress
*/
function uploadFiles() {
const fileInput = document.getElementById('file-input');
const files = fileInput.files;
if (files.length === 0) {
logUpload('Please select files to upload');
return;
}
for (let i = 0; i < files.length; i++) {
uploadFile(files[i]);
}
}
function uploadFile(file) {
const formData = new FormData();
formData.append('action', 'upload');
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
document.getElementById('upload-progress').style.width = percentComplete + '%';
}
});
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
try {
const response = JSON.parse(xhr.responseText);
if (response.status === 'success') {
logUpload('✓ ' + file.name + ' uploaded successfully');
} else {
logUpload('✗ ' + file.name + ' upload failed: ' + response.message);
}
} catch (e) {
logUpload('✗ ' + file.name + ' upload error');
}
document.getElementById('upload-progress').style.width = '0%';
}
};
xhr.open('POST', '', true);
xhr.send(formData);
logUpload('Uploading ' + file.name + '...');
}
function logUpload(message) {
const log = document.getElementById('upload-log');
log.innerHTML = '[' + new Date().toLocaleTimeString() + '] ' + message + '\n' + log.innerHTML;
}
/**
* Batch Processing
*/
function startBatchProcess() {
const items = [];
for (let i = 1; i <= 10; i++) {
items.push('Item ' + i);
}
const formData = new FormData();
formData.append('action', 'batch_process');
formData.append('items', JSON.stringify(items));
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
try {
const response = JSON.parse(xhr.responseText);
if (response.status === 'success') {
trackBatchProgress(response.batch_id);
}
} catch (e) {
document.getElementById('batch-status').innerHTML = 'Error starting batch process';
}
}
};
xhr.open('POST', '', true);
xhr.send(formData);
}
function trackBatchProgress(batchId) {
const checkProgress = function() {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
try {
const response = JSON.parse(xhr.responseText);
if (response.status === 'success') {
const data = response.data;
const percentage = (data.processed / data.total) * 100;
document.getElementById('batch-progress').style.width = percentage + '%';
document.getElementById('batch-status').innerHTML =
'Processing: ' + data.processed + '/' + data.total + ' (' + Math.round(percentage) + '%)';
if (data.status === 'completed') {
document.getElementById('batch-status').innerHTML += ' - Completed!';
} else {
setTimeout(checkProgress, 1000);
}
}
} catch (e) {
document.getElementById('batch-status').innerHTML = 'Error tracking progress';
}
}
};
xhr.open('GET', '?action=batch_progress&batch_id=' + batchId, true);
xhr.send();
};
checkProgress();
}
</script>
</body>
</html>
Related Topics
For more PHP web development topics:
- PHP Forms Processing - Form handling and validation
- PHP Sessions and Cookies - State management
- PHP URL Routing - URL routing systems
- PHP Security Best Practices - Security guidelines
- PHP File Uploads - File upload handling
Summary
AJAX and JSON integration with PHP enables modern, interactive web applications:
Asynchronous Communication: AJAX allows seamless data exchange without page reloads, improving user experience and application responsiveness.
JSON Data Format: Lightweight, readable format for data exchange between client and server, compatible with JavaScript and PHP.
Real-time Features: Server-Sent Events and long polling enable real-time updates and notifications without constant page refreshes.
Progressive Enhancement: AJAX can enhance existing functionality while maintaining fallback compatibility for non-JavaScript users.
API Development: JSON responses make PHP applications suitable for serving mobile apps, SPAs, and third-party integrations.
Error Handling: Proper error management ensures graceful degradation and meaningful user feedback during network or server issues.
Security Considerations: CSRF protection, input validation, and output sanitization are essential for secure AJAX endpoints.
Mastering AJAX and JSON with PHP enables you to build responsive, interactive web applications that provide excellent user experiences while maintaining security and reliability.