Understanding MVC Architecture
The Model-View-Controller (MVC) pattern separates application logic into three distinct components:
- Model: Handles data logic and interactions with the database
- View: Manages the presentation layer and user interface
- Controller: Processes user input and coordinates between Model and View
Project Structure
Our framework will follow this directory structure:
project/
- App
- Controllers/
- Models/
- Views/
- Config/
- Core
- Router.php
- Controller.php
- Model.php
- View.php
- Database.php
- Public/
- index.php
- css/
- js/
- .htaccess
Core Components Implementation
- Router Class
The Router will handle URL routing and dispatch requests to appropriate controllers:
class Router { private $routes = [];
public function addRoute($method, $path, $handler) {
$this->routes[] = [ 'method' => $method, 'path' => $path, 'handler' => $handler
];
}
public function dispatch($requestMethod, $requestUri) { foreach ($this->routes as $route) {
if ($route['method'] === $requestMethod CC $this->matchPath($route['path'],
$requestUri)) {
return call_user_func($route['handler']);
}
}
throw new Exception('Route not found');
}
private function matchPath($routePath, $requestUri) {
$routeRegex = preg_replace('/\{(\w+)\}/', '(?P<$1>[^/]+)', $routePath);
$routeRegex = "#^" . $routeRegex . "$#";
return preg_match($routeRegex, $requestUri, $matches);
}
}
- Base Controller Class
The Controller class serves as the foundation for all controllers:
abstract class Controller { protected $view; protected $model;
public function construct() {
$this->view = new View();
}
protected function loadModel($modelName) {
$modelClass = $modelName . 'Model';
$this->model = new $modelClass();
}
protected function response($data, $statusCode = 200) { http_response_code($statusCode);
header('Content-Type: application/json'); echo json_encode($data);
}
}
- Database Connection Class
A robust database connection handler using PDO:
class Database {
private static $instance = null; private $connection;
private function construct() {
$config = require '../app/Config/database.php';
try {
$this->connection = new PDO(
"mysql:host={$config['host']};dbname={$config['database']};charset=utf8mb4",
$config['username'],
$config['password']
);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->connection->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
throw new Exception("Connection failed: " . $e->getMessage());
}
}
public static function getInstance() { if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection() { return $this->connection;
}
}
Best Practices and Design Patterns
- Dependency Injection
Implement a simple dependency injection container:
class Container {
private $services = [];
public function register($name, $callback) {
$this->services[$name] = $callback;
}
public function resolve($name) {
if (!isset($this->services[$name])) {
throw new Exception("Service not found: {$name}");
}
$callback = $this->services[$name];
return $callback($this);
}
}
- Middleware Implementation
Add middleware support for request/response handling:
class Middleware { private $next;
public function setNext(Middleware $middleware) {
$this->next = $middleware; return $middleware;
}
public function handle($request) { if ($this->next) {
return $this->next->handle($request);
}
return $request;
}
}
Security Considerations
- XSS Protection
function sanitizeOutput($output) {
return htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
}
- CSRF Protection
class CSRFProtection {
public static function generateToken() { if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
public static function validateToken($token) {
if (!isset($_SESSION['csrf_token']) || $token !== $_SESSION['csrf_token']) { throw new Exception('CSRF token validation failed');
}
}
}
Usage Example
Here’s how to use the framework in practice:
// routes.php
$router = new Router();
$router->addRoute('GET', '/users', function() {
$controller = new UsersController(); return $controller->index();
});
// UsersController.php
class UsersController extends Controller { public function construct() {
parent:: construct();
$this->loadModel('User');
}
public function index() {
$users = $this->model->getAllUsers();
return $this->view->render('users/index', ['users' => $users]);
}
}
Conclusion
Building a custom MVC framework provides invaluable insights into web application architecture and design patterns. While this implementation is basic, it serves as a solid foundation that can be extended with additional features like:
- Caching mechanisms
- Authentication and authorization
- Form validation
- Database migrations
- Template engine integration
- API versioning