A PHP starter template with authentication, user management, and role-based permission control. Built on a pure MVC architecture with a custom PSR-4 autoloader and Composer for dependency management.
A solid starting point for PHP web applications that need a secure admin panel out of the box.
- Secure authentication — Login by email or document number, CSRF protection, anti-session hijacking, inactivity logout, persistent remember-me cookie, brute-force lockout
- User management — Full CRUD, profile images, account activation/deactivation, invitation by email (admin creates pending user → 48 h token link → user sets password → account activated)
- Role management — Role catalog with CRUD, role↔permission assignment UI, system-role protection (
is_system), logical delete only - Permission control — Two-level permission model: direct per-user assignments + role-inherited permissions (UNION, deduplicated); adaptive navigation menu; zero DB queries per check
- Audit Log — Append-only activity log for all admin actions (login, logout, user/role/permission CRUD); filterable by module, action, user, and date range; detail modal with human-readable key/value breakdown; export via DataTables; gated by
audit_logpermission - Metrics dashboard — Chart.js charts (donut active/inactive/pending, bar, line) + stat cards for users, permissions, roles, and today's audit events; toggleable access-metrics row (pending invitations, resets this week) with localStorage persistence and CSS slide+fade animation; staggered entrance animations for stat cards and chart cards; session-based cache with event-driven invalidation
- Custom error pages — Styled 403, 404, and 500 error pages via Apache
ErrorDocument - Composer-managed — Native PSR-4 autoloading for
App\*; Composer handles both autoloading and third-party packages - AdminLTE 3 — Production-ready responsive dashboard
- PDF generation — Built-in report generation with TCPDF
- Dark mode — system-aware toggle (moon/sun) in the navbar; preference stored in
localStorage, falls back toprefers-color-scheme; anti-FOUC inline script prevents flash on reload; covers all modules and auth standalone pages - Full UI toolkit — DataTables, Select2, SweetAlert2, Chart.js, jQuery Validate included
- PHP 8.2+ with PDO and GD extensions
- MySQL 5.7+ / MariaDB 10.4+
- Apache 2.4+ or Nginx
# 1. Clone the repository
git clone https://github.com/Jandres25/php-mvc-admin-starter.git
cd php-mvc-admin-starter
# 2. Install dependencies
composer install
# 3. Import the database
mysql -u root -p < database/schema.sql
mysql -u root -p < database/seeder.sql
# 4. Set up the environment
cp .env.example .env
# Edit .env with your credentials (see Configuration section)
# 5. Create the uploads directory
mkdir -p public/uploads/users
cp public/img/user_default.jpg public/uploads/users/Open http://localhost/php-mvc-admin-starter/ in your browser.
Default credentials: admin@sistema.com / admin123
DB_HOST=localhost
DB_NAME=auth_base
DB_USER=root
DB_PASS=yourpassword
DB_CHARSET=utf8mb4
APP_URL=http://localhost/php-mvc-admin-starter/public
TIMEZONE=America/La_Paz
DEBUG=true
APP_VERSION=3.15.1
# Dashboard cache TTL in seconds (0 to disable)
DASHBOARD_CACHE_TTL=300
# Session & Remember Me
SESSION_LIFETIME=1800
REMEMBER_ME_LIFETIME=2592000
REMEMBER_ME_COOKIE_NAME=remember_me
# Login throttling
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_MINUTES=15
# SMTP Configuration (Optional)
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USER=your_email@gmail.com
MAIL_PASS=your_app_password
MAIL_FROM=your_email@gmail.com
MAIL_FROM_NAME="Admin Starter"This project is designed to be extended. To add a module (e.g. Products):
- Controller — Create
app/Controllers/ProductController.phpwith namespaceApp\Controllers, extendingApp\Core\Controller - Model — Create
app/Models/Product.phpwith namespaceApp\Models, extendingApp\Core\Model - Views — Create
views/products/index.php,create.php, etc. - Routes — Add entries to
routes/web.php:['method' => 'GET', 'path' => '/products', 'controller' => 'Product@index', 'middleware' => ['auth', 'perm:products']], ['method' => 'POST', 'path' => '/products', 'controller' => 'Product@store', 'middleware' => ['auth', 'perm:products']], ['method' => 'GET', 'path' => '/products/create', 'controller' => 'Product@create', 'middleware' => ['auth', 'perm:products']],
- Assets — Add CSS to
public/css/modules/products/and JS topublic/js/modules/products/ - Permission — Insert the permission into the
permissionstable in the database - Menu — Add the link in
views/layouts/sidebar.phpwith a permission check:
<?php if (\App\Core\Auth::hasPermission('products')): ?>
<li class="nav-item">
<a href="<?= URL ?>products" class="nav-link">Products</a>
</li>
<?php endif; ?>The autoloader automatically resolves any class whose namespace matches the directory structure.
app/
├── Config/ # Bootstrap: config.php, Connection.php (PDO singleton), phpdotenv init
├── Controllers/ # Feature controllers (flat — no subdirectories)
├── Core/ # Controller.php, Model.php, Router.php, Auth.php, AssetRegistry.php, ErrorHandler.php, helpers.php
├── Middleware/ # AuthMiddleware, GuestMiddleware, PermissionMiddleware
├── Models/ # App\Models
└── Services/ # App\Services (ImageService, MailService, DashboardCache, LoginThrottleService, AuditLogger)
routes/ # web.php — all route definitions
views/ # PHP templates; layouts/header.php, sidebar.php and footer.php wrap content
public/ # Static assets (lib/, core/, plugins/, modules/) + index.php (Front Controller)
vendor/ # Composer dependencies (not committed — run composer install)
Request flow: All HTTP requests are routed through public/index.php via Apache rewriting. App\Core\Router matches the URI and method against routes/web.php, runs declared middleware (auth, guest, perm:NAME), and dispatches to the controller method. Clean URLs like /users, /users/5/edit, /permissions/3 replace the old direct-file access pattern.
# Run all tests
vendor/bin/phpunit
# Unit tests only (no DB required, ~2 s)
vendor/bin/phpunit --testsuite=Unit
# Integration tests (requires a test DB — see below)
vendor/bin/phpunit --testsuite=IntegrationTest DB setup (one time):
mysql -u root -p -e "CREATE DATABASE php_mvc_admin_starter_test CHARACTER SET utf8;"
cp .env.testing.example .env.testing
# Edit .env.testing with your local DB credentialsTests run automatically on every push and PR via GitHub Actions (see .github/workflows/tests.yml).
| Layer | Technology |
|---|---|
| Backend | PHP 8.2, PDO, Composer (native PSR-4 autoloading) |
| Dependencies | PHPMailer, TCPDF, phpdotenv (via Composer) |
| UI framework | AdminLTE 3, Bootstrap 4, FontAwesome |
| JavaScript | jQuery, DataTables, Select2, SweetAlert2, Chart.js, Moment.js |
| Database | MySQL / MariaDB |
| Testing | PHPUnit 11 (unit + integration suites, GitHub Actions CI) |
- Passwords hashed with
password_hash()(PASSWORD_DEFAULT), minimum 8 characters - CSRF tokens on all forms and AJAX endpoints; token regenerated after every POST (including error paths on sensitive endpoints); destructive non-AJAX actions use POST routes, never GET
- Prepared statements for all SQL queries
- XSS prevention via
htmlspecialchars()at the view layer on all output - Session cookie flags:
httponly,SameSite=Lax,use_strict_mode - Session hijacking protection (IP and User-Agent validation)
- Session ID regeneration on every login and on every remember-me auto-login
- Remember-me cookie:
HttpOnly,SameSite=Lax,Secure(HTTPS); token stored as SHA-256 hash, rotated on every use; revoked immediately when the associated account's password is changed (by the user or by an admin) - Configurable session lifetime via
SESSION_LIFETIMEin.env - Permission cache in session — navigation checks require zero DB queries per page load
- Users cannot deactivate or change the status of their own account
- Brute-force protection — account lockout after N failed login attempts (
LOGIN_MAX_ATTEMPTS); lazy unlock by time or manual admin unlock; locked accounts bypasspassword_verifyto prevent timing leaks
- docs/SEEDING.md — seeded users, rerun behavior, and permission matrix
- docs/ACCESS_CONTROL.md — session guards and permission cache flow
- docs/AJAX_AND_MODULES.md — AJAX endpoint and frontend module conventions
- docs/TESTING.md — test suites, DB setup, base classes, fixtures, and conventions
This project is configured for AI coding assistants like Claude Code. It includes custom skills, MCP server templates, and auto-approval settings. See docs/AI_SETUP.md for full details on how to use and extend the AI configuration.
See CONTRIBUTING.md for the full guide. We follow Conventional Commits and Semantic Versioning.
See CHANGELOG.md for the full change history.
MIT — see LICENSE for details.