SitePoint Sponsor

User Tag List

Results 1 to 11 of 11
  1. #1
    SitePoint Evangelist
    Join Date
    May 2006
    Posts
    438
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    Dependency Injection Question

    I think I have the hang of DI. Am I right in saying that you can inject in a function like this:

    PHP Code:
    public function foo(Bar $bar) {} 
    ^ This could be a constructor also.

    I haven't tried it yet but I assume you can do:

    PHP Code:
    private Bar $bar
    Is that the gist of it?

    My question is, how do you do dependency injection if you are creating an anonymous object? (Not sure if that is the right name for it)

    PHP Code:
    public function foo() {

        return new 
    Bar();


    How do you inject in that situation—or is that classed as injection?

  2. #2
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,234
    Mentioned
    154 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DrQuincy View Post
    I think I have the hang of DI. Am I right in saying that you can inject in a function like this:

    PHP Code:
    public function foo(Bar $bar) {} 
    ^ This could be a constructor also.
    Yes, to your first example, but I'd take it further and do

    PHP Code:
    public function foo(IBar $bar) {} 
    I like to use Interfaces instead purely because you can inject various implementations that abide by the contract of the interface giving you greater flexibility.

    Quote Originally Posted by DrQuincy View Post
    I haven't tried it yet but I assume you can do:

    PHP Code:
    private Bar $bar
    Is that the gist of it?
    I'm not sure what you are getting at here. Are you presuming you'd have a private variable within your class? Or are you creating that to send to your class? If the former, I'd again go with IBar to keep the flexibility and then the constructor would receive IBar as a parameter and assign it to the private variable.

    Quote Originally Posted by DrQuincy View Post
    My question is, how do you do dependency injection if you are creating an anonymous object? (Not sure if that is the right name for it)

    PHP Code:
    public function foo() {

        return new 
    Bar();


    How do you inject in that situation—or is that classed as injection?
    No, you are effectively making your class that contains foo() dependent on Bar now. This is not DI. This makes it nearly impossible to mock/test or process a different class that implements the same contract because you are forcing Bar to be used.

  3. #3
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,446
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)
    Quote Originally Posted by DrQuincy View Post
    I haven't tried it yet but I assume you can do:

    PHP Code:
    private Bar $bar
    ?
    No, that would be static typing, like in Java or C. PHP is dynamically typed, so you can't specify the type of a variable.

  4. #4
    SitePoint Evangelist
    Join Date
    May 2006
    Posts
    438
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    No, you are effectively making your class that contains foo() dependent on Bar now. This is not DI. This makes it nearly impossible to mock/test or process a different class that implements the same contract because you are forcing Bar to be used.
    Thanks, so in that instance, how do I make the class dependent on the class that has function foo()?

    PHP Code:
    public function foo(Bar $bar) {

        return new 
    Bar();


    That seems odd to me. I do find that this circumstance does come up every now and then.

  5. #5
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,234
    Mentioned
    154 Post(s)
    Tagged
    0 Thread(s)
    Let's go with a broader more specific example that is commonly used: Animals Speak

    PHP Code:
    <?php
    interface IAnimal
    {
        function 
    Speak();
        function 
    Eat();
        function 
    LastTimeAte();
    }

    abstract class 
    Animal implements IAnimal
    {
        protected 
    $lastTimeAte;
        public function 
    Speak()
        {
            throw new 
    NotImplementedException();
        }
        
        public function 
    Eat()
        {
            
    $this->lastTimeAte = new DateTime();
        }
        
        public function 
    LastTimeAte()
        {
            return 
    $this->lastTimeAte;
        }
    }

    class 
    Cat extends Animal
    {
        public function 
    Speak()
        {
            echo 
    "Meow!";
        }
    }

    class 
    Dog extends Animal
    {
        public function 
    Speak()
        {
            echo 
    "Woof!";
        }
    }

    class 
    Vet
    {
        private 
    $animal;
        public function 
    __construct(IAnimal $animal)
        {
            
    $this->animal $animal;
        }
        
        public function 
    Exam()
        {
            if (
    $this->animal->LastTimeAte() < new DateTime('1 week ago'))
            {
                
    $this->animal->Eat();
            }
        }
    }
    Now I can send either the Dog or the Cat to the Vet for an Exam. Vet doesn't need to know which one it is, it just needed to know that it implemented IAnimal.

  6. #6
    SitePoint Evangelist
    Join Date
    May 2006
    Posts
    438
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    That's a great example, thanks. But let me explain where I'm coming from.

    Example (simplified, not real code):

    PHP Code:
    class Database {

        
    // Other database stuff going on here

        
    public function query($query) {

            
    $result mysqli_query($this->link$query);

            return new 
    DatabaseResult($result);

        }

    }

    $result $database->query("SELECT * FROM foo"); 
    I can't create the result object until after the query so there is nothing to send to the database object. Can you not do DI under these circumstances?

  7. #7
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,234
    Mentioned
    154 Post(s)
    Tagged
    0 Thread(s)
    Let me ask this, what is the purpose of DatabaseResult?

    As an aside, I'd break up your query into two methods (query and fetch, and potentially fetchAll).
    PHP Code:
    class Database 

         private 
    $result;

        
    // Other database stuff going on here 

        
    public function query($query) { 

            
    $this->result mysqli_query($this->link$query); 
            return 
    $this// so I can chain other methods
            //return new DatabaseResult($result); 

        


        public function 
    fetch(IDatabaseResult $databaseResult)
        {
            return 
    $databaseResult->Process($this->result);
        }

        public function 
    fetchAll(IDatabaseResult $databaseResult)
        {
            return 
    $databaseResult->ProcessAll($this->result);
        }



    $result $database->query("SELECT * FROM foo")->fetch(new DatabaseResult());
    $result $database->query("SELECT * FROM foo")->fetchAll(new DatabaseResultCollection()); 

  8. #8
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,446
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)
    Cpradio, would DatabaseResult not be an instance of a 'newable' (see: http://misko.hevery.com/2008/09/30/t...or-not-to-new/) - a class that couldn't be retrieved from an injection container because it's an entity and holds state?

  9. #9
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,234
    Mentioned
    154 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by fretburner View Post
    Cpradio, would DatabaseResult not be an instance of a 'newable' (see: http://misko.hevery.com/2008/09/30/t...or-not-to-new/) - a class that couldn't be retrieved from an injection container because it's an entity and holds state?
    Not necessarily in the way I used it, but yes, it could be. Partially, which is why I asked what its' purpose is. If it's purpose is to store the information from the query and just relay that back (like a data transfer object), then yes, I'd agree it doesn't need DI.

    However, if the results could be parsed/processed differently based on application decisions, then it would benefit from DI.

  10. #10
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,314
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by DrQuincy View Post
    That's a great example, thanks. But let me explain where I'm coming from.

    Example (simplified, not real code):

    PHP Code:
    class Database {

        
    // Other database stuff going on here

        
    public function query($query) {

            
    $result mysqli_query($this->link$query);

            return new 
    DatabaseResult($result);

        }

    }

    $result $database->query("SELECT * FROM foo"); 
    I can't create the result object until after the query so there is nothing to send to the database object. Can you not do DI under these circumstances?
    In this case, I think you would need a factory class.

    PHP Code:
    class DatabaseResult
    {
        
    /*
        No changes needed here.
        This class can be ignorant of the larger outside world.
        */
    }

    class 
    DatabaseResultFactory
    {
        
    /*
        This class's sole job is to create DatabaseResult objects.
        It knows about the DatabaseResult class, but is otherwise ignorant of the larger outside world.
        */
        
        
    public function __construct()
        {
            
    /*
            Pass in any settings or information needed to create DatabaseResult objects.
            */
        
    }
        
        public function 
    create($queryResult)
        {
            return new 
    DatabaseResult($queryResult);
        }
    }

    class 
    Database
    {
        
    /*
        The database is injected with a DatabaseResultFactory.
        */
        
        
    private $databaseResultFactory;
        
        public function 
    __construct($databaseResultFactory)
        {
            
    $this->databaseResultFactory $databaseResultFactory;
        }
        
        public function 
    query($query)
        {
            
    $result mysqli_query($this->link$query);

            return 
    $this->databaseResultFactory->create($result);
        }

    For a small application, this will probably seem like overkill. But for large applications, it becomes critical that significant code changes nonetheless have a small impact on the overall system. Consider, for example, if one day we decide to swap out DatabaseResult for an EnhancedDatabaseResult. In your old code, you would have had to scrub your codebase to remove any use of DatabaseResult. But in this new code, we would make just one change in one place: we would inject Database with an EnhancedDatabaseResultFactory object instead of DatabaseResultFactory, and everything else would continue to work just like it did before.

    We can still make at least one more improvement. PHP is of course a loosely typed language, so we can pass any object to the Database constructor, and as long as that object has a create method, it will work. We can formalize that requirement by requiring an interface. (cpradio mentioned this earlier.)

    PHP Code:
    interface DatabaseResultFactoryInterface
    {
        public function 
    create($queryResult);
    }

    class 
    DatabaseResultFactory implements DatabaseResultFactoryInterface
    {
        
    /* ... */
    }

    class 
    Database
    {
        public function 
    __construct(DatabaseResultFactoryInterface $databaseResultFactory)
        {
            
    /*
            By requiring an interface, we ensure that whatever object that's passed in will have implemented a create method.
            */
        
    }

    "First make it work. Then make it better."

  11. #11
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,234
    Mentioned
    154 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    In this case, I think you would need a factory class.
    That's actually a really good example. Much more refine than mine and I was being too focused on DI and didn't look beyond it.


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
  •