Jul.25

Global Factories and Instance Organization

The hardest part of building out a framework in PHP is understanding how your design patterns will play together. All your code should be accessible by any part of your other code void of any knowledge of the workings of permissions. There are many good ways to accomplish this task, but there are plenty of bad ways too.

I have waded muck deep in spaghetti code of $GLOBALS to know that is not a good method. I have played with hooks and complex tree modules to know they can get confusing and bogged down with maintenance easily.

The perfect solution? The brilliant people behind PHP built Autoloading in version 5+.

Autoloading is a simple way of testing the existence of a function/singleton/object during runtime. This can alliviate any complex bootstrapping of your framework may need.

function __autoload($className) {
	$extensions = array(".php", ".class.php", ".inc");
	$paths = explode(PATH_SEPARATOR, get_include_path());
	$className = str_replace("_" , DIRECTORY_SEPARATOR, $className);
	foreach ($paths as $path) {
		$filename = $path . DIRECTORY_SEPARATOR . $className;
		foreach ($extensions as $ext) {
			if (is_readable($filename . $ext)) {
				require_once $filename . $ext;
				return;
				break;
			}
		}
	}
}

The above autoloading function will look for methods during runtime, so we have no need to include them directly into our source code. Adding directories to our path will allow our autoloader to know where to look for these unincluded methods. Per saw we keep our libs in ‘./libs’ then we could tell the autoloader to look there by saying :


set_include_path( get_include_path() . PATH_SEPARATOR . __DIR__ . PATH_SEPARATOR . __DIR__ . DIRECTORY_SEPARATOR . "libs" );

Now we can create a file named ‘MyFactory.php’ inside our ‘libs’ directory. When we try to access the static singleton class of MyFactory. this file will be automagically included into our code at runtime.

We can take it one step further and construct our framework with mostly Factory or Generator patterns. These would be functions or singletons that have globally accessible methods to create objects needed in our framework.

We can take it even further and enforce these factory singletons to keep track and organize their own spawned objects.

Here is a quick bit of code to do exactly that :

class MyFactory {
		static $instances;
		static $current_instance='default';

		public $base=NULL;

		static function instance($name=NULL,$base=NULL) {
			if (is_null($name)) {
				$name = self::$current_instance;
			} else {
				self::$current_instance = $name;
			}

			if ( ! isset( self::$instances[$name] )) {
				self::$instances[$name] = new self($base);
			}

			return self::$instances[$name];
		}

		function __construct($base=NULL) {
			$this->base = $base;
		}
}

This controls the singleton factory and organization part of our objects. We could then generate objects as such :

$myFactory = MyFactory::instance('new','New Base 1');
$otherFactory = MyFactory::instance('newer','Newer Base 2');

This would simple replace :

$myFactory = new MyFactory('New Base 1');
$otherFactory = new MyFactory('Newer Base 2');

But why replace it? And why store our instances in our singleton? Portability inside your own framework is why. While $myFactory was created in on part of the codebase, we can access it not by using globals, but by re-instancing it from the factory design singleton as such :

$thatOne = MyFactory::instance('newer');
echo $thatOne->base;
$stillMyFactory = MyFactory::instance('new');
echo $stillMyFactory->base;
$evenYetStill = MyFactory::instance();
echo $evenYetStill->base;

The end result would be : “Newer Base 2New Base 1New Base 1”

This method also works when written fluently :

echo MyFactory::instance('newer')->base;

Would result in ‘Newer Base 2’

This works well to make any part of your code-base more portable such as Templates, Cache controllers, even DB handlers are keep portable and organized.

Programming,PHP

Leave a comment