SitePoint Sponsor

User Tag List

Results 1 to 11 of 11
  1. #1
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)

    Introducing Dice, a minimal Dependency Injection Container for PHP

    I'm not usually one to release code into the wild but here's something which is both mature and unique enough to be worthwhile.

    Dice is a Convention-Over-Configuration Dependency Injection Container I've been working on. At its most basic level and with zero configuration it lets you build an object graph with little effort:

    PHP Code:
    class 
        private 
    $b

        public function 
    __construct(B $b) { 
            
    $this->$b
        } 


    class 

        private 
    $c,$d

        public function 
    __construct(C $cD $d) { 
            
    $this->$c
            
    $this->$d
        } 


    class 




    class 

        private 
    $e
         
        public function 
    __construct(E $e) { 
            
    $this->$e
        } 


    class 

         



    $dice = new Injection
    $a $dice->create('a'); 
    print_r($a); 
    Which will output:

    PHP Code:
    A Object 

        [
    b:A:private] => B Object 
            

                [
    c:B:private] => C Object 
                    

                    ) 

                [
    d:B:private] => D Object 
                    

                        [
    e:D:private] => E Object 
                            

                            ) 

                    ) 

            ) 



    As a practical example with a minimal configuration it allows you to do this:

    PHP Code:
    //Firstly create a rule 
    $rule = new DiceRule
    $rule->shared true

    //PDO will be constructed by the container with these parameters: 
    $rule->constructParams = array('mysql:host=127.0.0.1;dbname=mydb''username''password'); 

    $dice->addRule('PDO'$rule); 

    $pdo $dice->create('PDO'); 
    $pdo2 $dice->create('PDO'); 
    var_dump($pdo === $pdo2); //TRUE 


    //And any class which asks for an instance of PDO will be given the same instance: 
    class MyClass 
        public 
    $pdo
        public function 
    __construct(PDO $pdo) { 
            
    $this->pdo $pdo
        } 


    $myobj $dice->create('MyClass'); 
    var_dump($pdo === $myobj->pdo); 
    You would be able to define any class in the system with a dependency on PDO and it would be passed the shared instance of the PDO object on creation. This goes for a class which is any depth down the object graph:

    PHP Code:
    class MyApplication {

        public function 
    __construct(MyController $controller) {
            
        }

    }

    class 
    MyController {
        public function 
    __construct(PDO $pdo) {

        }
    }

    $dice->create('MyApplication'); 
    In this scenario, "MyController" would still be passed the shared instance of PDO when it was created.

    One important distinction that Dice makes is many other Dependency Injection Containers is that it doesn't need any metadata about most of the classes it's creating. Only those which have special rules such as being shared across the application

    Full Documentation/Downlad

    Any comments/suggestions?

    Yes, the temptation to call it TinyDIC or something was large but I thought I'd keep it serious because its uses are far fetched.

  2. #2
    Always A Novice bronze trophy
    K. Wolfe's Avatar
    Join Date
    Nov 2003
    Location
    Columbus, OH
    Posts
    2,178
    Mentioned
    64 Post(s)
    Tagged
    2 Thread(s)
    I had to read it twice (second time in the afternoon when I could comprehend what I'm reading). This seems very useful. For me, I've been trying to push the limits of php's ability to gain performance on multiple processes through forking, and most of the time that means duplicating out a new object to force a new connection.

    Quote Originally Posted by TomB View Post
    Yes, the temptation to call it TinyDIC or something was large but I thought I'd keep it serious because its uses are far fetched.
    Awesome! lol

  3. #3
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    May be worth comparing with Pimple -- an even tinier DIC.
    "First make it work. Then make it better."

  4. #4
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    As far as I'm aware Pimple doesn't support creation of object graphs or automatically resolve dependencies so is more of a way of creating a centralised set of factories than a true DI Container.

    For instance, in Pimple, this breaks it:

    PHP Code:
    class Foo {
            
    }


    $pimple = new Pimple;
    $foo $pimple['Foo'];

    var_dump($foo); 

    As such, Pimple requires a lot of interaction with the application developer and can't just sit transparently at the top level of the application.

    See the wikipedia entry here: http://en.wikipedia.org/wiki/Depende...ted_dependency for a demonstration of what I'm trying to achieve from a DIC

  5. #5
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    As far as I'm aware Pimple... is more of a way of creating a centralised set of factories than a true DI Container.
    A centralized set of factories actually seems to be a good description for exactly what a DIC is. Since a factory is a "method for creating objects" and a DIC is "an object that knows how to instantiate and configure objects."

    Your DIC, and automated injection in general, seems to try to reduce the number of factories we must define. It's an interesting feature and worth exploring. Though, if you're going to implement that feature, then calling your library minimalist may not be a fitting description.
    "First make it work. Then make it better."

  6. #6
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    I'm not sure that fully qualifies. If it cant create arbitrary objects and resolve their dependencies its usefulness is limited in scope because it has to be called every time a shared dependency is needed. The definition here: http://www.loosecouplings.com/2011/0...container.html suggests that "The entry points are the only places that should be aware that you are using a container. Everything else just gets supplied the required dependencies and doesn’t know or care where they came from. " with Pimple this isn't the case. If I have an object 10 steps down a chain that has a dependency which isn't shared by its parents, pimple can't create this easily unless the entire object graph is defined in a single factory.

    I suppose that depends on your definition of minimal. On one hand, "minimal" would be a simple factory, on the other, that requires the application developer to write a significant more code to use it. Although dice itself is small, my goal was the minimise the amount of code the end-user has to write in order to use it, so in this sense it is minimalist.

  7. #7
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    "The entry points are the only places that should be aware that you are using a container. Everything else just gets supplied the required dependencies and doesn’t know or care where they came from. " with Pimple this isn't the case.
    Seems to me that it is the case. For example:

    PHP Code:
    $container = new Pimple();

    // Tell Pimple how to create E objects
    $container['E'] = function ($container) {
        return new 
    E();
    };

    // Tell Pimple how to create D objects
    $container['D'] = function ($container) {
        
    // This is the entry point to D,
        // and entry points like this are the only places aware of the container.
        // Classes D and E are still unaware of the container.
        // They just get their dependencies without knowing where they came from.
        
    return new D$container['E'] );
    }; 
    I suppose that depends on your definition of minimal. On one hand, "minimal" would be a simple factory, on the other, that requires the application developer to write a significant more code to use it. Although dice itself is small, my goal was the minimise the amount of code the end-user has to write in order to use it, so in this sense it is minimalist.
    I guess you're right that it depends on our definition of minimal. In my experience, minimalist usually refers to the amount of code and features in a library. For example, a framework can minimize the amount of code the end-user has to write, but no one would claim that a framework is minimalist.
    "First make it work. Then make it better."

  8. #8
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Of course, but the container needs to know about every class it's ever going to create. In my opinion, that defeats the purpose and requires far too much configuration for any non-trivial real world project. Every time a class is added to the system the container needs to be reconfigured. Not good!

    True, which is why I only implemented features which are needed and avoided where possible the ability to achieve the same thing multiple ways. In my opinion, Dice doesn't contain anything that a DIC shouldn't and contains the fewest number of features that make it viable for real-world projects. I don't believe pimple does enough to make its use worthwhile; it requires just as much (if not more) work than just manually injecting every dependency, so I suppose that's more "minimal" but if it doesn't fulfil the requirements or add any benefit, what's the point? I could write a factory that referenced other factories and it would essentially be identical to pimple, yet "more minimal". Dice isn't actually much bigger than pimple (and almost half the code is for the optional XML loader).

  9. #9
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    Of course, but the container needs to know about every class it's ever going to create. In my opinion, that defeats the purpose and requires far too much configuration for any non-trivial real world project.
    Those kind of DICs are already being used in non-trivial, real-world projects, where use of the DIC is worthwhile and certainly better than manually injecting every dependency throughout your code. But that's beside the point. I didn't come here to debate manual vs automated injection, and I'm not even sure what my opinion is on that yet. I came here because you emphasized "tiny" and "minimal," so it seemed appropriate to mention Pimple. Except, it turns out, in your view, any container that doesn't use automated injection is not a "true" container.
    "First make it work. Then make it better."

  10. #10
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    I'm not quite sure why the debate over "minimal" is even happening, I clearly stated my project goals at the top of the page I linked to. It uses the smallest amount of code needed to complete the project goals with the fewest number of available options while retaining enough functionality to be useful in applications which need to push the limits of what the container can do.

    As for not a "true" container, my opinion is that if you're going make a decision to use a container but have to manually supply (often repeating) code for 99% of what the container is doing then the argument for adding an extra layer of complexity to the application is almost non-existent.

  11. #11
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    After re-reading that, I'll add some context:

    A container can save work and repeated code by using rules rather than hardcoded factories. For instance being able to say "Any class which asks for PDO in its constructor should be given this instance" or "Any class which inherits from Controller and asks for an instance of Session should be given this instance" instead of having to write factories for every single class in the system makes the container far easier to use, and means the developer doesn't need to repeat him/herself for each Controller that asks for a session instance. The most immediately obvious benefit is that a constructor can be changed completely and it'll just follow all the rules defined in the DIC- the DIC will not need to be reconfigured every time the class API changes. Whether or not a class has a dependency is irrelevant to the DIC, it just has to follow the rules on what it's doing (creating a new instance, using an existing instance) when it comes across a class which does ask for dependencies.


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
  •