How to Build a Real-Time PHP Notification System with MySQL and AJAX
Last edited on June 12, 2025

Real-time notifications are a staple of modern web applications โ€“ they alert users about new messages, updates, or alerts without requiring a page reload. In this tutorial, we will build a simple real-time PHP notification system using PHP, MySQL, and AJAX. The approach we’ll use is AJAX polling, where the browser periodically checks the server for new data. This method is straightforward and works well for small to medium projects, making it perfect for local development or simple web apps.

We’ll implement a basic comment notification feature: whenever a new comment is added (via a form), a notification count will update in real time on the navigation bar. Users can click on a bell icon to see the latest unseen comments in a dropdown list. We’ll use only core technologies โ€“ core PHP for the backend, MySQL for data storage, and jQuery AJAX on the front end โ€“ with no need for WebSockets or external services. By the end of this guide, you’ll have a working notification system running on your local server and a clear understanding of how to extend it for your projects.

Introduction To Real-Time PHP Notification

On many websites, you might notice a bell icon or a popup that shows you new notifications (like chat messages, alerts, or updates) as soon as they happen. These push notifications enhance user engagement by delivering information instantly rather than waiting for the user to refresh the page. For example, social networks use them to show new friend requests or messages, and e-commerce sites use them for real-time chat support or order updates.

There are several techniques to implement real-time notifications in web applications. Some common approaches include:

  • AJAX Polling: The client (browser) asks the server for updates at regular intervals (e.g., every few seconds). If new data is available, it’s sent back and displayed on the page.
  • Long Polling: The client requests data from the server, but the server holds the connection open until new data is available (or a timeout is reached) before responding. The client then immediately sends a new request, and the cycle repeats.
  • WebSockets: The client and server keep an open two-way connection, allowing the server to send updates to the client in real time as soon as they happen.
  • Server-Sent Events (SSE): The server continuously sends updates to the client over a single persistent connection, which the client listens to.
PHP Real Time Notification Overview

Each method has its pros and cons. In this guide, we’ll focus on AJAX polling because it’s simple to implement and doesn’t require special server configurations. While AJAX polling is not as efficient as WebSockets for high-traffic applications (due to the overhead of continuous requests), it works great for demonstrations and modest applications. We will walk through a step-by-step approach to creating a real-time notification system in PHP using AJAX polling in a local environment.

Prerequisites

Before we begin, make sure you have the following ready and understand the basics:

  • PHP and MySQL Setup: A local development environment with PHP (7.x or 8.x) and MySQL installed. You can use a setup like XAMPP, WAMP, MAMP, or a LAMP stack on Linux. Ensure that MySQL is running and that you can create a database.
  • Web Development Basics: Familiarity with HTML, CSS, and JavaScript. We will use a simple HTML form and some basic Bootstrap for styling (optional).
  • jQuery and AJAX: Basic knowledge of jQuery and how to perform AJAX requests. We will include the jQuery library via CDN for handling AJAX easily.
  • A Code Editor: Use any code editor or IDE you’re comfortable with to write PHP and HTML code.

Don’t worry if you’re not an expert in some of these areas โ€” we will provide the code and explanations for each part. By following along, you should be able to set up the notification system even with fundamental knowledge.

Database Setup

The first step is setting up the database for our notification system. We need a table to store the comments (which will trigger notifications). Each comment will have a subject, a message, and a status to indicate whether it has been seen (read) or not.

1. Create a Database (if not already created): If you don’t have a database for this project yet, create a new database using your preferred tool (such as phpMyAdmin or the MySQL command line). For example, you might create a database called notifications_demo. You can skip this if you want to use an existing database.

2. Create a Table for Notifications: Within your database, create a table named comments (or any suitable name). This table will store each comment that we treat as a notification. Use the following SQL query to create the table structure:

MySQL Command to Create Table
CREATE TABLE `comments` (
  `comment_id` INT AUTO_INCREMENT PRIMARY KEY,
  `comment_subject` VARCHAR(250) NOT NULL,
  `comment_text` TEXT NOT NULL,
  `comment_status` TINYINT(1) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Let’s break down the table columns:

  • comment_id: An auto-incrementing integer that uniquely identifies each comment/notification.
  • comment_subject: A short text field for the subject or title of the comment.
  • comment_text: A text field for the body of the comment.
  • comment_status: A tiny integer (either 0 or 1) that indicates whether the notification has been seen. We set the default value to 0, which means “unseen” or new. Later, when a notification is viewed, we’ll update this to 1 (“seen”).

Run this SQL query on your MySQL database. If it executes successfully, you will have your comments table ready to store notifications. Initially, it will be empty. We will populate it as users submit comments through our PHP application.

Step-by-Step Guide: Building the Notification System

Now that our database is ready, let’s build the PHP application step by step. We’ll create the front-end interface and the backend PHP scripts for inserting and fetching data and use jQuery AJAX to tie them together for real-time updates.

Step 1: Create the Front-End Interface (HTML and Bootstrap)

First, we need an interface where users can submit comments and see notifications. We’ll make a simple page (index.php) with the following elements:

  • A navigation bar that includes a notification icon (a bell) and a badge/counter for unread notifications.
  • A form where the user can enter a comment subject and comment text and submit it.

We will use Bootstrap (a popular CSS framework) for quick styling of the form and navigation bar. This is optional, but it makes the interface look nicer without custom CSS. We’ll include Bootstrap via CDN, along with jQuery.

Create a new file in your project directory called index.php and add the following code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>PHP Real-Time Notification Demo</title>
  <!-- Include jQuery library -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <!-- Include Bootstrap CSS and JS (for styling and dropdown functionality) -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<br><br>
<div class="container">
  <!-- Navigation bar with Notification Icon -->
  <nav class="navbar navbar-inverse">
    <div class="container-fluid">
      <div class="navbar-header">
        <a class="navbar-brand" href="#">PHP Notification System</a>
      </div>
      <ul class="nav navbar-nav navbar-right">
        <li class="dropdown">
          <!-- Dropdown toggler for notifications -->
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
            <span class="label label-danger count" style="border-radius:10px;"></span>
            <span class="glyphicon glyphicon-bell" style="font-size:18px;"></span>
          </a>
          <!-- Dropdown menu for notification list -->
          <ul class="dropdown-menu"></ul>
        </li>
      </ul>
    </div>
  </nav>

  <!-- Comment Submission Form -->
  <form method="post" id="comment_form">
    <div class="form-group">
      <label for="subject">Enter Subject</label>
      <input type="text" name="subject" id="subject" class="form-control" placeholder="Comment subject">
    </div>
    <div class="form-group">
      <label for="comment">Enter Comment</label>
      <textarea name="comment" id="comment" class="form-control" rows="5" placeholder="Write your comment here"></textarea>
    </div>
    <div class="form-group">
      <button type="submit" name="post" id="post" class="btn btn-info">Post</button>
    </div>
  </form>
</div>
</body>
</html>

What this code does:

  • We include the latest jQuery library (v3.6.0 in this case) and Bootstrap 3 via CDN. jQuery will simplify AJAX calls, and Bootstrap gives us ready-made styles and scripts for the navbar and dropdown.
  • The navigation bar contains a dropdown in the top-right corner. The bell icon (glyphicon glyphicon-bell) will serve as our notification icon. The <span class=”label label-danger count”></span> is an empty span where we will show the number of unread notifications (styled as a red badge). Initially, it’s empty.
  • The dropdown menu (<ul class=”dropdown-menu”></ul>) will be populated with notification items via AJAX. We leave it empty in HTML; our script will fill it with list items (<li>) dynamically.
  • Below the navbar, we have a simple form with two fields: Subject and Comment and a Post button to submit the comment. The form has an ID comment_form, which we will target in jQuery.

At this point, if you open index.php in a browser (via your local server, e.g., http://localhost/notifications_demo/index.php), you will see the form and the bell icon in the navbar. The bell icon won’t show a number yet (since there are no notifications and we haven’t added the script to fetch them), and the dropdown will appear empty. The form won’t do anything until we create the backend logic. So, next, we’ll build the server-side components.

Step 2: Set Up the Database Connection (connect.php)

We need a way for our PHP scripts to communicate with the MySQL database. For this, create a file connect.php, which will establish a connection to MySQL using the PHP MySQLi extension. We will include this file in other PHP scripts whenever we need to query the database.

Create connect.php with the following content:

<?php
// Database connection settings
$host = "localhost";
$username = "root";      // replace with your MySQL username
$password = "";          // replace with your MySQL password (if any)
$dbname = "notifications_demo";  // replace with your database name

// Create a connection
$con = mysqli_connect($host, $username, $password, $dbname);

// Check connection
if (!$con) {
    die("Connection failed: " . mysqli_connect_error());
}
?>

Make sure to replace the $username, $password, and $dbname values with your actual database credentials and name. For example, if you are using XAMPP on Windows, the default MySQL username is often root with an empty password. If you’ve set a password or are using a different user, use those details. The $host is “localhost” because the MySQL server is running locally.

This script will attempt to connect to MySQL and output an error message (and stop execution) if the connection fails. We’ll include connect.php in our other scripts to reuse this connection code.

Tip: After creating connect.php, you can test the connection by running this file via the PHP interpreter or temporarily including it in your index page to ensure there are no errors in connection.

Step 3: Handle New Comments (insert.php)

When a user submits the comment form on our page, we want to insert that comment into the database and trigger a notification. We’ll do this via an AJAX request. The form submission will be captured by jQuery, which will then send the form data to a PHP script asynchronously (without reloading the page). That PHP script will save the data to the database.

Create a file called insert.php with the following code:

<?php
// insert.php: Insert a new comment into the database
if (isset($_POST["subject"]) && isset($_POST["comment"])) {
    // Include database connection
    include("connect.php");

    // Sanitize input data to prevent SQL injection
    $subject = mysqli_real_escape_string($con, $_POST["subject"]);
    $comment = mysqli_real_escape_string($con, $_POST["comment"]);
    // Set default status for new comment (0 = unseen)
    $comment_status = 0;

    // Insert the new comment into the table
    $query = "INSERT INTO comments (comment_subject, comment_text, comment_status) 
              VALUES ('$subject', '$comment', '$comment_status')";

    if (mysqli_query($con, $query)) {
        echo "New comment added successfully.";
    } else {
        echo "Error: " . mysqli_error($con);
    }

    // Close the database connection
    mysqli_close($con);
}
?>

Explanation:

  • We first check if (isset($_POST[“subject”]) && isset($_POST[“comment”])) to ensure that this script is being accessed via a POST request with the expected form fields. This will be true when our jQuery AJAX call sends the form data.
  • We include connect.php to get the $con database connection.
  • We use mysqli_real_escape_string to sanitize the subject and comment inputs. This helps prevent SQL injection by escaping special characters.
  • We set $comment_status = 0 for the new comment, meaning it’s an unread notification.
  • We prepare an SQL INSERT query to add the new comment to the comments table with the provided subject, comment text, and status.
  • We execute the query with mysqli_query. If it succeeds, we output a success message; if it fails, we output the error. (The output isn’t actually shown to the user in the interface, but it’s good practice to return something. We could use this message for debugging or logging.)
  • Finally, we close the database connection with mysqli_close($con).

At this stage, when the user submits the form, this insert.php will run (we’ll wire it up via AJAX in a later step), and a new row will be added to the comments table. Every new comment is initially marked as comment_status = 0 (unseen). Next, we need to fetch these unseen comments and send them to the front end as notifications.

Step 4: Fetch Unseen Notifications (fetch.php)

This part is what allows the front end to get updates about new comments without reloading. We will create a script fetch.php that queries the database for unseen comments and returns data (most likely in JSON format) that our front end can understand and use to update the UI.

Create fetch.php and add the following code:

<?php
// fetch.php: Fetch unread notifications and count
include("connect.php");

// If this script is accessed via an AJAX request...
// Check if the 'view' flag is sent via POST to mark notifications as seen
if (isset($_POST["view"])) {
    if ($_POST["view"] != '') {
        // If 'view' is not empty, it implies the user has clicked the notification dropdown
        // Update all unseen (status 0) comments to seen (status 1)
        $update_query = "UPDATE comments SET comment_status = 1 WHERE comment_status = 0";
        mysqli_query($con, $update_query);
    }
}

// Fetch the 5 most recent comments that are still unseen (status 0)
$output = '';
$query = "SELECT * FROM comments WHERE comment_status = 0 ORDER BY comment_id DESC LIMIT 5";
$result = mysqli_query($con, $query);

// Build the notification list items
if (mysqli_num_rows($result) > 0) {
    while ($row = mysqli_fetch_assoc($result)) {
        $subject = htmlspecialchars($row["comment_subject"]);
        $text   = htmlspecialchars($row["comment_text"]);
        $output .= "
        <li>
          <a href='#'>
            <strong>{$subject}</strong><br/>
            <small>{$text}</small>
          </a>
        </li>
        <li class='divider'></li>
        ";
    }
} else {
    $output .= '<li><a href="#" class="text-bold text-italic">No new notifications</a></li>';
}

// Get the count of all unseen comments (notifications not yet viewed)
$count_query = "SELECT COUNT(*) AS count FROM comments WHERE comment_status = 0";
$count_result = mysqli_query($con, $count_query);
$count_data = mysqli_fetch_assoc($count_result);
$unseen_count = $count_data['count'];

// Prepare the data to return as JSON
$data = array(
    'notification' => $output,
    'unseen_count' => $unseen_count
);

// Output the data in JSON format
echo json_encode($data);
?>

Explanation:

  • We include the database connection at the top.
  • We check if (isset($_POST[“view”])): Our front-end will send a parameter view via AJAX (you’ll see this in the jQuery script soon). This parameter helps us determine if the user just opened the notification dropdown.
    • If $_POST[“view”] is not empty (for example, it might be the string “yes”), it means the user has clicked the notifications dropdown to view notifications. In that case, we execute an UPDATE query to set comment_status = 1 for all comments that are currently 0 (marking all unseen notifications as seen). This ensures that once the user has viewed the notifications, we don’t count them as unread anymore.
  • Next, we select up to the 5 most recent unseen comments from the database (WHERE comment_status = 0) and order them by newest first. These will be the notifications we display in the dropdown.
  • We loop through the results and build an HTML string $output containing
    • We also wrap the content in an anchor <a href=”#”>…</a> just to make the list items clickable (in a real application, these could link to a page or act when clicked).
    • We use htmlspecialchars on the output to safely encode any special characters in the data, preventing issues if comments contain quotes, HTML, etc.
  • If there are no unseen comments (mysqli_num_rows == 0), we output a single list item saying “No new notifications”.
  • Then, we get the total count of unseen notifications with another query (COUNT(*) of comments where status = 0). We fetch that count into $unseen_count.
  • We prepare an array $data with two pieces of information:
    • ‘notification’ => the HTML list items we built.
    • ‘unseen_count’ => the number of unseen notifications.
  • Finally, we use echo json_encode($data) to send this data back to the AJAX caller in JSON format. For example, it might return something like:
{
  "notification": "<li>...some list items...</li>",
  "unseen_count": 3
}
  • This is easy for our front-end script to handle.

With fetch.php in place, the server-side logic is ready. Now, we need to write the jQuery script on the front end to tie everything together: it will send form data to insert.php and retrieve notification data from fetch.php periodically.

Step 5: Implement the jQuery AJAX Script

The final piece of the puzzle is adding a client-side script to our HTML page (index.php) to handle AJAX interactions:

  • Capture the form submission event, send the form data to insert.php via AJAX, and update the UI accordingly.
  • Periodically poll the server (call fetch.php) to get the latest notifications and unread count.
  • Update the notification dropdown and the count badge in the navbar.
  • Mark notifications as “seen” when the user opens the dropdown.

We can add this script at the bottom of our index.php (just before the closing </body> tag). Open your index.php file and insert the following script:

<script>
$(document).ready(function() {

  // Function to load unseen notifications
  function load_unseen_notifications(view = '') {
    $.ajax({
      url: "fetch.php",
      method: "POST",
      data: { view: view },
      dataType: "json",
      success: function(data) {
        // Populate the dropdown menu with notifications
        $('.dropdown-menu').html(data.notification);
        // Update the notification count badge
        if (data.unseen_count > 0) {
          $('.count').text(data.unseen_count);
        } else {
          $('.count').text('');  // No unseen notifications
        }
      }
    });
  }

  // Initial fetch of notifications
  load_unseen_notifications();

  // Handle form submission for new comment
  $('#comment_form').on('submit', function(event) {
    event.preventDefault(); // Prevent the form from submitting normally (page reload)
    // Simple validation: ensure both fields are filled
    if ($('#subject').val().trim() !== '' && $('#comment').val().trim() !== '') {
      // Serialize the form data for AJAX
      var form_data = $(this).serialize();
      $.ajax({
        url: "insert.php",
        method: "POST",
        data: form_data,
        success: function(response) {
          // Optionally, you can alert or log the response from insert.php
          // Reset the form fields
          $('#comment_form')[0].reset();
          // Fetch the updated notifications including the new one
          load_unseen_notifications();
        }
      });
    } else {
      alert("Both Subject and Comment fields are required.");
    }
  });

  // When the notification icon is clicked (dropdown toggled)
  $(document).on('click', '.dropdown-toggle', function() {
    // Clear the notification count badge display (user is viewing them)
    $('.count').text('');
    // Mark notifications as seen in the database by calling fetch with a flag
    load_unseen_notifications('yes');
  });

  // Periodically check for new notifications (every 5 seconds)
  setInterval(function() {
    load_unseen_notifications(); 
  }, 5000);

});
</script>

How this jQuery script works:

  • We define a function load_unseen_notifications(view = ”) that makes an AJAX POST request to fetch.php. We send along a data parameter view. If the view is an empty string (default), the server script will just return notifications and count without marking any as seen. If the view is ‘yes’, the server will mark notifications as seen (set status to 1) before returning the data.
    • On success, the server responds with JSON containing the HTML for notifications and the count. We then update the .dropdown-menu <ul> with the HTML list items (data.notification) and update the .count span with the number of unseen notifications (data.unseen_count). If there are no unseen notifications, we clear the text.
  • We immediately call load_unseen_notifications() once when the document is ready. This initial call will populate any existing notifications when the page loads (in our case, initially, there will be none, so it will likely show “No new notifications” or just an empty state).
  • Next, we set up an event handler for the form submission (#comment_form). When the form is submitted:
    • We call event.preventDefault() to stop the default page refresh.
    • We check that both the Subject and Comment fields are not empty (using .trim() to ignore whitespace). If either is empty, we alert the user that both fields are required.
    • If both fields have input, we serialize the form data (this will create a URL-encoded string like subject=Hello&comment=This+is+a+test).
    • We send an AJAX POST request to insert.php with the form data. On success (which implies the comment was added to the database), we clear the form fields (reset() the form) and call load_unseen_notifications() again. This will fetch the latest unseen notifications, which now include the new comment we just added, and update the UI accordingly.
  • We then handle the click on the notification dropdown toggle (the bell icon). When a user clicks it:
    • We set the .count text to an empty string to visually reset the badge (since the user is now viewing notifications).
    • We call load_unseen_notifications(‘yes’) to fetch notifications and simultaneously inform the server that notifications are being viewed. This will trigger the PHP to mark all current notifications as seen (status 1) in the database. The next response will still show the notifications in the dropdown, but the unseen count will reset to 0.
  • Finally, the setInterval is used to repeatedly call load_unseen_notifications() every 5 seconds (5000 milliseconds). This means even if the user doesn’t manually perform any action, the page will check with the server every 5 seconds for new notifications. If a new comment is inserted into the database (for example, by another user of the application or from another browser window), the count and list will update within at most 5 seconds. You can adjust this interval timing as needed (shorter for more responsiveness or longer to reduce server load).

With this script in place, our front end is complete. The index.php file now contains the HTML and the embedded script to handle the dynamic behavior.

Testing Instructions

real-time notification system in PHP

Now that we have all the pieces (database table, PHP files, and front-end code), it’s time to test the notification system on your local server:

  • Start your local server and MySQL service (e.g., launch XAMPP/WAMP or start Apache/Nginx and MySQL). Make sure the database you set up is running and accessible with the credentials in connect.php.
  • Place your project files in the server directory. For example, if you’re using XAMPP, put the files (index.php, connect.php, insert.php, fetch.php) in a folder inside the htdocs directory (let’s say the folder is named notifications_demo). Ensure that your connect.php has the correct database name and credentials for the MySQL database you created.
  • Open the application in your web browser. Navigate to http://localhost/notifications_demo/index.php (adjust the URL to match your setup and folder name). You should see the form and the notification bell icon. The bell icon’s badge should be empty initially (no number showing).
  • Submit a test comment. Fill in the “Enter Subject” and “Enter Comment” fields with any test data, then click the Post button.
    • The form should clear itself (due to our script resetting it), and within a second, you should see the number “1” appear in a red badge next to the bell icon at the top. This indicates one unread notification.
    • Click the bell icon. A dropdown should appear below it, showing the subject and text of the comment you just submitted. This simulates a notification informing (you or other users) about the new comment. The badge number will disappear once you click the bell (because we mark the notification as seen).
  • Test the real-time aspect. Open a second browser window or tab to the same page (or use a different browser to simulate another user). Try submitting a comment from one window and observe the other:
    • When you post a new comment in one window, the other window (if it has been open and the script is running) should update its notification count within 5 seconds (thanks to the polling interval). The new notification should appear without a page refresh.
    • This demonstrates that the polling mechanism is working: one instance of the application detected a new entry in the database and updated the UI automatically.
  • Multiple notifications: Submit a few more comments. Each time, the badge count should increment (2, 3, 4, …), indicating how many new comments (notifications) are unseen. The dropdown list will show up to the 5 most recent unseen comments. Older ones (beyond the latest 5) won’t be listed, but they would still count toward the unseen number until marked seen. (You can increase the LIMIT in fetch.php if you want to display more at once.)
  • Mark as read: Click the bell icon on the interface after you have multiple notifications. The badge count will clear (reset to empty or 0) because we treat that action as having “seen” all notifications. The dropdown will show the list (which you can think of as read now), and if you close the dropdown, the badge stays cleared. New comments after that will again show a fresh count.

If everything is set up correctly, you now have a functioning real-time notification system on PHP without using any external service. The periodic AJAX polling is checking for new data and updating the page seamlessly.

Conclusion

In this tutorial, we successfully developed a real-time notification system in PHP using jQuery AJAX polling entirely in a local development environment. We covered setting up the MySQL database table to store notifications, creating PHP scripts to insert new notifications (comments) and fetch unseen notifications, and writing a frontend script to update the UI in real time without page reloads.

This AJAX polling method is an easy yet effective technique to keep users informed of new activity. By checking the server at regular intervals and updating the page, we achieved real-time behavior using only PHP and JavaScript. This approach is suitable for small to medium projects or prototypes. For larger applications or high-frequency updates, you might consider more efficient methods like WebSockets or long polling to reduce server load. However, the fundamental concepts remain the same: the client and server communicate to push new information to the user as it happens.

Feel free to expand and modify this notification system to suit your needs. For example, you could integrate user accounts and show notifications per user or trigger different types of notifications (messages, alerts, etc.). The simplicity of this implementation makes it easy to adapt.

Happy Coding! And when you’re ready to deploy your application, ensure you choose a reliable hosting environment. (For instance, Voxfor offers robust PHP hosting solutions where you can easily host your real-time applications.) With a solid foundation from this guide, you can confidently build more complex real-time features into your PHP projects. Enjoy your new notification system on your website!

About the writer

Hassan Tahir Author

Hassan Tahir wrote this article, drawing on his experience to clarify WordPress concepts and enhance developer understanding. Through his work, he aims to help both beginners and professionals refine their skills and tackle WordPress projects with greater confidence.

Leave a Reply

Your email address will not be published. Required fields are marked *

Lifetime Solutions:

VPS SSD

Lifetime Hosting

Lifetime Dedicated Servers