Code Your Own PHP MVC Framework in 1 Hour PDF
Code Your Own PHP MVC Framework in 1 Hour PDF
Code Your Own PHP MVC Framework in 1 Hour PDF
CPOL
5.00 (2 votes)
Introduction
MVC architectural pattern is almost in everywhere today, whether you are working on Java, C#, PHP, iOS projects. This
might not be 100% exact, but PHP community has the most amount of MVC frameworks. Today you might be using
Zend, tomorrow on another project you might have to change to Yii or Laravel or CakePHP. If you are new to MVC
frameworks and you just download one from the official website, you might feel overwhelmed when you look at the
frameworks source code, yes, it is complex, as these popular frameworks are not written in a month - they are
published, refined, tested again and again, and the functionalities are added constantly. So from my experience,
knowing the core design methodologies of MVC framework is critical, otherwise you might feel that you will have to
learn another framework and another framework again and again when you get new projects using new frameworks.
The best way to understand MVC is to write you own MVC framework from scratch! In this series of articles I am going
to show you how to code one, so that you might get to understand why certains things happen that way in a
framework.
C: Controller
The core concept of MVC is to separate business logic from displaying(the View part). First let me explain the whole
workflow of an HTTP request & HTTP response. For example, we have a commerce website and we want to add a
certain product. A very simple URL would be look like this:
http://bestshop.com/index.php?p=admin&c=goods&a=add
http://bestshop.com is the domain name or base URL;
p=admin means the Platform is admin panel, or the Backend site of the system. We also have a Frontend site, which is
public to the world(in this case, it would be p=public)
c=goods&a=add means this URL requests the goods Controllers add action method.
<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine on
This configuration file is very powerful and when you change it, you dont need to restart Apache. If you change
Apaches other configuration files, you need to restart it, the reason is that for other Apache configuration files, Apache
only will read them when its started, thats why any change requires a restart(Apache is mainly written in C by the way).
But for .htaccess distribution configuration file, no restart is needed for any change.
Simply put, index.php will also do the proper initialization for the framework and it will route the request to the proper
Controller(goodsController in above example) and an action method(member function) within that Controller class.
public directory is to store all the public static resources like html, css and js files.
index.php is the entry-point file, the front controller.
Now within the application/controllers folder, we will create two folders for the frontend and backend platforms:
And in the view folder, the same for frontend and backend:
As you can see, within the application folder, we created frontend and backend subfolder in controllers and views
folder, as our application has a frontend site and a backend site. But why we did not do the same in the models folder?
Well, the reason here is, normally for a web app:
Frontend and Backend can be two different websites, but the are CRUD the same database, thats why an internal user
updated a products price, the price is immediately show on the frontend public page - backend and frontend share
the same database/tables.
So thats why both backend and frontend can share a set of Model classes, and thats why we didnt create separate
folders in the models folder.
Now lets move on to the framework directory, some frameworks name this folder using the frameworks name, say
symfony. In this folder, we quickly create these subfolders first:
echo "run()";
<?php
require "framework/core/Framework.class.php";
Framework::run();
You can see the result in browser(how to config your virtual host is skipped here). Normally this static function is called
run() or bootstrap(). Within this function, we can do 3 main things here, as shown in the code below:
class Framework {
//
self::init();
self::autoload();
}
self::dispatch();
Initialization
Here is the code for the init() method:
// Initialization
define("DS", DIRECTORY_SEPARATOR);
session_start();
From the comments you can see the purpose of each steps.
Autoloading
We dont want to manually code include or require for a class file what we need in every script in the project, thats why
PHP MVC frameworks have this autoloading feature. For example, in Symfony, if you put your own class file under lib
folder, then it will be auto loaded. Magic? No, there is no magic. Lets implement our autoloading feature in our mini
framework.
Here we need to use a PHP built-in function called spl_autoload_register
// Autoloading
spl_autoload_register(array(__CLASS__,'load'));
require_once
MODEL_PATH . "$classname.class.php";
Every framework has a name conversion, ours is no exception. For a controller class, it should be
xxxController.class.php, for a model class, it should be xxxModel.class.php. Why for a new framework you come across,
you must follow its naming convention? Autoloading is one of the reasons.
Routing/Dispatching
// Routing and dispatching
$controller->$action_name();
In this step, index.php will dispatch the request to the proper Controller::Action() method. Its very simple here just for
an example.
<?php
// Base Controller
class Controller{
header("Location:$url");
} else {
}
exit;
Base Controller has a property called $loader, it is an instance of Loader class(introduced later). Please note the phrase
it is an instance of Loader class -- preciously speaking, $this->loader is a reference variable which reference/points to
an instance of Load class. We dont talk more about this here, but this is actually a very important concept. I have met
some PHP developers who believe after this statement:
$this->loader = new Loader();
$this->loader is an object. No, its a reference. This term starts from Java, before Java, its called pointer in C++ or
Objective C. Reference is an encapsulated pointer type. For example, in iOS(Objective-C) we create an object using:
UIButton *btn = [UIButton alloc] init];
Loader class
In framework.class.php, we have already implemented applications controller and model class autoloading. But how to
load classes in the framework directory? Here we can create a new class called Loader, it will be used to load the
frameworks classes and functions. When we need to load a frameworks class, just call this Loader classs method.
class Loader{
Implementing Model
We will implement Model in the simplest way, by creating two class files:
Mysql.class.php - this class is under framework/database, it is to encapsulate database connection and some basic SQL
query methods.
Model.class.php - this is the Base Model class, it contains methods for all kinds of CRUD
<?php
/**
*================================================================
*framework/database/Mysql.class.php
*Database operation class
*================================================================
*/
class Mysql{
/**
/**
* Set charset
* @access private
/**
* @return $resultif succeed, return resrouces; if fail return error message and exit
*/
file_put_contents("log.txt", $str,FILE_APPEND);
$result = mysql_query($this->sql,$this->conn);
if (! $result) {
return $result;
/**
$result = $this->query($sql);
$row = mysql_fetch_row($result);
if ($row) {
return $row[0];
} else {
return false;
/**
if ($result = $this->query($sql)) {
$row = mysql_fetch_assoc($result);
return $row;
} else {
}
return false;
/**
$result = $this->query($sql);
$list = array();
$list[] = $row;
return $list;
/**
$result = $this->query($sql);
$list = array();
$list[] = $row[0];
return $list;
/**
return mysql_insert_id($this->conn);
/**
return mysql_errno($this->conn);
/**
return mysql_error($this->conn);
<?php
// framework/core/Model.class.php
// Base Model Class
class Model{
//fields list
$dbconfig['host'] = $GLOBALS['config']['host'];
$dbconfig['user'] = $GLOBALS['config']['user'];
$dbconfig['password'] = $GLOBALS['config']['password'];
$dbconfig['dbname'] = $GLOBALS['config']['dbname'];
$dbconfig['port'] = $GLOBALS['config']['port'];
$dbconfig['charset'] = $GLOBALS['config']['charset'];
$this->db = new Mysql($dbconfig);
/**
*/
$result = $this->db->getAll($sql);
foreach ($result as $v) {
$this->fields[] = $v['Field'];
if ($v['Key'] == 'PRI') {
$pk = $v['Field'];
$this->fields['pk'] = $pk;
/**
* Insert records
* @access public
* @return mixed If succeed return inserted record id, else return false
*/
if (in_array($k, $this->fields)) {
$field_list = rtrim($field_list,',');
$value_list = rtrim($value_list,',');
// Construct sql statement
} else {
return false;
/**
* Update records
* @access public
* @return mixed If succeed return the count of affected rows, else return false
*/
if (in_array($k, $this->fields)) {
if ($k == $this->fields['pk']) {
} else {
$uplist .= "`$k`='$v'".",";
} else {
return false;
} else {
return false;
/**
* Delete records
* @access public
* @return mixed If succeed, return the count of deleted records, if fail, return false
*/
//Check if $pk is a single value or array, and construct where condition accordingly
if (is_array($pk)) {
// array
} else {
// single value
$where = "`{$this->fields['pk']}`=$pk";
} else {
} else {
return false;
return false;
/**
/**
*/
/**
} else {
}
return $this->db->getAll($sql);
Now we can create a User model class in our application folder, this is for our User table in the database. The code
would look like this:
<?php
// application/models/UserModel.class.php
class UserModel extends Model{
return $users;
<?php
// application/controllers/admin/IndexController.class.php
class IndexController extends BaseController{
public function mainAction(){
$this->loader->library("Captcha");
$captcha = new Captcha;
$captcha->hello();
$users = $userModel->getUsers();
$users = $userModel->getUsers();
// Load View template
include
CURR_VIEW_PATH . "index.html";
So far, our application backends Index controller is working, it communicates with Model and passes result variable to
the View templates, so it can be rendered in the browser.
This is a very brief introduction to a mini MVC framework, hope it clarifies some basic concepts in the MVC frameworks.
History
Feb 25, 2016 - initial version
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
Share
Chris_Yu
Web Developer
Australia
Chris Yu is a PHP and Mobile(iOS & Hybrid) developer, a Zend Certified PHP Engineer
SAPrefs - Netscape-like
Preferences Dialog