PHP:MVC - Model View Controller | Front Controller

Keeper
11 years ago

0

Introduction:

Now we know where the configuration folder is but the idea is that we could have more than one configuration files. So how would this class read all those files? There are three methods that I can personally come up with. One of them is whenever we make an instance of the class, the class to go through all configuration files, to obtain them and whenever we need them they are to be available somewhere in the memory. But nobody says that all the configurations files will be needed for the scope of a single request.

The second method is when we want to take something from a specific configuration file only then to include it and whenever we need it to take it from the memory. And the third is the worst scenario. It involves the inclusion of the configuration file/files we need every time. You can guess yourself why this is wrong.

<?php  

namespace KF;  
class Config {  

  private static $_instance=null;  
  private $_configFolder = null;  
  private $_configArray = array();  

  private function __construct() {  

  }  

  public function setConfigFolder($configFolder) {  
         if(!$configFolder){  
              throw new \Exception('Empty config folder path:');  
         }  
         $_configFolder = realpath($configFolder);  
         if ($_configFolder != FALSE && is_dir($_configFolder) && is_readable($_configFolder)) {  
              $this->_configArray = array();  
              $this->_configFolder = $_configFolder . DIRECTORY_SEPARATOR;  
         } else {  
              throw new \Exception('Config directory read error:' . $configFolder);  
         }  
         echo $this->_configFolder;  
   }  

  /**  
  *  
  *  [@return](/profile/return) \KF\Config  
  */  

  public static function getInstance(){  
    if (self::$_instance == null) {  
    self::$_instance = new \KF\App();  
    }  
    return self::$_instance;  
  }  
}   

So in two words, GET will be called every time when we try to use a property of a class that is not defined. And as a parameter to GET is gonna be the name of the property, which in our case is (app)

public function __get($name) {  

     if (!$this->_configArray[$name]) {  
          $this->includeConfigFile($this->_configFolder . $name . '.php');  
     }  
     if (array_key_exists($name, $this->_configArray)) {  
          return $this->_configArray[$name];  
     }  
     return null;  
}  

So whenever this method is executed, we’ll know the name of the file that we want to load. Now with the calling of _get we first check this file hasn’t already been loaded and we do this by checking whether the name of this file is a value of the elements from configArray(); After this process is passes, we validate whether this name exists in the corresponding array and if the value exists we return the whole array.

Basically, _configArray() holds those two arrays:

<?php  

$cnf['test1']='ok1';  
$cnf['test2']='ok2';  

Let’s now write our include method which is actually pretty easy compared to the other classes and methods we coded beforehand.

public function includeConfigFile($path) {  
      if (!$path) {  
         throw new \Exception;  
      }  
      $_file = realpath($path);  
      if ($_file != FALSE && is_file($_file) && is_readable($_file)) {  
           $_basename = explode('.php', basename($_file))[0];  
           include $_file;  
           $this->_configArray[$_basename] = $cnf;  
      } else {  
           throw new \Exception('Config file read error:' . $path);  
      }  
}  

It accept the parameter $path which consists of the directory of the configuration files and the name of the file with a forced intention of .php. If we don’t find a path, we kill the process and throw an exception. If the second if() statement is TRUE, we include the file and have control over the variable $cnf.

We can also do that with return_array():

return array(  

);  

I personally don’t favor this method because when using multidimensional arrays, things get messy. However that was just a stray away from our objective. Now $_basename is the name of the very file. The function basename() however, return only the name of the file and excludes the directories it’s within. And the [0] allows us to get the 0 element from an array and since explode return only arrays. That would be the same if you put it this way:

public function includeConfigFile($path) {  
      if (!$path) {  
         throw new \Exception;  
      }  
      $_file = realpath($path);  
      if ($_file != FALSE && is_file($_file) && is_readable($_file)) {  
           $_basename = explode('.php', basename($_file));  
           echo $_basename[0];  
           include $_file;  
           $this->_configArray[$_basename] = $cnf;  
      } else {  
           throw new \Exception('Config file read error:' . $path);  
      }  
}  

Suppose we have the name we want to load in our $name variable that is a parameter for the __get method. So why not pass it as a variable here instead?

public function __get($name) {  

     if (!$this->_configArray[$name]) {  
          $this->includeConfigFile($this->_configFolder . $name . '.php', $name);  
     }  
     if (array_key_exists($name, $this->_configArray)) {  
          return $this->_configArray[$name];  
     }  
     return null;  
}  

As a parameter to that method. Whenever we work with something that extracts the data, it’s better not to have multiple dependencies but a single source. Now to our testing to check whether it works:

<?php  

include '../../KF/App.php';  

$app = \KF\App::getInstance();  

$config = \KF\Config::getInstance();  
$config->setConfigFolder(',,/config');  

echo $config->app['test1'];  

$app->run();  

And we get the output of:

/store/work/www/kftest/config/  
Notice: Undefined index: app in /store/work/www/kf/Config.php on line ...  
ok1  

Now suppose we make a second configuration file and call it db.php with the content of:

<?php  

$cnf['name']='Keeper';  

And modify the index.php to include it there:

<?php  

include '../../KF/App.php';  

$app = \KF\App::getInstance();  

$config = \KF\Config::getInstance();  
$config->setConfigFolder(',,/config');  

echo $config->db['name'];  

$app->run();  

Which will simply output the string ‘Keeper’. So with a few LOC we managed to make a flexible mechanism for managing configuration files. Also another thing is that the answer is an array which means that we can manipulate it however we’d like to. Now, so as not to force the developer who is going to work with our framework to work only with the output variable in the app.php, we gonna return its value and I’m gonna talk more in-depth about this later. For now just return the value.

<?php  

$cnf['name']='Keeper';  
$cnf['name']='Keeper';  

return $cnf;  

The idea is that we need to return an array but since the Config.php file doesn’t expect the returning of a value from the include file and relies on the variable $cnf. In that case we’ll just simply do this little modification in our Config.php:

public function includeConfigFile($path) {  
      if (!$path) {  
         throw new \Exception;  
      }  
      $_file = realpath($path);  
      if ($_file != FALSE && is_file($_file) && is_readable($_file)) {  
           $_basename = explode('.php', basename($_file));  
           echo $_basename[0];  
           include $_file;  
           $this->_configArray[$_basename] = include $_file;  
      } else {  
           throw new \Exception('Config file read error:' . $path);  
      }  
}  

public function __get($name) {  

     if (!$this->_configArray[$name]) {  
          $this->includeConfigFile($this->_configFolder . $name . '.php', $name);  
     }  
     if (array_key_exists($name, $this->_configArray)) {  
          return $this->_configArray[$name];  
     }  
     return null;  
}  

So basically, the idea is to return an array. We are not limited to using only two of those methods. But overall we need an array as a return value. Now the next thing we gonna look into is when the developer misses to specify the config folder. Let’s make it accept a default value. In our App.php file:

<?php  

namespace KF;  
include_once 'Loader.php';  
class App {  

  private static $_instance=null;  
  private $_config = null;  

  private function __construct() {  
    \KF\Loader::registerNamespace('KF', dirname(__FILE__).DIRECTORY_SEPARATOR);  
    \KF\Loader::registerAutoLoad();  
    $this->_config = \KF\Config::getInstance();  
  }  

  public function run(){  
    echo 'Devoted to Antagonism and Infamous';  
  }  
  /**  
  *  
  *  [@return](/profile/return) \KF\App  
  */  

  public static function getInstance(){  
    if (self::$_instance == null) {  
    self::$_instance = new \KF\App();  
    }  
    return self::$_instance;  
  }  
}   

So in that case the variable _config contains the instance. Now we need two more methods that delegate the current situation and those are setConfigFolder and getConfigFolder:

<?php  

namespace KF;  
include_once 'Loader.php';  
class App {  

  private static $_instance=null;  
  private $_config = null;  

  private function __construct() {  
    \KF\Loader::registerNamespace('KF', dirname(__FILE__).DIRECTORY_SEPARATOR);  
    \KF\Loader::registerAutoLoad();  
    $this->_config = \KF\Config::getInstance();  
  }  

  public function setConfigFolder($path) {  
         $this->_config->setConfigFolder($path);  
  }  

  public function getConfigFolder() {  
         return $this->_configFolder;  
   }  

  public function run(){  
    echo 'Devoted to Antagonism and Infamous';  
  }  
  /**  
  *  
  *  [@return](/profile/return) \KF\App  
  */  

  public static function getInstance(){  
    if (self::$_instance == null) {  
    self::$_instance = new \KF\App();  
    }  
    return self::$_instance;  
  }  
}   

So whenever a folder is not located for the configuration files, we need to add such and apply it in our script. We can do that with just one conditional statement:

if ($this->_config->getConfigFolder() == null) {  
     $this->setConfigFolder('../config');  
}  

Conclusion:

I’m overall trying to keep all of the tutorials from these series in a certain length so as to be easily accessible my the members and to be read with bigger interest and expectation for the next part. Thanks for reading!

0replies
1voice
226views
You must be logged in to reply to this discussion. Login
1 of 1

This site only uses cookies that are essential for the functionality of this website. Cookies are not used for tracking or marketing purposes.

By using our site, you acknowledge that you have read and understand our Privacy Policy, and Terms of Service.

Dismiss