A demo project for a 4-role library system: admin, librarian, student, visitor.
Written in plain PHP with procedural mysqli and prepared statements. No frameworks,
no Composer, no build step. Copy it into XAMPP and it runs.
git clone https://github.com/BeingTonmoy/library-management-system-php-project.git1. Copy the library_management folder into C:\xampp\htdocs\
so it becomes htdocs/library_management/.
2. Start Apache and MySQL in the XAMPP control panel.
3. Open http://localhost/phpmyadmin → Import → choose database.sql → Go.
4. Open http://localhost/library_management/.
5. Sign in as the default admin: admin / admin123
The admin account is created automatically the first time a page loads
(see the bottom of config/config.php). Everyone else signs up on the register page.
If your MySQL uses a password, change DB_PASS in config/config.php.
2. Folder structure
```
library_management/
├── index.php Front controller: the ONLY entry point (router)
├── database.sql Schema + a few sample books
├── README.md
│
├── config/
│ └── config.php DB connection, session settings, app constants
│
├── helpers/
│ └── helpers.php esc(), CSRF, login guards, flash messages, fines
│
├── models/ M — every SQL query lives here
│ ├── user_model.php all 4 roles (one users table)
│ ├── book_model.php catalogue + stock
│ ├── borrow_model.php borrow requests + issue desk
│ ├── visitor_model.php passes, membership, suggestions
│ └── log_model.php activity log
│
├── controllers/ C — request handling, validation, decisions
│ ├── auth_controller.php login / register / logout
│ ├── admin_controller.php
│ ├── librarian_controller.php
│ ├── student_controller.php
│ ├── visitor_controller.php
│ └── ajax_controller.php all JSON endpoints
│
├── views/ V — HTML only
│ ├── partials/ header.php, footer.php (shared layout)
│ ├── auth/ login.php, register.php
│ ├── admin/dashboard.php
│ ├── librarian/dashboard.php
│ ├── student/dashboard.php
│ └── visitor/dashboard.php
│
└── assets/
├── css/style.css
└── js/app.js validation, escaping, live search, AJAX tables
```The MVC rule used throughout:** a view never runs a query, and a model never
prints HTML. The controller sits in the middle: it reads $_POST, validates,
calls the model, then requires the view.
Every URL looks like this:
index.php?page=<dashboard>&action=<what to do>&id=<row id>
URL | What happens |
|---|---|
| Login page |
| Signup page |
| Admin dashboard (list mode) |
| Load book 4 into the form |
| Cancel request 7 |
| Returns JSON |
| Sign out |
index.php loads config → helpers → models → controllers, checks the session timeout, then sends the request to one controller. require_role('admin') blocks anyone who is not an admin before the controller even starts.
Each role owns one table and does full Create, Read, Update, Delete and Search on its own dashboard. The form sits at the top of the page; the searchable table sits below it. Clicking Edit reloads the same page with the row loaded into that same form.
Role | Manages (CRUD) | Feature 1 | Feature 2 | Feature 3 |
|---|---|---|---|---|
Admin | User accounts (all roles) | Suspend / activate accounts, approve membership upgrades | System-wide activity log with search | Live statistics panel (AJAX, refreshes every 15s) |
Librarian | Books | Issue & return desk — stock and fines update automatically | Low stock alert list | Download the catalogue as a CSV file |
Student | My borrow requests | Catalogue browser showing live availability | Due-date and fine tracker | Printable digital library card |
Visitor | My visit passes | Apply for a student membership upgrade | Book suggestion box | Printable day pass with a unique code |
No feature appears on two dashboards.
A student requests a book → the librarian sees it on the issue desk.
The librarian clicks Issue → stock drops by 1, a due date is set (14 days).
The librarian clicks Return → stock goes back up, the fine is worked out (5 per day late) and stored.
A visitor applies for membership → the admin approves it → that visitor becomes a student and gets the student dashboard on the next sign-in.
Requirement | Where to look |
|---|---|
MVC |
|
DB (MySQLi procedural) | every function in |
Auth (session + cookie) |
|
PHP validation | the |
JS validation |
|
AJAX / JSON |
|
UI (HTML/CSS) |
|
Basic web security | see section 6 |
Feature completeness | CRUD + search + 3 features per role |
Attack | Defence | File |
|---|---|---|
SQL injection | Prepared statements everywhere — user text is never glued into SQL | all |
Stolen passwords |
|
|
XSS (server) |
|
|
XSS (client) |
|
|
CSRF | A secret token in every POST form and every delete/issue link |
|
Session fixation |
|
|
Cookie theft |
|
|
Idle machines | Automatic sign-out after 30 minutes |
|
Wrong role |
|
|
URL tampering | A student can only load their own rows ( |
|
Username guessing | Wrong username and wrong password give the same message |
|
Self-lockout | An admin cannot delete, suspend or demote themselves |
|
Two things worth saying out loud to students:
JavaScript validation is a convenience, not a defence. Anyone can turn JavaScript off. That is why every controller repeats the checks in PHP.
"Remember me" only refills the username, never the password.
All in config/config.php:
define('LOAN_DAYS', 14); // how long a student may keep a book
define('FINE_PER_DAY', 5); // fine for each day past the due date
define('LOW_STOCK', 3); // a book at or below this triggers the alert
define('CURRENCY', '$'); // symbol shown next to prices
define('SESSION_TIMEOUT', 1800); // idle sign-out, in secondsRole | Username | Password |
|---|---|---|
Admin |
|
|
Student | sign up on the register page | |
Librarian | sign up on the register page | |
Visitor | sign up on the register page |
Nobody can sign up as an admin — the register page only accepts the other three roles, and the controller checks that list again on the server. New admins are created by an existing admin.