Page 1 of 3
configuration variables passed to objects
Posted: Mon Dec 10, 2007 3:41 pm
by s.dot
When relying on configuration variables to do execution in your scripts (such as a mathematical value that gets operated on), what's a good way of implementing the configuration values so they can be accessed in objects?
Right now I've got config.php which is a basic php file like this:
Code: Select all
$config = array();
$config['key'] = 'value';
$config['key2'] = 'value2';
I've got this file included in every page (via my roll of site-wide includes). And accessing them within an object requires globalization:
Code: Select all
<?php
class test
{
public function add()
{
global $config;
return $config['key'] + $config['key2'];
}
}
Seems sort of hackish.
Posted: Mon Dec 10, 2007 3:47 pm
by arjan.top
I used this a while ago, may not be the best option:
Code: Select all
private static function load_vars($file){
if (file_exists($file) == false) {
return false;
}
include ($file);
unset($file);
$defined = get_defined_vars();
foreach($defined as $key => $val){
self::$vars[$key] = $val;
}
unset($defined);
}
Posted: Mon Dec 10, 2007 4:31 pm
by alex.barylski
You never want to bring a global into class scope - infact you should avoid using them period.
What I would do is create an config object, initialize it...and inject it into the objects scope.
Code: Select all
class MyConfig{
private $_var = '';
function getVar(){ return $this->_var; }
function setVar($var){ $this->_var = $var; }
}
class MyObject{
function __construct($config){ $this->_config = $config; }
function soSomething(){ return true; }
}
$cfg = new MyConfig();
$cfg->setVar(100);
$obj = new MyObject($cfg); // Inject the depdency into the class (now it's abstract not concrete)
p.s-What I've demonstarted is often called constructor injection. You may also use a setter or mutator method. So instead of injecting the dependency at construction you set it after construction using the setter. This is handy in instances where the dependency can change after the point of primary object construction, so you may want to consider that approach as well - depending on your situation.
Personally I always start with construction injection to prevent anything but the line of code that creates the object from changing the dependency. If the need arises I refactor and add a setter method. KISS.
HTH

Posted: Mon Dec 10, 2007 4:56 pm
by s.dot
So I should load my config file into an object and pass that around rather than globalize the array.
Something like..
Code: Select all
class config
{
private $_config = array();
public function __construct($configArray)
{
$this->_config = $configArray;
}
public function __get($key)
{
if (isset($this->_config[$key]))
{
return $this->_config[$key];
}
}
}
So then my object instantiation would look like
Code: Select all
//load the config array
require 'config.php';
$cfg = new config($config);
Since the configuration object will be a one-time needed object, I should probably make it a singleton?
I don't really want the configuration values to be modifiable at run time. But rather they have to be changed in the config.php file.
Posted: Mon Dec 10, 2007 4:58 pm
by arjan.top
wouldn't be it better to use for example singleton here, or at least static class?
EDIT:
pass array as parameter or use function I gave you in my first post (so you don't have $config defined)
Posted: Mon Dec 10, 2007 5:06 pm
by s.dot
I'm unclear of how to pass the configuration object to other objects.
Let's say somewhere on one of my pages I instantiate the config object.. is it available inside of other classes? Or do I have to pass the config object to all of the other classes I will need? (seems messy) Another alternative would be to have all classes extend config.. but that seems even more messy.
Sorry, I'm just getting into learning the OO ways.
Posted: Mon Dec 10, 2007 5:07 pm
by s.dot
arjan.top wrote:(so you don't have $config defined)
That's a good idea. I can just require in the __construct().
Posted: Mon Dec 10, 2007 5:13 pm
by alex.barylski
Edit: Found this article may be of interest (
http://martinfowler.com/articles/injection.html). Explains the problem your facing and it's possible solutions.
Yes, you would pass the array of configuration details to the config object during construction. Unless you hardcoded the config values into the class like so:
Code: Select all
class MyConfig{
private $_datafile = 'test.xml';
private $_name = 'My Application';
}
It's usually best to keep configuraiton data outside of classes, as it's easier to write a script to parse a INI file than it is a class. So something like this is best:
Code: Select all
config.ini
datafile = "test.xml"
name = "My Application"
Code: Select all
//
// Load config data from external file and pass to config object
$array = parse_ini_file('config.ini');
$config = new MyConfig($array);
//
// Inject config object into primary object
$obj = new MyObject($config);
If you do not wish to have your config object changed, just make sure your member variables are private and if you have setters make them protected, so only derived classes can change the data on you.
Posted: Mon Dec 10, 2007 5:31 pm
by arjan.top
I still think singleton is the way to go, if you want to have add for example set() method, you will be always accessing the same object, if you create config file with new you can have multiple instances of config objects, so that can lead to bugs etc.
Posted: Mon Dec 10, 2007 5:56 pm
by VladSun
arjan.top wrote:I still think singleton is the way to go, if you want to have add for example set() method, you will be always accessing the same object, if you create config file with new you can have multiple instances of config objects, so that can lead to bugs etc.
You could pass the config variable by reference in this case. But I think it's almost the same as using global variables...
Posted: Mon Dec 10, 2007 6:02 pm
by alex.barylski
arjan.top wrote:I still think singleton is the way to go, if you want to have add for example set() method, you will be always accessing the same object, if you create config file with new you can have multiple instances of config objects, so that can lead to bugs etc.
Singletons usually just introduce more complexity - it's yet another object to introduce.
Although two or more config objects don't make sense...I hardly think it would throw an exception if one were created. Using the single would also introduce a concrete dependency on the singleton.
Code: Select all
MyConfig::getInstance()->getData()
There is no benefit to using a singlton here I don't think. Granted it's easy enough to search & replace with a more dynamic implementaiton later, introducing the singleton object just adss clutter to the code. Start simple first and add these things as the need arises, don't just start hammering out design patterns because it's a documented best practice for a situation like this.
I say wait until the system requires the object remain at a single instance.
Let's say somewhere on one of my pages I instantiate the config object.. is it available inside of other classes? Or do I have to pass the config object to all of the other classes I will need? (seems messy) Another alternative would be to have all classes extend config.. but that seems even more messy.
Sorry, I'm just getting into learning the OO ways.
Here is where programmer discipline comes in...if your application is model 1 (multiple independent scripts) you need to take great care and make sure the config object is only created once at the start of every script. If you have a model 2 paradigm, you would likely create the object *once* inside the application entry point shortly before you instantiated your front controller.
This is why people tend to move towards model 2 - to avoid the nessecary disicpline in making sure the config object is created at the start of every script (and other reasons of centralized logic).
Here is a example:
Code: Select all
<?php // aboutus.php
include 'includes/config.php';
$obj = new MyObject($cfg); // Inject configuration object into MyObject() instance
$two = new MyTwoObject($cfg); // Inject configuration object again into another object
?>
Code: Select all
<?php // includes/config.php
$array = parse_ini_file('config.ini');
$config = new MyConfig($array);
?>
The latter example is an include which contains all your scripts common initialization code - config object, timezone, locale, etc
The first example is the actual script file which you invoke via the web browser. It creates 2 objects and injects the config object. I will show you how each of those objects would use the injected config object now:
Code: Select all
<?php
class MyObject{
$_config = null;
function __construct($config)
{
// Objects in PHP are passed by reference so this member is basically pointing back to the original config object
// but the dependency is no longer concrete, such as if we did something like this:
// $this->_config = new MyConfig('config.ini');
$this->_config = $config;
}
function executeQuery($keyword)
{
//
// Assume we store the user ID of a single blocked user inside the config object. We want the query to show all records
// except those of the blocked user (blog comments or similar). We use the injected config object and retreive the user ID
// store in a local (easier reading) and pass that along to the SQL query along with the single $keyword argument.
$userid = $this->_config->getUserId();
$res = mysql_query("SELECT * FROM table WHERE keyword LIKE $keyword AND userid = $userid");
}
}
Using the injection technique, you have removed the conrete dependency MyObject had on MyConfig and made it abstract. If you ever changed the name of the MyConfig class - say to some super config class called MySuperConfig, you need to only replace the single config object instantiation, not all the MyObject like classes that use the config class. Not only that but the configuration is left to the caller not the callee - this is typically much better design and makes for looser coupled classes - making them more reusable in the long run.
Posted: Mon Dec 10, 2007 6:05 pm
by alex.barylski
VladSun wrote:You could pass the config variable by reference in this case. But I think it's almost the same as using global variables...
Not really. For starters all objects are passed as reference in PHP 5. A global is just that GLOBAL. It can be accessed anywhere, especially if it's a super global. A reference is...well it's context is limited to whatever you set it to...
A function parameter is accessible only inside that function call...not in the caller namespace, etc...
Posted: Mon Dec 10, 2007 6:22 pm
by VladSun
Hockey wrote:Not really.
Too short ...
Hockey wrote:For starters all objects are passed as reference in PHP 5.
OK ...
Hockey wrote:A global is just that GLOBAL. It can be accessed anywhere, especially if it's a super global.
OK ...
Hockey wrote:A reference is...well it's context is limited to whatever you set it to...
OK ...
Hockey wrote:A function parameter is accessible only inside that function call...not in the caller namespace, etc...
OK ...
Yes all of your statements are true in general (except the first one - I couldn't' interpret it). But you didn't say anything closely related to the issue above.
I meant that either having a global variable (declared as by scope modifier "global") inside a function or either passing this variable by reference to this function is almost the same because:
* You can change its value in global scope;
* If its value gets changed while the function is executing, then it will continue to operate on its new value.
Posted: Mon Dec 10, 2007 6:49 pm
by alex.barylski
I can see where your heading with GLOBALS and references being alike, however there is the distinction that a reference is not typically accessible *everywhere* like a global. IMHO thats a key difference and important to understand.
Posted: Mon Dec 10, 2007 6:59 pm
by s.dot
So not to stray off into something I don't understand (it's heading that way

), all of my base classes will have to be injected with the config data (in which I also think a singleton is a good idea). Once the base class is injected, all classes that extend that class will already have the data injected by means of extension.
I just thought it was a bit ugly to pass the object as a parameter to the constructer method, but I guess this is accepted practice.
So, I pass the config object to a different class like so..
Code: Select all
class test
{
private $_cfg;
public function __construct($cfgObject)
{
$this->_cfg = $cfgObject;
}
}
How do I access the config object properties within the class?
Code: Select all
return $this->_cfg->value + $this->_cfg->value2;
Like that?