configuration variables passed to objects

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

scottayy - the deep end is where all the fun in arguing is ;).

Don't be discouraged if a simple topic goes off the cliff. The problem with OOP is that if you've never used it before, it takes a bit of time before it makes sense, then a little more time before you understand how all the practices (e.g. encapsulation/decoupling) fit together. You think I was running around preaching OOP when I started? It took me a long time to figure things out - you have the benefit of a forum where everyone can talk it to death ;).

I think what's missing from the thread, from a starter's perspective, is why we'd make certain choices. I bet throwing out "encapsulation" and "coupling" and "Registry Pattern" isn't helping here unless we explain it properly. For that you have my apologies.

If the Registry looks useful (I use it a lot, esp. in application code) I wrote an article for that pattern over on:
http://www.patternsforphp.com/wiki/Registry
Let me know if the article is useful.

To offer a concrete example. Your approach depends on whether the code needing the configuration is a) application specific or b) generally reusable. The difference is that application code tends to be less reusable between applications and so using "global" like constructs like functions, Singletons, and Registries have less of a maintenance cost. That's a rule of thumb rather than a hard fact. In such code, and using the PFP article code:

Code: Select all

// establish a bucket, i.e. Registry
$registry = new Scottayy_Registry;

// get your settings, class just uses parse_ini_file and some setters/getters
// quite a simple class to write - or steal Zend_Config 
$settings = new Scottayy_Settings_Ini;
$settings->import('./path/to/settings.ini');

// Add settings to Registry
$registry->settings = $settings;
The above can be placed in a central include file so that it's loaded very early for every request to the app. I call this sort of setup script a "bootstrap" file.

Next is getting classes to access the Settings. Say you have a class to create a database connections (whether an abstraction layer, or Model, or whatever:

Code: Select all

class Scottayy_Database
{

    protected $_settings = null;
    protected $_conn = null;

    public function __construct() 
    {
        $registry = Scottayy_Registry::getInstance(); // Singleton Registry
        $this->_settings = $registry->settings;
    }

    public function connect() 
    {
        $host = $this->_settings->host;
        $dbname = $this->_settings->dbname;
        $user = $this->_settings->user;
        $pass = $this->_settings->password;
        $this->_conn = $dbh = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    }

    // ...
}
Now if you wanted to get even more concerned, when you're more comfortable with OOP. You could note that the Database class is coupled to the Registry class. What this means is asking the question - what if I reuse Database in an application where there's no Registry? With one class, you could do a little edit maybe, but usually you could have numerous classes all referring to the same class - that's a lot of editing, and there's no way of maintaining a single central version. This is a bad for a few reasons - if you fix a bug in one edited version, you have to fix it manually in all the others.

One way of escaping this, is using the Factory Pattern. Which you can read about here:
http://www.patternsforphp.com/wiki/Factory

What this does, is make a little shuffle and move the Registry reference in Scottayy_Database to a separate class. I'll show the potential Database code first:

Code: Select all

class Scottayy_Database
{

    protected $_settings = null;
    protected $_conn = null;

    public function __construct(stdClass $settings) 
    {
        $this->_settings = $settings;
    }

    public function connect() 
    {
        $host = $this->_settings->host;
        $dbname = $this->_settings->dbname;
        $user = $this->_settings->user;
        $pass = $this->_settings->password;
        $this->_conn = $dbh = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    }

    // ...
}
The difference here is that Database now has no clue Registry exists (we can move it to new apps safely that have no Registry). Furthermore, the interface has become more general - using a stdClass for settings input. Pretty easy to play with anywhere you reuse it. In fact, this all lets us introduce a class to create Databases called Scottayy_Database_Factory. Bear with me...:)

Code: Select all

class Scottayy_Database_Factory
{

    public static function createInstance() 
    {
        $args = func_get_args(); // be flexible in how you get input
        if ($args[0] instanceof Scottayy_Registry) {
            $stdInput = self::_createFromScottayyRegistry($args[0]);
        }
        $db = new Scottayy_Database($stdInput);
        return $db;
    }

    protected static function _createFromScottayyRegistry(Scottayy_Registry $registry) 
    {
        $stdInput = new stdClass;
        $settings = $registry->settings;
        $stdInput->host = $settings->host;
        $stdInput->dbname = $settings->dbname;
        $stdInput->user = $settings->user;
        $stdInput->password = $settings->password;
        return $stdInput;
    }
}
The point of the Factory is to manage creating Database classes. Here it allows for a Registry. In another app, you could add support for an array, or a different Registry type, or single value params, or... Without a Factory to play with, you'd need to edit the Database class itself all the time - a Factory is a much smaller, easy to design ad-hoc as needed.

Our previous bootstrap code could be amended (if we include database conn here) to:

Code: Select all

// establish a bucket, i.e. Registry
$registry = new Scottayy_Registry;

// get your settings, class just uses parse_ini_file and some setters/getters
// quite a simple class to write - or steal Zend_Config 
$settings = new Scottayy_Settings_Ini;
$settings->import('./path/to/settings.ini');

// Add settings to Registry
$registry->settings = $settings;

// create a database connection object (and add to Registry for fun)
$database = Scottayy_Database_Factory::createInstance($registry);
$registry->database = $database;
Note. Most of the time I use OO syntax for Registries/Settings. You could implement both using an array style access method (e.g. $registry['settings'] instead of $registry->settings).

Is this a bit more informative and what would be useful to you?
User avatar
kyberfabrikken
Forum Commoner
Posts: 84
Joined: Tue Jul 20, 2004 10:27 am

Post by kyberfabrikken »

nathanr wrote:well yeah..

Code: Select all

<?php
class test {
	public function c($var1, $var2, $var4, $var5, $var8, $var9, $var11, $var13) {
		print_r(get_defined_vars());
	}
}
?>
If you have a function with that many parameters, it's probably ripe for refactoring. Good candidates could be to combine related parameters into parameter objects or splitting the method up.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Hey Maugrim, where were you when I was learning OOP ;)
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Maugrim, that was one of the absolute best posts I've ever read. Thank you for taking the time to spell some things out to me. I'm actually going to copy that to a text file and look at it when I'm writing this new application i'm working on.

It turns out I've already had a "registry" of sorts. I included a central file where i loaded things that I used most often.. so that's pretty cool. I just did it informally, and didn't know what the terminology for it was called.

Thanks a lot!

You and JCart have helped me immensely.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Ok, so basically the concept of a registry is to load objects that are oft-needed early and centrally. So theoretically (and probably realistically for me at first) a registry is no more than:

Code: Select all

<?php

class registry
{
	private $_vars = array();
	
	public functin __set($var, $value)
	{
		$this->_vars[$var] = $value;
	}
	
	public function __get($var)
	{
		if (isset($this->_vars[$var]))
		{
			return $this->_vars[$var];
		}
	}
}

$registry = new registry;
$registry->db = db::getInstance();
$registry->config = new config;

?>
This brings about a couple questions (sorry). One, where is this required to be included site-wide? Do I hard code require 'registry.php'; into every page? Probably not, but I think this question will lead into a topic of design patterns (is that a separate issue than learning OOP?). Usually I have a header file that does my includes when I do it procedurally. I could do that in an OOP fashion, but that would feel more like procedural OOP (if that makes sense). Another would be just have index.php be the main controller and require it in there.

Another question would be, do application specific objects belong in the registry? Say I use a $user object in many, many places throughout my application. This user object is specific to my application only. It doesn't seem "right" to load that in the registry with such things as the database and configuration objects.

In this function posted by Maugrim:

Code: Select all

public function __construct()
    {
        $registry = Scottayy_Registry::getInstance(); // Singleton Registry
        $this->_settings = $registry->settings;
    }
How is Scottayy_Registry available in the scope of that object?

And finally, it seems what I want to do is create a set of base files that allow me to create other application specific objects that perhaps extend or implement those base files.. am I right? That sounds a bit like a framework!
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

scottayy wrote:

Code: Select all

<?php

class registry
{
	private $_vars = array();
	
	public functin __set($var, $value)
	{
		$this->_vars[$var] = $value;
	}
	
	public function __get($var)
	{
		if (isset($this->_vars[$var]))
		{
			return $this->_vars[$var];
		}
	}
}

$registry = new registry;
$registry->db = db::getInstance();
$registry->config = new config;

?>
Indeed, yes that is a simple registry.
This brings about a couple questions (sorry).


No need to be sorry! Let me know if any of the terminology is not understood.
One, where is this required to be included site-wide? Do I hard code require 'registry.php'; into every page? Probably not, but I think this question will lead into a topic of design patterns (is that a separate issue than learning OOP?). Usually I have a header file that does my includes when I do it procedurally. I could do that in an OOP fashion, but that would feel more like procedural OOP (if that makes sense). Another would be just have index.php be the main controller and require it in there.
I take it that you are some a page-controller'esk pattern, where you have pages like login.php, members.php etc --- the opposite of a front controller where all your requests are routed through a single entry point (i.e., index.php?page=login). I havn't used page controllers in many years so I'm not exactly sure how you would implement this. However, like the front controller you need to initialize many components on every request. Like many frameworks, there is a bootstrap file to initialize things such as the registry, database connection, session handlers, etc, etc. To answer your question the registry should generally be initialized as early as possible.
Another question would be, do application specific objects belong in the registry? Say I use a $user object in many, many places throughout my application. This user object is specific to my application only. It doesn't seem "right" to load that in the registry with such things as the database and configuration objects.
You can put anything in the registry you want. I'm trying to think of any examples where I put application specific stuff in my registry but I seriously think I don't ever. However, if you did I wouldn't hold you accountable for murder :)

In this function posted by Maugrim:

Code: Select all

public function __construct()
    {
        $registry = Scottayy_Registry::getInstance(); // Singleton Registry
        $this->_settings = $registry->settings;
    }
How is Scottayy_Registry available in the scope of that object?
Once the class has been loaded (included or required), it belongs to the global namespace. The object itself only exists in the scope of where it was initialized, however the namespace is global therefore can be statically called globally (anywhere). Make sure you understand the difference between a singleton registry and a registry though.
And finally, it seems what I want to do is create a set of base files that allow me to create other application specific objects that perhaps extend or implement those base files.. am I right? That sounds a bit like a framework!
Yes, you are on a long path... but there is light at the end of the tunnel. Frameworks are certainly one of the best learning experiences ever. One of the reasons I have joined arborint's open source framework and we have begun developing it :). Fun stuff indeed.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Jcart wrote:
scottayy wrote:

Code: Select all

<?php

class registry
{
	private $_vars = array();
	
	public functin __set($var, $value)
	{
		$this->_vars[$var] = $value;
	}
	
	public function __get($var)
	{
		if (isset($this->_vars[$var]))
		{
			return $this->_vars[$var];
		}
	}
}

$registry = new registry;
$registry->db = db::getInstance();
$registry->config = new config;

?>
Indeed, yes that is a simple registry.
:) I'm good! :P I know it's bad practice to have class members public, but the way I used the __get() and __set() magic methods, it would be the same as having the member public, right?


JCart wrote:
I take it that you are some a page-controller'esk pattern, where you have pages like login.php, members.php etc --- the opposite of a front controller where all your requests are routed through a single entry point (i.e., index.php?page=login). I havn't used page controllers in many years so I'm not exactly sure how you would implement this. However, like the front controller you need to initialize many components on every request. Like many frameworks, there is a bootstrap file to initialize things such as the registry, database connection, session handlers, etc, etc. To answer your question the registry should generally be initialized as early as possible.
Yeah, man. In all of my projects in the past I would have a header.php file, a footer.php file. And say, login.php would require 'header.php'; //content here require 'footer.php to produce the full page.

I'm beginning to see the advantage of having a single entry point. Based on the request ($_GET) I can load appropriate controllers and send actions off to be processed. I'll have to read more on the MVC design pattern.. right now I'm a bit confused about the tasks of the Model.
JCart wrote: In this function posted by Maugrim:
scottayy wrote:

Code: Select all

public function __construct()
    {
        $registry = Scottayy_Registry::getInstance(); // Singleton Registry
        $this->_settings = $registry->settings;
    }
How is Scottayy_Registry available in the scope of that object?
Once the class has been loaded (included or required), it belongs to the global namespace. The object itself only exists in the scope of where it was initialized, however the namespace is global therefore can be statically called globally (anywhere). Make sure you understand the difference between a singleton registry and a registry though.
Yeah, I see that now. Actually, I don't see a reason why the registry wouldn't be a singleton. Why would any application need more than one instance of a registry? I can see the point in having two instances of say, a database wrapper, but.. the registry could just hold both of those and still be a singleton.
jCart wrote:Yes, you are on a long path... but there is light at the end of the tunnel. Frameworks are certainly one of the best learning experiences ever. One of the reasons I have joined arborint's open source framework and we have begun developing it :). Fun stuff indeed.
I'd still love to poke around at that code... :P But yeah, the terms are coming along now. And I think that's a good portion of understanding what's going on. I realized that I was doing many things already that I didn't know what they were called. It's actually becoming sort of fun!
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Probably my note of caution is to be wary of the term "framework". You are correct that this all leads up to a framework, but a framework can mean a simple library of reusable structural components (like a Registry, Database, Settings collection of classes) all the way up to a fully integrated and highly complex structure like Symfony/CakePHP.

You'll find in OOP, that frameworks and libraries become a big deal. Since OOP moves you to create general solutions, they are widely applicable. The more classes you write, and the more you pick up practices, experiences and ideas, the more you'll find yourself organising code into framework-like collections. I know from experience this looks horribly complex for your future - but once you take that path you'll find a lot of enjoyment from approaching a new project with a mass of code to easily build on.

Let's say you write Scottayy_Settings and Scottayy_Database for real. If they solve the problem you're targeting, then it's solved - no need to persistently resolve for each new project - just dig out the centralised version from subversion/cvs and away you go. There's a Scottayy_* for every occasion ;). This is precisely why nearly all OOP developers have a few "framework" like collections in their closet. I have one which I still maintain called Partholan (it's no longer maintained open source since its target open source project has since lapsed), arborint and few others are working on another, etc. I'd go so far as to say, developers are notorious for migrating from one framework to another always searching for the "silver bullet" framework, and nearly always retaining their own personalised collections to draw on. Even Ruby On Rails users are currently grabbing hold of Merb attracted by it's (about time) alternative approach.

I definitely agree that even mere weeks spent idling on a personal framework is invaluable. Frameworks face all the OOP problems imaginable and get you thinking, making mistakes (mistakes are good!), and learning. As I said before, you're luckier than me - this forum has hard case OOP adopters who can offer you tons of advice.

Back to the topic:
Ok, so basically the concept of a registry is to load objects that are oft-needed early and centrally. So theoretically (and probably realistically for me at first) a registry is no more than:
The registry, like many patterns, is a general solution. It's applicable anywhere you need to pass around >1 objects without handling them individually. It's most public in the sense you describe since it's user accessible - but I use registries internally in libraries in a similar fashion. The concept, as you gather, is simple - I just call it a "bucket" most of the time since everyone knows what a bucket is good for ;).

You could feasibly have two or more buckets if you feel keeping types of objects separate is important. This is more difficult with a Singleton Registry, since that design assumes you only want one Registry. (Here's an example where a new need exposes a weakness in an otherwise sound design). The API use case: Scottayy_Registry::getInstance('Application') may suggest a solution to support multiple Singleton Registries.

Free tip in designing OOP classes - the secret is finding an API you like. Then mangle the code to make that API a reality. It's a good habit - if you learn Unit Testing in the future it will save you hours of head scratching. A test afterall is really just a use case.
This brings about a couple questions (sorry). One, where is this required to be included site-wide? Do I hard code require 'registry.php'; into every page?
In applications applying a Front Controller Pattern, it's included in either index.php, or a file included by index.php. In a page-by-page design it needs either a hard-coded common include, or you can also utilise auto_prepend_file in php.ini or .htaccess.
How is Scottayy_Registry available in the scope of that object?
The :: operator calls static methods - simply put, procedural functions defined in a class. In PHP5, such methods are marked using the "static" keyword (you can't use the normal -> on static methods). It's a useful feature where a class has one or more methods that are useful for setup tasks, or easy retrieval tasks. It's just a good idea to use them very sparingly.
And finally, it seems what I want to do is create a set of base files that allow me to create other application specific objects that perhaps extend or implement those base files.. am I right?
You're getting there :). Applications are irksome things. Every application has a set of functionality shared by most others - database, settings, templating, models, external libs, etc. One of the reasons OOP is popular, is that it can reduce those to a general set of reusable components. Then applications are like icing - underneath is the hard work that took a few hours to bake to perfection, the icing the actual features you get paid for which extend, use, depend on the cake. I feel like I'm robbing the CakePHP desert wholesale...

Back in a previous mention. In my apps I could have a hierarchy (not necessarily inheritance classes) of:

PDO->Database->Model_Base->User. Only the User differs between apps, the other three are usually the same, or variants of. It may be a poor example in a sense - how objects relate to database rows and tables is a massive point of debate among developers. Some prefer simple ActiveRecord (1 object = 1 row) solutions, others full-blown ORM (many objects <=> many database rows/tables). Save researching this one for another day ;).
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Off for the night, just want to mention you managed to vary the cases in my name "Jcart" differently in the 3/3 times I was quoted.

Jcart, JCart, jCart.. :)
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Sorry about that. :)

I just drew up the mock beginnings of a little bit of nothing, implementing what I've learned in this topic.

Code: Select all

<?php

class registry
{
	private $_vars = array();
	static private $_instance = null;
	
	static public function getInstance()
	{
		if (self::$_instance === null)
		{
			self::$_instance = new registry();
		}
		
		return self::$_instance;
	}
	
	public function __set($var, $value)
	{
		if (!isset($this->_vars[$var]))
		{
			$this->_vars[$var] = $value;
		}
	}
	
	public function __get($var)
	{
		if (isset($this->_vars[$var]))
		{
			return $this->_vars[$var];
		}
	}
	
	public function __unset($var)
	{
		if (isset($this->_vars[$var]))
		{
			unset($this->_vars[$var]);
		}
	}
}

class config
{
	private $_settings = array();
	
	public function __construct($ini)
	{
		if (file_exists($ini))
		{
			$this->_settings = parse_ini_file($ini);
		}	
	}
	
	public function __get($setting)
	{
		if (isset($this->_settings[$setting]))
		{
			return $this->_setttings[$setting];
		}
	}
	
	public function __set($setting, $value)
	{
		$this->_settings[$setting] = $value;
	}
}

class db
{
	private $_registry;
	
	public function __construct()
	{
		$this->_registry = registry::getInstance();
	}
	
	public function connect()
	{
		return mysql_connect(
			$this->_registry->config->dbhost,
			$this->_registry->config->dbusername,
			$this->_registry->config->dbpassword
		);
	}
	
	public function insert($table, $data)
	{
		return $this->query(
			"INSERT INTO $table (`" . implode('`, `', array_keys($data)) . "`) VALUES ('" . implode('\', \'', $data) . "')"
		);
	}
	
	public function query($sql)
	{
		return mysql_query($sql);
	}
}

//load the registry
$registry = registry::getInstance();

//register the configuration file
$registry->config = new config('/config.ini');

//we'll just set a few settings for the purpose of setting them
$registry->config->dbhost = 'localhost';
$registry->config->dbusername = 'test';
$registry->config->dbpassword = 'password';

//register the database
$registry->db = new db();

//now I can access the database object or config object from anywhere
//whether it be here, or in another object
$registry->db->insert('users', array('username' => 'scott', 'password' => hash('sha256', 'password')));
$mySetting = $registry->config->mySetting;
Looks good?
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

I'm not sold on why you still prefer a singleton registry. Singletons are much more difficult to test and from experience they are generally indicators of needing refactoring.

Like I said it is dificult to test singletons, and for that reason alone I avoid them at all costs. When using singletons you are essentially using globals, which I'm sure you know is frowned upon by many. By passing the registry or even just any object as a parameter you are making the relationship much more clear as well.

From the code you've demonstrated below, I'm a bit confused on why you even need a registry at this point. I wouldn't recommend using a registry for the sake of using one, but moving on -- this is a learning experience after all. Your example may be simplified, but in that case it would make much more sense to simply pass the components around as is -- KISS. Even still, what many refer to as bootstrapping (which is the loading of all the initial components ie. config, front controller or whatever) typically are passed around normally as parameters, and then the components are then thrown into the registry to uses elsewhere. This will save you a bit of overhead as well.

Using your example,

Code: Select all

$registry = new registry();

$config = new config('/config.ini');
$config = $config->loadContents(); //replace the configuration object with file contents, array accesor, stdClass, whatever.
$database = new db($config);

$registry->config = $config;
$registry->database = $database;
My rule of thumb is only pass a object the registry itself if it depends on more than one object. So if our target object is only reliant on the configuration object, then we will only give it that and keep things as clean as possible.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

The main point I see on scottayy's code is to use a DB factory - keep the DB class itself unaware of the Registry. Where Jenk is concerned about testing, this leaves DB itself free of any singleton references, and opens a constructor parameter to inject mocked settings when writing unit tests.

Essentially, Registries are then loosely coupled to generic code, but can be referred to more openly in application specific code where it's used by factories, or page controller code. I don't think there's a lot more decoupling possible until you move towards an MVC approach (when you get there a Registry can be partially replaced with another design idea that let's the Front Controller handle object injection into controllers/actions without needing Singletons.
thinsoldier
Forum Contributor
Posts: 367
Joined: Fri Jul 20, 2007 11:29 am
Contact:

Re: configuration variables passed to objects

Post by thinsoldier »

I thought it was more likely you'd use multiple different registries and less likely you'd need more than 1 config? So why is your registry a singleton and your config not?

Your config looks like it could just extend your registry, just adding the ini parsing __construct.

You should consider using PDO or something in your DB for flexibility in the future...(or so i've been told a dozen times). Your db would then only consist of creating a pdo object and telling it the connection user/pass from the registry info.

^ any of that sound like I know what I'm talking about?

This has to be one of the best threads I've ever read. Thanks everybody.
Warning: I have no idea what I'm talking about.
Post Reply