Posted: Sun May 28, 2006 9:40 am
Just to reference the near final version...
And unit tests...
Todo - cache files at each search location for quicker lookups (perf optimisation), remove AUTO from array foreach in get/exists/remove since it was checked already (perf optimisation), get/exists/remove are largely duplicated if..else conditions - maybe see if can be replaced (maybe).
Thanks for the input guys.
Code: Select all
<?php
class Partholan_ServiceLocator /* implements Partholan_iServiceLocator */ {
const SERVICE = 'service';
const FACTORY = 'factory';
const AUTO = 'auto';
private $store = array(
self::SERVICE => array(),
self::FACTORY => array(),
self::AUTO => array()
);
private static $instance = false;
private $searchLocations = array();
public function __construct() {
}
static function getInstance() {
if(!self::$instance)
{
self::$instance = new Partholan_ServiceLocator();
}
return self::$instance;
}
public function register($className, $instance, $classType = self::AUTO) {
if(is_object($instance) && strlen($className))
{
$this->store[$classType][$className] = $instance;
return true;
}
trigger_error('Instance could not be registered. Requires a valid Instance name and object.');
}
public function remove($className, $classType = self::AUTO) {
if($this->store[$classType][$className])
{
unset($this->store[$classType][$className]);
return true;
}
elseif($classType == self::AUTO)
{
foreach($this->store as $typeStore)
{
if($typeStore[$className])
{
unset($typeStore[$className]);
return true;
}
}
}
trigger_error('Instance to remove could not be located.', E_USER_NOTICE);
}
public function get($className, $classType = self::AUTO) {
if($this->store[$classType][$className])
{
return $this->store[$classType][$className];
}
elseif($classType == self::AUTO)
{
foreach($this->store as $typeStore)
{
if($typeStore[$className])
{
return $typeStore[$className];
}
}
}
if(class_exists($className))
{
$this->store[$classType][$className] = new $className();
return $this->store[$classType][$className];
}
$return = $this->_getFromFile($className, $classType);
if(!$return && $classType != self::FACTORY)
{
$return = $this->_getFromFactory($className, $classType);
}
if($return) return $return;
trigger_error('Unable to locate ' . $className . ' of type "' . $classType . '"', E_USER_ERROR);
}
public function addSearchLocations() {
foreach(func_get_args() as $directoryPath)
{
$this->searchLocations[] = $directoryPath;
}
}
public function exists($className, $classType = self::AUTO) {
if($this->store[$classType][$className])
{
return true;
}
elseif($classType == self::AUTO)
{
foreach($this->store as $typeStore)
{
if($typeStore[$className])
{
return true;
}
}
}
return false;
}
public function hasSearchLocation($directoryPath) {
if(in_array($directoryPath, $this->searchLocations))
{
return true;
}
return false;
}
private function _getFromFile($className, $classType) {
foreach($this->searchLocations as $location)
{
if(file_exists($location . DIRECTORY_SEPARATOR . $className . '.php'))
{
require_once($location . DIRECTORY_SEPARATOR . $className . '.php');
$this->store[$classType][$className] = new $className();
return $this->store[$classType][$className];
}
}
return false;
}
private function _getFromFactory($className, $classType) {
$factory = $this->get($className . 'Factory', self::FACTORY);
if($factory)
{
$this->store[$classType][$className = $factory->create();
return $this->store[$classType][$className];
}
return false;
}
}
?>Code: Select all
<?php
require_once(APPROOT . 'Partholan/Classes/ServiceLocator.php');
require_once('Partholan/EmptyObject.php');
class Test_ServiceLocator extends UnitTestCase {
public function __construct() {
$this->UnitTestCase('ServiceLocator Test');
}
public function setUp() { }
public function tearDown() { }
public function testRegistering() {
$sl = new Partholan_ServiceLocator();
$sl->register('EmptyObject', new EmptyObject(), Partholan_ServiceLocator::SERVICE);
$this->assertTrue($sl->exists('EmptyObject', Partholan_ServiceLocator::SERVICE));
$this->assertIsA($sl->get('EmptyObject', Partholan_ServiceLocator::SERVICE), 'EmptyObject');
}
public function testRemoving() {
$sl = new Partholan_ServiceLocator();
$sl->register('EmptyObject', new EmptyObject);
$sl->remove('EmptyObject');
$this->assertFalse($sl->exists('EmptyObject'));
}
public function testAddingSearchLocations() {
$sl = new Partholan_ServiceLocator();
$sl->addSearchLocations('/tmp', '/home');
$this->assertTrue($sl->hasSearchLocation('/tmp'));
$this->assertTrue($sl->hasSearchLocation('/home'));
}
// test: no class, no file included - get file and instantiate object
public function testGettingInstanceFromFile() {
$sl = new Partholan_ServiceLocator();
$sl->addSearchLocations(dirname(__FILE__)); // or location of EmptyObject2 class
$mk = $sl->get('EmptyObject2');
$this->assertIsA($mk, 'EmptyObject2');
$this->assertTrue($sl->exists('EmptyObject2'));
}
// test: class_exists() method where file pre-included()
public function testGettingInstance() {
$sl = new Partholan_ServiceLocator();
$mk = $sl->get('EmptyObject2');
$this->assertIsA($mk, 'EmptyObject2');
$this->assertTrue($sl->exists('EmptyObject2'));
}
}
?>Thanks for the input guys.