Service Locator
Moderator: General Moderators
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Here's the list again:
- Be able to register a list of parameters to pass to the constructor (we may need eval() for that) DONE
- Be able to specify that a parameter is another "registered" object that we get() an instance of to pass as a parameter (think of a Model object that need a DB connection object passed to its constructor)
- Allow get() to get objects as singletons or multiple instances
- Allow instances to be accessed by a name that is not the class name. DONE
- Have the class name to file name mapping be configurable when you create the Service Locator (not everyone likes your naming style ) DONE
- Keeping Lazy Loading so that if we never get() the class it is never instansiated (say an error redirect occurs before the get()). DONE
- Make it so it is easy to use as __autoload() or spl_autoload().
- How does PHP5 make our job easier or the interface nicer?
- Do we want to add a settable list of paths that get() will search -- like Todd_Z does as an option?
- Be able to register a list of parameters to pass to the constructor (we may need eval() for that) DONE
- Be able to specify that a parameter is another "registered" object that we get() an instance of to pass as a parameter (think of a Model object that need a DB connection object passed to its constructor)
- Allow get() to get objects as singletons or multiple instances
- Allow instances to be accessed by a name that is not the class name. DONE
- Have the class name to file name mapping be configurable when you create the Service Locator (not everyone likes your naming style ) DONE
- Keeping Lazy Loading so that if we never get() the class it is never instansiated (say an error redirect occurs before the get()). DONE
- Make it so it is easy to use as __autoload() or spl_autoload().
- How does PHP5 make our job easier or the interface nicer?
- Do we want to add a settable list of paths that get() will search -- like Todd_Z does as an option?
(#10850)
It can do thisarborint wrote:- Be able to specify that a parameter is another "registered" object that we get() an instance of to pass as a parameter (think of a Model object that need a DB connection object passed to its constructor)
I am working on this... if anybody has any suggestions, please let me know because I am struggling a littlearborint wrote:- Allow get() to get objects as singletons or multiple instances
how would I know if it is easy as those?arborint wrote:- Make it so it is easy to use as __autoload() or spl_autoload().
Uhh....arborint wrote:- How does PHP5 make our job easier or the interface nicer?
Where is this example?arborint wrote:- Do we want to add a settable list of paths that get() will search -- like Todd_Z does as an option?
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Well where is Todd_Z to say adding this feature is worth doing. I had the comments about it here. I think that resolving PEAR style class names would be a better addition.
(#10850)
For some reason, if you get() from the registry more than once, it throws this error...
Fatal error: Cannot use object of type MysqlConnect as array in c:\wamp\www\module_admin\classes\Registry.inc.php on line 80
Code: Select all
<?php
class Registry{
private $objects;
private $path;
private $prefix;
private $suffix;
function __construct($path='/', $suffix='.inc.php', $prefix='') {
// Path to classes
$this->path = $path;
// Allows for classes to be searched out by a prefix such as ClassSession.inc.php (Where Class is the prefix)
$this->prefix = $prefix;
// Allows for classes to be suffixed and have any allowed extension
$this->suffix = $suffix;
}
private function instantiate($class, $name) {
if(!is_object($name)){
$arglist = array();
if (isset($this->objects[$name]['params'])) { // there are args
$n = count($this->objects[$name]['params']);
for ($i=0; $i<$n; ++$i) {
$arglist[$i] = "\$this->objects['$name']['params'][$i]";
}
}
//ob_start(); // This doesn't prevent jack, does it?
eval ("\$object = new " . "\$class(" . implode(', ', $arglist) . ');');
//ob_end_clean();
// Unset temporary params and class name as they are no longer needed
unset($this->objects[$name]['class']);
unset($this->objects[$name]['params']);
// Store object in registry
$this->objects[$name] = $object;
}
return $object;
}
public function register($class, $name=null /*, $arg1, $arg2... */){
// If object name is not set, object is named the same as it's class
if(is_null($name)) $name = $class;
// Store object's classname and params temporarily (if class hasn't already been registered)
if(!isset($this->objects[$name])){
$params = func_get_args();
array_shift($params); // Remove $name from param list
array_shift($params); // Remove $class from param list
// Register params
$this->objects[$name]['params'] = $params;
$this->objects[$name]['class'] = $class;
}
}
// TODO: Make child classes autoload their parent before instantiation
private function load($class){
if (! class_exists($class)) {
include($this->path . $this->prefix . $class . $this->suffix);
}
}
public function get($name){
// Load the class file
$this->load($this->objects[$name]['class']); // LINE 80
// Instantiate class
$object = $this->instantiate($this->objects[$name]['class'], $name);
// Return object id
return $object;
}
}
?>It seems odd to me that you use $this->objects[$name] in multiple ways.
Sometimes you are storing $this->objects[$name]['class'] and $this->objects[$name]['params'], but then you later override this and just store an object. Your error is probably from going back later and expecting the 'class' and 'params' stuff to be there.
It seems like you should create $this->meta as an array for the metadata about the objects you are storing, and keep $this->objects purely for the objects themselves.b
Sometimes you are storing $this->objects[$name]['class'] and $this->objects[$name]['params'], but then you later override this and just store an object. Your error is probably from going back later and expecting the 'class' and 'params' stuff to be there.
It seems like you should create $this->meta as an array for the metadata about the objects you are storing, and keep $this->objects purely for the objects themselves.b
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Jason is right. Perhaps it would be clearer if we created a little class just to hold these bits of information about a class/file/object. An object might also allows us to add other features more easily. Something as simple as:
Code: Select all
class Registry_Entry {
var $class;
var $name;
var $path;
var $params;
}(#10850)
I went ahead and just used a seperate array for now... I am considering using a seperate class for the entries though... I just can't think of why I would need to. What other features are there? Here it is...
Code: Select all
<?php
class Registry{
private $objects;
private $entries;
private $path;
private $prefix;
private $suffix;
function __construct($path='/', $suffix='.inc.php', $prefix='') {
// Path to classes
$this->path = $path;
// Allows for classes to be searched out by a prefix such as ClassSession.inc.php (Where Class is the prefix)
$this->prefix = $prefix;
// Allows for classes to be suffixed and have any allowed extension
$this->suffix = $suffix;
}
private function instantiate($class, $name) {
if(!is_object($name)){
$arglist = array();
if (isset($this->entries[$name]['params'])) { // there are args
$n = count($this->entries[$name]['params']);
for ($i=0; $i<$n; ++$i) {
$arglist[$i] = "\$this->entries['$name']['params'][$i]";
}
}
//ob_start(); // This doesn't prevent jack, does it?
eval ("\$object = new " . "\$class(" . implode(', ', $arglist) . ');');
//ob_end_clean();
// Unset registry entry as it is no longer needed
unset($this->entries[$name]);
// Store object in registry
$this->objects[$name] = $object;
}
return $object;
}
public function register($class, $name=null /*, $arg1, $arg2... */){
// If object name is not set, object is named the same as it's class
if(is_null($name)) $name = $class;
// Store object's classname and params temporarily (if class hasn't already been registered)
if(!isset($this->entries[$name])){
$params = func_get_args();
array_shift($params); // Remove $name from param list
array_shift($params); // Remove $class from param list
// Register params
$this->entries[$name]['params'] = $params;
$this->entries[$name]['class'] = $class;
}
}
// TODO: Make child classes autoload their parent before instantiation
private function load($class){
if (! class_exists($class)) {
include($this->path . $this->prefix . $class . $this->suffix);
}
}
public function get($name){
// If object has already been stored
if(isset($this->objects[$name]) && is_object($this->objects[$name])){
// Set return value to stored object
$object = $this->objects[$name];
}
else{
// Load the class file
$this->load($this->entries[$name]['class']);
// Instantiate class
$object = $this->instantiate($this->entries[$name]['class'], $name);
}
// Return object id
return $object;
}
}
?>This isn't meant to be a bump, just posting my newest version of this: I used an array for entries and I added the setParams method so you don't have to specify the parameters in the construct if you don't want to. I also set a $path parameter in the get method so you can specify what directory to get the class from (if not the one set in the construct)
Code: Select all
<?php
class Registry{
private $objects;
private $entries;
private $path;
private $prefix;
private $suffix;
function __construct($path='/', $suffix='.inc.php', $prefix='') {
// Path to classes
$this->path = $path;
// Allows for classes to be searched out by a prefix such as ClassSession.inc.php (Where Class is the prefix)
$this->prefix = $prefix;
// Allows for classes to be suffixed and have any allowed extension
$this->suffix = $suffix;
}
private function instantiate($class, $name) {
if(!is_object($name)){
$arglist = array();
if (isset($this->entries[$name]['params'])) { // there are args
$n = count($this->entries[$name]['params']);
for ($i=0; $i<$n; ++$i) {
$arglist[$i] = "\$this->entries['$name']['params'][$i]";
}
}
//ob_start(); // This doesn't prevent jack, does it?
eval ("\$object = new " . "\$class(" . implode(', ', $arglist) . ');');
//ob_end_clean();
// Unset registry entry as it is no longer needed
unset($this->entries[$name]);
// Store object in registry
$this->objects[$name] = $object;
}
return $object;
}
public function register($class, $name=null /*, $arg1, $arg2... */){
// If object name is not set, object is named the same as it's class
if(is_null($name)) $name = $class;
// Store object's classname and params temporarily (if class hasn't already been registered)
if(!isset($this->entries[$name])){
$params = func_get_args();
array_shift($params); // Remove $name from param list
array_shift($params); // Remove $class from param list
// Register params
$this->entries[$name]['params'] = $params;
$this->entries[$name]['class'] = $class;
}
}
// TODO: Make child classes autoload their parent before instantiation
private function load($class, $path, $suffix, $prefix){
$path = $path ? $path : $this->path;
$suffix = $suffix ? $suffix : $this->suffix;
$prefix = $prefix ? $prefix : $this->prefix;
if (! class_exists($class)) {
require_once($path . $prefix . $class . $suffix);
}
}
public function setParams($name, $params){
if(isset($this->entries[$name]) && !is_object($this->objects[$name])) $this->entries[$name]['params'] = $params;
}
public function get($name, $path=false, $suffix=false, $prefix=false){
// If object has already been stored
if(isset($this->objects[$name]) && is_object($this->objects[$name])){
// Set return value to stored object
$object = $this->objects[$name];
}
else{
if(!isset($this->entries[$name])){
$this->register($name);
}
// Load the class file
$this->load($this->entries[$name]['class'], $path, $suffix, $prefix);
// Instantiate class
$object = $this->instantiate($this->entries[$name]['class'], $name);
}
// Return object id
return $object;
}
}
?>Just started re-writing a lot of my code the other day, and this reminded me of the Service Locator I was struggling with a while back.
I remember bashing my head to the desk a lot when figuring out a way to pass eventual parameters to the requested classes, and never really got too happy with the result. I just go stuck with my setArgs-method.
I know my solution is crap, but sometimes you get ideas just from looking at someone elses code. So here is my current Service Locator:
Keep up the great work! Can't wait to see the final result
I remember bashing my head to the desk a lot when figuring out a way to pass eventual parameters to the requested classes, and never really got too happy with the result. I just go stuck with my setArgs-method.
I know my solution is crap, but sometimes you get ideas just from looking at someone elses code. So here is my current Service Locator:
Code: Select all
<?php
class Locator
{
private $objects = array();
private $args = array();
// Empty construct
public function __construct()
{
}
// Add module to array
public function set($handle,&$class)
{
$this->objects[$handle] =& $class;
}
// Set arguments
public function setArgs($handle)
{
$args = array();
if(func_num_args()>1)
{
for($i=1;$i<func_num_args();$i++)
{
$args[$i-1] = func_get_arg($i);
}
}
$this->args[$handle] = $args;
}
// Fetch arguments
public function getArgs($handle)
{
return $this->args[$handle];
}
// Fetch module from array
public function get($handle,$args)
{
// Check if the requested object exists
if(!isset($this->objects[$handle]))
{
if(!isset($this->objects[$handle]))
{
if(!class_exists($handle))
{
@include_once($handle . '.php');
}
if(class_exists($handle))
{
// If the module has any arguments, pass them as well
if(isset($this->args[$handle]))
{
if(count($this->args[$handle])==1)
{
$arg = $this->args[$handle][0];
$this->objects[$handle] =& new $handle($arg);
}
else
{
$this->objects[$handle] =& new $handle($this->args[$handle]);
}
}
else
{
$this->objects[$handle] =& new $handle();
}
}
}
}
return $this->objects[$handle];
}
}
?>Keep up the great work! Can't wait to see the final result