SitePoint Sponsor

User Tag List

Page 4 of 5 FirstFirst 12345 LastLast
Results 76 to 100 of 116
  1. #76
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DougBTX
    Well, while we are coding in PHP, we might a well code in PHP.

    PHP Code:
    <pre><?php

    class FileLogger { function FileLogger $foo ) { } }
    class 
    Application { function run ( ) { echo '..running..'; } }

    class 
    Container {
        var 
    $things = array();
        function &
    get $something ) {
            if ( !isset(
    $this->registry[$something]) ) {
                
    $this->things[$something] = $something($this);
            }
            return 
    $this->things[$something];
        }
    }

    function 
    LogFile (&$c) { return "logfile.log"; }
    function 
    Logger (&$c) { return new FileLogger($c->get('LogFile')); }
    function 
    Application (&$c) {
        
    $application = new Application;
        
    $application->logger $c->get('Logger');
        return 
    $application;
    }

    $c =& new Container;
    $app =& $c->get('Application');
    $app->run();

    ?></pre>
    Even if it is as close a rip of the Ruby as I can get in a first try. I think PHP dulls the mind, and that Container should be a singleton. (In the Ruby version there is a ContainerRegistry, and it is the singleton. What's the code of the day for making singletons in PHP4/5 = PHP0.8 as I think I'll call it?)

    Douglas
    Very nice code. Thumbs up.

  2. #77
    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 stereofrog
    Very nice code. Thumbs up.
    In the post after that, I wrap the set of functions in a class namespace-style so that you can swap out containers easily.

    Douglas
    Hello World

  3. #78
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I like your ideas, DougBTX. It must be amazing feeling to be that creative.

  4. #79
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by stereofrog
    Very nice code. Thumbs up.
    Agreed. It's simple and effective. I don't think it's appoipriate to label it DI though. It's more of a registry of factories, and thus a ServiceLocator. To call it DI, I would think that the DI would need to be more intelligent. In this implementation, the smartness is handcoded by the programmer, who write the factories.
    I may be on thin ice here - the boundries between the various patterns are really fuzzy.

    Incidentially, I created an implementation which very much resemble that code, recently. I handled the singleton-strategy by simply writing a static class, which is the userland-api to the locator. This would then fetch the needed factories from a registry. The registry itself can be replaced at runtime, allowing for some flexibility. (useful during testing for example)

  5. #80
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    It's simple and effective. I don't think it's appoipriate to label it DI though. It's more of a registry of factories, and thus a ServiceLocator. To call it DI, I would think that the DI would need to be more intelligent. In this implementation, the smartness is handcoded by the programmer, who write the factories.
    And in Java DI containers, the factories are generated by inspecting the component to be instantiated. I think thats mostly a convenience and not essential to the pattern. The feature that makes it DI is that the component receives instances of the objects that it depends on from a third party assembler object. It may be "dumb" DI, but its still DI.

  6. #81
    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 kyberfabrikken
    Agreed. It's simple and effective. I don't think it's appoipriate to label it DI though. It's more of a registry of factories
    I'd say that a "registry of factories" would be a good description for a ServiceLocator or DI, but not good enough to distinguish them. The defining difference (as has been said a couple of times in this thread) is that a ServiceLocator is used to locate dependencies (== services which are depended on) by the object which needs them, whereas in Dependency Injection the dependencies are provided to the object by something external.

    In the first case, the "registry of factories" are only responsible for constructing the dependencies, in the second case (when using constructor injection) the "registry of factories" is responsible for constructing the object itself too, which is what the Logger and Application functions do.

    With setter injection you could pass in instances instead of having the DI construct them, here's some code to inject dependencies using setters:

    PHP Code:
    function &inject_dependencies_using_setters(&$object$dependencies) {
        
    reset($dependencies);
        while (list(
    $method$value) = each($dependencies)) {
            
    $object->$method($value);
        }
        return 
    $object;
    }
    $thing = new Thing();
    inject_dependencies_using_setters($thing, array('setOrder' => new Order)); 
    (You'll need to add some more code to manage storing instances or to interact with a separate registry to make it useful though.)

    Quote Originally Posted by kyberfabrikken
    In this implementation, the smartness is handcoded by the programmer, who write the factories.
    Yep, I'm with you on that, it isn't particularly flexable, which is why I made this post. There, you can replace the Container class instance easily (it is in the second block of code, below the fold.)

    Douglas
    Hello World

  7. #82
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Selkirk
    The feature that makes it DI is that the component receives instances of the objects that it depends on from a third party assembler object. It may be "dumb" DI, but its still DI.
    In that case, passing an object is DI. While that may well be true in the strictest sense of the word, it doesn't describe the pattern, which pico et al follow. Perhaps we should destinguish between DI as a general technique and ComponentContainer as a specific way of archiving it. ServiceLocator being another way to do the same thing.

    Quote Originally Posted by DougBTX
    Yep, I'm with you on that, it isn't particularly flexable
    On the contrary - it's extremely flexible, but at the cost of automation. Combining it with a default automated behaviour solves this problem somehow. There'll still be redundant code in the specialcases, but that may be an acceptable tradeoff.

  8. #83
    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 kyberfabrikken
    In that case, passing an object is DI. While that may well be true in the strictest sense of the word, it doesn't describe the pattern, which pico et al follow.
    Yes, passing an object is DI - when you put things so simply you wonder why people make such a fuss, don't you?

    Pico is just an implementation, and an implementation which mimics Pico is just an implementation which mimics Pico, it doesn't redefine the pattern.

    Douglas
    Hello World

  9. #84
    ********* 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 Selkirk
    The feature that makes it DI is that the component receives instances of the objects that it depends on from a third party assembler object. It may be "dumb" DI, but its still DI.
    Disagree. That statement describes a builder pattern and factory pattern combined only. I think DI is a combination of things and it's this combination that makes it qualitively different. Without the autowiring, the container becomes more of a builder becuase you need to give explicit instructions. However, it can still automatically apply decorators and select from a hiearchy of choices. That's a very specific smart builder.

    Of course it now means that I have to define it and if I do, I will take something for granted and miss that out to . So I'm not going to . Here is a partial list of important properties...
    1) Autowiring (automatic dependency instantiation)
    2) Smart lifecycle instantiation (singleton, sessions, etc)
    3) Hierachical service location from preregistered descriptions (usually just a class name with recency of registration)
    4) Invisible to components (uses setters, constructor)

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  10. #85
    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 kyberfabrikken
    On the contrary - it's extremely flexible
    I meant in the sense that you can't have two Logger functions in the same scope like this:

    PHP Code:
    function Logger() {
        return new 
    BasicLogger();
    }

    function 
    Logger() {
        return new 
    TestingLogger();

    So limiting yourself to the global scope makes things less flexable, wrapping in classes makes it easier.

    Douglas
    Hello World

  11. #86
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DougBTX
    I meant in the sense that you can't have two Logger functions in the same scope like this
    I misunderstood you then. I concur.

  12. #87
    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 lastcraft
    Here is a partial list of important properties...
    1) Autowiring (automatic dependency instantiation)
    2) Smart lifecycle instantiation (singleton, sessions, etc)
    3) Hierachical service location from preregistered descriptions (usually just a class name with recency of registration)
    4) Invisible to components (uses setters, constructor)
    I'd say that the first three are implementation details, and only the fourth distinguishes DI.

    From the Fowler article:

    For this new breed of containers the inversion is about how they lookup a plugin implementation. In my naive example the lister looked up the finder implementation by directly instantiating it. This stops the finder from being a plugin. The approach that these containers use is to ensure that any user of a plugin follows some convention that allows a separate assembler module to inject the implementation into the lister.
    Where a "plugin" is just an object.

    Douglas
    Hello World

  13. #88
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    In that case, passing an object is DI.
    I think having a third party "assembler" is an essential part of the DI pattern. What is NOT essential to the DI pattern is the method of registeration. All the following are still DI:

    PHP Code:
    $container->register('MyStore');
    // use reflection and interfaces ala pico 
    PHP Code:
    DI::register
        
    'store'
        
    'return new MyStore(DI::get("gateway"));');
        
    // ala Needle 
    PHP Code:
    $container->registerComponent('store'
        new 
    Handle('MyStore', array(new Injection('gateway'))));  
        
    // WACT style 
    HiveMind uses an XML description for registering components. They are all implementations of the Dependency Injection pattern.

  14. #89
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Forgive me if I misunderstand the previous conversation regarding DI::get(...) versus $di->get(...). I don't think i've ever used the static method call singleton where I haven't later regretted it. How about $this->get(...) instead?

  15. #90
    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 Selkirk
    Forgive me if I misunderstand the previous conversation regarding DI::get(...) versus $di->get(...). I don't think i've ever used the static method call singleton where I haven't later regretted it. How about $this->get(...) instead?
    What is $this? Here is the code for $di->get(...).

    Douglas
    Hello World

  16. #91
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oops. Sorry, I was thinking eval() not create_function():
    PHP Code:
            $registry[$serviceName]['constructor'] = create_function('&$di'$block); 

  17. #92
    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 Selkirk
    Forgive me if I misunderstand the previous conversation regarding DI::get(...) versus $di->get(...). I don't think i've ever used the static method call singleton where I haven't later regretted it. How about $this->get(...) instead?
    Unfortunately, as Douglas explained, the "$this" scope is not available from within the function, although theres the "$di" parameter in case you want to make a traditional-style method call. Either way you are bound to either the name of the class or the name of the parameter, so it's kinda six on the one hand or a half a dozen on the other. I favor this style:
    PHP Code:
     DI::register(
        
    'store',
        
    'return new MyStore(DI::get("gateway"));'); 
    My reasoning behind it being that if you ever have to change the name of the singleton class, all the changes are centralized on the same line.
    Quote Originally Posted by sweatje
    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.
    (sorry that it took me so long to reply to that, Jason, this thread is like a part time job )
    As Jason and others have pointed out, it might be a good idea for some to allow separate instances of the container/injector to be created, and do away with the singleton idea. Jason gives the example of unit testing, where you obviously want a set of mock objects that are not quite the same "services" you define in other parts of your application. For an instance, within your test class, you may want to use the container to retrieve an instance of the "Test" database connector as opposed to the live ones in order to do your testing.

    I know that I am in the minority in this opinion, but, personally, I would probably tackle the issue from a different angle, while still using a singleton registry. I would either do something like this in my configuration script:

    PHP Code:
     DI::register(
        
    'database',
        
    'return new DbDriver("mysql", "host_prod", "username1", "password1");'); 

     
    DI::register(
        
    'test_database',
        
    'return new DbDriver("mysql", "host_test", "username2", "password2");'); 

     
    DI::register(
        
    'order',
        
    'return new MyOrder(DI::get("database"));'); 

     
    DI::register(
        
    'test_order',
        
    'return new MyOrder(DI::get("test_database"));'); 
    Which allows you to replace any aspect of your registry to inject it with mock objects. At the same time you get to see at a glance and from one single place what each "service" or "component" is made up of.

    Additionally, this is also an aspect of flexibility that other DI implementations do not have: to allow for different named services that may share the same interface dependencies, but do not neccesarily have to be composed of the same classes. Unless I am mistaken, because of its reliance on interfaces, Pico does not allow you to create two different named components that share the same interface. Since interfaces are the "lookup key" within pico, you can only get either the one or the other. Personally, I think this is very limiting, specially for testing purposes. There are ways around it, as seen here in this portion of Jason's example:

    PHP Code:
          $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); 
    But then you have to mess with unregistering and re-registering components, because Pico limits you to "one interface per service". I would rather have the control over how many services I want to define which follow the same interface.

    The second option you have, if you would rather not mess with the central registry in order to set up your tests, would be to redefine them inside your test class like this:

    PHP Code:
     DI::register(
        
    'order',
        
    'return new MyOrder(DbDriver("mysql", "host_test", "username2", "password2"));'); 
    (a little more like the approach you currently use in your test, Jason)

    From this point forward in your script, you know that whenever you retrieve the named service/component "order", it will be a mock one. I would even argue that this method allows you more flexibility during testing in that not every single class that you are testing needs to be extracted into a mock, you can inject the mocks at any point in the dependency chain, where it matters. I don't need a MockOrder class if all that is different about it is the database driver that I pass to it in order to run my tests.

    My example is probably flawed in a dozen different ways, since I am no unit testing expert (I am falling way behind the majority on these forums) but it shows what I think would be a straightforward approach to modifying the dependency registry in order to carry out your tests.
    Garcia

  18. #93
    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 stereofrog
    I like your ideas, DougBTX. It must be amazing feeling to be that creative.
    I second that. That's why it's so important to have people like Douglas in this forum; he's never afraid of thinking outside the box or questioning the status quo.
    Garcia

  19. #94
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ghurtado
    Unfortunately, as Douglas explained, the "$this" scope is not available from within the function, although theres the "$di" parameter in case you want to make a traditional-style method call. Either way you are bound to either the name of the class or the name of the parameter, so it's kinda six on the one hand or a half a dozen on the other.
    Would it be worth it to switch to eval() to get a $this and thus avoid the binding altogether?

  20. #95
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You do Lazy Instansiate but not Lazy Load of classes. Probably could be handled elsewhere, but you might add the optional ablity to load the classes used. This give it some Handle qualities but you don't have to use it if you don't want to.
    PHP Code:
    DI::register(
        
    'gateway',

        
    '$gateway = new MyPaymentGateway(new DBConnection('mydb'));
         $gateway->setOrder($di->get("order"));
         return $gateway;'
    ,

         array(
    'DBConnection' => 'lib/DB/DBConnection.php',
              
    'MyPaymentGateway' => 'app/gateways/MyPaymentGateway.php'
              
    ),
         ); 
    with
    PHP Code:
        function register $serviceName$block$classes=array() ) {
            
    $di =& DI::_getInstance();
            
    $registry =& $di->registry;
            
    $registry[$serviceName] = array();
            
    $registry[$serviceName]['constructor'] = create_function('$di'$block); 
    // * NEW
            
    $registry[$serviceName]['classes'] = $classes
        }

        function &
    get $serviceName ) {
            
    $di =& DI::_getInstance();
            
    $registry =& $di->registry;
            
    $service =& $registry[$serviceName];
            
            if ( isset(
    $service['object']) ) {
                return 
    $service['object'];
            }
            
            if ( isset(
    $service['constructor']) ) {
    // * NEW
                
    if ($service['classes']) {
                    foreach (
    $service['classes'] as $class => $filename) {
                        if (! 
    class_exists($class)) {
                            include_once(
    $filename);
                        }
                    }
                    
    $service['classes'] = null;    // only do this once
                
    }

                
    $service['object'] = $service['constructor'](DI::_getInstance());
                return 
    $service['object'];
            }
            return 
    false;
        } 
    Christopher

  21. #96
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I like the idea, but would it be best to separate that functionality into another class? After all, _autoload will give us the perfect hook point for just this kind of situation (or will it?)

    Also, I don't think you need to check whether the class exists, since include_once will, well, only "include once"
    Garcia

  22. #97
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    More in the spirit would be:

    PHP Code:
    DI::register(
        
    'gateway',
        
    '$gateway = new MyPaymentGateway($di->get("dbconn"));
         $gateway->setOrder($di->get("order"));
         return $gateway;'
    ,
         
    'app/gateways/MyPaymentGateway.php'); 
    But... I don't really like having all that code in quotes. In Ruby, great. In PHP, it feels half baked. In Ruby you have the auto-return, you don't need "return $gateway;" and "$gateway =" is optional.

    So, I'd say keep it simple by only doing constructor injection, then have this:

    PHP Code:
    $di->reg('gateway''MyPaymentGateway', array('dbconn'),
                        
    'app/gateways/MyPaymentGateway.php'); 
    With the last two params optional. I'd also have the third param auto wrapped in an array if there is only one dependency, so that this shorter version would be fine:

    PHP Code:
    $di->reg('gateway''MyPaymentGateway''dbconn'); 
    Douglas
    Hello World

  23. #98
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    At the risk of adding even more DI code to the mix... I've just put together a preview release of my own home grown DI. It's vary rudimentary right now, but the current d51MutableInterfaceContainer is completely automatic. You call register('KnownClass') and it looks up the interfaces it implements.

    Like I said, this is all very rudimentary. I need to create an interface container that doesn't rely on Reflection so the class does not have to be loaded in order to work. I will probably handle that by using containers that load config files. With that (those?) container(s) in place, it'll be easier to backport to PHP 4. ObjectFactory would need to change its instantiation so it automatically knows what/how to load itself instead of figuring it out when loadObject() is called.

    Thoughts and comments are always welcome.

    As a side note - regardless of the actual proveable usefullness of DI in the real world... this was a heck of a lot of fun to code

    edit Got so caught up in the moment I forgot to add a link...

    bz2: https://svn.domain51.net/svn/d51DM/tags/d51dm-preview-0.1.tar.bz2
    svn: https://svn.domain51.net/svn/d51DM/tags/preview-0.1/

  24. #99
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ironically enough, we actually dont need any scaffolding code for "block-based" DI (except tiny singleton wrapper)
    PHP Code:
    function &single($klass) {
        static 
    $map = array();
        if(!isset(
    $map[$klass])) {
            
    $e=(($c=count($a=func_get_args()))<2)?''
                
    :'$a['.implode('],$a[',range(1,--$c)).']';
            eval(
    "\$map['$klass']=&new $klass($e);");
        }
        return 
    $map[$klass];

    PHP Code:
    // playtoys
    // ids show that we have singletons
    class App {
        function 
    App(&$db, &$logger) {
            
    $this->id rand();
            
    $this->db = &$db;
            
    $this->logger = &$logger;
        }
        function 
    run() {
            
    var_dump($this);
        }
    }
    class 
    DbLogger {
        function 
    DbLogger(&$db) {
            
    $this->id rand();
            
    $this->db = &$db;
    }}
    class 
    DB {
        function 
    DB($dsn) {
            
    $this->id rand();
            
    $this->dsn $dsn;
    }} 
    PHP Code:
    // Config is able to handle everything by itself.
    class Config {
        var 
    $dsn "mysql://user:pass@host/db";

        function &
    App() {
            return 
    single('App'$this->DB(), $this->Logger());
        }
        function &
    Logger() {
            
    $l = &single('DbLogger');
            
    $l->db = &$this->DB();
            return 
    $l;
        }
        function &
    DB() {
            return 
    single('DB'$this->dsn);
        }

    PHP Code:
    // here we go
    $conf = new Config();
    $app = &$conf->App();
    $app->run(); 
    So we have full DI without any DI code... lovely

  25. #100
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by stereofrog
    So we have full DI without any DI code... lovely
    Only, you lost all track of referential integrity.


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
  •