无需第三方服务,基于纯PHP和JS实现Web社交应用站内推送通知
Got it, let's build a pure PHP + JS (Comet-based) in-app notification system for your Facebook-like social app—no third-party services, no mobile/desktop notifications required. I’ll walk you through a working example using long polling (a common Comet technique) since it’s straightforward to implement with vanilla tools.
You’ll need a table to store notifications. Here’s a simple MySQL schema tailored for this use case:
CREATE TABLE notifications ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, -- Links to your existing users table message VARCHAR(255) NOT NULL, -- e.g., "Sarah liked your post" is_read BOOLEAN DEFAULT FALSE, -- Track if the user has seen the notification created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
Create a file named notification_poll.php—this handles the long-lived connection from the frontend, waiting for new notifications to appear before sending a response.
<?php // Start session to fetch the logged-in user's ID session_start(); if (!isset($_SESSION['user_id'])) { http_response_code(401); exit('Unauthorized'); } $user_id = $_SESSION['user_id']; // Release the session lock immediately so other user requests don't hang session_write_close(); // Set headers optimized for long polling header('Content-Type: application/json'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); // Poll for new notifications for up to 30 seconds before timing out $timeout = 30; $start_time = time(); while (time() - $start_time < $timeout) { // Connect to your database (replace with your credentials) $pdo = new PDO('mysql:host=localhost;dbname=your_app_db', 'db_user', 'db_password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Fetch unread notifications for the current user $stmt = $pdo->prepare("SELECT id, message, created_at FROM notifications WHERE user_id = ? AND is_read = FALSE ORDER BY created_at DESC"); $stmt->execute([$user_id]); $notifications = $stmt->fetchAll(PDO::FETCH_ASSOC); if (!empty($notifications)) { // Optional: Mark notifications as read (you could also do this after frontend confirms) $stmt = $pdo->prepare("UPDATE notifications SET is_read = TRUE WHERE user_id = ? AND is_read = FALSE"); $stmt->execute([$user_id]); echo json_encode($notifications); exit(); } // Wait 2 seconds before polling again to reduce server load sleep(2); } // If no notifications found in the timeout window, return empty array echo json_encode([]); ?>
Add this script to your main app page (e.g., your dashboard template). It will repeatedly call the long polling endpoint and update the UI when new notifications arrive.
// Core function to handle long polling function pollForNotifications() { fetch('notification_poll.php') .then(response => { if (!response.ok) throw new Error('Poll request failed'); return response.json(); }) .then(notifications => { if (notifications.length > 0) { // Render new notifications in the UI displayNotifications(notifications); } // Restart polling immediately after the current request finishes setTimeout(pollForNotifications, 100); }) .catch(error => { console.error('Polling error:', error); // Retry after 5 seconds if there's a network issue setTimeout(pollForNotifications, 5000); }); } // Render notifications to the page function displayNotifications(notifications) { const notificationContainer = document.getElementById('notification-container'); const badge = document.getElementById('notification-badge'); // Add each new notification to the top of the container notifications.forEach(notification => { const notificationEl = document.createElement('div'); notificationEl.className = 'notification-item'; notificationEl.innerHTML = ` <p>${notification.message}</p> <small>${new Date(notification.created_at).toLocaleTimeString()}</small> `; notificationContainer.prepend(notificationEl); }); // Update the unread count badge badge.textContent = parseInt(badge.textContent) + notifications.length; } // Start polling when the page loads document.addEventListener('DOMContentLoaded', () => { pollForNotifications(); });
Add basic HTML to test the UI:
<div class="notification-wrapper"> <button id="notification-button">🔔</button> <span id="notification-badge">0</span> <div id="notification-container"></div> </div> <style> .notification-wrapper { position: relative; display: inline-block; margin: 20px; } #notification-badge { position: absolute; top: -8px; right: -8px; background: #ff4444; color: white; border-radius: 50%; padding: 2px 6px; font-size: 12px; } .notification-item { padding: 12px; border-bottom: 1px solid #eee; background: #f9f9f9; margin-top: 8px; } </style>
- Server Settings: Ensure PHP’s
max_execution_timeis set to at least 35 seconds (to cover the 30-second timeout + buffer). For Nginx/Apache, adjust connection timeout settings to avoid closing the long poll early. - Database Load: For high-traffic apps, replace MySQL polling with a memory store like Redis to track new notifications—it’s much faster than repeated database queries.
- Read Status: Instead of marking notifications as read on the backend, you could add a separate endpoint (e.g.,
mark_notifications_read.php) that the frontend calls when the user opens the notification panel.
内容的提问来源于stack exchange,提问作者Bernard Gritsmiller

