A lightweight, zero-dependency PHP MVC framework built for rapid application development.
This repository contains:
- The Framework (
src/) - The core AzFramework code you'll use - A Working Demo (
app/,config/,public/) - Shows the framework in action - you can test it right now! - Project Template (
skeleton/) - A clean starting point for your own projects
Quick Test: Visit http://localhost/azframework/public/ to see the demo working!
To Start Your Own Project: Copy the skeleton/ folder (see Quick Start below)
📖 New to the framework? Read STRUCTURE.md for a beginner-friendly visual guide!
- ✅ Zero Dependencies - Pure PHP, no Composer required
- ✅ MVC Architecture - Clean separation of concerns
- ✅ Active Record ORM - Elegant database abstraction
- ✅ Built-in Authentication - Session management and CSRF protection
- ✅ Middleware Support - Route-level middleware
- ✅ Pagination - Built-in pagination with search
- ✅ Environment Configuration -
.envfile support - ✅ Dynamic Path System - Deploy in any directory structure
- ✅ Helper Functions - Common tasks made easy
- ✅ Security First - CSRF protection, input sanitization, prepared statements
- PHP 8.0 or higher
- MySQL 5.7+ or MariaDB 10.2+
- Apache with mod_rewrite enabled (or nginx equivalent)
The demo is already working! Just:
- Make sure XAMPP is running
- Visit:
http://localhost/azframework/public/ - You should see the framework working with styled pages
- Explore the code:
- Controllers:
app/Controllers/HomeController.php - Views:
app/Views/home.php - Routes:
config/routes.php
- Controllers:
This helps you understand how the framework works before creating your own project!
# 1. Copy the skeleton to your project folder
cp -r azframework/skeleton/* /path/to/myproject/
# 2. Copy framework source into your project
mkdir -p /path/to/myproject/azframework
cp -r azframework/src /path/to/myproject/azframework/
# 3. Set up your .env
cd /path/to/myproject
cp .env.example .env
# Edit .env with your settingscd myproject
cp .env.example .env
# Edit .env with your configuration
nano .envUpdate these values:
APP_NAME="My Application"
APP_URL=http://localhost/myproject
APP_BASE_PATH=/myproject
DB_HOST=127.0.0.1
DB_DATABASE=my_database
DB_USERNAME=root
DB_PASSWORD=your_passwordCREATE DATABASE my_database CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;chmod -R 755 storage/
chmod -R 777 storage/logs storage/sessions storage/uploadsEnsure your web server points to the public/ directory, or update .htaccess RewriteBase:
# In public/.htaccess
RewriteBase /myproject/Visit: http://localhost/myproject/
azframework/
├── src/
│ ├── Core/
│ │ ├── Database.php # PDO singleton connection
│ │ ├── Model.php # Active Record base class
│ │ ├── Controller.php # Base controller with helpers
│ │ └── Router.php # Pattern-based router
│ ├── Http/
│ │ ├── Middleware.php # Middleware interface
│ │ ├── AuthMiddleware.php # Authentication middleware
│ │ └── CsrfMiddleware.php # CSRF protection
│ ├── Support/
│ │ └── helpers.php # Global helper functions
│ └── bootstrap.php # Framework initialization
├── config/
│ ├── app.php # Application config
│ └── database.php # Database config
├── public/
│ ├── index.php # Entry point
│ └── .htaccess # URL rewriting
└── README.md # This file
<?php
namespace App\Models;
use AzFramework\Core\Model;
class User extends Model
{
protected $table = 'users';
protected $primaryKey = 'user_id';
protected $fillable = ['username', 'email', 'password', 'role'];
public function setPassword($password)
{
$this->attributes['password'] = password_hash($password, PASSWORD_BCRYPT);
}
public function getFullNameAttribute()
{
return $this->first_name . ' ' . $this->last_name;
}
}<?php
namespace App\Controllers;
use AzFramework\Core\Controller;
use App\Models\User;
class UserController extends Controller
{
public function index()
{
$this->requireAuth();
$result = (new User())->paginate(
page: $_GET['page'] ?? 1,
perPage: 20,
orderBy: 'created_at DESC'
);
$this->view('users/index', [
'title' => 'Users',
'users' => $result['data'],
'pagination' => $result['pagination'],
'csrf_token' => $this->csrf()
]);
}
public function store()
{
$this->requireAuth();
$this->verifyCsrf();
$data = $this->validateAndSanitize($_POST, [
'username' => 'required|min:3',
'email' => 'required|email',
'password' => 'required|min:6'
]);
if ($data === false) {
return $this->redirect('/users/create');
}
$user = new User();
$user->setPassword($data['password']);
unset($data['password']);
if ($user->create($data)) {
$this->setSuccess('User created successfully');
$this->redirect('/users');
} else {
$this->setError('Failed to create user');
$this->redirect('/users/create');
}
}
}// Find by primary key
$user = (new User())->find($id);
// Find all with conditions
$users = (new User())->findAll(['role' => 'admin'], 'created_at DESC');
// Query by field
$users = (new User())->where('email', 'LIKE', '%@example.com%');
// Count records
$count = (new User())->count(['role' => 'admin']);$user = new User();
$user->create([
'username' => 'john',
'email' => 'john@example.com',
'role' => 'user'
]);
// Or use save()
$user = new User();
$user->fill($_POST);
$user->save();$user = (new User())->find($id);
$user->email = 'newemail@example.com';
$user->update();
// Or update specific fields
$user->update(['email' => 'newemail@example.com']);$user = (new User())->find($id);
$user->delete();$result = (new User())->paginate(
page: 1,
perPage: 20,
conditions: ['active' => 1],
orderBy: 'created_at DESC',
allowedOrderColumns: ['created_at', 'username', 'email']
);
// Access data
$users = $result['data'];
$pagination = $result['pagination'];$result = (new User())->searchAndPaginate(
searchTerm: 'john',
searchFields: ['username', 'email', 'first_name', 'last_name'],
page: 1,
perPage: 20
);// Require user to be logged in
$this->requireAuth();
// Require specific role
$this->requireRole('administrator');
// Get current user ID
$userId = $_SESSION['user_id'];// Generate token (in view)
<input type="hidden" name="csrf_token" value="<?= $csrf_token ?>">
// Or use helper
<?= csrf_field() ?>
// Verify token (in controller)
$this->verifyCsrf();$data = $this->validateAndSanitize($_POST, [
'email' => 'required|email',
'password' => 'required|min:6',
'age' => 'required|min:1'
]);
if ($data === false) {
// Validation failed, errors stored in session
return $this->redirect('/form');
}// Set messages
$this->setSuccess('Operation completed successfully');
$this->setError('Something went wrong');
// Display in view
<?php if (isset($_SESSION['success'])): ?>
<div class="alert alert-success"><?= $_SESSION['success'] ?></div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>// Render view
$this->view('users/index', ['users' => $users]);
// JSON response
$this->json(['success' => true, 'data' => $users]);
// Redirect
$this->redirect('/dashboard');
// Abort with error
abort(404, 'Page not found');// Environment variables
$debug = env('APP_DEBUG', false);
// Generate URLs
$url = base_path('users/123'); // /myproject/users/123
$asset = asset('css/app.css'); // /myproject/assets/css/app.css
// Configuration
$appName = config('app.name', 'My App');
// Session
$userId = session('user_id');
// Request input
$email = request('email');
// HTML escaping
echo e($userInput);
// Dump and die (debugging)
dd($variable, $anotherVar);
// Old input (form repopulation)
<input name="email" value="<?= old('email') ?>">
// CSRF token
<?= csrf_field() ?>
$token = csrf_token();// config/routes.php
return [
'/' => 'HomeController@index',
'/about' => 'PageController@about',
'/contact' => 'ContactController@show',
];'/users/{id}' => 'UserController@show',
'/posts/{id}/comments/{comment_id}' => 'CommentController@show',Parameters are passed to controller methods:
public function show($id)
{
$user = (new User())->find($id);
// ...
}<?php
namespace App\Middleware;
use AzFramework\Http\Middleware;
class AdminMiddleware implements Middleware
{
public function handle()
{
if (session('user_role') !== 'administrator') {
abort(403, 'Access denied');
}
}
}Middleware support is built into the router but needs to be configured per route.
- Update .env for production
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
APP_BASE_PATH=- Secure file permissions
chmod -R 755 .
chmod -R 700 storage/
chmod 600 .env- Update .htaccess
RewriteBase /-
Enable HTTPS Update in .env and ensure certificate is installed.
-
Database backups Set up automated backups for your database.
- Never commit
.envfiles - Add to.gitignore - Use prepared statements - Already handled by Model class
- Validate all input - Use
validateAndSanitize() - CSRF protection - Use
verifyCsrf()on POST routes - Hash passwords - Use
password_hash()(bcrypt) - Set secure headers - Already in
.htaccess - Limit file uploads - Configure in
config/app.php
// Controller
public function create()
{
$this->view('users/form', [
'csrf_token' => $this->csrf(),
'user' => null
]);
}
public function store()
{
$this->verifyCsrf();
$data = $this->validateAndSanitize($_POST, [
'username' => 'required|min:3',
'email' => 'required|email'
]);
if ($data === false) {
return $this->redirect('/users/create');
}
$user = new User();
if ($user->create($data)) {
$this->setSuccess('User created');
$this->redirect('/users');
}
}<?php foreach ($users as $user): ?>
<div><?= e($user->username) ?></div>
<?php endforeach; ?>
<?= $this->renderPagination($pagination, base_path('users')) ?>namespace App\Core;
use AzFramework\Core\Controller as BaseController;
class Controller extends BaseController
{
protected function getCurrentUser()
{
if (!isset($_SESSION['user_id'])) {
return null;
}
return (new \App\Models\User())->find($_SESSION['user_id']);
}
}Then extend your custom controller:
namespace App\Controllers;
use App\Core\Controller;
class UserController extends Controller
{
// Your methods
}- Check
.htaccessRewriteBase matches your deployment path - Ensure mod_rewrite is enabled in Apache
- Verify web server points to
public/directory
- Check APP_BASE_PATH matches deployment location
- Verify session is starting properly
- Check storage/sessions is writable
- Verify database credentials in
.env - Ensure database exists
- Check MySQL/MariaDB is running
- Check storage/sessions permissions (777)
- Verify session cookie path in bootstrap
- Check browser cookies are enabled
This framework is open-source software. Use it freely for personal or commercial projects.
For issues, questions, or contributions, contact the framework author or check the documentation.
Built with ❤️ for rapid PHP development