Simple PHP Admin Panel (Free Source Code Download)

Welcome to a tutorial on how to create a simple PHP admin panel. Since you are reading this, I will assume that you are interested in “powering up” your existing project and want to build an administrative component to it. So here is a sharing of my own – All done in pure HTML, CSS, Javascript, and PHP. Zero third-party frameworks. 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 Quick Start How It Works
Useful Bits & Links The End

 

 

DOWNLOAD & NOTES

First, here is the download link to the source code as promised.

 

QUICK NOTES

For you guys who just want to quickstart and work on your own:

  • Create a database and import lib/SQL-users.sql.
  • Open lib/Core.php, change all the settings marked with CHANGE TO YOUR OWN.
  • Make sure that the PDO extension is enabled in php.ini.
  • That’s all. Accessing index.php should redirect to the login page. The default user is john@doe.com, and the password is 123456.
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 the 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.

 

QUICKSTART & WALKTHROUGH

All right, let us now get started with a quick walkthrough and how to customize the admin panel.

 

FOLDERS

There are only two self-explanatory folders for this project.

  • lib Put all your PHP libraries and “important files” in this folder.
  • public All the public resources – CSS, Javascript, images, videos, etc…

 

MODIFYING THE HTML TEMPLATE

  • lib/PAGE-top.php is the top half of the HTML template.
  • lib/PAGE-bottom.php is the bottom half.
  • There is only one stylesheet public/admin.css and basic public/admin.js to drive the HTML interface.
  • As in the introduction, there are no third-party frameworks. Feel free to use a library of your choice – jQuery, React, Angular, Bootstrap, Dojo, etc…

 

 

ADD YOUR OWN PAGE

myPage.php
<?php
// (A) LOAD THE CORE
require "lib/Core.php";
 
// (B) HTML
require PATH_LIB . "PAGE-top.php"; ?>
<h1>MY PAGE</h1>
<p>Do something.</p>
<?php require PATH_LIB . "PAGE-bottom.php"; ?>
  1. As you already know – lib/Core.php contains all the necessary core settings.
  2. Then, just load the HTML templates, sandwich your content in-between.

 

UPDATE THE SIDEBAR

lib/PAGE-top.php
<!-- (C) SIDE BAR -->
<nav id="page-sidebar">
  <a href="<?=URL_HOST?>myPage.php">
    <span class="ico">&#9879;</span> MY PAGE
  </a>
</nav>

Finally, update the sidebar and add a link to your page.

 

ADD OR UPDATE USERS

// (A) PASSWORD HASH
$clear = "mypassword";
$hash = password_hash($clear, PASSWORD_DEFAULT);
 
// (B) PASSWORD VERIFY
$valid = password_verify($clear, $hash);

The user password is encrypted using password_hash() and can be verified using password_verify(). Create your own user library if you want, do your own INSERT and UPDATE SQL.

 

 

HOW IT WORKS

With that, let us now get into more details of “how it works” – This is for you guys who want to learn a little more and “deep customize” the admin panel.

 

PART 1) PROTECTING THE LIBRARY FOLDER

lib/.htaccess
Deny from all

For you guys who are on Apache, this one line will prevent people from directly accessing and messing with the library files. For example, accessing http://site.com/lib/LIB-database.php will show a 403 unauthorized error. But no worries, PHP can still access and work with the library files.

P.S. IIS and NGINX users, do your own homework and protect your own library.

 

PART 2) USERS DATABASE

lib/SQL-user.sql
-- (A) USERS TABLE
CREATE TABLE `users` (
  `user_id` int(11) NOT NULL,
  `user_email` varchar(255) NOT NULL,
  `user_name` varchar(255) NOT NULL,
  `user_password` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `users`
  ADD PRIMARY KEY (`user_id`),
  ADD UNIQUE KEY `user_email` (`user_email`),
  ADD KEY `user_name` (`user_name`);

ALTER TABLE `users`
  MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;
 
-- (B) DEFAULT USER
INSERT INTO `users` (`user_id`, `user_email`, `user_name`, `user_password`) VALUES
(1, 'john@doe.com', 'John Doe', '$2y$10$vZJy7y4uqQQTRN3zdi2RE.5ZJJzGEEPnzEjFXm4nEOx023XQ2Qe..');
Field Description
user_id Primary key, the user ID.
user_email The user email, set to unique to prevent duplicate registrations.
user_name The user’s name.
user_password The password.

I initially didn’t want to create a users table, as some of you guys already have an existing one. But this should benefit those who don’t, and the admin panel should work “out of the box” with proper login and logoff mechanism.

 

 

PART 3) CORE CONFIG FILE

lib/Core.php
<?php
// (A) ERROR HANDLING - CHANGE TO YOUR OWN
error_reporting(E_ALL & ~E_NOTICE);
ini_set("display_errors", 1);
// ini_set("log_errors", 1);
// ini_set("error_log", "PATH/error.log");

// (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) URL
define("URL_HOST", "http://localhost/"); // CHANGE TO YOUR OWN
define("URL_PUBLIC", URL_HOST . "public/");
 
// (D) FILE PATHS
define("PATH_LIB", __DIR__ . DIRECTORY_SEPARATOR);
define("PATH_BASE", dirname(PATH_LIB) . DIRECTORY_SEPARATOR);
 
// (E) START SESSION
session_start();

Next, we have a simple core config file – To store all the common system settings and start the session.

 

PART 4) DATABASE LIBRARY

lib/LIB-database.php
<?php
class DB {
  // (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
  function fetch ($sql, $data=null) {
    if ($this->exec($sql, $data) === false) { return false; }
    return $this->stmt->fetch();
  }
}

This simple PDO database library is another small piece to help you guys develop a little faster – Go ahead and adopt it, or delete and use your own library.

  • (A & B) When a $DB = new DB() is created, the constructor will connect to the database. The destructor closes the connection.
  • (C) exec() Executes an SQL query.
  • (D) fetch() Runs a SELECT SQL query.

 

 

PART 5) USER SESSION AJAX HANDLER

ajax-session.php
<?php
require "lib/Core.php";
if (isset($_POST["req"])) { switch ($_POST["req"]) {
  // (A) INVALID REQUEST
  default: exit("INVALID REQUEST");

  // (B) LOGIN
  case "in":
    // (B1) ALREADY SIGNED IN
    if (isset($_SESSION["user"])) { exit("OK"); }
 
    // (B2) VERIFY
    else {
      require PATH_LIB . "LIB-database.php";
      $DB = new DB();
      $user = $DB->fetch(
        "SELECT * FROM `users` WHERE `user_email`=?", [$_POST["email"]]
      );
      $pass = is_array($user);
      if ($pass) { $pass = password_verify($_POST["password"], $user["user_password"]); }
      if ($pass) {
        $_SESSION["user"] = [];
        foreach ($user as $k=>$v) { if ($k!="user_password") {
          $_SESSION["user"][$k] = $v;
        }}
        exit("OK");
      }
      exit("Invalid user/password");
    }

  // (C) LOGOUT
  case "out":
    unset($_SESSION["user"]);
    exit("OK");
}}

This AJAX handler deals with the user login and logout. Very simply:

  • (B) To request for login, we send $_POST = ["req"=>"in", "email"=>"EMAIL", "password"=>"PASS"] to this script.
  • (C) To request a logout, we send $_POST = ["req"=>"out"].

Yep, that’s all. You may want to build your own user library, and further clean up the login sequence.

 

PART 6) HTML TEMPLATE

lib/PAGE-top.php
<?php
// (A) LOGIN CHECK
$_ISADMIN = isset($_SESSION["user"]);
if (!$_ISADMIN && !isset($_ISLOGIN)) {
  header("Location: ".URL_HOST."login.php"); exit();
}
if ($_ISADMIN && isset($_ISLOGIN)) {
  header("Location: ".URL_HOST); exit();
} ?>
<!DOCTYPE html>
<html>
  <head>
    <title>ADMIN PANEL</title>
    <meta name="robots" content="noindex, nofollow">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=2">
    <link href="<?=URL_PUBLIC?>admin.css" rel="stylesheet">
    <script>var url = {host:"<?=URL_HOST?>",public:"<?=URL_PUBLIC?>"};</script>
    <script async src="<?=URL_PUBLIC?>admin.js"></script>
  </head>
  <body>
    <!-- (B) NOW LOADING SPINNER -->
    <div id="page-loader">
      <img src="<?=URL_PUBLIC?>cube-loader.svg"/>
    </div>

    <?php if ($_ISADMIN) { ?>
    <!-- (C) SIDE BAR -->
    <nav id="page-sidebar">
      <a href="<?=URL_HOST?>pageA.php">
        <span class="ico">&#9879;</span> Module A
      </a>
      <a href="<?=URL_HOST?>pageB.php">
        <span class="ico">&#9880;</span> Module B
      </a>
    </nav>
    <?php } ?>

    <!-- (D) MAIN CONTENTS -->
    <div id="page-main">
      <?php if ($_ISADMIN) { ?>
      <!-- (D1) NAVIGATION BAR -->
      <nav id="page-nav">
        <div id="page-button-side" onclick="adm.side();">&#9776;</div>
        <div id="page-button-out" onclick="adm.bye();">&#9747;</div>
      </nav>
      <?php } ?>

      <!-- (D2) PAGE CONTENTS -->
      <main id="page-contents">
lib/PAGE-bottom.php
      </main>
    </div>
  </body>
</html>

This looks complicated at first, but look closely. There are only a few main sections:

  • <nav id="page-sidebar"> Sidebar to the left. Hides itself on small screens.
  • <div id="page-main"> The main area is to the right.
  • <nav id="page-nav"> Top navigation bar (inside the main area).
    • <div id="page-button-side"> This button is used to toggle show/hide the sidebar on small screens.
    • <div id="page-button-out"> The logout button.
  • <main id="page-contents"> Actual contents (inside the main area)

That’s about it for the template, the only other interesting thing is the small block of PHP code at the top – It detects if the user is signed in, and redirects to the login page if not. Feel free to change this if you intend to use a different login URL.

 

 

PART 7) THE JAVASCRIPT

public/admin.js
var admin = {
  // (A) SHOW/HIDE "NOW LOADING" BLOCK
  loading : (show) => {
    var block = document.getElementById("page-loader");
    if (show) { block.classList.add("active"); }
    else { block.classList.remove("active"); }
  },
 
  // (B) TOGGLE SIDE BAR
  sidebar : () => {
    document.getElementById("page-sidebar").classList.toggle("active");
  },
 
  // (C) SIGN OFF
  bye : () => { if (confirm("Sign off?")) {
    admin.ajax({
      url : url.host + "ajax-session.php",
      data : { req : "out" },
      ok : () => { location.reload(); }
    });
  }},
 
  // (D) AJAX FETCH
  // url : target url
  // data : data to send
  // ok : function to run on server "OK"
  ajax : (opt) => {
    // (D1) FORM DATA
    let data = new FormData();
    for (let [k, v] of Object.entries(opt.data)) { data.append(k, v); }
 
    // (D2) FETCH
    fetch(opt.url, { method:"POST", body:data })
    .then((res) => {
      if (res.status != 200) {
        alert(`Server ${res.status} error`);
        console.error(res);
      } else { return res.text(); }
    })
    .then((txt) => {
      if (txt != "OK") { alert(txt); }
      else { opt.ok(); }
    })
    .catch((err) => { console.error(err); });
  }
};
Function Description
loading() Show/hide the “now loading” spinner.
sidebar() Toggle show/hide the sidebar.
bye() Sign off the user.
ajax() AJAX fetch.

 

PART 8) LOGIN PAGE

login.php
<?php
// (A) INIT
require "lib/Core.php";
$_ISLOGIN = 1;
require PATH_LIB . "PAGE-top.php"; ?>
 
<!-- (B) JAVASCRIPT -->
<script>
function login () {
  admin.ajax({
    url : url.host + "ajax-session.php",
    data : {
      req : "in",
      email : document.getElementById("user_email").value,
      password : document.getElementById("user_password").value
    },
    ok : () => { location.href = "index.php"; }
  });
  return false;
}
</script>
 
<!-- (C) CSS -->
<style>
#login-form {
  max-width: 320px; margin: 0 auto; padding: 20px;
  background: #eee; border: 1px solid #ccc;
}
#login-form input { width: 100%; margin-top: 20px; }
</style>
 
<!-- (D) LOGIN FORM -->
<form id="login-form" onsubmit="return login();">
  <h1>LOGIN</h1>
  <input type="email" placeholder="Email" id="user_email" required />
  <input type="password" placeholder="Password" id="user_password" required />
  <input type="submit" value="Sign In"/>
</form>
<?php require PATH_LIB . "PAGE-bottom.php"; ?>

Just a “regular AJAX login form”.

 

PART 9) MAIN PAGE

index.php
<?php
require "lib/Core.php";
require PATH_LIB . "PAGE-top.php";?>
YOUR DASHBOARD HERE
<?php require PATH_LIB . "PAGE-bottom.php"; ?>

The main page is nothing but an empty shell – Feel free to create your own dashboard.

 

USEFUL BITS & LINKS

That’s all for the tutorial, and here are a few small extras and links that may be useful to you.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end of this tutorial. I hope that it has helped speed up your project development, and if you have anything to share with this guide, please feel free to comment below. Good luck and happy coding!

21 thoughts on “Simple PHP Admin Panel (Free Source Code Download)”

  1. Could you possibly help.
    What should I change in line10?
    Struggling. Very grateful for all your hard work.
    login.php line10

Leave a Comment

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