Slowly but surely my framework is coming off the ground. I decided to adopt a singleton design pattern for my Cache, Template, and possibly any other engines I might have. Originally my design started as a factory, however it is now somewhat of a hybrid as there is a ::load() function that loads arbitrary classes into our singleton (rather than an instance of the same class like a proper singleton).
I wanted the system pluggable so the developer was not beholden to any particular engine, but I also needed to guarantee access to the Cache and Template objects to the models without having to either add to the $GLOBALS[] array or add to a controller’s attribute (and call like $controller->cache->…).
So I adopted a singleton design pattern, because (a) one only needs 1 cache or template object and (b) it just looks plain nice.
For example, lets look at our cache object. I modeled the interface for the cache object around the APC naming conventions since I will be using APC myself, I wanted to retain all of APC’s functionality without locking any other developers down to making sure they have APC installed.
final class Cache
{
protected static $obj;
public static function load( $drivername )
{
App::include_framework("drivers.cache/class.".strtolower($drivername).".php");
$drivername .= "CacheEngine";
self::$obj = new $drivername();
}
//Add if not exists
public static function add( $key, $var, $life = 0 )
{
return self::$obj->add($key,$var,$life);
}
//Overwrite if exists
public static function store( $key, $var, $life = 0 )
{
return self::$obj->store($key,$var,$life);
}
public static function fetch( $key )
{
return self::$obj->fetch($key);
}
public static function delete( $key )
{
return self::$obj->delete($key);
}
public static function clear()
{
return self::$obj->clear();
}
}
Our boostrapping file (or any file where we initialize the Cache class) will have Cache::load('drivername'); statement, filling our static attribute with an instance of the proper Cache Engine, of which we have a nice pretty interface for:
interface CacheEngine
{
//Add if not exists
public function add( $key, $var, $life = 0 );
//Overwrite if exists
public function store( $key, $var, $life = 0 );
public function fetch( $key );
public function delete( $key );
public function clear();
}
From here on out in our code, all we have to do to access the application’s caching ability is call say Cache::store( ... );. What this results in is a completely transparent abstraction layer allowing for drop-in replacements without having to worry about managing object instances, etc.
As for now I’m working on how I want to manage DB connections and DB abstraction. I desire the framework to be flexible and handle multiple database connections should a developer so require, or at the very least not restrain to a single connection, HOWEVER I need to make things remain easy for developers only maintaining a single connection. Obviously my Singleton-Factory hybrid will not work here.
Then comes the fun of working out the ORM aspects of the models…