Simple Messaging System With PHP MySQL (Free Download)

Welcome to a tutorial on how to create a simple messaging system with PHP and MYSQL. Want to add a “send message” or “private message” feature to your website? There are quite a number of things we need to cover – The database, library, AJAX handlers, messaging page, and more. Let us walk through a step-by-step example in this guide – Read on!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TABLE OF CONTENTS

Download & Notes Messaging Useful Bits & Links
The End

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

If you prefer to “quickstart” and study on your own:

  • Create a database. Import 1a-users.sql and 1b-messages.sql.
  • Change the database settings in 2-core.php to your own.
  • Launch 4-user-list.php in the browser, send messages. Change $_SESSION["user"] in 2-core.php to act as another user.
If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

 

EXAMPLE CODE DOWNLOAD

Click here to download all the example source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

 

PHP MYSQL MESSAGING SYSTEM

All right, let us now get into the steps of building a messaging system with PHP MYSQL.

 

STEP 1) DATABASE TABLES

USERS TABLE

1a-users.sql
CREATE TABLE `users` (
  `user_id` int(11) NOT NULL,
  `user_name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `users`
  ADD PRIMARY KEY (`user_id`);
  ALTER TABLE `users`
  MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;

INSERT INTO `users` (`user_id`, `user_name`) VALUES
  (1, 'Joa Doe'),
  (2, 'Job Doe'),
  (3, 'Joe Doe'),
  (4, 'Jon Doe'),
  (5, 'Joy Doe');

I know, you already have an existing user table. But this is what we will use as an example in this tutorial.

MESSAGES TABLE

1b-messages.sql
CREATE TABLE `messages` (
  `user_from` int(11) NOT NULL,
  `user_to` int(11) NOT NULL,
  `date_send` datetime NOT NULL DEFAULT current_timestamp(),
  `date_read` datetime DEFAULT NULL,
  `message` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
ALTER TABLE `messages`
  ADD PRIMARY KEY (`user_from`,`user_to`,`date_send`),
  ADD KEY `date_read` (`date_read`);

Yep, that’s all we need for messaging. A very straightforward messages table.

  • user_from Sent from (User ID). Partial primary key.
  • user_to Sent to (User ID). Partial primary key.
  • date_send Time when the message is sent. Partial primary key.
  • date_read Time when the message is read.
  • message The message itself.

 

 

STEP 2) CORE SETTINGS

2-core.php
<?php
// (A) ERROR REPORTING
error_reporting(E_ALL & ~E_NOTICE);
ini_set("display_errors", 1);
 
// (B) DATABASE SETTINGS - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8");
define("DB_USER", "root");
define("DB_PASSWORD", "");
 
// (C) START SESSION - ACT AS USER
session_start();
$_SESSION["user"] = ["id" => 1, "name" => "Joa Doe"];
// $_SESSION["user"] = ["id" => 2, "name" => "Job Doe"];
// $_SESSION["user"] = ["id" => 3, "name" => "Joe Doe"];
// $_SESSION["user"] = ["id" => 4, "name" => "Jon Doe"];
// $_SESSION["user"] = ["id" => 5, "name" => "Joy Doe"];

Next, we have a script to hold and set all the system settings. Take note of $_SESSION["user"] here – Use this to act as different user accounts, send messages to each other.

P.S. I normally keep this in a protected lib folder. Just being lazy and trying to keep things simple here. If you want:

  • Create a lib folder. Move 2-core.php inside.
  • Apache users, create a .htaccess file in lib with this line – deny from all.
  • That’s all. Users who try to directly access http://site.com/lib/2-core.php will get a 403 error. But PHP can still access the scripts.
  • Sorry IIS and NGINX users – Do your own research, it’s not that difficult.

 

STEP 3) MESSAGES LIBRARY

3-lib-msg.php
<?php
class Message {
  // (A) CONSTRUCTOR - CONNECT TO THE DATABASE
  private $pdo = null;
  private $stmt = null;
  public $error;
  function __construct () {
    try {
      $this->pdo = new PDO(
        "mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
        DB_USER, DB_PASSWORD, [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
      ]);
    } catch (Exception $ex) { exit($ex->getMessage()); }
  }
 
  // (B) DESTRUCTOR - CLOSE DATABSE CONNECTION
  function __destruct () {
    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }
 
  // (C) EXECUTE SQL QUERY
  function exec ($sql, $data=null) {
    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($data);
      return true;
    } catch (Exception $ex) {
      $this->error = $ex->getMessage();
      return false;
    }
  }
 
  // (D) FETCH (SINGLE ROW)
  function fetch ($sql, $data=null) {
    if ($this->exec($sql, $data) === false) { return false; }
    return $this->stmt->fetch();
  }
 
  // (E) FETCH ALL (MULTIPLE ROWS)
  function fetchAll ($sql, $data=null, $key=null) {
    if ($this->exec($sql, $data) === false) { return false; }
    if ($key === null) { return $this->stmt->fetchAll(); }
    else {
      $data = [];
      while ($r = $this->stmt->fetch()) { $data[$r[$key]] = $r; }
      return $data;
    }
  }
}
 
// (Z) START!
$MSG = new Message();

No need to panic. Keep calm and look carefully.

  • (A & B) When new Message() is created, the constructor connects to the database. The destructor closes the connection.
  • (C) exec() Runs an SQL statement.
  • (D) fetch() Runs a SELECT SQL, returns a single row.
  • (E) fetchAll() Runs a SELECT SQL, returns multiple rows.

Easy. Put this into the lib folder as well – Protect all your important library files.

 

 

STEP 4) USERS LIST & SEND MESSAGE PAGE

4-user-list.php
<?php
// (A) GET USERS
require "2-core.php";
require "3-lib-msg.php";
$users = $MSG->getUsers($_SESSION["user"]["id"]);
?>
 
<!-- (B) LEFT : USER NOW & LIST -->
<div id="userLeft">
  <!-- (B1) CURRENT USER -->
  <div id="userNow">
    You are <?=$_SESSION["user"]["name"]?>
  </div>
 
  <!-- (B2) USER LIST -->
  <?php foreach ($users as $uid=>$u) { ?>
  <div class="userRow" id="usr<?=$uid?>" onclick="msg.show(<?=$uid?>)">
    <?php if (isset($u["unread"])) { ?>
    <u class="userUR" id="ur<?=$uid?>"><?=$u["unread"]?></u>
    <?php } ?>
    <?=$u["user_name"]?>
  </div>
  <?php } ?>
</div>
 
 <!-- (C) RIGHT : MESSAGES LIST -->
<div id="userRight">
  <!-- (C1) SEND MESSAGE -->
  <form id="userSend" onsubmit="return msg.send()">
    <input type="text" id="msgTxt" required/>
    <input type="submit" value="Send"/>
  </form>
 
  <!-- (C2) MESSAGES -->
  <div id="userMsg"></div>
</div>

This looks complicated again, but keep calm and look carefully.

  • The page is split into left and right sides.
  • <div id="userLeft"> Left – Shows the current users and users list.
  • <div id="userRight"> Right – On selecting a user, show the “send message” form and messages.

 

 

STEP 5) MESSAGING JAVASCRIPT

5-message.js
var msg = {
  // (A) HELPER - AJAX FETCH
  ajax : (data, after) => {
    // (A1) FORM DATA
    let fdata = new FormData();
    for (const [k,v] of Object.entries(data)) { fdata.append(k, v); }
 
    // (A2) FETCH
    fetch("6-ajax.php", { method:"POST", body:fdata })
    .then((res) => {
      if (res.status!=200) { alert(`Server ${res.status} error.`) }
      else { return res.text(); }
    }).then(after).catch((err) => { console.error(err); });
  },
 
  // (B) SHOW MESSAGES
  uid : null, // CURRENT SELECTED USER
  show : (uid) => {
    // (B1) SET SELECTED USER ID
    msg.uid = uid;
 
    // (B2) HTML INTERFACE UPDATE
    let form = document.getElementById("userSend"),
        field = document.getElementById("msgTxt"),
        unread = document.getElementById("ur"+uid),
        wrap = document.getElementById("userMsg");
    wrap.innerHTML = "";
    form.style.display = "flex";
    field.value = "";
    field.focus();
    for (let r of document.querySelectorAll(".userRow")) {
      if (r.id=="usr"+uid) { r.classList.add("now"); }
      else { r.classList.remove("now"); }
    }
 
    // (B3) AJAX LOAD MESSAGES
    msg.ajax({
      "req" : "list",
      "uid" : uid
    }, (txt) => {
      wrap.innerHTML = txt;
      if (unread!==null) { unread.remove(); }
    });
  },
 
  // (C) SEND MESSAGE
  send : () => {
    let field = document.getElementById("msgTxt");
    msg.ajax({
      "req" : "send",
      "to" : msg.uid,
      "msg" : field.value
    }, (txt) => {
      if (txt == "OK") {
        msg.show(msg.uid);
        field.value = "";
      } else { alert(txt); }
    });
    return false;
  }
};

Yep, the messaging is heavily driven via AJAX. Don’t need to sweat, this is long-winded, but there are actually only 3 functions here.

  1. ajax() Support function. Does an AJAX call to the server.
  2. show() Fired when a user is selected – AJAX fetch the messages and show them.
  3. send() Self-explanatory. Send a message.

 

 

STEP 6) MESSAGE AJAX HANDLER

6-ajax.php
<?php
if (isset($_POST["req"])) {
  // (A) LOAD LIBRARY
  require "2-core.php";
  require "3-lib-msg.php";
 
  switch ($_POST["req"]) {
    // (B) INVALID
    default: echo "Invalid request"; break;
 
    // (C) LIST MESSAGES
    case "list":
      $msg = $MSG->getMsg($_POST["uid"], $_SESSION["user"]["id"]);
      if (count($msg)>0) { foreach ($msg as $m) {
        $css = $m["user_from"] == $_SESSION["user"]["id"] ? "mout" : "min" ; ?>
        <div class="<?=$css?>">
          <div class="mdate"><?=$m["date_send"]?></div>
          <div class="mtxt"><?=$m["message"]?></div>
        </div>
      <?php }}
      break;
 
    // (D) SEND MESSAGE
    case "send":
      echo $MSG->send($_SESSION["user"]["id"], $_POST["to"], $_POST["msg"])
        ? "OK" : $MSG->error ;
      break;
  }
}

The last piece of the puzzle – Use the library to fetch the messages, save a “send message”.

 

USEFUL BITS & LINKS

That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.

 

IT’S NOT REAL-TIME!

For the lost souls – You need to refresh the page to show new messages. This is a “private messaging system”, not a “live messaging system”. Big difference. If you want “live chat”, check out the links below.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

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