PHP

Create Your Own lightweight PHP MVC Framework

Using the MVC design pattern is a great way to simplify PHP app development. Using a framework written by someone else is usually the best option; the Zend Framework for example. It is, however, at least a good exercise to write your own MVC framework to gain a better understanding of how the roles of the MVC interact. It may also be appropriated to use such a light implementation of the MVC as we are about to roll together for certain site’s development.

Folder structure

First thing first, setup a basic file structure to hold the various elements of our simple framework.

mvc folder structure

Bootstrapping

Next create an index.php file. You of course have to have this file display ‘hello world’. Now what must happen is all requests made to your site must be internally directed to this file. This is done using apache’s rewrite module; it allows your browser to display the URL the user requested while internally handling a different URL, among other things. So create your .htaccess file and add the following code. This code sends all requests to index.php with a GET variable named request storing the URL the user requested. Make sure this works, when requesting any URL within the site, below the .htaccess file, you should see ‘hello world’ rather than a 404 error.

.htaccess

1.RewriteEngine on
2.
3.RewriteCond %{REQUEST_FILENAME} !-f
4.RewriteCond %{REQUEST_FILENAME} !-d
5.
6.RewriteRule ^(.*)$ index.php?request=$1 [L,QSA]

Now the bootstrapping itself is responsible for requiring all of the needed MVC components used for displaying the page the user requested. By filter all requests to a single file, we are able to use a development approach similar to traditional software development. So you can think of the index.php as your main class. Now we require three classes. The first is the Router class; this class is responsible for parsing the request GET variable and including a controller class and calling a method of this controller class based on the this GET variable data. The second is the Registry class; this class stores application variables used by the framework. The third class is the Template class; this class will both store variables assigned by the controller for the view to use and include the view itself.

The bootstrapping process also provides: the instantiation of a Router, Registry and Template class instance; the call to the router instances route method; and a __autoload method for any model class that may be used by the controller classes.

index.php

01.<?php
02.
03.require_once('application/Router.php');
04.require_once('application/Registry.php');
05.require_once('application/Template.php');
06.
07.$router = new Router();
08.$registry = new Registry();
09.$registry->template = new Template();
10.
11.$router->route($registry);
12.
13./*** auto load model classes ***/
14.function __autoload($class_name)
15.{
16.try
17.{
18.$filename = strtolower($class_name) . '.php';
19.$file = 'application/models/' . $filename;
20.
21.if (file_exists($file))
22.include ($file);
23.else
24.throw new Exception('model ' . $class_name . '.php not found');
25.}
26.catch(Exception $e)
27.{
28.echo $e->getMessage();
29.exit(0);
30.}
31.}

Registry.php
The registry class contains the the __get and __set magic methods.

01.<?php
02.
03.class Registry
04.{
05.private $vars = array();
06.
07.public function __set($index, $value)
08.{
09.$this->vars[$index] = $value;
10.}
11.
12.public function __get($index)
13.{
14.return $this->vars[$index];
15.}
16.}

The Router Class

As you can see the __construct method parses the request variable assigning the value of index if any part is null. This would be the case if index.php is the file the user actually requested in the case of the controller being called, and the case for the action of the controller if the requested is for a directory’s default file.
The route method itself is passed a reference to the registry file, which in turn is passed to the constructor of the controller class. This registry at very least will contain a reference to the template class. Again, what this method does is include the controller first and then call one of its methods. The request var is exploded by the delimited ‘/’ the first element of returned array is the name of the controller and second is the name of the method to be called from the controller. In other words it you request http://www.yoursite.com: the index method of your indexController would be called. If your requested http://www.yoursite.com/city: the index method of your cityController would be called. Now if you requested http://www.yoursite.com/city/cleveland: the cleveland method of your cityController would be called.
Notice that if the controller requested doesn’t exist an Error404Controller is included instead, and has its index method called. This is the controller class for your custom 404 page.
Router.php

01.<?php
02.
03.class Router
04.{
05.private $path, $controller, $action;
06.static $instance;
07.
08.public function __construct()
09.{
10.$request = $_GET['request'];
11.$split = explode('/',trim($request,'/'));
12.
13.$this->controller = !empty($split[0]) ? ucfirst($split[0]) : 'Index';
14.$this->action = !empty($split[1]) ? $split[1] : 'index';
15.}
16.
17.public function route($registry)
18.{
19.require_once('application/BaseController.php');
20.$file = 'application/controllers/' . $this->controller . 'Controller.php';
21.if(is_readable($file))
22.{
23.include $file;
24.$class = $this->controller . 'Controller';
25.}
26.else
27.{
28.include 'application/controllers/Error404Controller.php';
29.$class = 'Error404Controller';
30.}
31.$controller = new $class($registry);
32.
33.if (is_callable(array($controller, $this->action)))
34.$action = $this->action;
35.else
36.$action = 'index';
37.$controller->$action();
38.}
39.}

controllers

First here is the abstract BaseController class which holds a protected reference to the registry reference and demands and index method exists in all subclasses.

BaseController.php

01.<?php
02.
03.abstract class BaseController
04.{
05.protected $registry;
06.
07.function __construct($registry)
08.{
09.$this->registry = $registry;
10.}
11.
12.abstract function index();
13.}

The concrete controller classes’ role is to provide the interface between to model data and view. For this example, I am only showing a one way interaction; the controller is getting data from the model to be displayed by the view. This is done by adding variables to the template class that the view will need to use.
Notice how there is no require of include needed for the TestModel class. This is because of the __autoload function in the index.php file. It automatically includes the model classes if they are not present.
IndexController.php

01.<?php
02.
03.class IndexController extends BaseController
04.{
05.public function index()
06.{
07.$this->registry->template->test = $this->getModelData();
08.$this->registry->template->show('indexView.php');
09.}
10.
11.private function getModelData()
12.{
13.$model = TestModel::getInstance();
14.return $model->getData();
15.}
16.}

models

Here you will generally interacting with a database. For this example I am just returning a string to show the full model to view interaction via the controller.
TestModel.php

01.<?php
02.
03.class TestModel
04.{
05.private $testData = 'data from the model';
06.static $instance;
07.
08.public static function getInstance()
09.{
10.if(self::$instance ==  null)
11.self::$instance = new self();
12.return self::$instance;
13.}
14.
15.private function __construct(){}
16.private function __clone(){}
17.
18.public function getData()
19.{
20.return $this->testData;
21.}
22.}

views

The template class is responsible for including the view the controller sends as a parameter as well as providing this newly included file access to the template variables set by the controller. This is all done in the show method.

Template.php

01.<?php
02.
03.class Template
04.{
05.private $vars = array();
06.
07.public function __set($index, $value)
08.{
09.$this->vars[$index] = $value;
10.}
11.
12.public function show($viewName)
13.{
14.try
15.{
16.$file = 'application/views/' . $viewName;
17.
18.if (!file_exists($file))
19.throw new Exception('View ' . $viewName . ' not found.');          else
20.
21.foreach ($this->vars as $key => $value)
22.{
23.$$key = $value;
24.}
25.
26.include($file);
27.}
28.catch(Exception $e)
29.{
30.echo $e->getMessage();
31.exit(0);
32.}
33.}
34.}

A view itself is not a class but a PHP page with PHP blocks used only to display template variables for dynamic data provided by the controller. The variables data should not need to be further parsed or manipulated at this point; that was the responsibility of the controller.
indexView.php

01.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
03.<head>
04.<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
05.<title>Untitled Document</title>
06.</head>
07.
08.<body>
09.<h1>Test Page</h1>
10.<p>
11.<?php echo $test; ?>
12.</p>
13.</body>
14.</html>

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s