Timon Schroeter 1 www.php-schulung.de Dependency Injection DI
Timon Schroeter 1 www.php-schulung.de
Dependency Injection
DI
Timon Schroeter 2 www.php-schulung.de
Specificity
Stability
UnitTests
AcceptanceTests
FunctionalTests
IntegrationTests
Not all tests are created equal
Timon Schroeter 3 www.php-schulung.de
Specificity
Stability
UnitTests
AcceptanceTests
FunctionalTests
IntegrationTests
Most valuable,require high qualitywell structured code
Not all tests are created equal
Timon Schroeter 4 www.php-schulung.de
Real Unit Tests
● Test a single unit of code, i.e. a single class
Timon Schroeter 5 www.php-schulung.de
Real Unit Tests
● Test a single unit of code, i.e. a single class
We need to be able toreplace (mock/stub)
dependencies dynamically
Timon Schroeter 6 www.php-schulung.de
Structure of this presentation
● Why do we want Dependency Injection?● Code example: DI for generic PHP classes● Code example: DI in Symfony 2
You are here
Timon Schroeter 7 www.php-schulung.de
<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;
class FeedAggregator {private $client;private $logger;
public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Why is this classdifficult to unit test?
Timon Schroeter 8 www.php-schulung.de
<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;
class FeedAggregator {private $client;private $logger;
public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
What if we want unit tests to run fastwithout waiting for the network?
Why is this classdifficult to unit test?
Timon Schroeter 9 www.php-schulung.de
<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;
class FeedAggregator {private $client;private $logger;
public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
What if we want unit tests to run fastwithout waiting for the network?
What if we want unit tests to run fastwithout logging?
What if we want unit tests to run fastwithout logging?
Why is this classdifficult to unit test?
Timon Schroeter 10 www.php-schulung.de
<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;
class FeedAggregator {private $client;private $logger;
public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
What if we want unit tests to run fastwithout waiting for the network?
What if we ever want to
use a different HTTP client?
What if we want unit tests to run fastwithout logging?
What if we want unit tests to run fastwithout logging?
Why is this classdifficult to unit test?
Timon Schroeter 11 www.php-schulung.de
<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;
class FeedAggregator {private $client;private $logger;
public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
What if we want unit tests to run fastwithout waiting for the network?
What if we ever want to
use a different HTTP client?
What if we want unit tests to run fastwithout logging?
What if we ever want to
use a different logger class?
What if we want unit tests to run fastwithout logging?
Why is this classdifficult to unit test?
Timon Schroeter 12 www.php-schulung.de
<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;
class FeedAggregator {private $client;private $logger;
public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
What if we want unit tests to run fastwithout waiting for the network?
What if we ever want to
use a different HTTP client?
What if we want unit tests to run fastwithout logging?
What if we ever want to
use a different logger class?
What if we ever want to
use a different log format?
What if we want unit tests to run fastwithout logging?
Why is this classdifficult to unit test?
Timon Schroeter 13 www.php-schulung.de
<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;
class FeedAggregator {private $client;private $logger;
public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
What if we want unit tests to run fastwithout waiting for the network?
What if we ever want to
use a different HTTP client?
What if we want unit tests to run fastwithout logging?
What if we ever want to
use a different logger class?
What if we ever want to
use a different log format?
What if we want unit tests to run fastwithout logging?
Why is this classdifficult to unit test?
Dependencies are pulled.=> Replacing requires refactoring=> Dynamic replacing (only for
testing) is impossible
Timon Schroeter 14 www.php-schulung.de
<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;
class FeedAggregator {private $client;private $logger;
public function __construct () {
$this->client = new Client();$this->logger = new XmlLogger();
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Dependencies are pulled.
Timon Schroeter 15 www.php-schulung.de
<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;
class FeedAggregator {private $client;private $logger;
public function __construct(ClientInterface $client, LoggerInterface $logger) {
$this->client = $client;$this->logger = $logger;
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Dependencies are pushed.
Timon Schroeter 16 www.php-schulung.de
<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;
class FeedAggregator {private $client;private $logger;
public function __construct(ClientInterface $client, LoggerInterface $logger) {
$this->client = $client;$this->logger = $logger;
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Dependencies are pushed.
Class only dependson interfaces
Timon Schroeter 17 www.php-schulung.de
<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;
class FeedAggregator {private $client;private $logger;
public function __construct(ClientInterface $client, LoggerInterface $logger) {
$this->client = $client;$this->logger = $logger;
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Dependencies are pushed.
Class only dependson interfaces
Implementations areinjected at runtime
Timon Schroeter 18 www.php-schulung.de
<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;
class FeedAggregator {private $client;private $logger;
public function __construct(ClientInterface $client, LoggerInterface $logger) {
$this->client = $client;$this->logger = $logger;
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Dependencies are pushed.
Class only dependson interfaces
Implementations areinjected at runtime
Easy to replace, evendynamically (for testing)
Timon Schroeter 19 www.php-schulung.de
<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;
class FeedAggregator {private $client;private $logger;
public function __construct(ClientInterface $client, LoggerInterface $logger) {
$this->client = $client;$this->logger = $logger;
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Dependencies are pushed.
Class only dependson interfaces
Implementations areinjected at runtime
Easy to replace, evendynamically (for testing)
On the level of the class,You are now experts for Dependency Injection.
Timon Schroeter 20 www.php-schulung.de
<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;
class FeedAggregator {private $client;private $logger;
public function __construct(ClientInterface $client, LoggerInterface $logger) {
$this->client = $client;$this->logger = $logger;
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Dependencies are pushed.
Class only dependson interfaces
Implementations areinjected at runtime
Easy to replace, evendynamically (for testing)
On the level of the class,You are now experts for Dependency Injection.
Any questions?
Timon Schroeter 21 www.php-schulung.de
<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;
class FeedAggregator {private $client;private $logger;
public function __construct(ClientInterface $client, LoggerInterface $logger) {
$this->client = $client;$this->logger = $logger;
}
public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {
$this->logger->log('Could not get: '.$host.$path);return null;
}
return $response->getBody();}// ...
}
Dependencies are pushed.
Class only dependson interfaces
Implementations areinjected at runtime
Easy to replace, evendynamically (for testing)
On the level of the class,You are now experts for Dependency Injection.
Who constructsand pushes all the
dependencies?
Timon Schroeter 22 www.php-schulung.de
Timon Schroeter 23 www.php-schulung.de
Dependency Injection Container
DIC
“DI Container”, “DIC”, “Service Container”, “the Container”
Timon Schroeter 24 www.php-schulung.de
Timon Schroeter 25 www.php-schulung.de
Very Many Frameworks support Dependency Injection
Timon Schroeter 26 www.php-schulung.de
Vielen Dank für Eure Aufmerksamkeit!
Timon Schroeter 27 www.php-schulung.de
SOLID
● S Single Responsibility Principle● O Open / Close Principle● L Liskov Substitution Principle● I Interface Segregation Principle● D Dependency Inversion Principle