3 Steps Simple PHP Admin Panel (Source Code Included)

Welcome to a tutorial on how to create a simple PHP admin panel. Since you are reading this guide, I am going to assume that you are interested in “powering up” your existing project and planning to build an administrative component to it. Fear not, this tutorial will exactly walk you through how to build one and speed up your development.

This one is made in pure HTML, CSS, Javascript, and PHP. It does not rely on jQuery, Bootstrap, nor any other frameworks – So beginners can rest easy, and advanced code ninjas can add those if you wish. 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 The Database Libraries and Handlers
User Interface Building Modules The End

 

 

DOWNLOAD & NOTES

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

 

SOURCE 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.

 

FOLDERS

A quick introduction to how the folders are being organized in the zip file:

  • lib Where we store all the PHP library files.
  • public Where we put all the CSS and Javascript.

 

QUICK NOTES

  • Import lib/users.sql into the database.
  • Update lib/core.php, change the database settings to your own.
  • Accessing index.php should redirect you back to the login page. The default user is john@doe.com, and the password is 123456.

If you spot a bug, please feel free to comment below. I try to answer 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.

 

PROBLEMS?

  • For you guys who are getting a “cannot access page” error while trying to sign in – There is probably some path issue. Try using absolute URL in line 10 of login.php (and all other AJAX calls).
  • If you are getting password problems, please take note that you will need at least PHP 5.5 for password_hash() and password_verify() to work properly. Change the password encryption method if you cannot upgrade the PHP version on the server.

 

 

STEP 1) THE DATABASE

For the first step, we are going to create a user database… An admin panel that is open to the public doesn’t make any sense, does it? If you already have an existing user database, please feel free to skip this section – All you need is to make sure that there is a password field and a way to identify your administrators.

 

THE USERS TABLE

lib/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.

Please take note that the default admin account above is john@doe.com and the password is 123456. Feel free to change this to fit your own needs – For example, you can add an additional user status flag or even create extra fields to capture the user’s telephone numbers and stuff.

 

 

STEP 2) LIBRARIES & AJAX HANDLERS

Now that we have established the database, let us now deal with the next part of the foundations – Creating a user library, and an AJAX handler that will deal with user login and log out.

 

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 the library files. For example, accessing http://site.com/lib/lib-users.php will show a 403 unauthorized error. Very useful.

 

THE CORE CONFIG FILE

lib/core.php
<?php
// (A) ERROR REPORTING
error_reporting(E_ALL & ~E_NOTICE);

// (B) DATABASE SETTINGS
define('DB_HOST', 'localhost');
define('DB_NAME', 'test');
define('DB_CHARSET', 'utf8');
define('DB_USER', 'root');
define('DB_PASSWORD', '');

// (C) FILE PATHS
define("PATH_LIB", PATH_ROOT . "lib" . DIRECTORY_SEPARATOR);
 
// (D) START SESSION
session_start();

Next, we have a simple core config file – Just a couple of settings and starting the session here. Remember to change the database settings to your own.

 

USER LIBRARY

lib/lib-user.php
<?php
class Users {
  // (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,
          PDO::ATTR_EMULATE_PREPARES => false
        ]
      );
    } 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) GET USER BY ID OR EMAIL
  function get ($id) {
    // (C1) SQL QUERY
    $sql = is_numeric($id) 
      ? "SELECT * FROM `users` WHERE `user_id`=?"
      : "SELECT * FROM `users` WHERE `user_email`=?" ;
 
    // (C2) GET USER
    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute([$id]);
    $entry = $this->stmt->fetchAll();
    return count($entry)==0 ? false : $entry[0] ;
  }

  // (D) GET ALL USERS
  function getAll () {
    $this->stmt = $this->pdo->prepare("SELECT * FROM `users`");
    $this->stmt->execute();
    $entry = $this->stmt->fetchAll();
    return count($entry)==0 ? false : $entry ;
  }
 
  // (E) VERIFY USER
  function verify ($email, $password, $session=true) {
    // (E1) GET USER
    $user = $this->get($email);
    $pass = $user !== false;
 
    // (E2) CHECK PASSWORD
    if ($pass) { $pass = password_verify($password, $user['user_password']); }
 
    // (E3) START SESSION?
    if ($pass && $session) {
      $_SESSION['user'] = [
        "id" => $user['user_id'],
        "name" => $user['user_name'],
        "email" => $user['user_email']
      ];
    }
 
    // (E4) RESULT
    if ($pass) { return true; }
    else {
      $this->error = "Invalid user/password";
      return false;
    }
  }

  // (F) SAVE USER (ADD OR EDIT)
  function save ($email, $name, $password, $id=null) {
    // (F1) SQL + DATA
    $sql = $id===null
      ? "INSERT INTO `users` (`user_email`, `user_name`, `user_password`) VALUES (?, ?, ?)"
      : "UPDATE `users` SET `user_email`=?, `user_name`=?, `user_password`=? WHERE `user_id`=?" ;
    $data = [$email, $name, password_hash($password, PASSWORD_DEFAULT)];
    if ($id!==null) { $data[] = $id; }

    // (F2) SAVE USER
    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($data);
      return true;
    } catch (Exception $ex) {
      $this->error = $ex->getMessage();
      return false;
    }
  }

  // (G) DELETE USER
  function del ($id) {
    try {
      $this->stmt = $this->pdo->prepare("DELETE FROM `users` WHERE `user_id`=?");
      $this->stmt->execute([$id]);
      return true;
    } catch (Exception $ex) {
      $this->error = $ex->getMessage();
      return false;
    }
  }
}

This user library looks pretty massive, but in actual fact, just a bunch of functions that deal with SQL queries.

Function Description
__construct() The constructor. Connects to the database when the object is being created.
__destruct() The destructor. Closes the database connection when the object is being destroyed.
get() Get the user with the given ID or email address.
getAll() Get all users.
verify() Verify the given email and password. Can be used for login and extra security checks.
save() Add or update a user.
del() Delete the specified user.

 

USER SESSION AJAX HANDLER

ajax-session.php
<?php
require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "core.php";
switch ($_POST['req']) {
  // (A) INVALID REQUEST
  default:
    echo "INVALID REQUEST";
    break;

  // (B) LOGIN
  case "in":
    // (B1) ALREADY SIGNED IN
    if (is_array($_SESSION['user'])) { die("OK"); }

    // (B2) VERIFY
    else {
      // LOAD USER LIBRARY
      require PATH_LIB . "lib-users.php";
      $_USR = new Users();
      echo $_USR->verify($_POST['email'], $_POST['password']) ? "OK" : $_USR->error ;
    }
    break;

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

Finally, we create an AJAX handler to manage the user login/out requests. How it works is very simple –

  • To request for login, we post a $_POST['req'] = "in" to this script, followed by the email and password.
  • To request a logout, we post a $_POST['req'] = "out".

Yep, that’s it.

 

 

STEP 3) USER INTERFACE

Now that we have all the foundations laid out, all that is left is to create the user interface for the admin panel.

 

PAGE TEMPLATES

Let us start by creating the HTML templates in the lib folder:

lib/page-top.php
<?php
// (A) REDIRECT TO LOGIN PAGE IF NOT SIGNED IN
$_ADMIN = isset($_SESSION['user']);
if (!$_ADMIN && basename($_SERVER["SCRIPT_FILENAME"], '.php')!="login") {
  header('Location: login.php');
  exit();
}
?>
<!DOCTYPE html>
<html>
  <head>
    <title>ADMIN PANEL</title>
    <meta name="robots" content="noindex, nofollow">
    <link href="public/admin.css" rel="stylesheet">
    <script src="public/admin.js"></script>
  </head>
  <body>
    <!-- (B) NOW LOADING SPINNER -->
    <div id="page-loader">
      <img id="page-loader-spin" src="public/cube-loader.svg">
    </div>

    <?php if ($_ADMIN) { ?>
    <!-- (C) SIDE BAR -->
    <nav id="page-sidebar">
      <a href="#">
        <span class="ico">&#9880;</span> Add Modules Here
      </a>
    </nav>
    <?php } ?>

    <!-- (D) MAIN CONTENTS -->
    <div id="page-main">
      <?php if ($_ADMIN) { ?>
      <!-- (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 may seem to be very complicated, but here is how the layout looks like:

  • The page is divided into 2 main parts –
    • Sidebar on the left <nav id="page-sidebar">.
    • Main contents on the right <div id="page-main">.
  • The navigation bar <nav id="page-nav"> on top of the main contents contains 2 buttons.
    • <div id="page-button-side"> We hide the sidebar on small screens, and this button is used to toggle show/hide the sidebar.
    • <div id="page-button-out"> A log out button.

That’s about it for the page templates, and the only other interesting thing is the small block of PHP code right at the top – It simply 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.

 

THE CSS

public/admin.css
/* (A) UNIVERSAL */
body, button, input, textarea, select {
  font-family: Arial, sans-serif;
  font-size: 16px;
}
h1, h2, h3, h4 { margin: 5px 0; }

/* (B) NOW LOADING */
#page-loader, #page-loader-spin {
  position: fixed;
  transition: opacity 0.2s;
}
#page-loader {
  top:0; left:0;
  width: 100vw; height: 100vh;
  z-index: 9999;
  background: rgba(0, 0, 0, 0.5);
  visibility: hidden; opacity: 0;
}
#page-loader.active {
  visibility: visible; opacity: 1;
}
#page-loader-spin{
  top: 50%; left: 50%;
  margin: -50px 0 0 -50px;
}

/* (C) PAGE LAYOUT */
body {
  margin: 0; padding: 0;
  min-height: 100vh;
  display: flex;
  align-items: stretch;
}
#page-sidebar, #page-sidebar a, #page-nav { color: #fff; }

/* (D) SIDE BAR */
/* (D1) SIDEBAR LAYOUT ON BIG SCREEN */
#page-sidebar {
  min-width: 200px;
  max-width: 200px;
  transition: margin 0.2s;
  background: #353535;
}
/* (D2) SIDEBAR ITEMS */
#page-sidebar a {
  display: block;
  width: 100%;
  padding: 10px;
  text-decoration: none;
  border-bottom: 1px solid #555;
}
#page-sidebar a:hover { background: #222; }
/* (D3) SIDEBAR ICONS */
#page-sidebar .ico {
  display: inline-block;
  width: 25px;
  font-size: 24px;
  line-height: 24px;
  text-align: center;
  color: #f48042;
}
/* (D4) HIDE SIDEBAR ON SMALL SCREEN */
@media (max-width: 768px) {
  #page-sidebar { margin-left: -200px; }
  #page-sidebar.active { margin-left: 0; }
  #page-button-side { display: block !important; }
}

/* (E) PAGE MAIN */
#page-main {
  width: 100%;
  background: #f7f9fa;
}

/* (F) NAVIGATION BAR */
#page-nav {
  position: relative;
  height: 50px;
  background: #474747;
}
#page-button-side, #page-button-out {
  position: absolute; top: 0;
  width: 50px; height: 50px; line-height: 50px;
  font-size: 28px;
  font-weight: bold;
  text-align: center;
  background: #87260e;
  cursor: pointer;
}
#page-button-side {
  display: none;
  left: 0;
}
#page-button-out { right: 0; }

/* (G) CONTENTS */
#page-contents { padding: 20px; }

/* (H) FORMS */
/* (H1) INPUT FIELDS & BUTTONS */
input, button, select, textarea {
  box-sizing: border-box;
  padding: 8px;
  margin: 8px 2px;
}
button, input[type=button], input[type=submit] {
  background: #87260e;
  border: 0;
  color: #fff;
}
/* (H2) ZEBRA TABLE */
table.zebra {
  width: 100%;
  border-collapse: collapse;
}
table.zebra td { padding: 10px; }
table.zebra td.right { text-align: right; }
table.zebra tr:nth-child(odd) { background-color: #f1f1f1; }
/* (H3) STANDARD FORM */
form.standard {
  width: 320px; 
  padding: 20px;
  background: #eee;
  border: 1px solid #ccc;
}
form.standard label, form.standard input { width: 100%; }

Yep. This seemingly massive chunk of CSS is actually nothing more than cosmetics… Feel free to modify it to fit your own project’s theme.

 

THE JAVASCRIPT

public/admin.js
var admin = {
  // (A) SHOW/HIDE "NOW LOADING" BLOCK
  loading : function (show) {
    var block = document.getElementById("page-loader");
    if (show) { block.classList.add("active"); }
    else { block.classList.remove("active"); }
  },
 
  // (B) TOGGLE SIDE BAR
  sidebar : function () {
    document.getElementById("page-sidebar").classList.toggle("active");
  },

  // (C) DO AJAX CALL
  /* -- AJAX OPTIONS --
   *  url : target URL
   *  data : data to send
   *  target : (optional) put server response into this HTML element
   *  ok : function to run on success
   *  error : function to run on failure
   *  debug : console log response if set to true
   */
  ajax : function (opt) {
    // (C1) FORM DATA
    var data = new FormData();
    if (opt.data) { for (let key in opt.data) {
      data.append(key, opt.data[key]);
    }}

    // (C2) AJAX
    var xhr = new XMLHttpRequest();
    xhr.open('POST', opt.url);
    xhr.onload = function(){
      // DONE - HIDE "NOW LOADING"
      admin.loading(0);
 
      // DEBUG MODE
      if (opt.debug) { console.log(this.response); }
 
      // BAD SERVER RESPONSE
      if (this.status != 200) {
        alert(`Cannot access ${opt.url} - Server response code ${this.status}`);
      }
 
      // SERVER RESPONSE OK
      else {
        // PUT SERVER RESPONSE INTO HTML ELEMENT
        if (opt.target) {
          document.getElementById(opt.target).innerHTML = this.response;
          if (typeof opt.ok == "function") { opt.ok(this.response); }
        }

        // JUST RUN "POST REQUEST" FUNCTIONS
        else {
          if (this.response == "OK") {
            if (typeof opt.ok == "function") { opt.ok(this.response); }
          } else {
            alert(this.response);
            if (typeof opt.error == "function") { opt.error(this.response); }
          }
        }
      }
    };
 
    // (C3) SHOW "NOW LOADING" + GO!
    admin.loading(1);
    xhr.send(data);
  },

  // (D) SIGN OFF
  bye : function () { if (confirm("Sign off?")) {
    admin.ajax({
      url : "ajax-session.php",
      data : { req : "out" },
      ok : function () { location.reload(); }
    });
  }}
};
Function Description
loading() Show/hide the “now loading” spinner.
sidebar() Toggle show/hide the sidebar.
ajax() AJAX process with bells and whistles.
bye() Sign off the user.

 

 

MAIN & LOGIN PAGE

Finally, all we need to do is to use the templates and create a login form.

index.php
<?php
// (A) INIT
require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "core.php";

// (B) HTML
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.

login.php
<?php
// (A) INIT
require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "core.php";
require PATH_LIB . "page-top.php"; ?>
 
<!-- (B) JAVASCRIPT -->
<script>
function login () {
  admin.ajax({
    url : "ajax-session.php",
    data : {
      req : "in",
      email : document.getElementById("user_email").value,
      password : document.getElementById("user_password").value
    },
    ok : function () { location.href = "index.php"; }
  });
  return false;
}
</script>
 
<!-- (C) CSS -->
<style>
#login-form {
  max-width: 340px;
  margin: 50px auto 0 auto;
  padding: 10px 20px 20px 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"; ?>

The login page should be pretty straightforward as well – It is nothing more than a login form.

 

EXTRA) BUILDING MODULES

Yes, we now have a fully functional admin shell. But just how do we build on top of it? In this extra section, we will walk through how to add a user’s management section to the admin panel.

 

CREATE LIBRARY AND AJAX HANDLER

Start by creating the module library and respective AJAX handler. Since we already have the users library, we will just use it to create the AJAX handler:

ajax-users.php
<?php
// (A) RESTRICT ACCESS
require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "core.php";
if (!isset($_SESSION['user'])) { exit("No permisssion to access"); }

// (B) INIT
require PATH_LIB . "lib-users.php";
$_USR = new Users();

// (C) HANDLE AJAX REQUEST
switch ($_POST['req']) {
 // (C1) INVALID REQUEST
  default:
    echo "INVALID REQUEST";
    break;

  // (C2) SHOW ALL USERS
  case "list":
    $users = $_USR->getAll(); ?>
    <h1>MANAGE USERS</h1>
    <input type="button" value="Add User" onclick="usr.addEdit()"/>
    <?php
    if (is_array($users)) {
      echo "<table class='zebra'>";
      foreach ($users as $u) {
        printf("<tr><td>%s (%s)</td><td class='right'>"
          . "<input type='button' value='Delete' onclick='usr.del(%u)'>"
          . "<input type='button' value='Edit' onclick='usr.addEdit(%u)'>"
          . "</td></tr>", 
          $u['user_name'], $u['user_email'],
          $u['user_id'], $u['user_id']
        );
      }
      echo "</table>";
    } else { echo "<div>No userrs found.</div>"; }
    break;

  // (C3) ADD/EDIT USER DOCKET
  case "addEdit":
    $edit = is_numeric($_POST['id']);
    if ($edit) { $user = $_USR->get($_POST['id']); } ?>
    <h1><?=$edit?"EDIT":"ADD"?> USER</h1>
    <form onsubmit="return usr.save()">
      <input type="hidden" id="usr_id" value="<?=isset($user['user_id'])?$user['user_id']:""?>"/>
      <label for="usr_name">Name:</label>
      <input type="text" id="usr_name" required value="<?=isset($user['user_name'])?$user['user_name']:""?>"/>
      <label for="usr_email">Email:</label>
      <input type="text" id="usr_email" required value="<?=isset($user['user_email'])?$user['user_email']:""?>"/>
      <label for="usr_password">Password:</label>
      <input type="password" id="usr_password" required minlength="6"/>
      <input type="button" value="Cancel" onclick="usr.list()"/>
      <input type="submit" value="Save"/>
    </form>
    <?php break;

  // (D) ADD USER
  case "add":
    echo $_USR->save($_POST['email'], $_POST['name'], $_POST['password']) ? "OK" : $_USR->error ;
    break;

  // (E) EDIT USER
  case "edit":
    echo $_USR->save($_POST['email'], $_POST['name'], $_POST['password'], $_POST['id']) ? "OK" : $_USR->error ;
    break;

  // (F) DELETE USER
  case "del":
    echo $_USR->del($_POST['id']) ? "OK" : $_USR->error ;
    break;
}

 

CREATE USER PAGE

With that, we can now create a user page:

users.php
<?php
// (A) INIT
require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "core.php";
 
// (B) HTML
require PATH_LIB . "page-top.php"; ?>
<script src="public/users.js"></script>
<div id="container"></div>
<?php require PATH_LIB . "page-bottom.php"; ?>

Yep, nothing much with the users landing page since most of it is AJAX driven.

 

public/users.js
var usr = {
  // (A) SHOW ALL USERS
  list : function () {
    admin.load({
      url : "ajax-users.php",
      target : "container",
      data : { req : "list" }
    });
  },

  // (B) SHOW ADD/EDIT USER DOCKET
  addEdit : function (id) {
    admin.load({
      url : "ajax-users.php",
      target : "container",
      data : {
        req : "addEdit",
        id : id
      }
    });
  },
 
  // (C) SAVE USER
  save : function () {
    var id = document.getElementById("usr_id").value;
    admin.ajax({
      url : "ajax-users.php",
      data : {
        req : (id=="" ? "add" : "edit"),
        id : (id=="" ? null : id),
        name : document.getElementById("usr_name").value,
        email : document.getElementById("usr_email").value,
        password : document.getElementById("usr_password").value
      },
      ok : usr.list
    });
    return false;
  },

  // (D) DELETE USER
  del : function (id) { if (confirm("Delete user?")) {
    admin.ajax({
      url : "ajax-users.php",
      data : {
        req : "del",
        id : id
      },
      ok : usr.list
    });
  }}
};
window.addEventListener("DOMContentLoaded", usr.list);

The Javascript is the one that does the heavy lifting – It may look complicated, but look closely… It is nothing but simple AJAX calls.

 

UPDATE SIDEBAR

Finally, simply update the sidebar so that the new landing page is accessible.

lib/page-top.php
<nav id="page-sidebar">
  <a href="users.php">
    <span class="ico">&#9879;</span> Manage Users
  </a>
</nav>

 

 

ADDING A NEW MODULE – THE SUMMARY

  • Create your own module library lib/lib-module.php and AJAX handler ajax-module.php.
  • Create the module page – module.php.
  • Update the sidebar – <a href="module.php">MODULE</a>.
  • Done!

 

IMPROVEMENT IDEAS

As much as this is a functional skeleton, there are still a lot of improvements to be done with this admin panel:

  • A better way to manage expired user sessions – Redirect the users back to the login page when the session has expired, or ask the user to sign in again?
  • More security – Do more server-side checks, maybe implement a simple CSRF token. Read my other guide if you are interested.
  • The users’ management is still very raw – Implement your own user search and pagination.
  • Of course, more CSS cosmetics if you want.
  • Change the password encryption method if you don’t feel safe – Here is my other guide on how to deal with password protection in PHP.

 

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!

19 thoughts on “3 Steps Simple PHP Admin Panel (Source Code Included)”

  1. Hi there,

    Great tutorial and script.
    Quick question!
    Have you or do you have a tutorial on implementing User permissions in this admin panel?
    I have tried but keep receiving this error:
    Warning: in_array() expects parameter 2 to be array, null given – line 11
    and Line 11 is: return in_array($id, $_SESSION[‘user’][‘permissions’][$module]);

    Thanks so much

  2. Shijil Thachilodi

    hi, this works very well for me.
    I would like to know how can I display the name of currently logged in user’s name in index.php

  3. I have an extra row in my sql called rights, this is to check whether a user is an admin or not with 0 being no and 1 being yes, how would i make it so a user can only login if rights=1?

    1. Very simple. Just modify ajax-session.php, case "in", add another line of check.

      $pass = is_array($user);
      if ($pass) { $pass = $user['rights'] == 1; }

    1. Check that you have at least PHP 5.5 on the server – That is required to make password_hash() and password_verify() work. If it still fails, write a simple script to call the edit(EMAIL, NAME, PASSWORD, ID) function to update the user/password.

  4. Great!
    but though, I downloaded the zip file of this admin tutorial, when I click the login btn this is all I get.
    Invalid user/password

    Please how do I make this work?

    1. Got it – Updated the zip file a little. That is probably a “false alarm”, and an AJAX error instead. Change “ajax-session.php” to the absolute URL in login.php, line 13 if you are still getting the same error.

Leave a Comment

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