Creating a PHP website using MVC – 1
Now about the framework, what we are creating here is a website which have an admin area and a public site area. A basic idea of a model-view-controller is we ask for a url to the server. All our calls will go through a single php page which creates the respective controller class instance and process our action. In the process it loads required Models and views. Consider, view as a template and the controller writes the data into the view and returns the html to the user. A picture is worth thousand words.
An example of a call will be www.oursite.com/car/show?name=ferrari
Yes, but where is admin area?

The folder structure for our website looks like this.
root folder admin/ controller/ view/ common/ model/ lib/ public/ admin/ css/ js/ images/ site/ css/ js/ images/ index.php site/ controller/ view/ system/ config/ log/
<ifmodule mod_rewrite.c=""> RewriteEngine on RewriteRule ^$ public/ [L] RewriteRule (.*) public/$1 [L] </ifmodule>
<ifmodule mod_rewrite.c=""> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^(.*)$ index.php?url=$1 [QSA,PT,L] </ifmodule>
The index.php file in the public folder just redirects to the root folder.
<?php @$url = $_GET['url']; require_once ('../index.php'); ?>
Most of the code in the index.php in the root folder is same as that of the one in the website given earlier though I have added some things like auto root url detection, error logging to an external file, an output trace function etc. Let’s take a look at the code.
<?php define('ROOT',dirname(realpath(__FILE__))."/"); /** * * DO NOT DEFINE ANY CONSTANTS HERE. DEFINE THOSE IN CONFIG.PHP * */ $thisDir=explode("/", ROOT); $conflen=strlen(array_pop($thisDir)); $B=substr(__FILE__, 0, strrpos(__FILE__, '/')); $A=substr($_SERVER['DOCUMENT_ROOT'], strrpos($_SERVER['DOCUMENT_ROOT'], $_SERVER['PHP_SELF'])); $C=substr($B, strlen($A)); $posconf=strlen($C) - $conflen; $D=substr($C, 0, $posconf); $host='http://' . $_SERVER['SERVER_NAME'] . '/' . $D; define('ROOT_URL', $host); include(ROOT . 'system/config/config.php'); include(ROOT . 'lib/functions.php'); /** * Set error reporting */ function setErrorLogging(){ if(DEVELOPMENT_ENVIRONMENT == true){ error_reporting(E_ALL); ini_set('display_errors', "1"); }else{ error_reporting(E_ALL); ini_set('display_errors', "0"); } ini_set('log_errors', "1"); ini_set('error_log',ROOT . 'system/log/error_log.php'); } /** * Trace function which outputs variables to system/log/output.php file */ function trace($var,$append=false){ $oldString="<?php\ndie();/*"; if($append){ $oldString=file_get_contents(ROOT . 'system/log/output.php') . "/*"; } file_put_contents(ROOT . 'system/log/output.php', $oldString . "\n---\n" . print_r($var, true) . "\n*/"); } /** Check for Magic Quotes and remove them **/ function stripSlashesDeep($value) { $value = is_array($value) ? array_map('stripSlashesDeep', $value) : stripslashes($value); return $value; } function removeMagicQuotes() { if ( get_magic_quotes_gpc() ) { $_GET = stripSlashesDeep($_GET ); $_POST = stripSlashesDeep($_POST ); $_COOKIE = stripSlashesDeep($_COOKIE); } } /** Check register globals and remove them **/ function unregisterGlobals() { if (ini_get('register_globals')) { $array = array('_SESSION', '_POST', '_GET', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES'); foreach ($array as $value) { foreach ($GLOBALS[$value] as $key => $var) { if ($var === $GLOBALS[$key]) { unset($GLOBALS[$key]); } } } } } /** Main Call Function **/ function callHook() { global $url; global $area; $url = rtrim($url,"/"); $urlArray = array(); $urlArray = explode("/",$url); $controller = DEFAULT_CONROLLER; $action = DEFAULT_ACTION; //Check if the call is to admin area if($urlArray[0] == "admin"){ $area = "admin"; array_shift($urlArray); } //Controller if(isset($urlArray[0]) && !empty($urlArray[0])){ $controller = array_shift($urlArray); } //Action if(isset($urlArray[0]) && !empty($urlArray[0])){ $action = array_shift($urlArray); } //LOAD THE CONTROLLER $controllerName = $controller; $controller = ucwords($controller); $model = rtrim($controller, 's'); $controller .= 'Controller'; $dispatch = new $controller(); echo $area . "-->".$controller."--->".$action; if ((int)method_exists($controller, $action)) { call_user_func(array($dispatch,$action)); } else { error_log("Unknown page/action, Controller = ".$controller.", action = ".$action); } } function __autoload($className){ $paths = array( ROOT."/lib/", ROOT."/site/controller/", ROOT."/admin/controller/", ROOT."/common/" ); foreach($paths as $path){ if(file_exists($path.$className.".class.php")){ require_once($path.$className.".class.php"); break; } } } $area = "site"; setErrorLogging(); removeMagicQuotes(); unregisterGlobals(); callHook(); ?>
In the first block of code, we define the ROOT folder and the ROOT_URL. These two are defined as global constants and available inside our php website everywhere. We have included two files – config.php and functions.php. We will create these two files after exploring this file, but I’ll let you know that config.php has three constants defined – DEVELOPMENT_ENVIRONMENT, DEFAULT_CONTROLLER and DEFAULT_ACTION and the values are ‘true’, ‘index’, and ‘index’ respectively.
From now on, whatever constants we may need, define those in the config.php only.
The setErrorLogging() function checks whether the current session is a development environment or not by checking the constant DEVELOPMENT_ENVIRONMENT. During the time of development, set this to true and after publishing your site without any errors, set this to false. If the current session is a development session, it configures php to display all errors in the page and if not, it doesn’t show any error or warning on our website. In both occasions, if any error is happened, it will be written to the error_log.php so that we can open this file later and check the error details.
The trace function accepts any value and writes it to output.php file. This is a php file and a die() method is added on first line, so we cannot see the data if it is opened in the browser. We need to open it in the editor to see the data. There is an optional parameter $append, if true, it will append the data to the old data, else, it will erase all before writing the new data.
The next two functions stripSlashesDeep and removeMagicQuotes checks the input and sanitize the data. You also know what unregisterGlobals() do.
The callHook() is the main processing function which checks and determines the controller, load and call it. First it checks the url, strips out any leading slashes and split the url by ‘/’. The output will be an array. If the first element is ‘admin’ this is an admin area call. The second element will be the controller and the third, controller action to be performed. If the first element is not ‘admin’, it is a site area call and that will be the controller and the next element is the action. It creates an instance of the controller and calls the action on it. If a controller does not exist, it redirects to an error page.
The __autoload function is called before a class is called so that we can check and load the corresponding php file for that class. This is helpful because we don’t want to load all the php files on startup. It loads whatever is required whenever is needed.
The above things are all methods. First we default the $area to the site area and calls all the methods.
Now the config.php
<?php define('DEVELOPMENT_ENVIRONMENT', TRUE); define('DEFAULT_CONROLLER', 'index'); define('DEFAULT_ACTION', 'index'); ?>
functions.php is an empty file now. We will add things into it later when needed.
Comment out the controller loading section and test it in the browser. You can see the area, controller and the action is parsed perfectly from the url.
Call: http://localhost/mvc_blueprint/ site-->index--->index Call: http://localhost/mvc_blueprint/car/get site-->car--->get Call: http://localhost/mvc_blueprint/admin/main/login admin-->main--->login
In the next part, we will create a base controller from where all our custom controllers are extended, a base Model class and a template renderer.