How to build your own Multilingual PHP MVC CMS from scratch - Part 2 - Starting Point: index.php

Wednesday, October 29, 2014

Welcome to this second settlement of our tutorial series on how to build your own Multilingual MVC CMS from scratch and in which we will be discussing the entry point of our application: The index.php file.
During the first part, we have, among other things, reviewed the core principles around which we will develop our project and also briefly introduced its directory structure.
But now, all those would be totally useless if not for one single file, the one and only entry point of our application, the index file, responsible for handling user input, requiring all the needed components along the way and which we are now going to decorticate, step by step.


How to build your own Multilingual PHP MVC CMS from scratch - Part 2 on RolandC.net
Gumbo-CMS is the application on which these series are based


Before we start: I have based these series entirely on Gumbo-CMS, the new Multilingual PHP5 MVC CMS in town, which I have recently released under the GPL V2 License. So feel free to download yourself a copy before we actually get started, as it will contain every single snippet of code you will be able to find throughout the entire tutorial. This will provide you with some guidance if you ever get lost during one of the forthcoming settlements. Equally, you will find useful insights as to which kind of feature Gumbo-CMS offers by taking a look at its Product Overview PDF.

So, we jump right in by declaring a session on line 2, just below the opening php tag and we also include our config file.
 <?php  
      //we initiate a session, useful for user sessions handling,
      // last visited URLs, etc.
      session_start();   
      //and we include our config file
      include 'config.php';   
Next, we instantiate the UtilsModel class, which contains a few useful 'all-purpose' functions.
 // we instantiate our general-purpose class called UtilsModel
 $utils = new UtilsModel();  
Then we define our timezone.
 date\_default\_timezone_set(DDTS); // we define our timezone  
DDTS will be the config constant that holds our timezone value, eg: Europe/Stockholm, Europe/Paris. This is required since PHP 5.10 because otherwise, I quote from the php.net site : 'every call to a date/time function will generate a E_NOTICE if the timezone isn't valid, and/or a E_WARNING message if using the system settings or the TZ environment variable'. Alternatively, you can also set the date.timezone parameter in php.ini.
 /*recording the last visited url*/  
      if(isset($_GET[RLINK]) && $_GET[RLINK] != 'signin' && $_GET[RLINK] != 'fpwd' && $_GET[RLINK] != 'fpwd'){  
           //if a module is set and that is neither the signin model nor the forgotten password one, we store the request URI in a session variable  
           $_SESSION['last_url'] = $_SERVER['REQUEST_URI'];  
      }  
This above is a snippet that pertains to the logic of our CMS, in regard to the recording of the last visited URL. This is also a good occasion to start introducing the URL constant naming scheme we will be using throughout the whole project while developing it.

RLINK, implicitly meaning Router Link, is the constant that holds the $_GET name of a module name, be it current or sought after. Equally important are PLINK and LN, which will respectively hold page and language names.

The value of all URL constants can be modified to taste, as long as the CLEAN_URLS constant is set to false. This because of the rewrite rules in .htaccess that will not allow the dynamic naming of target variables within its rules. But as long as clean URLS aren't enabled, you are free to give any value you wish to your URL $_GET variables.

Let's break away momentarily from the index.php file to take a look at this extract from config.php
 /* config.php */  
      ...  
      define("CLEAN_URLS",false); //true or false. true to enable it, false to disable - NOTE : mod_rewrite has to be enabled for it to work. We will set it to true when we reach our settlement about setting up clean URL's, so for now, let's keep it disabled  
      /*routing*/  
      define("RLINK","q"); //Router Link- var that holds the current module name in $_GET requests - make sure it's different from the other url variables found in this file, and different from google cse reserved terms (cx, language, sa, etc...) - use 'ctrl + F' to check within this file  
      define("DEFAULT_RLINK","page");          // Warning: if you choose a default module that's different from the page module,  
      define("DEFAULT_PLINK","formatted_name_of_the_page_you_wish");     //Page Link     // then you will have to leave the DEFAULT_PLINK value blank;  
      define("PLINK","p");  
      ...  
But back to our little snippet in the index (commented as 'recording the last visited url'), what it says is: If a module is set and that module is different from the signin module and different from the forgotten password module too, we then store the REQUEST URI in a session variable we call $_SESSION['last_url']. We do this because we simply don't want to have the user automatically come back to the login screen or the forgotten password procedure once it has successfully logged in.

And now has come the time to call and instantiate some of our core classes, namely the router, registry and template classes.
 /* requiring the core clases */  
      require_once('application/Router.php');   
      require_once('application/Registry.php');  
      require_once('application/Template.php');  
      /* instantiating them */  
      $router = new Router();  
      $registry = new Registry();  
      $registry->template = new Template(); // here we add the new template object to the registry object, as one of its properties  
The router will do the work of routing user input by retrieving the requested GET variable, then including and calling the corresponding controller, based on the value of that variable. We will review what the router actually does in detail when the route() function is called later in the the file.

We call on the registry, which contains magic __set() and __get() methods: this will simply provide a registry to our application in which to store some of our application variables.

We also call and instantiate the template class, adding it to our registry object as one of its properties and which contains our template engine. It will store both the variables assigned by the controller for the view to use and include the view itself.
 /* DB connection initiating */  
      MySQLModel::get_mysql_instance()->newConnection(DBHOST,DBUSER,DBPWD,DBNAME,DBPORT);   
      // we instantiate our Singleton MySQLModel class and invoke the newConnection() method, passing our config's db settings as parameters  
To connect to and interact with the MySQL database, we will use a custom MySQLModel class, which I have found on the Internet and turned into a Singleton class, so its instantiation gets restricted to one object only and which we will be able to access via the get_mysql_instance() method, every time we need to perform an operation with the database.

Singleton? Yes. Design patterns, in the software development industry, are patterns that have been observed, tried and accepted as being the most likely solutions to a set of commonly encountered problems, so that you do not have to reinvent the wheel and implementing solutions to commonly encountered patterns is somehow the best way you can use to make sure you're being on the right track, at least on the same track as most others.

The Singleton pattern is a very popular software design pattern that's very useful in our case. Our MySQLModel class does not need and shall not be instantiated more than once, all while it will still be able to perform its duties CMS-wide.

Having made our MySQLModel class a Singleton one allows us, once loaded, to access it from anywhere using the :: syntax, thus we do not have to store our database connection inside a global variable, a session variable or whatever else.

Using the MySQLModel::get_mysql_instance()->newConnection(params) syntax, we simply instantiate our class and add a call to the newConnection() method altogether, passing our database connection constants from the config file as parameters. Since the call is made early enough in our index file, we do not need to perform any additional connection anywhere else. We will only need to use the MySQLModel::get_mysql_instance()->method_name(params); syntax every time we need to perform an operation with MySQL.

Note that even though we have named our class the MySQL model, we do connect to MYSQL via the mysqli extension.

Next, we engage in pre-routing user input, or what remains of it. Up to Gumbo-CMS v0.96beta (included), pre-routing was operated by means of header redirects, under the logic that we always wanted to have a valid module link (RLINK) and a valid language link (LN) defined at a minimum in the URL, at any time.

Now bare with me, Google does not like header redirects, even though they claim 302 status is fine for them, so you will have all the trouble of the world trying to index your site using such (security?) procedure. While the Bing engine will index your code just fine, Google's spiders will not because they see these kinds of redirects as hijacking attempts. Which is why v.97 features a new pre-routing approach which does not use PHP headers at all.

In this new approach, we will simply determine a value for PLINK, RLINK and LN and assign those to constants, respectively CURRENT_PLINK, CURRENT_RLINK and CURRENT_LANG
 /* pre-routing (or what's left of it) */  
      
      $plink = ''; 
      // if there is a $_GET[PLINK] set, let's use it
      if(isset($_GET[PLINK])){$plink = $_GET[PLINK];}
      // otherwise let's use DEFAULT_PLINK
      else{$plink = DEFAULT_PLINK;}
      // whatever what set, we assign it to CURRENT_PLINK, to use it wherever needed, whenever needed
      define("CURRENT_PLINK",$plink);  
      
      $rlink = '';  //same here for RLINK, with the difference that :
      // we use a module name list stored in config.php to check if that 
      // if we have a module set in URL, it has to be a valid module name
      $exp_menu_type_list = explode(";", MENU_TYPE_LIST);  
      if(isset($_GET[RLINK]) && in_array($_GET[RLINK], $exp_menu_type_list) ){$rlink = $_GET[RLINK];}  
      // otherwise, we use the default RLINK value, namely DEFAULT_RLINK 
      else{$rlink = DEFAULT_RLINK;}
      // same as for PLINK, we store what we have in a constant  
      define("CURRENT_RLINK",$rlink);  
      
      $dlang = $utils->get_default_lang(); //we need to fetch the default language 
      $available_languages = LocModel::getInstance()->getData_language_bar(); // we also need a list of the currently existing language in the database 
      
      // Since the available language list we've just fetched contains more than what we need
      // we create an array in which we will store just the elements we seek 
      $available_language_codes = array();   
      foreach($available_languages as $a_l){array_push($available_language_codes, $a_l[0]);}  
      
      // Now, if a language is set in the URL, AND is a valid language (made of only 2 lowercase letters) 
      // AND can be traced in our available language codes array we've just filled in, 
      // then it's all a valid language that we will store in $langf
      if(isset($_GET[LN]) && preg_match('/^[a-z]{2}$/',$_GET[LN]) && in_array($_GET[LN], $available_language_codes)){$langf = $_GET[LN];}
      // else if Google's language URL variable is set (we will have to rely on it while using the search module)
      // we use it  
      elseif(isset($_GET['language']) && preg_match('/^[a-z]{2}$/',$_GET['language']) && in_array($_GET['language'], $available_language_codes) ){$langf = $_GET['language'];}
      // else if a session language variable is set, we use it
      elseif(isset($_SESSION['C_LANG'])){$langf = $_SESSION['C_LANG'];}       
      // else we assign the default language we fetch a little earlier
      else{$langf = trim($dlang);}  
      // and define a CURRENT_LANG constant to assign it whatever value we have just fetched 
      define("CURRENT_LANG",$langf);
  
      // we create a language session variable, that will help in cases when a language was selected other than the default one and the user/visitor reloads the home link with no parameter, so the current language is not set back to default.
      $_SESSION['C_LANG'] = $langf;
  
So, here we first take care off PLINK - if it wasn't set, we use the default value found in config.php (DEFAULT_PLINK), assign what we have to CURRENT_PLINK, that we create.

We do just the same for RLINK, except we will confront it to a menu type list found in config.php under the name of MENU_TYPE_LIST and which contains all the valid module names in use on the front-end part. If a module is set in the URL and can be found in the menu list, we then assign it to $rlink. Otherwise $rlink takes the default rlink constant value, DEFAULT_RLINK. We again create a constant to hold that value, which we name CURRENT_RLINK.

LocModel, also Singleton, is a very useful class that contains a few methods that will help us manage languages effectively. We will be using it here to fetch all the available languages.

We then fetch the default language and store it into $dlang and we then also fetch a list of our available languages, which we process to a new array called $available_language_codes.

Now, if a language was set in the URL and is found valid, by format and reference to our existing language list, we do use it.

Else, if a Google-owned language URL parameter is set (namely $_GET['language']), we will use it instead. This will happen only when using the search module (powered by Goggle CSE), while in two-column view.

Else again, if we have a language session variable set, we make use of it, otherwise, and only otherwise, we try the default language that we have fetched a little earlier

In any case, passed that point, we do have a valid module name, page name (empty or not), and language name, so that, we can route the proper translation files, using a try/catch bloc as follows :
 /* routing translations now */  
      try{  
           $ln_file = PATH_TO_LANGUAGES.'/'.CURRENT_LANG.'/'.CURRENT_LANG.'.php';  
           if (file_exists($ln_file)){include ($ln_file);} // if our language file exists, we include it  
    //otherwise we throw and exception
           else {throw new Exception('<p>&nbsp;</p><p>&nbsp;</p><div class="loading_langfile_fails"><u>' .$_GET[LN].'.php </u> cannot be found</div>');} 
      }  
      catch(Exception $e){  
           echo $e->getMessage(); //which we catch and echo here  
           exit(0);  
      }  
This snippet is pretty much self explanatory. If the language file we are trying to include exists, we do include it, otherwise we throw and exception containing whatever error message we need to display.

And then we pull our current theme from the database
 //we first define our query  
      $q_tpl = "SELECT id_frontend_settings, frontend_settings_name, frontend_settings_value   
                FROM frontend_settings   
                WHERE frontend_settings_name = 'theme'  
           ";  
      MySQLModel::get_mysql_instance()->executeQuery($q_tpl); //we call on our MySQLModel to execute this query  
      if($myrows = MySQLModel::get_mysql_instance()->getRows($q_tpl)){ //if a result is found  
           define("CURRENT_THEME",$myrows['frontend_settings_value']); //we define a constant named CURRENT_THEME and we assign it the value the extract from the returned result  
      }  
We simply store our freshly-pulled theme name in the constant named CURRENT_THEME, which we will use throughout the CMS wherever necessary.

And now time to take care off the user wanting to log out
 $auth = AuthModel::getInstance(); we instantiate our Singleton authentification class  
      if(isset($_SESSION['c_login']) && isset($_SESSION['c_pwd'])){ if the user is logged in and wanting to log out, we invoke our let_out() method from the Authmodel class  
           if(isset($_GET[LETOUT]) && $_GET[LETOUT] == 'yes'){  
                $auth->let_out($_SESSION['c_login'],$_SESSION['c_pwd']);  
           }  
           if(preg_match('/\/out/',$_SERVER['REQUEST_URI'])){  
                $auth->let_out($_SESSION['c_login'],$_SESSION['c_pwd']);  
           }  
      }  
We store an instance of the AuthModel class in the variable named $auth. Then if the user is logged in and wanting to log out, we call the let_out() method, which will perform the logging out itself.



Then comes somehow the most important call of our index file, the call to the router's route() method.

This is exactly where we go deeper in trying to understand the core mechanisms that will make a module load upon being requested via the URL, be it a module to handle the serving of content, a module to allow the user to sign in or a module to manage some options in the back-end, it all loads from one object only - the router object.

In fact, back to design patterns, the very pattern that we will encounter now is called the factory pattern.

The factory pattern maybe be defined as a pattern in which the factory object creates other objects. This has a number of obvious benefits, such as the centralizing of the process of creating new objects. Therefore, if you need to change anything to the way you create and instantiate your new objects, then you only have to do so inside the factory.

In our case, the factory is our router. Our router, based on automatic URL parsing, will programmatically instantiate the controller of the module the client/user has asked for, and set it to work it by calling its initialize() method.

Just like with a regular factory. The client company needs a product, sends an order form, the order being our URL, the marketing people take the order form and tear down only the relevant page where the needed product is described, and handle it to the main plant people which in turn create the product and initialize it.

The __construct() method of our router class would be the order handling department, and the route method() would be the product building plant, in which is passed, as a parameter, the registry object we have created earlier, to be passed on, as a parameter again, to the controllers while being instantiated.

The registry works much as a stocking unit or a phone directory if you prefer, in the sense that you can store objects in it, to be later pulled out whenever required. How is that done? by using magic set() and get() methods, that will be triggered internally every time we will try to write/read to/from inaccessible properties, instead of having PHP generate an error.
 <?php  
      // application/Registry.php  
      class Registry{   

           private $vars = array(); //we declare the vars array as private  

           public function __set($index, $value){ we declare the magic __set() method, triggered when trying to write inaccessable properties  
                $this->vars[$index] = $value; // we store in the array the index associated with its value  
           }  

           public function __get($index){ we declare the magic __get() method, triggered when trying to access inaccessable properties  
                return $this->vars[$index]; //we return the value of a given index contained in the array  
           }  
      }  
      ?>  
In our __set() method, which is run when writing data to inaccessible properties and which accepts an index/value pair, we simply store the index in the private array named $vars, which is the stocking unit or phone directory really, and we store its value altogether.

In the __get() method, which is run when accessing data to from inaccessible properties, we simply return the value of a given array vars index by passing it as a parameter.

But back to our Router class
 <?php  
 class Router{
  
      private $path, $controller, $action;  //declaring the private properties we need

      public function __construct(){  //initializing our router object for when instantiated

           $exp_menu_type_list = explode(";", MENU_TYPE_LIST);  //we fetch and explode our menu type list again, we will need it here too

           if(isset($_GET[RLINK])){  //if a module name is set in the URL

                if(     CLEAN_URLS == true   // if clean URLs are on and google search is active and in two-column view mode
                     && GOOGLE_CSE_PAGING_TYPE == 'two_column'        
                     && preg_match('/cx=(.*)&gs=(.*)&sa.x=(.*)&sa.y=(.*)/', $_SERVER['REQUEST_URI']))  
                {$request = 'search';}  // we set our current module name as search, wich we store in the $request variable
                else{  //otherwise
   // if the set module name can be traced in our existing module type list
                     if(in_array($_GET[RLINK], $exp_menu_type_list)){$request = $_GET[RLINK];}  // we assign that name to $request
                     else{ 
                          // otherwise we use the DEFAULT_RLINK constant value  
                          $request = DEFAULT_RLINK;  
                     }  
                }       
           }  
           else{$request = DEFAULT_RLINK;} //// otherwise we use the DEFAULT_RLINK constant value 

           $this->controller = !empty($request) ? ucfirst($request) : 'Index';  // if $request is not empty, we assign its value (with the first letter made uppercase) to the controller property, otherwise, we assign it the value 'Index'
           $this->action = 'initialize';  // we set the action property to 'initialize'
      }  

      public function route($registry){ // our factory's building plant  

                require_once('application/BaseController.php'); we include our abstract controller class from which every created controller will extend  

                $file = 'application/controllers/' . $this->controller . 'Controller.php'; // we build the full controller's class path using the retrieve URL module variable retrieved in the __construct() method  

                if(is_readable($file)){ // if the controller class is readable,  
                     include $file; //we do include it  
                     $class = $this->controller . 'Controller'; //and set the $class variable  
                }  
                else{  
                     include 'application/controllers/Error404Controller.php'; //otherwise we include the 404error class  
                     $class = 'Error404Controller'; //and set the $class to that  
                }  

                $controller = new $class($registry); //whatever the included controller, we instantiate it, creating the $controller object, passing our $registry object as parameter, itself containing our template object.  

                if (is_callable(array($controller, $this->action))){ //if within the newly created $controller object there's an initialize() method (demanded by the baseController) that's callable   
                     $action = $this->action;  
                }  

                $controller->$action(); we do invoke that initialize() method we have called above 
      }  
 }  
We first need to pay attention to its __constructor() method, in which we retrieve our URL module variable ($_GET[RLINK]) (the page we tore down from the order form), assign it, after checking it corresponds to a valid module name, to the $controller property, otherwise we assign it the value 'Index'. If google CSE is active in two-column view mode and with clean URLs on, we assign 'search' to the $controller property.

Now onto the route() method, we bring our last core class, the base class, by including an abstract baseController class inside the router. The soon-to-be instantiated controller class will extend that BaseController class, so that it will inherit from its protected registry property, however, we as programmers will be free to decide how we want to implement the initialize() method that baseController is imposing on us. Note that as an abstract class, BaseController cannot be instantiated.
 <?php  
      // application/BaseController.php  
      abstract class BaseController{  
           protected $registry; //holds a protected reference of the registry  
           function __construct($registry){  
                $this->registry = $registry; //sets the registry  
           }  
           abstract function initialize(); //forces each sub-class to have an initialize() method   
      }  
      ?>  
But back to the route() method, if the router then finds our controller file to be readable, it includes it, otherwise it includes the Error404Controller class.

Whatever it has included, it instantiates. So now we have a controller object ready.

Further on, if that controller object's initialize() method is found callable, our router will call it. The initialize() method is where we will define the actions we want each controller to execute for itself and have it write some properties to the registry's template object to be passed directly to the corresponding view, in order to be used there. Within that method, we will also have each controller call the assign_theme() method of the registry's template object it holds.

This is how our user input is being routed. But then, we have to find and instantiate the model that corresponds to each created controller too. Because the models are the essential component that will provide our application with the business logic and data it needs. So, this is where the magic __autoload() function comes handy
 function __autoload($class_name){   
           //The $class_name parameter is internal to PHP, so we don't have to set it ourselves.   
           //In fact, this will be set for us when the controller will first call the model it needs but the model will still not be included though.   
           //That will trigger the __autoload() function, in which we specify where to look to find the needed model and include it.  
           try{  
                $filename = $class_name.'.php';  
                $file = 'application/models/' . $filename; //but we make sure the __autoload() function performs its search within our model directory only  
                if (file_exists($file)){include ($file);} // if the class can be found, where we have told the __autoload() function to look for, it includes it  
                else { //otherswise throws and expception  
                          throw new Exception('<p>&nbsp;</p><p>&nbsp;</p><div class="loading_model_fails">'.$GLOBALS['t_the_model_named'].' <u>' . $class_name . '</u> '.$GLOBALS['t_couldnt_be_found'].'</div>');   
                          // note the use of globals for handling the translations, we will come back to that in the next settlements  
                }  
           }  
           catch(Exception $e){  
                echo $e->getMessage();  
                exit(0);  
           }  
      }  
Did I say magic? Yes, it is a magic function, just as __set() and __get() are too. It will be called each time a called model class will not be recognized. So, it really is magic because, we do not know in advance which controller is going to be triggered. So, instead of having to include all our model classes, we only need to let PHP do the work of 'not recognizing' the model class the then-working controller object is trying to instantiate, call and instantiate it for us. Awesome, isn't it?

But what of the view then? As we said earlier, the view files, which are in fact basic php files acting as templates, will be included by the template object's assign_theme() method, itself called in the controller's initialize() method.
 <?php  
      // application/Template.php  
      class Template{  
           private $vars = array(); //declaring the array that will store the variables to be passed to the view  
           public function __set($index, $value){  
                     //magically assigning index/pairs to that array  
               $this->vars[$index] = $value;   
            }  
            public function assign_theme(){   
                //the method the controller calls once it has finished   
                //interacting with the model and wants to display the view  
                try{  
                          //We loop thru the vars array containing the variables the __set() method above has stored  
                                    foreach ($this->vars as $key => $value){  
                                         $$key = $value;  
                                         //we turn each key name to a variable using the $$ syntax, assigning it the same value it already held before entering the loop.  
                                    }  
                          $common_template_files = array('header','main','footer'); // in reality, we do not display only one php view file per/module,   
                          //as for obvious abstraction or visual design reasons, we choose to arbitrarily include the header, main and footer php view file,   
                          //and we will include [module_name].php view file only from within the main view file,   
                          //if the module_name variable has been sent by the controller  
                          foreach($common_template_files as $ctf){ //for each common tpl files  
                               $file = PATH_TO_THEMES.'/'.CURRENT_THEME.'/'.$ctf.'.php';  
                               //we build its full path  
                               if (!file_exists($file)){ //if the common tpl file does not exist  
                                    throw new Exception('<p>&nbsp;</p><p>&nbsp;</p><div class="loading_tpl_fails"><u>'.$ctf.'</u> '.$GLOBALS['t_couldnt_be_found'].'</div>');  
                                    // we throw an exception  
                               }       
                               else{  
                                    include($file); //but if it does exist, we include it  
                               }  
                          }  
                     }  
                     catch(Exception $e){  
                          echo $e->getMessage();  
                          exit(0);  
                     }  
                }  
           }  
      ?>  
Note from the code above that we don't actually display only one php view file per/module, as for obvious abstraction or visual design reasons, we choose to arbitrarily include the header, main and footer view file instead and we will include the [module_name].php view file only from within the main view file called main.php, if the module_name variable has been set and sent by the controller.

So here, in essence, we already have a full-blown MVC application in our hands. A router (factory) that loads the needed controller based on user input, a magically loaded model to provide, through its own set of properties and methods, for all the logic and data our application requires and a view file, fed by the template engine, itself containing a magic set() method and being contained in the registry object that was passed on to the controller as a parameter while being instantiated and that includes, at the controller's request, all the views needed for display, plus, that also stores, by means of set() methods, and displays all the data the controller wishes to handle to the view.

In the next settlement, we will set the focus on the controller, the model and the view, on how they are built, work together and we will come full circle by creating our first complete module, the page module. Do not miss it!

This article first appeared on Tuesday the 28th of October 2014 on RolandC.net.

How to build your own Multilingual PHP MVC CMS from scratch - Part 1 - Introduction

Thursday, October 23, 2014

While the MVC architecture has been around for quite a long time, it somehow took longer for PHP than for other languages like C++ to come to embrace it, allowing it to serve as solid ground in a growing number of popular CMS's and Frameworks. But even if nowadays the number of Web developers that can truly claim they have never heard of CMS's like Drupal or OpenCart is becoming increasingly seldom, you, as a programmer, may have often been wondering, as I once have too, just how does the CMS your Project Manager has assigned you on really works, so you could start making the most out of it and stop having to loose half your evenings, week-ends and personal life just to barely keep up with or learn it, let alone the personal stigma that not getting along too well with a custom in-house CMS may at times create in a developer's life.
Since the best way to understand how a device works is by creating one yourself, in this beginning twelve-part series, I will be teaching you how to build a complete and working lightweight Multilingual MVC PHP5 CMS from scratch, that you will be able to use for your own purposes or that will at least leave you with a better insight on how those things really work, get you more pro-efficient. And we will only be using free software tools throughout the whole course of these series.
But during this very first part, and once I have discussed the minimum knowledge required to attend these tutorials, i will be defining what the acronyms CMS and MVC stand for, then I will be reviewing the reasons as to why building your own CMS should only be an exciting, rewarding and beneficial experience. I will also be listing the software you will need and finally, I will expose and explain the basic folder structure around which we will be developing our new project.

Target audience

Although being a senior PHP developer is definitely not mandatory here (especially that after all, we all have to learn things before we can claim we know them), you should at least be confident with the use of classes and of some of the most common practices/algorithms in PHP/Web development, so that, you should not be a total beginner. This however does not mean beginners are not welcome, on the contrary they are, as beginners, always encouraged to read code, especially code they cannot figure out, for their own benefit.
You should have a fair understanding of what Object Oriented Programming means, and ideally of software design patterns, but this is not essential for now, as it will be discussed later in the series. You should also have a strong interest and the time required to build a complete CMS from scratch of course.

So, what does CMS stand for and what is it used for anyway?

For you to properly understand what a CMS is, I will first define what a Framework is and what the difference between the two actually is. In Web development, a Framework, hear a Web Framework or Web Application Framework, is a collection of files and folders (software) arranged according a specific structure or architecture and in such a way as to allow, to a certain degree of abstraction, for the quick/rapid development of Web applications (most commonly Websites) and which aim is basically to reduce the overhead traditionally associated with Web development. In other words, a Web Framework can mostly be seen as some sort of abstract software that would allow someone building a complete full-fledged site ready for production and in the least possible development time. For the sake of this tutorial, this is the definition we are going to stick with for now.

How does a CMS compare to a Web Framework then?

A Content Management System, or Web CMS in this context, simply put, is a Web application (most commonly a Website), that could have been built on top of a Web Framework, or not, but that is designed and optimized to help manage content easily and that ideally provides a certain level of customization capability, as, a CMS like for example Drupal, should be manageable/customizable by a non programmer, eg: the admin of the site can add/edit new articles, change theme etc., without having to know about its underlying structure. So a CMS, in essence, offers an extra layer to what a Framework usually provides, and can be viewed as a ready-made Website/Web application that can be deployed as is. Now for some more advanced customization, programming knowledge may be required.
So, while CakePHP is a Framework (by the definition we have used at the beginning) that could well serve as the basis for developing your own CMS, that is, depending on what your needs as a developer are anyway, it does not, by essence, offer out-of-the-box content-management oriented functionality like Drupal for example does and can only be interacted with by a programmer.
Now, it tends to get complicated when you know Drupal is called either a CMS, a Framework or Web Framework or even Web Application Framework (WAF), when you know that CakePHP is a rapid development Framework(as explained above) but that the company's built-in CMS you may be struggling with all day and that was created out of mostly popular CMS's parts is called a Framework too by everyone in the company you work for.
Frankly, most people's definition of a Framework/CMS nowadays is that it's some software/script that's to some level of abstraction is meant to ease/speed up the process of developing/creating a Web site or Web project, and it's still true in its own right.
Finally, popular CMS provide many functionality you don't necessarily need or use, and that are likely to increase the risk of bugs, vulnerabilities and so forth. While with a more abstract Framework like CakePHP, you do not have to build feature you don't need.

And what about MVC in all that?

MVC, which has been around for some time with languages other than PHP, and which stands for Model View Controller, is a type of software architecture that is designed to clearly separate an application(a Web application here) into three distinct parts: the control, the model and the view. The way we will implement it in our project, basically, the controller receives an input (via the index file) from the mouse or the keyboard for example, addresses the corresponding model in order to get the needed data and/or to process it according to the business logic contained in the given model, which then sends it back to the controller. Then the controller transmits that data to the view, which is responsible for managing visual display on the application, thus, how the site or app looks like. In other words, the controller will act as an interface between the model and the view, allowing for a clear logical separation between those processes, and most evidently for a cleaner PHP code, not being obfuscated by HTML or inline CSS snippets. Now, as the MVC pattern itself however does not define how the data should be passed between entities, it is therefore possible to find Web applications that use this architecture in a different way, having for example the model updating the view.
Regardless of how it is being used, such an architecture represents an evolved way to design an application and differs from the three-tier organization model, somehow very close in essence but where applications are structured around three layers: the client(presentation), the application(logic), and data access(database). That said, MVC is meant to make both the programmers and designers life easier, as, the designer can concentrate on working on the visual aspect of the project, generally through template files, while the programmer takes care of the business logic, eg: PHP code found in models. The controller of a PHP-driven CMS contains PHP but does not hold, or should not hold any element of your business logic.
Now that we have explored the main concepts around which we will be developing our application, a little word again on what we will exactly be building during these twelve-part series. We are going to build a simple lightweight Multilingual CMS that follows the MVC pattern and that will act as finished Website, ready to be deployed and administered by non-programmers. Customizing it for various other small-scale projects should only require little programming knowledge, if any at all. It should be perfect for a small-scale organization.

Fine, but why building your own CMS anyway?

Let's face it, creating your own CMS does require a lot of spare time, BUT
  • as a hobbyist, it is a great way to learn about the inner workings of CMS's and to learn about the MVC structure, which is growing enormously in popularity
  • as a PHP professional working inside a company, it will help you better cope with the company's custom-built CMS you already work on daily, if you do.
  • as a PHP professional working independently and looking to get things further, it will help you create the custom CMS you perhaps have always dreamed of, tailored to almost instantly meet your most recurrent client prerequisites, but best of all, that you will fully know and understand, thus allowing you to cut down on development time dramatically, leaving you more time to focus on other aspects of your activity.
  • There are lots of great free popular CMS's on the market, that some developers even specialize in customizing, but for the others, wasting hours looking to solve problems that you can't put your finger on just because you haven't coded it yourself and there is simply too much data to deal with - is never that fun, and gives you one more excellent reason to give it a go and see for yourself how much you could benefit from building your own CMS build from scratch.
Now, before we start getting our hands all greased up, let's review the tools you will need to to be working with:
  • Your favorite programming editor.
  • PHP 5 (with GD and jpg support enabled) - version 5.4 to 5.6 should be fine.
  • Apache HTTPd server 2.4, version 2.2 should be fine too.
  • MySQL Community Servers version 5.5 or 5.6 are good choices for our project.
  • An image editing program: I will be using the Gimp 2.8, but you could still be using Photoshop or alike if you prefer.
  • Optional (although strongly recommended) :
    • A debugger like XDebug and a tracer such as Valgrind.
    • Composer to help you manage our plugins (PHPMailer/TinyMCE) dynamically.

Basic overview of our project's folder structure

So let's take a brief look at what our overall folder structure is going to look like


rolandc.net
Our project's general folder structure

  • From the image above, you can see the admin section has its own standalone folder.
  • Config.php will be our config file for the whole project, and serve for the admin as well. Some settings do rely on the database but most of the configuration will be found in config.php though, including database access parameters.
  • The /application folder, as it exists in /admin too, contains our controller and model files. It is the core part of our application, thus the name application. It also contains our base controller, registry, router and template engine, which we will review in the next part of our tutorial, while studying the index.php file.
  • The /languages folder will hold the needed translation files, country flags and everything needed to manage encoding as well as Javascript translation strings, all on a per-language basis.
  • In /vendors we will simply store the plugin files needed for our project. In fact, composer will store them for us, as we will only have to pull new versions of our plugins from the command line, whenever needed. The plugins we will use are TinyMCE 4 (Wysiwyg editor) and PHPMailer 5.2.6.
  • The /public folder will contain our Javascript files.
  • In /themes, we will store all the CSS files, template files and site images(site's visual identity) pertaining to each existing theme.
  • At root level, we also find the index.php file, which actually is, or should be, the one and only entry point to our application, as all user requests should reach the index first, or be redirected to it, no matter what.
  • about.php, provided you launch it manually, will check your server configuration, and more specifically, it will check your Apache/PHP/MySQL versions, as well as your GD library version, and if you happen to have support for JPEG (used for the captcha image in the registration form).
  • The /list folder will contain lists like country or language lists and the tools to dynamically manage them.
  • The files named composer.json and composer.lock are inherent to Composer. They contain data necessary to manage our plugins. Again, we will only have to use the command line to pull in the updated plugin files whenever available.
  • The second multi-level screen capture stops at the beginning of the vendor folder because there only remains the content structure of our plugins, which will will not take interest in.
To help you understand the structure better, here is a multi-level folder representation of the same project, without the files.

1.2 Multi-level view 1/2 - rolandc.net
Multi-level view 1/2

Multi-level view 2/2 - rolandc.net
Multi-level view 2/2

As you can clearly see, the files and folders that are inside the /admin folder repeat almost exactly the same folder structure as we have just reviewed. The admin in itself will be like some sort of modified front-end within the front end.
So far, we have discussed who the minimum knowledge requirements for this tutorial, defined what the acronyms CMS and MVC mean and explained what differentiates a Web CMS from a Web Framework. We have also reviewed the reasons as to why choosing to build your own MVC CMS can be a very exciting and rewarding decision to make for yourself, before enumerating the tools we need to get started, and ultimately engaged in reviewing the core folder structure of our project. In the next chapter, I will be focusing on what I have called earlier the one and only entry point to our application, that is the index.php file, slowly evolving toward the /application folder, which represents the core system of our CMS, containing all the controller/model classes that will empower the MVC structure in our project.

This article first appeared on Thursday the 23rd of October 2014 on RolandC.net.

Linux must-have essential software 2014

Wednesday, October 22, 2014

Whether you're being new to the world of Linux or not, at one point or another, you're going to periodically re-question the software that's already installed on your station.
As a beginner user, you will be haunted by the quest of ever trying to find new alternatives or best, to find copycats of the programs you used to have on... you know. But once you have gained significant experience, you will love digging the net for even more of that great software you already have and use, that is, not just to kill time though.
So here is my 2014 list of all Linux software I believe all of us (or almost) should have, hoping this will help you achieve even more and meet greater challenges through your PC, at least complement your current installation.

Tux trying to figure out the software he needs on RolandC.net