Go Back   SitePoint Forums > Forum Index > Program Your Site > PHP > PHP Application Design
Newsletter FAQ Members List Calendar Mark Forums Read

New to SitePoint Forums? Register here for free!

SitePoint Sponsor
 
Reply
 
Thread Tools Display Modes
Old Apr 23, 2005, 13:12   #1
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,273
The smallest dependency injector?

Hi.

As a result of an article for PHP|Architect I have finally gotten a chance to play with dependency injection and the result is called "Phemto". It's the bare minimum of a dependency injector as I understand it. I am sure that I have missed some vital feature that Jason, et al. will tell me about. It only works with PHP5.

Here is a sample piece of usage...
PHP Code:

class Datacash implements PaymentGateway { ... }


class
PayementInAdvance implements SignUpProcess {
    function
__construct(PaymentGateway $gateway) { ... }
    ...
}

$injector = new Injector();
$injector->register('Datacash');
$injector->register('PaymentInAdvance');

$sign_up = $injector->create('SignUpProcess');
That is the wiring can be done purely by interfaces and is handled by typed constructors. As usual when I post code on the forum, it's a plaything that anyone is welcome to carry forward. Idon't advise using it in the real world though .

yours, Marcus
Attached Files
File Type: txt phemto.tar.gz.txt (1.1 KB, 237 views)
lastcraft is offline   Reply With Quote
Old Apr 24, 2005, 11:17   #2
Selkirk
SitePoint Guru
 
Join Date: Nov 2002
Posts: 848
So what is the killer app for a dependency injection container in PHP. (versus manual dependency injection.)

I can't give you any more rep, Marcus.
Selkirk is offline   Reply With Quote
Old Apr 24, 2005, 12:34   #3
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,273
Hi...

Quote:
Originally Posted by Selkirk
So what is the killer app for a dependency injection container in PHP.
I have absolutely no idea .

Given that a PHP "program" is just a small slice of the application, the construction needs shouldn't be that great, right? No need for complicated wiring and all that? Well, I now have a kind of half idea that I may have been missing out on something.

The injector seems to be of most use for wiring up large chunks of application when the authors of these chunks are constantly changing their dependencies on each other. By giving each other interfaces (or writing adapters) you can gather together say a catalog, a store backend and a shopping cart without having to see the actual code at all. You don't have to see the constructors of these objects either and thus you know nothing of their dependencies as long as you can satisfy each one on demand.

Because of the constructor passing, the components don't know anything about the injector either, they just have to follow the rules for that injector. In the case of Phemto, everything in the constructor with a type hint.

Is this any use? I have no idea. I have never tried it or yet needed to.

It's currently just a curiosity to me. To make it useful in a real program anyway, you would have to add Injector::registerAsSingleton() and, especially for PHP, Injector::registerAsSession(). I would also want to allow superclass specifications, rather than just interfaces, for completeness. It's currently only 50 lines of code after all.

I do have a vague feeling it could be useful...

yours, Marcus
lastcraft is offline   Reply With Quote
Old Apr 24, 2005, 14:09   #4
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Off Topic:

Quote:
I can't give you any more rep, Marcus.


But I can
Dr Livingston is offline   Reply With Quote
Old Apr 25, 2005, 03:14   #5
kyberfabrikken
SitePoint Mentor
silver trophy
 
kyberfabrikken's Avatar
 
Join Date: Jun 2004
Location: Copenhagen, Denmark
Posts: 5,916
the code looks impressively clean. i still haven't completely understood what the use for dependency injections is, but well ... seems to be a common problem though ?
kyberfabrikken is offline   Reply With Quote
Old Apr 25, 2005, 04:27   #6
dbevfat
SitePoint Guru
 
dbevfat's Avatar
 
Join Date: Dec 2004
Location: ljubljana, slovenia
Posts: 678
Nice work, although it's not very clear to me, why if you register two classes (that implement the same interface) and then create the interface, you get the latter one. I mean, I see why it works like that, since in the register method, a class is saved in the hash with the interface name, so the latter class 'overrides' the former. What I don't understand is, why is it so? Does it have to be like that? Is constructing a class by passing the interface name to the injector usable at all?

Regards,
f
dbevfat is offline   Reply With Quote
Old Apr 25, 2005, 05:43   #7
Ren
SitePoint Guru
 
Ren's Avatar
 
Join Date: Aug 2003
Location: UK
Posts: 969
Quote:
Originally Posted by dbevfat
Nice work, although it's not very clear to me, why if you register two classes (that implement the same interface) and then create the interface, you get the latter one. I mean, I see why it works like that, since in the register method, a class is saved in the hash with the interface name, so the latter class 'overrides' the former. What I don't understand is, why is it so? Does it have to be like that? Is constructing a class by passing the interface name to the injector usable at all?

Regards,
f
I think your only meant to register only one implementation of any given interface.

Think I'd remove the dynamic code building/eval(), using ReflectionClass::newInstance()

$reflection = new ReflectionClass($class);
$object = call_user_func_array(array($relection, 'newInstance'), $objects);

Wouldn't this also need recursion protection, if a class deeper the construction depended on something higher? Probably shouldn't happen in a well designed application, but it'd be good to make sure.
Ren is offline   Reply With Quote
Old Apr 25, 2005, 08:09   #8
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Yer, I have a hard time following this as well, but maybe someone can gleam something more from this image I've attached?

I've proberly had the need for this but not realised it myself, what I could do with is more examples... Hint, hint
Dr Livingston is offline   Reply With Quote
Old Apr 25, 2005, 13:59   #9
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,273
Hi...

Quote:
Originally Posted by kyberfabrikken
the code looks impressively clean. i still haven't completely understood what the use for dependency injections is, but well ... seems to be a common problem though ?
Funnily, the more I am aware of it, the more (potentially) useful it seems to become. Take this hypothetical example. I have a class, that I'll call Gateway, that is created in a complicated web page that I want to test. I am using a web tester for this test, so mocking it out is not an option. I would like to use a server stub though to emulate behaviour for the rest of the script. Trouble is this runs in a separate process than the test, so how to get the stub in?

Suppose the test script does this...
PHP Code:

// At top...

$injector = new Injector();
$injector->register('Gateway');

// Later on...
$injector = new Injector();
$gateway = $injector->create('Gateway');
That's the hook in place. Now suppose that in the web test we make this call...
PHP Code:

class TestOfGatewayPage extends WebTestCase {

    function
testStubbedOutGateway() {
        
$this->get('stub_gateway_registration.php');
        
$this->get('gateway_page.php');
        ...
    }
}
In the stub registration page we have...
PHP Code:

$injector = new Injector();

$injector->registerInSession('StubGateway', 'gateway_stub.php');
$injector->create('Gateway');
And in gateway_stub.php we have...
PHP Code:

class StubGateway implements Gateway {

    ...
}
If the search order is to look for session objects first, then the StubGateway will fill the dependency first in the live script.

Probably not a good example, but I do get the feeling that there is something to the DI stuff after all. I'm still groping. Other lifecycle options might be shared memory, or even automatic stubbing for the trick above. It's the way the lifecycle is defines outside of the client code which is the other half of the DI puzzle I think.

yours, Marcus

p.s. that is "Dependency Injection", not "Dependency Inversion" .
lastcraft is offline   Reply With Quote
Old Apr 25, 2005, 14:09   #10
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
What? They're not the same then?
Dr Livingston is offline   Reply With Quote
Old Apr 25, 2005, 14:26   #11
Brenden Vickery
SitePoint Addict
 
Join Date: May 2003
Location: Calgary, Alberta, Canada
Posts: 277
Quote:
Originally Posted by lastcraft
Funnily, the more I am aware of it, the more (potentially) useful it seems to become.
Im pretty skeptical about dependency injection containers (Would that or things like Pico be called a container?). I can see where it could be very usefull for the reasons already mentioned however it seems to me there are some big hurdles to using one.

Object creation isnt as simple as laid out in most DI examples. Usually creation of objects is hidden behind some sort of creational method. This means as already mentioned that the Injector would need to be a singleton (Kind of goes against the entire DI priciple ) or passed into the object (Dont really like this idea either as it dirties the interface).

On top of being hidden behind factories objects are rarely constructed like this:
PHP Code:

$object = new Object(new ObjectB(), new ObjectC(new Object D())); 

They are constructed using objects that have been built during the process of the script. They use request variables, configuration file variables, etc in their construction parameters.

For some reason the idea seems to break down and become more trouble than its worth. I could be and hope Im wrong though, because the theory behind it should enable you to make dependency changes much easier, and thats always good.
Brenden Vickery is offline   Reply With Quote
Old Apr 25, 2005, 17:12   #12
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,273
Hi...

Quote:
Originally Posted by Brenden Vickery
They are constructed using objects that have been built during the process of the script. They use request variables, configuration file variables, etc in their construction parameters.
This stuff is for top level components only. You would not create the low level objects this way. You still need all of those AbstractFactories, Registries and Builders, etc for most of the code.

Does anyone have any experience using DI tools? Everyone (me included) seems to be judging something that we have no personal experience of .

yours, Marcus
lastcraft is offline   Reply With Quote
Old Apr 25, 2005, 17:35   #13
Brenden Vickery
SitePoint Addict
 
Join Date: May 2003
Location: Calgary, Alberta, Canada
Posts: 277
Quote:
Originally Posted by lastcraft
Does anyone have any experience using DI tools? Everyone (me included) seems to be judging something that we have no personal experience of .
Im guilty . I have no experience using one. Hopefully we will all get a chance to use Pawel's port of pico soon though.
Brenden Vickery is offline   Reply With Quote
Old Apr 25, 2005, 18:18   #14
dylanegan
SitePoint Enthusiast
 
Join Date: Jan 2005
Location: Sydney
Posts: 43
I think it would be good in a system, where there may be a range of objects that focus on one task, run pretty much the same, but are different in their naming.

So instead of going through all of your code, you could just write two or three lines in the beginning that would create a register for say a class to communicate with the database.

This would also make it easier if someone had set up a pgsql and mysql class, but didn't write them in a way that they we're easily swapped with a configuration setting. Although the example isn't that good it does show when it can be a good thing to use something like DI.

I don't really see it in a situation where you change the dependency and this also breaks the code. But that's just my take on it.
dylanegan is offline   Reply With Quote
Old Apr 25, 2005, 19:10   #15
Selkirk
SitePoint Guru
 
Join Date: Nov 2002
Posts: 848
Quote:
Originally Posted by dylanegan
So instead of going through all of your code, you could just write two or three lines in the beginning that would create a register for say a class to communicate with the database.
This is an interesting "dependency injection" like pattern that keeps cropping up in various MVC frameworks, where one will stuff a database connection into the request object in an intercepting filter. See this thread:

Quote:
Global filters are good for setting up database objects, for example. You perform all the code to connect to the database and instantiate a db connection object. The way you get this to your Actions is by putting it in the $request object in the filter and retrieving it in your actions (see $request->setAttributeByRef(), $request->setAttribute() and $request->getAttribute()).
This seems like a type of dependency injection to me.
Selkirk is offline   Reply With Quote
Old Apr 25, 2005, 19:29   #16
sweatje
eschew sesquipedalians
silver trophy
 
sweatje's Avatar
 
Join Date: Jun 2003
Location: Iowa, USA
Posts: 3,779
Quote:
Originally Posted by Selkirk
This is an interesting "dependency injection" like pattern that keeps cropping up in various MVC frameworks, where one will stuff a database connection into the request object in an intercepting filter.
Ewwww! And "keeps cropping up" implies you have seen this more than once?!?

Quote:
Originally Posted by Brenden Vickery
On top of being hidden behind factories objects are rarely constructed like this:
PHP Code:

$object = new Object(new ObjectB(), new ObjectC(new Object D())); 

No...but it could be I think the DI pattern may make you want to put your thinking cap on a little askew. Whereas when you are coding up things by hand, passing in to many different objects in the constructor is a bit of a pain, but when you ask for an object, which as another object which was constructed with two other objects from the DI container, and it just hands it to you, it puts things in a slightly different perspective.

You can see some of my efforts at combining WACT and Pico here. All I have used it for is to "auto-wire" the application to run, and allow for injection of MockObjects during testing.
sweatje is offline   Reply With Quote
Old Apr 25, 2005, 22:18   #17
Brenden Vickery
SitePoint Addict
 
Join Date: May 2003
Location: Calgary, Alberta, Canada
Posts: 277
Quote:
Originally Posted by sweatje
No...but it could be I think the DI pattern may make you want to put your thinking cap on a little askew.
I like putting my thinking cap on askew. Maybe you guys can help me here.

The example from Fowlers article is, as he admits, a naive example. "Super simple, small enough to be unreal". But Ill use it anyway. Here is his test case converted to php/simpletest using pico contatiner.
PHP Code:

class TestOfMovieLister extends UnitTestCase {

    
private function configureContainer() {
        
$pico = new DefaultPicoContainer();
        
$finderParams = array(new ConstantParameter("movies1.txt"));
        
$pico->registerComponentImplementation('MovieFinder', 'ColonMovieFinder', $finderParams);
        
$pico->registerComponentImplementation('MovieList');
        return
$pico;
    }
    
public function testWithPico() {
        
$pico = $this->configureContainer();
        
$lister = $pico->getComponentInstance('MovieLister');
        
$movies = $lister->moviesDirectedBy("Sergio Leone");
        
$this->assertEquals("Once Upon a Time in the West", $movies[0]->getTitle());
    }
}
Now this is the same test without pico:
PHP Code:

class TestOfMovieLister extends UnitTestCase {

    
public function testWithoutPico() {
        
$lister = new MovieLister(new ColonMovieFinder('movies.txt'));
        
$movies = $lister->moviesDirectedBy("Sergio Leone");
        
$this->assertEquals("Once Upon a Time in the West", $movies[0]->getTitle());
    }
}
So.. what do we gain with all those extra lines for pico?

If I had a pico expert beside me here is what I would ask:
- Am I supposed to be passing pico to all my objects like it is a huge factory?
- Am I supposed to be making multiple instances of pico all over my code?
- Am I only supposed to be using pico in my tests?
- How do I deal with objects that need to be created using objects that have already been constructed (eg. Not just class names but real objects that need to be passed into the constuctor).
Brenden Vickery is offline   Reply With Quote
Old Apr 25, 2005, 22:23   #18
Selkirk
SitePoint Guru
 
Join Date: Nov 2002
Posts: 848
Quote:
Originally Posted by sweatje
Ewwww! And "keeps cropping up" implies you have seen this more than once?!?
Well, the thing is that passing the database connection to the action instead of a singleton may be a good thing. Crufty java inspired implementation aside, perhaps this is part of the "killer application" for dependency injection.

Ever since that picoContainer thread, I've had a hunch that there was a dependency injection container hidden somewhere in the WACT Handle class. I just don't know where yet.
Selkirk is offline   Reply With Quote
Old Apr 25, 2005, 22:56   #19
arborint
SitePoint Wizard
 
Join Date: Aug 2004
Location: California
Posts: 1,672
Quote:
// At top...
$injector = new Injector();
$injector->register('Gateway');

// Later on...
$injector = new Injector();
$gateway = $injector->create('Gateway');
It is interesting that some of the examples look as much like a Service Locator as Dependency Injection. Both provide a path to Inversion of Control. Fowler seems positive on both Service Locator and Dependency Injection, and definitely likes Service Locator's simplicity. I wonder if Service Locator might be a better path in PHP given the lack of plumbing (short of a C extension) to support Dependency Injection as done in Java.
arborint is offline   Reply With Quote
Old Apr 25, 2005, 23:06   #20
Selkirk
SitePoint Guru
 
Join Date: Nov 2002
Posts: 848
Possibly related: Singleton registry
Selkirk is offline   Reply With Quote
Old Apr 26, 2005, 05:34   #21
sweatje
eschew sesquipedalians
silver trophy
 
sweatje's Avatar
 
Join Date: Jun 2003
Location: Iowa, USA
Posts: 3,779
Unfortunatly, the need for the project I was working on with WACT and Pico evaporated before I completed the project, and a new project of similar complexity has not come along yet.

Here is where I got to (remember this is basically initial experimentation, not a well factored design ).

a bootstrap file:
PHP Code:

require_once 'setup.php'; //constants

session_start();

$pico = new DefaultPicoContainer;
ApplicationController::registerComponents($pico);
ApplicationController::processActions($pico);

$page_ctrlr = new ParameterDispatchController;
$page_ctrlr->setParameterName(VIEW);
ApplicationController::addPages($pico, $page_ctrlr);
$page_ctrlr->start();
I was also playing with WACT controllers at this point (fall 04, IIRC). I think I would use more of the hierarchical controllers in post WACT-0.2 now.

Here was the ApplicationController. Static calls, so more of just a namespace thing:
PHP Code:

class ApplicationController {

    static function
registerComponents($pico) {
        
$pico->registerComponentImplementation('AmpAdodbConn', 'AmpAdodbConn',
             array(
'db' => new ConstantParameter(DBC)));
        foreach(array(
            
// util
            
'Post',
            
// models
            
'CDPlan', 'FilterState',
            
// actions
            
'SetFilterState', 'UpdPlan'
            
) as $item) {
            
$pico->registerComponentImplementation($item, $item);
        }
    }
    
protected static function actionMap() {
        return array(
            
'filter'    => 'SetFilterState'
            
,'updplan'    => 'UpdPlan'
            
,'splitplan'    => 'SplitPlan'
            
);
    }
    static function
processActions($pico) {
        
$post = $pico->getComponentInstance('Post');
        if (
$post->hasKey(ACTION)
            &&
array_key_exists($request = $post->get(ACTION)
                ,
$map = ApplicationController::actionMap())
            ) {
            
$class = $map[$request];
            if (!
class_exists($class)) {
                require_once
'actions/'.$class.'.php';
            }
            
$action = $pico->getComponentInstance($class);
            if (
method_exists($action, 'process'))
                
$action->process();
        }
    }
    static function
addPages($pico, $page_ctrlr) {
        
$view_map = array(
            
'plan'    => 'PlanView'
            
);
        foreach(
$view_map as $view => $class) {
            
$pico->registerComponentImplementation($class, $class);
            
$page_ctrlr->addView($view, new PicoLazyProxy($pico, $class, 'views/'));
        }
        
$page_ctrlr->setDefaultView(new PicoLazyProxy($pico, 'PlanView', 'views/'));
    }
    static function
addGlobalTemplateVars($ds) {
        
$ds->set('action', ACTION);
        
$ds->set('appl_link', BASE_URL);
        
$ds->set('view_link', VIEW_URL);
        
$ds->set('action_link', ACTION_URL);
        
$ds->set('jsstart', '<script type="text/javascript">');
        
$ds->set('jsend', '</script>');
    }
}


class
PicoLazyProxy {
    
protected $pico;
    
protected $class;
    
protected $path;
    
protected $subject;
    
public function __construct($pico, $class, $path='') {
        
$this->pico = $pico;
        
$this->class = $class;
        
$this->path = $path;
    }
    
protected function subject() {
        if (!(
$this->subject instanceof View)) {
            if (!
class_exists($this->class)) {
                require_once
$this->path.$this->class.'.php';
            }
            
$this->subject = $this->pico->getComponentInstance($this->class);
        }
        return
$this->subject;
    }
    
public function __call($method, $args) {
            return
call_user_func_array(array($this->subject(), $method), $args);
    }
}
So there was my big secret use a LazyLoading Proxy which remembers the PicoContainer.

So an action might need a model and the request:
PHP Code:

interface iUpdPlan {

    
public function process();
}

class
UpdPlan extends BaseCdplanAction implements iUpdPlan {
    
protected $cdp;
    
protected $post;
    function
__construct(iCDPlan $cdp, iRequestHash $post) {
        
$this->cdp = $cdp;
        
$this->post = $post;
    }
    
    
public function process() {
  
//...
        
}
}
and a model might need a database connection:
PHP Code:

interface iCDPlan {

    
public function getPlants($requery=false);
    
public function getRfps($requery=false);
    
public function getPlan($filter=false);
    
public function update($plant, $rfp, $month, $wgt);
    
public function getAreSplits();
}

class
CDPlan extends BaseCdplanModel implements iCDPlan {
//...
}

class
BaseCdplanModel {
    
protected $conn;

    function
__construct(AmpAdodbConn $db) {
        
$this->conn = $db;
    }
}
I ended up using interfaces for all of the type hinting, so that I could use MockObjects in testing.

PHP Code:

if (!class_exists('MockCDPlan')) {

    
Mock::Generate('CDPlan', 'IntermediateCDPlan');
    class
MockCDPlan extends IntermediateCDPlan implements iCDPlan {
        
public function getPlants($requery=false) {
            
parent::getPlants($requery);
        }        
        
public function getRfps($requery=false) {
            
parent::getRfps($requery);
        }        
        
public function getPlan($filter=false) {
            
parent::getPlan($filter);
        }        
        
public function update($plant, $rfp, $month, $wgt) {
            
parent::update($plant, $rfp, $month, $wgt);
        }        
        
public function getAreSplits() {
            
parent::getAreSplits();
        }
    }
}

class
TestUpdPlan extends CdplanActionUnitTestCase
{
    function
TestProcess() {
        
$key = 'DPWA|-9999';
        
$post = $this->getPost(array(
            
'process' => array($key)
            ,
$key.'|1' => '10,000'
            
,$key.'|2' => '12,000'
            
), true);
        
$post->expectCallCount('hasKey',14);
        
        
$cdp = new MockCDPlan($this);
        
$cdp->expectArgumentsAt(0, 'update', array(
            
'DPWA', '-9999', '200501', '10000'));
        
$cdp->expectArgumentsAt(1, 'update', array(
            
'DPWA', '-9999', '200502', '12000'));
        
$cdp->expectArgumentsAt(2, 'update', array(
            
'DPWA', '-9999', '200503', null));
        
$cdp->expectCallCount('update', 2);
        
        
$pico = $this->getPico($post, $cdp);
        
ApplicationController::processActions($pico);
        
        
$this->assertRedirect('cdplan.php');
        
        
$post->tally();
        
$cdp->tally();
    }
    
protected function getPico($post=false, $cdp=false) {
        
$pico = new DefaultPicoContainer;
        
ApplicationController::registerComponents($pico);
        if (
$post instanceof iRequestHash) {
            
$pico->unregisterComponent('Post');
            
$pico->registerComponent(new InstanceComponentAdapter($post, 'Post'));
            
$this->assertIdentical($post, $pico->getComponentInstance('Post'));
        }
        if (
$cdp instanceof iCDPlan) {
            
$pico->unregisterComponent('CDPlan');
            
$pico->registerComponent(new InstanceComponentAdapter($cdp, 'CDPlan'));
            
$this->assertIdentical($cdp, $pico->getComponentInstance('CDPlan'));
        }
        return
$pico;
    }
    
//...
}
sweatje is offline   Reply With Quote
Old Apr 26, 2005, 05:36   #22
sweatje
eschew sesquipedalians
silver trophy
 
sweatje's Avatar
 
Join Date: Jun 2003
Location: Iowa, USA
Posts: 3,779
Quote:
Originally Posted by Selkirk
Well, the thing is that passing the database connection to the action instead of a singleton may be a good thing.
I don't mind passing around the connection, but stuffing it into the request smells of MagicContainer to me
sweatje is offline   Reply With Quote
Old Apr 26, 2005, 08:12   #23
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
SweatJe, interesting concepts you have, indeed

I don't like the idea either of putting objects into the request, this I feel is a bit suspect in my mind.

Quote:
Crufty java inspired implementation aside, perhaps this is part of the "killer application" for dependency injection.
I'm all for learning more from our Java cousins, in that we can learn something but that's not to say we've to jump of the cliff like lemmings in putting the Java implementation into practice
Dr Livingston is offline   Reply With Quote
Old Apr 26, 2005, 11:35   #24
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,273
Hi...

Quote:
Originally Posted by arborint
It is interesting that some of the examples look as much like a Service Locator as Dependency Injection.
They are closely related. Needle supplies both for example.

yours, Marcus
lastcraft is offline   Reply With Quote
Old Apr 26, 2005, 13:06   #25
arborint
SitePoint Wizard
 
Join Date: Aug 2004
Location: California
Posts: 1,672
Quote:
They are closely related. Needle supplies both for example.
Thanks for the link to Needle. Now I have a lot more reading to do.

Given that Service Locator is described as both a subset of Dependency Injection and easier to implement, when are they applicable in PHP?

It seems that with the PHP Request/Response cycle only executing a small slice of the whole application that the disadvantages of Service Locator in that you need to pass the object around are minimal. Current PHP frameworks already pass a Request object about as many places. Why not just replace the Request object with a Service Locator object and at a minimum register the Request object with the Service Locator. Quick example:
PHP Code:

class Locator {

    var
$_cache;
    
    function
Locator() {
        
$this->_cache = array();
    }
    function
setLib($name, &$lib) {
        
$this->_cache[$name] = &$lib;
    }
    function &
getLib($name) {
        return
$this->_cache[$name];
    }
    function
isLib($name) {
        return (
$this->getLib($name) !== null);
    }
}
PHP Code:

class Request {

var
$_request = array();
    
function
Request() {
    if (!
strcasecmp($_SERVER['REQUEST_METHOD'], 'POST')) {
        
$this->_request =& $_POST;
    } else {
        
$this->_request =& $_GET;
    }
    if (!
get_magic_quotes_gpc()) {
        
$this->removeSlashes($this->_request);
    }
}

function
removeSlashes(&$str) {
    if (
is_array($str)) {
        foreach (
$str as $key => $val) {
            if (
is_array($val)) {
                
$this->removeSlashes($val);
            } else {
                
$array[$key] = stripslashes($val);
            }
       }
    } else {
        
$str = stripslashes($str);
    }
}

function
get($name) {
    return
$this->_request[$name];
}
    
function
set($name, $value) {
    if (
$name) {
        
$this->_request[$name] = $value;
    }
}
    
}
PHP Code:

class Controller {

    function
execute(&$locator) {
        
$request =& $locator->getLib('request');

// code here
    
}
}
PHP Code:

$locator =& new Locator();

$locator->setLib('request', new Request());

$controller =& new Controller();
$controller->execute($locator);
But you could also do:
PHP Code:

$locator =& new Locator();

$locator->setLib('request', new Request());
$locator->setLib('db', new PostgressConnector('localhost', 'user'));
$locator->setLib('template', new Template('./templates'));

$controller =& new Controller();
$controller->execute($locator);
Dependency Injection make possible more transparent injection, so I am definitely interested in delving more into Phemto to explore the possiblities.
arborint is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread | Next Thread »

Thread Tools
Display Modes

 
Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Sponsored Links
 
Forum Jump


All times are GMT -7. The time now is 22:05.


Powered by vBulletin® Version 3.7.1
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Copyright 1998-2009, SitePoint Pty Ltd. All Rights Reserved