Oh, you are correct. IIRC, the false constant should require a temp var though.
| SitePoint Sponsor |
Oh, you are correct. IIRC, the false constant should require a temp var though.





Yes, that's true, though it is an error anywayOriginally Posted by sweatje
Might be an idea to add a trigger_error there, I've added it in the attached. Could do with converting the test file into a SimpleTest and to make sure that all the references are good.
Douglas
Hello World
Thanks for the zip. I have been wanting to play, but I am sitting at home with a cold and screaming kids all around me. Not exactly the best conditions for sifting through a thread and pulling out code![]()



I knew you would like itOriginally Posted by DougBTX
I think you wanted to change the references to registry[$serviceName] to $service, so I just finished that for you.Originally Posted by DougBTX
I also polished the test script a little bit, so usage is a little clearer for newcomers.
Like Douglas said, basically the lambda function is only called once to initialize the object, after that the object is assigned to the static array, so it seems we don't have a problem with references. A proper set of unit tests would hopefully cover that though and make sure we are not missing something.Originally Posted by sweatje
One thing I forgot to mention. When I was looking at the Ruby way of doing things, I automatically decided to create the lambda function with a reference to the injector, probably just imitating the original example using closures.
Then later on I thought it was nice to have, since it allows the developer to choose the syntax to use in the constructor, either like:PHP Code:$registry[$serviceName]['constructor'] = create_function('$di', $block);
Or...PHP Code:DI::register(
'store',
'return new MyStore($di->("gateway"));');
But now I'm thinking that, since this is a singleton anyway and all the methods should be accessed statically in the way that it's designed, is it neccesary or even good to have that choice? I even think I like the second example better, since it makes it more clear that you are calling both register and get of the same class, makes the syntax more consistent. So perhaps it's not a good idea to allow the "$di->" style in the code block anyway. I guess what I am asking you guys is, can you phathom a situation where it would be useful for you to have access to the container singleton object like that, since you can always call the static methods anyway? If the answer is no, I think we should do away with that parameter and simplify just a tiny tad more.PHP Code:DI::register(
'store',
'return new MyStore(DI::get("gateway"));');
I would love to hear everybody's opinion on the injector; specially Marcus, since he created Phemto, and he may be able to see some shortcoming we are missing.Originally Posted by DougBTX
If kyber / arborint et all think they can use this style of a dependency injector in their application controller design, I would love to help them fit it in. At a personal level, this is likely to become the core of all of my newly developed applications, the advantages of elevating dependency wiring to the top level are just too good to pass.
Maybe the next step is to create the proper unit tests, and I would like to write a small document explaining the advantages of DI and the use of the injector.
Thanks to heathd indeed, this has been very fun to work on, and it's helped me (and hopefuly others) understand Dependency Injection much better.Originally Posted by DougBTX
Latest version attached.
Garcia





I'm in the background following this thread. It appears that Dependency Injection has some interesting aspects to it I suppose but I'm still pondering on all this myself.
Waiting to see where it goes![]()





Hi...
There is a thread running on the Solar mail list about DI as well, so at least it is becoming a popular topic at last. Unfortunately I am right in the middle of moving house and could lose my net connection at anytime.Originally Posted by ghurtado
This thread, and the others, have given me lot's of ideas for Phemto as you can imagine. The eval/block based approach is certainly a drastic solution. I could never quite bring mysef to do it (having had bad experiences adding PartialMock hooks, now removed). Maybe I am becoming a conservative old fuddy, duddy, but won't syntax and other errors (all those extra semicolons) really confuse the hell out of people? Espcially if it all goes wrong inside a framework which you don't know a lot about? Not so bad in PHP5 with exceptions, but I just get a little uneasy.
Yeah, I had to write one just to get a handle on the whole idea. The consequences are both subtle and far reaching. I think DI should be used in small doses, as in some ways it's is a little too powerful. I can imagine bugs being hard to track down.Originally Posted by ghurtado
Or I could just be an old fuddy, duddy...
yours, Marcus
Marcus Baker
Testing: SimpleTest, Cgreen, Fakemail
Other: Phemto dependency injector
Books: PHP in Action, 97 things





The consequences are both subtle and far reaching.
A while back, sometime last year I read over the 1, 2 and 5 minute tutorials on the PicoContainer site and at the time didn't really understand it all, nor did I understand the need for it.
Been back there today, and after reading them again (and catching up on the Service Locator) I understand a bit more, thanks partly to the discussions on this forum which has sparkled my interest again.
So, to Marcus, Kyber, et al thanks for your interesting thoughts on this, and maybe I'll see about putting something together soon, so I know for myself if I can implement this as well![]()





I can see people wanting to use the class like a "normal" singleton, like this:Originally Posted by ghurtado
So I think we should leave them in. I've not added tests for that in the attachment, so if someone wants to make that use official, feel free to contribute testcasesPHP Code:$di =& DI::getInstance();
$di->register(
'order',
'return new MyOrder();');
I've added testcases for using the dependency injector and standard constructor initialization. It does give some insight into how the two methods compare, and just what the code looks like for the same set of model classes.Originally Posted by ghurtado
Latest version attached. (I think it helps a lot that the discussion on how to write something is done in code, not just words, and that every time something changes, the new code is posted. Keeps everything transparent, and it is possibly easier to associate comments in a post with the code at that state than directly via CVS.)
Douglas
Hello World
Howdy all,
Wow... Marcus is right, the general DI concept is getting a lot of discussion in the PHP world as of late.
The discussion on the Solar list that Marcus mentioned got moved off-list with Jason and Marcus (two guys I both knew were interested in DI) being CC'd. Jason suggested that the comments could be contributed here, and I believe he's right.
I'll save you the whole thread and bring it in on the ending part:
After reading through this post and glancing at some of the code, I believe what I'm trying to do is a cross of ServiceLocater (something I need to read more on) and what Pico is now referring to as the Contextualized Lookup. They actually sound very similar, so I believe they may just be two different words for the same thing, but I can't say for sure yet.Originally Posted by myself in an email thread
One thing I am looking to do is remove all of the register() calls that Pico and Phemto have and keep from having to fully load every object I might want to use. The latter of these removes the need for reflection provided in PHP 5 and I had already thought would lend itself to being backported to PHP 4.
Marcus, I do have an issue with the code you're using - it requires your injector to become a Singleton. Unless a Registry is implemented, how would I go about having two seperate injectors in the same execution? I think by becoming a Singleton, you're overstepping what the injector needs to do. Worrying about how objects are made aware of it should happen at the implementation level.





For the code in this thread, that's probably a good argument for the $di->get() syntax because it lets you extend to this later:Originally Posted by Travis S
The internal $di =& DI::getInstance(); code would be replaced with normal $this->registry calls. You're still going to have $di->get() inside the register() calls though.PHP Code:$di =& DI::getInstance('First Instance');
$di->register(
'store',
'return new MyStore($di->get("gateway"));');
$c =& DI::getInstance('Second Instance');
$c->register(
'store',
'return new MyOtherStore($di->get("gateway"));');
Regards,
Douglas
Hello World



I can totally simpathize with that. It was a leap of faith even for me too. But then again, anonymous functions are an everyday tool in other dynamic languages (Ruby, Javascript, Python), and put to good use, it can broaden your perspective of what's possible within the language. create_function just happens to be PHP's ugly-ish method to use anonymous functions. I think the tradeoff is between freedom and safety.Originally Posted by lastcraft
I agree, that can be a problem, but on the other hand, I think the impact should be minimal. Syntax errors should be easy to avoid in 2/3 lines of (should-be) very simple constructor code. In many cases we are probably just talking about something as simple as "return new something()", which is pretty hard to screw up. Any error within the block will trigger the appropriate PHP error message (albeit pointing to the wrong line), such as non-existent objects, invalid function names or undefined classes. Go ahead, try it out. After all, it is still PHP who compiles the lambda function, and it will let you know if there's something wrong with it in the same terms as it would with any regular ol' function. Yes, you lose the pointer to the line number, but if you get any errors using DI::register, you know where to look for them right away, since hopefully all of your dependencies are defined together in the same file anyway.Originally Posted by lastcraft
So in short, I agree that there are shortcomings, it is just that I believe that for a developer who is advanced enough to know what Dependency Injection is in the first place, these are more than likely minimal issues, and the tradeoff between flexibility and safety might be a worthy one. Or maybe you have to be truly "dynamically typed" at heart to feel totally comfortable with this design.
Absolutely. The same can be said for create_function. A comment on the PHP manual entry put it best:Originally Posted by lastcraft
This is the first time in 5 years of PHP programming that I have run into a good justification for runtime parsed code. But I truly believe that in this case, barring a cleaner way to do it, it is a very suitable and elegant solution for the problem at hand.Using eval() and create_function() is like pushing the queen early. Chessmasters can do it because they know what they're doing. Beginners and intermediates, however, are quick to attack without recognizing the long term danger.
So too, programmers who have *actually completed a curriculum in computer science* and are aware of their algorithmic options and computational complexities, can find incredible possibilities for eval() and create_function() and should find no fear in calling those functions.
Thanks a lot for your input, it really means a lot.
Garcia



I agree. Best left in for those who may need it. Personally, I still favor the following syntax:Originally Posted by DougBTX
But you are right, there are probably many people who would rather use it as a "normal" singleton.PHP Code:DI::register(
'store',
'return new MyStore(DI::get("gateway"));');
I like the tests, but I have a confession to make: I haven't installed simpleTest yetOriginally Posted by DougBTX
Shameful, I know, but this is the excuse I was waiting for to jump in, I think I am becoming test infected too.
I noticed you changed _getInstance to getInstance, which is funny, because I myself went back and forth a few times with that trying to decide whether it was a "private" method or not. I like DI::getInstance better anyway, since we want to allow traditional singleton usage.
Absolutely, I can't think of a better way to brainstorm than ping-pong coding.Originally Posted by DougBTX
Travis,
I have a question about your implementation
How would you handle lazy loading with that setup?PHP Code:$dm = new DependencyManager();
$dm->registerPackage(new Package1());
$dm->registerPackage(new Package2());
Thanks to everyone for their input.
Garcia
This moves from a Singleton to a Registry of Singletons. I understand it's usefulness, but what if I store all my "registry" type variables in _SESSION or _GLOBAL, or already have my own Registry class? I think this is feature bloat. The injector shouldn't care how its stored instantiated.Originally Posted by DougBTX
Package1 and Package2 are "packages" of interfaces. Each one implements InterfaceContainer which has has hasComponent(). DependencyManager->loadObject() goes through the list of registered packages looking for the first object that says it has the desired component, then calls getInterface().Originally Posted by ghurtado
Here's a PNG of the diagram I was working off of, I think it will help clarify:
With this setup, InterfaceContainer contains knowledge of X number of implementations. If hasComponent('Bird') returns true, then getComponent('Bird') will return the object InterfaceContainer knows about that implements Bird. Though InterfaceContainer only requires one interface to attempt to do a match, my plan is to allow hasComponent('Bird'[, 'Blue']) which would only return objects implementing Bird that and Blue so you are given finer grain control.
As far as the InterfaceProxy object, it will only require that it knows how to load the Interface that it represents. My initial thought, and the reason that I included several private methods in the UML diagram, is that it will know how that particular Interface should behave. This can currently be done with Pico or Phemto, but I think each one of the InterfaceProxies should know this. Though I haven't studied them intently, I believe what I'm implementing here is similar to Pico's Adapter interfaces.
This brings me back full circle to how I'll acheive lazy loading. The various InterfaceContainer objects will already know via configuration (hard coded, from storage, or some other implementation-dependent means) what it has access to, where it's located, what it implements, what it requires, etc. Until the call $dm->loadObject() is made, the only thing that will be loaded is the meta data for the object. Since I don't plan on using Reflections (though there's nothing stopping someone from implementing an InterfaceContainer that does so), the class won't need to be loaded until it is requested.
One thing that might need clarification...
This would be the equivilant of having:Originally Posted by Travis S
So long as the container implements InterfaceContainer, how it determines what it has is up to the user. You could use a Pico/Phemto style container as well:PHP Code:$iniContainer = new IniInterfaceContainer('/path/to/config.ini');
$xmlContainer = new XmlInterfaceContainer('/path/to/config.xml');
$arrayContainer = new ArrayInterfaceContainer('/path/to/config.array.php');
$domContainer = new DomInterfaceContainer($domObject);
//etc., etc., etc.
If anyone's interested, I've put together my first little bit of code in my Subversion repository here. Right now the only concrete implementation is d51DependencyManager which is being tested via Mocks. Be forewarned though, the unit tests require the CVS version of SimpleTest to work as expected. I found a bug in assertIsA() when checking for the PHP 5 equivilent of $object instanceof Interface. Also, it relies on the new ability of Mock to mock an interface.PHP Code:$pico = new PicoLikeInterfaceContainer();
$pico->registerComponentInstance('BlueBird');
$dm = new DependencyManager();
$dm->registerPackage($pico);
$component = $dm->loadObject('Bird', 'Blue');
$component instanceof BlueBird;





Hi...
I agree, and the next iteration will split this behaviour off. A little background: Phemto is an introduction, almost a tutorial piece, into DI. In fact it used to be twenty five lines of code in a PHP|Architect article, but since grew to a whopping 50. I always viewed it as a stepping stone to Pico. My design constraint was "how can I get someone new to DI using it as quickly as possible?".Originally Posted by Travis S
There has been enough interest in it since, that I may place it on a more formal footing once the current SimpleTest release cycle is done.
yours, Marcus
Marcus Baker
Testing: SimpleTest, Cgreen, Fakemail
Other: Phemto dependency injector
Books: PHP in Action, 97 things




I always thought the Handle mechanism from WACT could evolve into a DI container. Inspired by this thread, an experiment your consideration:
The Handle object encapsulates object construction. So these are equivalent:PHP Code:<?php
require 'framework/util/inject.inc.php';
class DatabaseConfiguration {
var $driver = 'yowza!';
}
class DatabaseConnection {
var $config;
function DatabaseConnection(&$configuration) {
$this->config =& $configuration;
}
function showDriver() {
echo $this->config->driver;
}
}
class EventListener {
function yowza(&$connection) {
$connection->showDriver();
}
}
class EventSource {
var $eventListeners;
var $injectionContainer;
function EventSource(&$injectionContainer) {
$this->eventListeners =& new MulticastNotifier();
$this->eventListeners->setInjectionContainer($injectionContainer);
$this->injectionContainer =& $injectionContainer;
}
function onEvent() {
$connection =& $this->injectionContainer->getInstance('ConnectionAlias');
$this->eventListeners->invokeAll(array(&$connection));
}
function registerEventListener(&$listener) {
$this->eventListeners->addListener($listener);
}
}
$container =& new InjectionContainer();
// register a specific instance
$container->registerSingletonComponent('DatabaseConfiguration', new DatabaseConfiguration());
// register a class to be constructed on demand
$container->registerComponent('GlobalListener', new Handle('EventListener'));
// register a class to be constructed on demand with a construction parameter
// that is also a component
$container->registerComponent('DatabaseConnection',
new Handle('DatabaseConnection', array(new Injection('DatabaseConfiguration'))));
// register an alias for no real reason other than we can
$container->registerComponent('ConnectionAlias', new Injection('DatabaseConnection'));
// A Scary complex example with both constructor and setter injection
$container->registerComponent('EventSource',
new Handle('EventSource',
array(
new Injection('injectionContainer'),
'registerEventListener' => new Callback(
new Injection('GlobalListener'),
'yowza'
)
)
)
);
// We can register ourself
$container->registerComponent('injectionContainer', $container);
$source =& $container->getInstance('EventSource');
$source->onEvent();
?>
The second parameter to Handle is an array of parameters for the constructor. So these are equivalent:PHP Code:Handle('class');
new class();
The array of construction parameters can specify calls to setter methods. So these are equivalent:PHP Code:Handle('class', array('a'));
new class('a');
The Injection class (bad name?) encapsulates a request to the injection container for an object instance. If any of the parameters for a handle is an Injection instance, then the requested object is substituted for the parameter when the handle is constructed. For example:PHP Code:Handle('class', array('setter' => 'value'));
$obj =& new class();
$obj->setter('value');
Any place a Handle can be used, an object instance can be used as well. Thus these cases work:PHP Code:$container->registerComponent('DatabaseConnection',
new Handle('DatabaseConnection', array(new Injection('DatabaseConfiguration'))));
Interestingly, anywhere a Handle can be used, an Injection object can also be used. This allows the alias case:PHP Code:$container->registerComponent('injectionContainer', $container);
$container->registerSingletonComponent('DatabaseConfiguration', new DatabaseConfiguration());
Since the Callback object can also accept a handle, it too can accept an injection object. This is used in the event handling example to represent a method call to an object retrieved from the container:PHP Code:$container->registerComponent('ConnectionAlias', new Injection('DatabaseConnection'));
PHP Code:new Callback(new Injection('GlobalListener'), 'yowza')
Last edited by Selkirk; Sep 10, 2005 at 22:53. Reason: Went to bed but decided to get back up and offer more explaination





I finally had some time to catch up with these DI threads. It is fascinating. I am getting the sense that DI in PHP is evolving toward a kind of Service Locator + Lazy Loading + Object Persistence. The nature of how PHP executes scripts and the support (or lack of it) in the language seems to push us that way. DI is really needed in PHP for what are often called "enterprise" apps. And this subject dovetails with the framework treads because DI should be part of the core of the next generation of PHP frameworks.
Christopher





That strikes me as the bulk of the problem, once you have the store the rest is trivial (2-4 lines of code). The DI classes we've been working on here seem to me to be very much a superset of a Registry, rather than something you would use at the same time as a registry.Originally Posted by Travis S
Douglas
Hello World



Well, in my understanding, a Dependency Injector is an advanced Service Locator (which is in turn an advanced Registry). The definitions I have seen of a Service Locator explain that the Locator must be a singleton, since otherwise you add another problem to the pile: locating the service locator or passing it around. If you have to pass it around to your domain classes then you don't have a Dependency Injector at all, since that would imply awareness of the injector. And if you have to "locate" the Service Locator itself, well, how would you handle that? With a "service locator locator"? I would argue that by design and neccesity a Dependency Injector or a Service Locator must always be a singleton. I don't understand why anyone would need more than one such registry in their application, and if such were the case, perhaps the pattern is being misused?Originally Posted by Travis S
Feel free to prove me wrong, hopefuly with some example code![]()
Garcia
With a Service Locator, I believe you're correct. Before I proceed though, I'll warn you that my knowledge of a service locator is minimal at best and mostly derived just a basic understanding of the role it is supposed to fill.
This is where the SL and DI patterns seperate. An SL is used by an object, a DI isn't even known to an object.
The first example shows that Person is always going to have a SmartBrain (yes, I am just being smart-aleck here). The second shows that person will always have a brain, but Person must know how to look it up. The third shows that Person will always be given a Brain before it instantiated by the outside "container" (either a programmer coding it, or a DI-type containter).PHP Code:// Using no locator/injection
class Person {
public function __construct() {
$this->_brain = new SmartBrain();
}
}
// Using Service Locator
class Person {
public function __construct() {
$sl = SL::instance();
$this->_brain = $sl->lookup('Brain');
}
}
// Using Dependency Inection
class Person {
public function __construct(Brain $brain) {
$this->_brain = $brain;
}
}
This is where DI picks up its usefullness. With the first example, you can't test without creating a real Brain - and we all know how expensive an operation it would be to create a smart brain out of thin air.With the second example, you can't test without creating an SL to handle locating the brain. With the third example, however, you can test by directly handing in a MockBrain and not worry about how the DI is going to work later on.
To extend this into a test:
Of course, you would probably want more conditions for killSomeone - did they just eat the last Oreo? That might change it. Likewise, going for a while is going to depend on other things such as how much sleep you've had, etc., but you've got the idea.PHP Code:class Person {
public function __construct(Brain $brain) {
$this->_brain = $brain;
}
public function goForAWalk() {
return $this->_brain->consider('walk');
}
public function killSomeone() {
return $this->_brain->consider('kill');
}
}
interface Brain {
/**
* Returns true or false as to whether this is a valid action
*
* @return boolean
*/
public function consider($action);
}
Mock::generate('Brain');
class TestOfPerson extends UnitTestCase
{
public function testWillWalk() {
$brain = new MockBrain($this);
$brain->setReturnValue('considerAction', true);
$person = new Person($brain);
$this->assertTrue($person->goForAWalk();
}
public function testKillSomeone() {
$brain = new MockBrain($this);
$brain->setReturnValue('considerAction', false);
$person = new Person($brain);
$this->assertFalse($person->killSomeone());
}
}
Herein lies my issue with creating a DI of any sort as a Singleton. Within the context of Person, if I need to utilize a DI container to handle lookups I don't need to know about everything else that's been registered before me. Pico's coders have already written about this at Container Instantiation and Container Dependency and they are definitely more articulate than I could be.
At some point, Container Dependency has to happen. Even if it's at your index.php that sets the container up, at that point you become dependent on it to some degree. An example where Container Dependency would useful and not an anti-pattern is in the case of third party library. Let's use a data access object, it might want to offer a configuration means to allow you to specify which type of driver to use (driverType() for example). It could then use DI to figure out which drivers to insert into the various objects that it creates as it goes about is normal business. The drivers are all based on interfaces anyhow (or should be) so they can be interchanged without any issue. In this case, your DAO doesn't need to know about the Brain implementations you have registered for Person; it needs to keep track of its own DI. Likewise, from outside you don't need to know what the DAO is doing internally, just that it's handing out what you expect.
Have I won you over?![]()
Well, I don't know if I will "prove you wrong", but I can show some of my experiments with Pico and WACT in combination. In retrospect, it appears that I use Pico as a ServiceLocater at a high level (the code is aware of the DI container) but the nut & bolts "glue" classes (things like various Command actions) and views are unaware of container, and thus "dependancy injected" for real.Originally Posted by ghurtado
My bootstrap looks like:
setup.php loads various libraries. An instance of Pico is created. ApplicationController::registerComponents() does all of the setup for DI for this application, ApplicationController::processActions() is an Application Controller, dispatching to one of the aformentioned actions if required. Assuming the code make it that far (i.e. no redirect and exit were issued by an action) then a WACT ParameterDispatchController takes over. ApplicationController::addPages() is a second stage of setup for DI, and then $page_ctrlr->start() displays the view.PHP Code:require_once 'setup.php';
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();
So far, we only have one instance of the DI container active, so you could just make it a singleton, right? I think the reason for keeping the DI container an instance variable is for testing code.
In my tests, I have some code for each action which validates the conditions for dispatching to that particular action class. Here is a custom assertion for this application which performs this check.
So this code make a brand new instance of the DI container within the assertion, sets up some expectations, and then tests the output of that container. I run group tests on all of the actions inside of the application, so quite a number of different instances of the DI container are created and used while running the test suite.PHP Code:protected function assertDispatch() {
$post = new MockPost($this);
$post->setReturnValue('hasKey', true, array(ACTION));
$post->expectArguments('hasKey', array(ACTION));
$post->setReturnValue('get', $this->actionRequest, array(ACTION));
$post->expectArguments('get', array(ACTION));
$mock_action = 'Mock'.$this->actionClass;
$this->assertTrue(class_exists($mock_action));
if (class_exists($mock_action)) {
$action = new $mock_action($this);
$action->expectOnce('process');
$action->expectCallCount('process', 1);
$pico = new DefaultPicoContainer;
ApplicationController::registerComponents($pico);
$pico->unregisterComponent('Post');
$pico->registerComponent(new InstanceComponentAdapter($post, 'Post'));
$this->assertIdentical($post, $pico->getComponentInstance('Post'));
$pico->unregisterComponent($this->actionClass);
$pico->registerComponent(new InstanceComponentAdapter($action, $this->actionClass));
$this->assertIdentical($action, $pico->getComponentInstance($this->actionClass));
ApplicationController::processActions($pico);
}
$post->tally();
$action->tally();
}
Now one might argue that a singleton DI containter with a "clear()" method would be enough, but I somehow feel safer and more confident of less potential test interference with separate instances.
Jason Sweat ZCE - jsweat_php@yahoo.com
Book: PHP Patterns
Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
Detestable (adjective): software that isn't testable.





I've refactored the code so that it acts like a loose singleton - in that you can get the singleton instance using DI::getInstance(), or you can get your own instance with new DI(); and they won't interfere with eachother. The singleton instance is only created if you make a call to DI::getInstance(), so there shouldn't be any extra overhead.
Here are a couple of snippets from the testcases:
To make much use of the new DI(); method, you'll probably need your own registry of some sort to look after the instance, but I assume most people have one lying around (or, if you really want to confuse someone, you could quite easily use the DI class as its own registry!).PHP Code:function test_with_new_instance () {
$di = new DI();
$di->register(
'order',
'return new MyOtherOrder();');
// Use the same instance
$order =& $di->get('order');
$this->assertEqual($order->save(), 'Other order saved!');
}
function test_with_singleton_instance () {
// implicitly create the singleton instance
$di =& DI::getInstance();
$di->register(
'order',
'return new MyOtherOrder();');
unset($di);
// retrieve the singleton instance
$di =& DI::getInstance();
$order =& $di->get('order');
$this->assertEqual($order->save(), 'Other order saved!');
}
I'm running PHP4.4 and PHP5.0.4, I'd appreciate if anyone could test with the PHP5.1 RC1, I'm not 100% sure about line 33 in dependency_injector.php in that version.
Thanks,
Douglas
Hello World



I just read through those two pages, and a few more at the Pico site. A couple of things that I thought were interesting:Originally Posted by Travis S
- The developers warn us of the dangers of using independent instances of the container, such as when you instantiate it directly inside of a dependent class, or in a unit test. This is indeed an unwanted, obscure dependency (I agree with all of that so far). What they don't tell you is that this pitfall is only made possible because multiple instances of the container can be instantiated anywhere in your code.
- If you read their take on the Singleton it is immediately obvious that they consider the singleton an anti-pattern, and as such, Pico has been designed not to be a Singleton and allow multiple container instances.
You don't have to give this much thought to realize that if they had required for the container to be a Singleton, they would have been able to eliminate not one, but at least three "anti-patterns" (or in other words "ways to really screw it up using Pico") from their list. If I only allow one single instance of the container anywhere in my application I dont have to worry about classes internally or obscurely using the container, since they will still be referring to the same container all throughout the app. This may (is) still be a bad practice in general, but at least now I don't have any side effects from it.
This is probably more of a case of semantics between yours and my definition of "dependence". As far as your domain classes, it is true that, stricty speaking, you can never truly remove any of these dependencies. But what the Dependency Injection strategy proposes is that, if you can move those dependencies all the way to the top level of your app (like the index.php file) you have effectively managed those dependencies and brought them under control. The idea is that the day that I have to change some class in my dependence chain, I will only have to make one change in one single place (hopefully in a config file or a bootstrap class) vs having to hunt down the dependence in potentially dozens of files spread all throughout my source tree.Originally Posted by Travis S
It is the same reason why we don't hardcode the database username and password string everywhere throughout our application, and instead we put it into a configuration file: our applications are all still dependent on that information, but when you make it that easy to change, the dependance is effectively managed (or "neutralized" if you will)
As far as dependence on the container itself I would argue that there is no dependence whatsoever using Dependency Injection (or at least a proper implementation of it). A well-used DI container can be swaped for any other with a completely different interface and implementation with no side effects.
This is how I would handle that situation based on our proposed DI posted above:Originally Posted by Travis S
Hopefuly that illustrates the correct way of managing the dependence on the driver: now you only have one spot where the concrete driver class is referenced, bringing you to the "minimal dependence" required, or -some may say- "removing" the dependence.PHP Code:DI::register(
'driver',
'return new MysqlDriver();');
// or
/* DI::register(
'driver',
'return new PostgreSqlDriver();'); */
// or
/* DI::register(
'driver',
'return new MSSqlDriver();'); */
DI::register(
'brain',
'return new BrainDao(DI::get("driver"));');
DI::register(
'person',
'return new Person(DI::get("brain"));');
In my opinion, the DAO should be completely ignorant of the existence of a DI at all. That's the beauty of DI right there, being able to manage any class from any library without having to change one single line of code in it.Originally Posted by Travis S
Sorry, not yetOriginally Posted by Travis S
But thanks for your patience, I am enjoying this discussion greatly.
Garcia





I'm with you on this one, but this code might be helpful: (any extra PHP5.1 hacks not withstanding...)Originally Posted by ghurtado
I've not tested it yet, but I think that it should allow all three syntax styles ( DI::get(), $di = new DI(); $di->get() and $di =& DI::getInstance(); $di->get() ) at the same time, so this one really does come down to what works best in your application. The code I posted above already allows for the second two styles.PHP Code:function &getInstance () {
static $instance;
if ( isset($this) && is_a($this, 'DI')) {
return $this;
}
if ( !isset($instance) ) {
$instance = new DI();
}
return $instance;
}
Douglas
Hello World
I hope I don't gloss over too much, but I believe this makes the point I'm interested in and where we're diverging in opinion:
That would work, but I'm looking at it from DAO being completely independent and brought into a project. I quite often take parts of my code like DAO and make them completely independent. They are then brought in as 3rd-party libraries and treated as read-only.Originally Posted by ghurtado
To continue the DAO use, I might do something like this:
When dao->query() is called, it would then use its own internal container to figure out which driver to pull.PHP Code:$dm = new DependencyManager();
// just using generals here, not the syntax I've implemented in the code I linked to earlier...
$dao = new MyDAO();
$dao->useDriver('mysql');
$dm->register('dao', $dao);
$dm->register('brain', 'SmartBrain');
$dm->register('person', 'MyPerson');
On a slightly different topic...
One of the big things I don't like about DI is the configuration steps. Too often, those seem to be put forward as an integral part of DI which I don't fully agree with. It seems to me that the configuration of the container is an implementation specific thing. I could just as easily hard code my own internal container, and expose it. That's actually one area that the underlying code of Pico (php) has got right. There are two container interfaces - the PicoContainer and the MutablePicoContainer. If I already know how my container needs to be configured and I don't want it being messed with (in the case of the DAO above, for example, where new options might be discovered by attempting to locate files in a given path), I should only implement PicoContainer and just expose it for loading classes.
Within a given scope... I'll refer back to my previous mentions in this post. I'm looking at DAO being completely seperate. The only thing that should be configurable on it is what I expose. The underlying logic of how it executes those configurations is left up to DAO. In my case, I might want to use some sort of DI to do it.Originally Posted by ghurtado
Well, I'm tryingOriginally Posted by ghurtado
![]()




Good links. I have to agree. I did some reading about HiveMind, Needle, and Pico today. I don't think its a good idea to force the container to be a singleton.Originally Posted by Travis S
Bookmarks