Hi guys,
In an effort to continue reducing my reliance on global variables I thought I would make a simple config registry. Here is the code for the class:

<?php

namespace core;

class config {
	use traits\backtrace; // vBulletin seems to be removing the namespace slashes

public $settings = array();

public function __construct(Array $settings) {
	foreach( $settings as $k => $v ) {
		if( is_array($v) ) {
			$this->settings[$k] = new config($v);
		} else {
			$this->settings[$k] = $v;
		}
	}
}

public function __get($name) {
	if( isset($this->settings[$name]) ) return $this->settings[$name];
	trigger_error('Undefined property '.$name.$this->getCallerTrace(),E_USER_ERROR);
	return NULL;
}

public function __set($name,$val) {
	$this->settings[$name] = $val;
}

public function __isset($name) {
	return ( isset($this->$name) || isset($this->settings[$name]) );
}

public function __unset($name) {
	if( isset($this->settings[$name]) ) unset($this->settings[$name]);
}
}

And of course his is an extremely simplified use case:

$config = array();
$config['database']['engine'] = 'mysql';
$config['database']['user'] = 'username';
$config['database']['pass'] = 'password';
$config['database']['schema'] = 'database';
$config['database']['host'] = 'localhost';
$config['mail']['type'] = 'smtp';
$config['mail']['host'] = 'localhost';
$config['mail']['post'] = 587;
$config['mail']['user'] = 'user@domain.net';
$config['mail']['pass'] = 'mysupersecretemailpassword';

$config = new \core\config($config); // For some reason vBulletin is removing slashes here that point to the namespace

$PDO = new PDO($config->database->engine .':dbname='. $config->database->schema .';host='. $config->database->host, $config->database->user, $config->database->pass);

$mail = new Mailer($config->mail->host, $config->mail->port, $config->mail->user, $config->mail->pass, $config->mail->type);

Basically I'm not even sure if this is the right way to do a config registry (or if it even qualifies as a registry) but I was hoping you guys would give me your feedback (or critique as it were) on this simple class.

    Might be a good candidate for the singleton pattern, just to ensure that you don't end up with multiple configuration objects floating around.

    You might consider putting the actual configuration settings in a non-PHP file that is read/processed by your configuration object. (A JSON file could be a quick and simple way to organize the data and is easy to read into a PHP array via json_decode().)

      How would I use a singleton pattern and still have it be a child of itself? Or would this part have to change completely?

              foreach( $settings as $k => $v ) { 
                  if( is_array($v) ) { 
                      $this->settings[$k] = new config($v); 
                  } else { 
                      $this->settings[$k] = $v; 
                  } 
              } 

      My example wasn't a good one, because in reality my plan was to pass just the DB Config object to my database wrapper, etc. Like so:

      $db = new myDBWrapper($config->database);
      $mail = new myMailWrapper($config->mail);

      Maybe I don't understand something with singleton, but I thought the idea behind it was once an object of that type was made, from that point forward it can't be made again only reused?

      As far as using a JSON file, I like this idea, but would it still be possible to keep settings in separate files? Right now I have a folder called config, with files in it called database.cfg.php mail.cfg.php global.cfg.php etc and I use glob to include them. So my configuration initialization looks like this right now:

      $config = array(); // to prevent undefined variable at first use
      $files = glob(APPROOT . 'config/*.cfg.php');
      foreach( $files as $file ) {
         require_once($file);
      }
      $config = new Config($config);

        I use objects for config properties - it's easier to access dynamically than assoc. arrays are (plus I like typing -> much more than ['...']). If you made a "configProperty" class, then the config class itself could still be a singleton.

          traq;11001641 wrote:

          I use objects for config properties - it's easier to access dynamically than assoc. arrays are (plus I like typing -> much more than ['...']). If you made a "configProperty" class, then the config class itself could still be a singleton.

          I'm sorry I don't understand at all what you mean. Any chance you could show a link explaining what you mean? Or perhaps an example if a link isn't readily available.

            Derokorian;11001640 wrote:

            How would I use a singleton pattern and still have it be a child of itself? Or would this part have to change completely?

            I did not see (during my quick scan) that you were doing that. I guess you could create a ConfigContainer (or some better named class 😉 ) that might be a child of your config class, with the only change being to implement the Singleton pattern, but the cost/benefit analysis of doing that would depend on whether you have any concern or not about multiple instances existing (or if you would rather control that logically instead of structurally).

            The other option might be for each type of config to be a separate, child class that is a singleton -- perhaps extending an abstract config class? Again, whether it's worth the effort (and would require a new -- if small -- class definition for each new config type) is essentially an application size/scope/complexity question best answered by you.

            Personally, I'm lazy enough that I'd probably just have one config class with everything in an array (or array of objects), generated from a JSON file. 🙂

            {
                "database": {
                    "host": "localhost",
                    "login": "foo",
                    "password": "bar"
                },
                "mail": {
                    "smtp_host": "mail.foo.bar",
                    "user": "john",
                    "password": "doe"
                }
            }
            

              Hmm looking at the JSON formatted like that leads me to this thought: file_get_contents on the globbed files and concatenated together like this:

              // db.json
                  "database": {
                      "host": "localhost",
                      "login": "foo",
                      "password": "bar"
                  }
              
              // mail.json
                  "mail": {
                      "smtp_host": "mail.foo.bar",
                      "user": "john",
                      "password": "doe"
                  }
              
              // ConfigHandler.class.php
              class ConfigHandler {
                 public $config = array();
                 private static $_instance = NULL;
              
                 public static function getInstance() {
                    if( is_null(self::$_instance) ) {
                       self::$_instance = new ConfigHandler();
                    }
                    return self::$_instance;
                 }
              
                 private function __construct() {
                    $files = glob(APPROOT .'config/*.json');
                    $cfg = '{';
                    foreach( $files as $file ) {
                       $cfg .= file_get_contents($file) .',';
                    }
                    $cfg = rtrim($cfg,',') .'}';
                    $cfg = json_decode($cfg);
                    foreach( $cfg as $k => $v ) {
                       if( is_array($v) ) {
                          $this->config[$k] = new Config($v);
                       } else {
                          $this->config[$k] = $v;
                       }
                    } //foreach
                 } //construct
              }//class

              Questions: Did I do singleton right? Do you think I should do sub-classes still? In which case I might have to dynamically call that class, something like:

              $class = $k.'Config';
              $this->config[$k] = new $class($v);

              I feel like having identical subclasses is a waste, I would end up just doing the following since they wouldn't have to behave differently:

              abstract class Config {
                 public $settings = array();
              
                 public function __construct() {
                    // construct code
                 }
                 public function __get($k) {
                    return $this->settings[$k];
                 }
                 public function __set($k,$v) {
                    $this->settings[$k] = $v;
                 }
              }
              class DatabaseConfig extends Config {}
              class MailConfig extends Config {}

              Maybe I just don't understand, but what is the point then of even creating the subclasses if they don't have different methods and/or properties.

                Derokorian;11001695 wrote:

                ....but what is the point then of even creating the subclasses if they don't have different methods and/or properties.

                Just me brain-storming -- I don't particularly care for it either. 🙂

                  I'll explain a little (and I didn't actually use a singleton pattern, or make a specific subclass for the properties - I just used stdClass -- but you could do those things. making it a singleton might actually be helpful):

                  class config{
                  
                  public function __construct(){
                       // load "default" config options here, however you want to do that
                       // pass the options to the addConfig method
                  }
                  
                  public function addConfig( $property ){
                  // $property could be key=>value, JSON, etc.
                      if( is_array( $property ) ){  return $this->addConfig_array( $property ); }
                      if( $array = @json_decode( $property,TRUE ) ){ return $this->addConfig_array( $array ); }
                      return FALSE;
                  }
                  
                  public function addConfig_array( $property ){
                      foreach( $property as $k=>$v ){
                          // if config property doesn't exist yet, set it
                          if( !isset( $this->$k ) ){ 
                              $this->$k = $v; 
                              return TRUE;
                          }
                          // otherwise, to avoid accidental overwriting, ignore it
                          //    (use changeConfig() method instead)
                      }
                      return FALSE;
                  }
                  
                  public function changeConfig( $property ){
                      // however you want to approach that
                  }
                  
                  }
                  
                  // usage:
                  
                  $config = new config();
                  
                  $DB = array(
                      'host' => 'localhost'
                     ,'user' => 'username'
                     ,'pass' => 'password'
                     ,'name' => 'database name'
                  );
                  
                  $config->addConfig( array( $DB ) );
                  // this would actually be done automatically in the constructor, of course
                  
                  print "My database password is {$config->DB->pass}";
                  
                  

                  This isn't the actual code, just the idea behind what I did. I'll try to find it when I get home.

                  The reason I went with objects vs. arrays is that I wanted some properties to be private, and I was using get and set to work with them, but I was running into trouble trying to work with my parameters in $array['index'] format -- whereas using $object->property worked just fine.

                    Ok playing around with suggestions this is what I've come up with: Using a singleton ConfigHandler to hold Config objects. Each config object will be specific to something, such as mail, database, local file system, etc. I even took the singleton a step further and created an function to use the ConfigHandler to return a single Config object. As always I welcome all comments and criticism.

                    <?php
                    /** ConfigHandler.class.php **/
                    if( !define('ROOT') ) die('No direct access allowed.');
                    
                    namespace core;
                    
                    class ConfigHandler {
                    	use traits\backtrace,
                    		 traits\singleton;
                    
                    public $configs = array();
                    
                    private function __construct() {
                    	$config = array();
                    	$files = glob(APPROOT .'config/*.config.php');
                    	foreach( $files as $file ) {
                    		require_once($file);
                    	}
                    	foreach( $config as $k => $v ) {
                    		$this->configs[$k] = new Config($v);
                    	}
                    }
                    
                    public static function getInstanceOf($k) {
                    	if( isset(self::$_instance->configs[$k]) ) {
                    		return self::$_instance->configs[$k];
                    	}
                    	trigger_error('Property '.$k.' does not exists'.self::$_instance->getCallerTrace(),E_USER_ERROR);
                    }
                    
                    public function __get($k) {
                    	return isset($this->configs[$k]) ? $this->configs[$k] : NULL;
                    }
                    
                    public function __set($k,$v) {
                    	if( is_a($v,'\core\Config') ) {
                     		$this->configs[$k] = $v;
                    	}
                    	trigger_error('ConfigHandler can only hold class Config as properties'.self::$_instance->getCallerTrace(),E_USER_ERROR);
                    }
                    
                    public function __isset($k) {
                    	return isset($this->configs[$k]);
                    }
                    }
                    ?>
                    
                    <?php
                    /** Config.class.php **/
                    if( !defined('ROOT') ) die('No direct access allowed.');
                    
                    namespace core;
                    
                    class Config {
                    	use traits\backtrace;
                    
                    public $settings = array();
                    
                    public function __construct(Array $settings) {
                    	foreach( $settings as $k => $v ) {
                    		if( is_array($v) ) {
                    			$this->settings[$k] = new config($v);
                    		} else {
                    			$this->settings[$k] = $v;
                    		}
                    	}
                    }
                    
                    public function __get($name) {
                    	if( isset($this->settings[$name]) ) return $this->settings[$name];
                    	trigger_error('Undefined property '.$name.$this->getCallerTrace(),E_USER_ERROR);
                    	return NULL;
                    }
                    
                    public function __set($name,$val) {
                    	$this->settings[$name] = $val;
                    }
                    
                    public function __isset($name) {
                    	return ( isset($this->$name) || isset($this->settings[$name]) );
                    }
                    
                    public function __unset($name) {
                    	if( isset($this->settings[$name]) ) unset($this->settings[$name]);
                    }
                    }

                      Design patterns can be useful, but avoid them if you dont need them. Why use a singleton at all for a config object. Why not create it in the bootstrap and pass it as an argument where its needed?

                      Hi guys,
                      In an effort to continue reducing my reliance on global variables

                      Then you drop singletons also because its a pattern to give global access. When you really need something to have global access why not just make a global one?

                        Singleton as global argument has been around for probably as long as singletons. I happen to disagree that singleton's introduce globalization. Personally I believe it to be no more 'global' than the data in a database, or a flat file. In the end its not globally available, but rather you can ask for it any where. Just like you can ask for the contents of a file from anywhere. Of course this is my position on the argument, obviously yours is the opposite.

                        Second why would I want to make a global object or variable, and pass it as an argument to everything that needs it? This creates a chained effect, if I want to use a class that needs this argument from inside another class that DOESN'T need this argument, I then I have to pass it to the class that doesn't need it, just so the class it uses for a function can have it. This in turn (in my mind, correct me if I'm wrong) tightly couples everything together far more than should be necessary to simply load configuration settings. In the end you end up passing arguments to things that don't need it so they can pass it as an argument to something else. Instead of just having that something else ask for what it needs, without involving whatever is using it.

                          gammaster wrote:

                          Then you drop singletons also because its a pattern to give global access.

                          No, it's a pattern to ensure instantiation of only a single instance of an object. Typically representing some unique resource such as the system's environment ("the world").

                            Weedpacket wrote:
                            gammaster wrote:

                            Then you drop singletons also because its a pattern to give global access.

                            No, it's a pattern to ensure instantiation of only a single instance of an object.

                            It is a pattern for both 🙂

                              laserlight;11002020 wrote:

                              It is a pattern for both 🙂

                              THE WORLD IS ROUND!!!

                              On a more prosaic note: I can come up with use cases for multiple configuration registries (e.g., swapping between environments). The current configuration would be unique, and a registry would be part of that.

                                Derokorian;11002005 wrote:

                                ... This creates a chained effect, if I want to use a class that needs this argument from inside another class that DOESN'T need this argument, I then I have to pass it to the class that doesn't need it, just so the class it uses for a function can have it...

                                Can you give me an example? My opinion isnt that you should have an agrument just for a pass through like you discribe, but cant see many situations where this is a case. Maybe more than one config objects is the right thing for you and just creat it when its needed? Or maybe create the object that needs the config outside the "first" class and inject it?

                                One of the main goals in zend framework 2 is to get rid of all the singelton use they have, because its considered as bad practice and a pain to test in bigger applications.

                                  Sure how about MVC? Everything runs from the controller right? But why would the controller need to know the DB configuration? So the process would be God controller gets called, it has to be instantiated with the config to put it in the right scope. God then calls any number of other controllers who in turn instantiate their respective models, these controllers also need to have the config passed to them, just so they can pass it to the models. OR I can just have the model request the configuration item it needs from my configuration registry and neither of the controllers need to care that there is even a configuration involved in the model.

                                  I'd also like to point out that everything I know about PHP comes from the manual and these forums. I've never gone to school for anything related to computers and everything I know about them is self taught. So my assumptions may not be considered best practice but until someone can logically show me why my assumptions are bad I will continue to base my decisions off them.

                                    I have a simple MVC my self and can show an example how I do it. I dont say its the best way, but its no defined constants and no singletons there.

                                    index.php

                                    <?php
                                    require __DIR__.'/wwf/Application.php';
                                    new Application('development');

                                    pseudocode for Application::__construct($environment)

                                       // Do some initial stuff and...
                                    
                                       require __DIR__.'/Autoloader.php';
                                       $autoloader   = new Autoloader();
                                    
                                       $config            = new Config($filename);
                                       $errorhandler = new Errorhandler($config);
                                       $request         = new Request();
                                    
                                       // Do some routing.
                                       $router = new Router($config, $request);
                                    
                                       $dispatcher = new Dispatcher($config, $request);
                                       $dispatcher->run();
                                    

                                    Then in the dispatcher I find the right controller and creates a controller object that takes $request and $config as parameters in the constructor.

                                    I have a base controller class for the spesific application that all the other controllers inherits from and can load the database there if its used all over. I also creates the models, main layout that is used all over. If you want to create the database here and pass it to the model or just send the config to the model and let it do its own stuff should be nearly the same. If you dont use database in all your controllers, create it in the controller constructor that dos.

                                    With my naming conventions and autoloader there is no hard coding of anything(outside the the bootstrap), no globals and no singletons.

                                      Write a Reply...