SitePoint Sponsor

User Tag List

Page 3 of 5 FirstFirst 12345 LastLast
Results 51 to 75 of 116
  1. #51
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh, you are correct. IIRC, the false constant should require a temp var though.

  2. #52
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sweatje
    Oh, you are correct. IIRC, the false constant should require a temp var though.
    Yes, that's true, though it is an error anyway

    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
    Attached Files Attached Files
    Hello World

  3. #53
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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

  4. #54
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DougBTX
    Ooh, I like, nice job!
    I knew you would like it

    Quote Originally Posted by DougBTX
    I've added a line to DI::get ...
    I think you wanted to change the references to registry[$serviceName] to $service, so I just finished that for you.

    I also polished the test script a little bit, so usage is a little clearer for newcomers.

    Quote Originally Posted by sweatje
    The first question which comes to my mind is can a lambda return by reference? If not, you might have some trouble returning registered mockobjects effectivly.
    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.

    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.

    PHP Code:
    $registry[$serviceName]['constructor'] = create_function('$di'$block); 
    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:
    DI::register
        
    'store'
        
    'return new MyStore($di->("gateway"));'); 
    Or...
    PHP Code:
    DI::register
        
    'store'
        
    'return new MyStore(DI::get("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.

    Quote Originally Posted by DougBTX
    Anything else anyone would like to add? Would this be good code to use in the "skeleton" thread anywhere?
    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.

    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.

    Quote Originally Posted by DougBTX
    And it only took us two pages! Thanks for starting the thread heathd.
    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.

    Latest version attached.
    Attached Files Attached Files
    Garcia

  5. #55
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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

  6. #56
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by ghurtado
    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.
    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.

    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.

    Quote Originally Posted by ghurtado
    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.
    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.

    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

  7. #57
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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

  8. #58
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ghurtado
    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 can see people wanting to use the class like a "normal" singleton, like this:

    PHP Code:
    $di =& DI::getInstance();
    $di->register(
        
    'order'
        
    'return new MyOrder();'); 
    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 testcases

    Quote Originally Posted by ghurtado
    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.
    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.

    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
    Attached Files Attached Files
    Hello World

  9. #59
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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:

    Quote Originally Posted by myself in an email thread
    Ok - I'll try to summerize now.

    Basic Dependency Injection such as Pico works on create depedencies that it knows about as it builds the objects. This is done via configuration or reflection. Contextualized Lookup moves the burden of fulfilling dependencies to the object itself. What I believe would be useful in library/framework settings is the ability to register namespaces/packages of possible implementations. The container would then iterate through the available packages and attempt to find an implementation.

    One thing I am considering is whether or not the InterfaceContainer should return just the name of the object, or some sort of variant of the Proxy pattern. My thought is that the InterfaceProxy could then determine how the interface should be instantiated (does it need other objects, is this a Singleton, etc.).

    I've put together a UML diagram in dia. The last two revisions of the .dia file located at:
    https://svn.domain51.net/svn/article...alized-lookup/
    The latest revision shows the use of an InterfaceProxy, though that's really an implementation detail that I can worry about later.

    The code to use something like this would be:
    PHP Code:
    <?php
       
    include '/path/to/primary/Package1.php';
       include 
    '/path/to/alternative/Package2.php';

       
    $dm = new DependencyManager();
       
    $dm->registerPackage(new Package1());
       
    $dm->registerPackage(new Package2());

       
    $myObject $dm->loadObject('RequestedInterface');
    ?>
    There are two implementation specific issues. First, how to make objects aware of the DependencyManager. A Registry could easily be used, but that should be left to the developer in my opinion. The second issue is how the InterfacePackages are implemented. This is an obvious choice for some sort of configuration file usage, though it could just as easily be hard-coded (as the example above would indicate).

    My initial thought is to provide an INI-based configuration, an XML-based configuration, and possibly a native array-based configuration. Moving the knowledge of what files exist into an external configuration file would help in lazy loading as the configuration would know what file a given class implements. It would probably be worthwhile to have some sort of utility object that could scan a directory and create the various configuration files so you wouldn't have to hand-edit package configuration at every time a new object/interface is introduced, but as I so often do, I'm getting ahead of myself now.

    Over the next few days, I'm going to put together some tests and code and get something like this together and I'll email you with it again.
    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.

    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.

  10. #60
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Travis S
    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:

    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"));'); 
    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.

    Regards,
    Douglas
    Hello World

  11. #61
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    The eval/block based approach is certainly a drastic solution. I could never quite bring mysef to do it
    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.

    Quote Originally Posted by lastcraft
    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?
    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.

    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.

    Quote Originally Posted by lastcraft
    I think DI should be used in small doses, as in some ways it's is a little too powerful.
    Absolutely. The same can be said for create_function. A comment on the PHP manual entry put it best:
    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.
    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.

    Thanks a lot for your input, it really means a lot.
    Garcia

  12. #62
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DougBTX
    I can see people wanting to use the class like a "normal" singleton
    I agree. Best left in for those who may need it. Personally, I still favor the following syntax:
    PHP Code:
     DI::register(
        
    'store',
        
    'return new MyStore(DI::get("gateway"));'); 
    But you are right, there are probably many people who would rather use it as a "normal" singleton.

    Quote Originally Posted by DougBTX
    Latest version attached.
    I like the tests, but I have a confession to make: I haven't installed simpleTest yet 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.

    Quote Originally Posted by DougBTX
    (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.)
    Absolutely, I can't think of a better way to brainstorm than ping-pong coding.

    Travis,

    I have a question about your implementation
    PHP Code:
    $dm = new DependencyManager();
    $dm->registerPackage(new Package1());
    $dm->registerPackage(new Package2()); 
    How would you handle lazy loading with that setup?

    Thanks to everyone for their input.
    Garcia

  13. #63
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DougBTX
    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"));'); 
    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.


    Quote Originally Posted by ghurtado
    Travis,

    I have a question about your implementation
    PHP Code:
    $dm = new DependencyManager();
    $dm->registerPackage(new Package1());
    $dm->registerPackage(new Package2()); 
    How would you handle lazy loading with that setup?

    Thanks to everyone for their input.
    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().

    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...

    Quote Originally Posted by Travis S
    My initial thought is to provide an INI-based configuration, an XML-based configuration, and possibly a native array-based configuration....
    This would be the equivilant of having:
    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. 
    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:
    $pico = new PicoLikeInterfaceContainer();
    $pico->registerComponentInstance('BlueBird');
    $dm = new DependencyManager();
    $dm->registerPackage($pico);
    $component $dm->loadObject('Bird''Blue');
    $component instanceof BlueBird
    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.

  14. #64
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by Travis S
    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.
    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?".

    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

  15. #65
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I always thought the Handle mechanism from WACT could evolve into a DI container. Inspired by this thread, an experiment your consideration:


    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 Handle object encapsulates object construction. So these are equivalent:
    PHP Code:
    Handle('class');
    new class(); 
    The second parameter to Handle is an array of parameters for the constructor. So these are equivalent:
    PHP Code:
    Handle('class', array('a'));
    new class(
    'a'); 
    The array of construction parameters can specify calls to setter methods. So these are equivalent:
    PHP Code:
    Handle('class', array('setter' => 'value'));

    $obj =& new class();
    $obj->setter('value'); 
    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:
    $container->registerComponent('DatabaseConnection'
        new 
    Handle('DatabaseConnection', array(new Injection('DatabaseConfiguration')))); 
    Any place a Handle can be used, an object instance can be used as well. Thus these cases work:
    PHP Code:
    $container->registerComponent('injectionContainer'$container);
    $container->registerSingletonComponent('DatabaseConfiguration', new DatabaseConfiguration()); 
    Interestingly, anywhere a Handle can be used, an Injection object can also be used. This allows the alias case:
    PHP Code:
    $container->registerComponent('ConnectionAlias', new Injection('DatabaseConnection')); 
    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:
    new Callback(new Injection('GlobalListener'), 'yowza'
    Attached Files Attached Files
    Last edited by Selkirk; Sep 10, 2005 at 21:53. Reason: Went to bed but decided to get back up and offer more explaination

  16. #66
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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

  17. #67
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Travis S
    The injector shouldn't care how its stored instantiated.
    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.

    Douglas
    Hello World

  18. #68
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Travis S
    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.
    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?

    Feel free to prove me wrong, hopefuly with some example code
    Garcia

  19. #69
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

    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;
        }

    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).

    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:
    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());
        }

    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.

    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?

  20. #70
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ghurtado
    Feel free to prove me wrong, hopefuly with some example code :)
    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.

    My bootstrap looks like:
    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(); 
    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.

    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.

    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();
      } 
    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.

    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.

  21. #71
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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:

    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!');

    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!).

    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
    Attached Files Attached Files
    Hello World

  22. #72
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Travis S
    Pico's coders have already written about this at Container Instantiation and Container Dependency and they are definitely more articulate than I could be.
    I just read through those two pages, and a few more at the Pico site. A couple of things that I thought were interesting:
    • 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.

    Quote Originally Posted by Travis S
    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.
    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.

    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.

    Quote Originally Posted by Travis S
    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.
    This is how I would handle that situation based on our proposed DI posted above:
    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"));'); 
    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.

    Quote Originally Posted by Travis S
    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.
    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.

    Quote Originally Posted by Travis S
    Have I won you over?
    Sorry, not yet But thanks for your patience, I am enjoying this discussion greatly.
    Garcia

  23. #73
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ghurtado
    Sorry, not yet But thanks for your patience, I am enjoying this discussion greatly.
    I'm with you on this one, but this code might be helpful: (any extra PHP5.1 hacks not withstanding...)

    PHP Code:
    function &getInstance () {
            static 
    $instance;
            
            if ( isset(
    $this) && is_a($this'DI')) {
               return 
    $this;
            }
            
            if ( !isset(
    $instance) ) {
                
    $instance = new DI();
            }
            
            return 
    $instance;

    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.

    Douglas
    Hello World

  24. #74
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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:

    Quote Originally Posted by ghurtado
    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"));'); 
    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.
    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.

    To continue the DAO use, I might do something like this:
    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'); 
    When dao->query() is called, it would then use its own internal container to figure out which driver to pull.


    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.


    Quote Originally Posted by ghurtado
    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.
    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.

    Quote Originally Posted by ghurtado
    Sorry, not yet But thanks for your patience, I am enjoying this discussion greatly.
    Well, I'm trying

  25. #75
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Travis S
    Pico's coders have already written about this at Container Instantiation and Container Dependency and they are definitely more articulate than I could be.
    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.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •