2.6. Dependency Injection

2.6.1. Purpose

To implement a loosely coupled architecture in order to get bettertestable, maintainable and extendable code.

2.6.2. Usage

DatabaseConfiguration gets injected and DatabaseConnection will get all that itneeds from $config. Without DI, the configuration would be createddirectly in DatabaseConnection, which is not very good for testing andextending it.

2.6.3. Examples

  • The Doctrine2 ORM uses dependency injection e.g. for configurationthat is injected into a Connection object. For testing purposes,one can easily create a mock object of the configuration and injectthat into the Connection object
  • Symfony and Zend Framework 2 already have containers for DI thatcreate objects via a configuration array and inject them where needed(i.e. in Controllers)

2.6.4. UML Diagram

Alt DependencyInjection UML Diagram

2.6.5. Code

You can also find this code on GitHub

DatabaseConfiguration.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Structural\DependencyInjection;
  4.  
  5. class DatabaseConfiguration
  6. {
  7. /**
  8. * @var string
  9. */
  10. private $host;
  11.  
  12. /**
  13. * @var int
  14. */
  15. private $port;
  16.  
  17. /**
  18. * @var string
  19. */
  20. private $username;
  21.  
  22. /**
  23. * @var string
  24. */
  25. private $password;
  26.  
  27. public function __construct(string $host, int $port, string $username, string $password)
  28. {
  29. $this->host = $host;
  30. $this->port = $port;
  31. $this->username = $username;
  32. $this->password = $password;
  33. }
  34.  
  35. public function getHost(): string
  36. {
  37. return $this->host;
  38. }
  39.  
  40. public function getPort(): int
  41. {
  42. return $this->port;
  43. }
  44.  
  45. public function getUsername(): string
  46. {
  47. return $this->username;
  48. }
  49.  
  50. public function getPassword(): string
  51. {
  52. return $this->password;
  53. }
  54. }

DatabaseConnection.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Structural\DependencyInjection;
  4.  
  5. class DatabaseConnection
  6. {
  7. /**
  8. * @var DatabaseConfiguration
  9. */
  10. private $configuration;
  11.  
  12. /**
  13. * @param DatabaseConfiguration $config
  14. */
  15. public function __construct(DatabaseConfiguration $config)
  16. {
  17. $this->configuration = $config;
  18. }
  19.  
  20. public function getDsn(): string
  21. {
  22. // this is just for the sake of demonstration, not a real DSN
  23. // notice that only the injected config is used here, so there is
  24. // a real separation of concerns here
  25.  
  26. return sprintf(
  27. '%s:%s@%s:%d',
  28. $this->configuration->getUsername(),
  29. $this->configuration->getPassword(),
  30. $this->configuration->getHost(),
  31. $this->configuration->getPort()
  32. );
  33. }
  34. }

2.6.6. Test

Tests/DependencyInjectionTest.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Structural\DependencyInjection\Tests;
  4.  
  5. use DesignPatterns\Structural\DependencyInjection\DatabaseConfiguration;
  6. use DesignPatterns\Structural\DependencyInjection\DatabaseConnection;
  7. use PHPUnit\Framework\TestCase;
  8.  
  9. class DependencyInjectionTest extends TestCase
  10. {
  11. public function testDependencyInjection()
  12. {
  13. $config = new DatabaseConfiguration('localhost', 3306, 'domnikl', '1234');
  14. $connection = new DatabaseConnection($config);
  15.  
  16. $this->assertEquals('domnikl:1234@localhost:3306', $connection->getDsn());
  17. }
  18. }