Building a Custom MVC Framework from Scratch with Core PHP: Architecture and BestPractices

Share this post on:

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

  1. 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);
}
}
  1. 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);
}
}
  1. 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

  1. 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);
}
}
  1. 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

  1. XSS Protection
function sanitizeOutput($output) {
return htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
}
  1. 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
At 200OK Solutions, we excel in crafting custom MVC frameworks using core PHP tailored to meet your specific business requirements. With our deep expertise in PHP architecture and adherence to best practices, we build scalable, efficient, and high-performance web applications from scratch. Whether you’re starting a new venture or optimizing an existing platform, our experienced development team ensures robust design, clean code, and long-term maintainability.
Partner with 200OK Solutions for reliable and future-ready web solutions. Ready to build your next big idea? Contact us today!

Piyush Solanki

PHP Tech Lead & Backend Architect

10+ years experience
UK market specialist
Global brands & SMEs
Full-stack expertise

Core Technologies

PHP 95%
MySQL 90%
WordPress 92%
AWS 88%
  • Backend: PHP, MySQL, CodeIgniter, Laravel
  • CMS: WordPress customization & plugin development
  • APIs: RESTful design, microservices architecture
  • Frontend: React, TypeScript, modern admin panels
  • Cloud: AWS S3, Linux deployments
  • Integrations: Stripe, SMS/OTP gateways
  • Finance: Secure payment systems & compliance
  • Hospitality: Booking & reservation systems
  • Retail: E-commerce platforms & inventory
  • Consulting: Custom business solutions
  • Food Services: Delivery & ordering systems
  • Modernizing legacy systems for scalability
  • Building secure, high-performance products
  • Mobile-first API development
  • Agile collaboration with cross-functional teams
  • Focus on operational efficiency & innovation

Piyush is a seasoned PHP Tech Lead with 10+ years of experience architecting and delivering scalable web and mobile backend solutions for global brands and fast-growing SMEs.

He specializes in PHP, MySQL, CodeIgniter, WordPress, and custom API development, helping businesses modernize legacy systems and launch secure, high-performance digital products.

He collaborates closely with mobile teams building Android & iOS apps, developing RESTful APIs, cloud integrations, and secure payment systems. With extensive experience in the UK market and across multiple sectors, Piyush is passionate about helping SMEs scale technology teams and accelerate innovation through backend excellence.