2. Real World Dependency Injection – Special Edition
Über mich
Stephan Hochdörfer, bitExpert AG
Department Manager Research Labs
PHP`ler seit 1999
S.Hochdoerfer@bitExpert.de
@shochdoerfer
12. Real World Dependency Injection – Special Edition
„new“ is evil!
<?php
class DeletePage extends Mvc_Action_AAction {
private $pageManager;
public function __construct() {
$this->pageManager = new PageManager();
}
protected function execute(Mvc_Request $request) {
$this->pageManager->delete(
(int) $request->get('pageId')
);
}
}
13. Real World Dependency Injection – Special Edition
„new“ is evil!
<?php
class DeletePage extends Mvc_Action_AAction {
private $pageManager;
public function __construct(PageManager $pm) {
$this->pageManager = $pm;
}
protected function execute(Mvc_Request $request) {
$this->pageManager->delete(
(int) $request->get('pageId')
);
}
}
14. Real World Dependency Injection – Special Edition
"High-level modules should not
depend on low-level modules.
Both should depend on
abstractions."
Robert C. Martin
16. Real World Dependency Injection – Special Edition
„new“ is evil!
<?php
class DeletePage extends Mvc_Action_AAction {
private $pageManager;
public function __construct(IPageManager $pm) {
$this->pageManager = $pm;
}
protected function execute(Mvc_Request $request) {
$this->pageManager->delete(
(int) $request->get('pageId')
);
}
}
25. Real World Dependency Injection – Special Edition
Constructor Injection
<?php
class MySampleService implements IMySampleService {
/**
* @var ISampleDao
*/
private $sampleDao;
public function __construct(ISampleDao $sampleDao) {
$this->sampleDao = $sampleDao;
}
}
26. Real World Dependency Injection – Special Edition
Setter Injection
<?php
class MySampleService implements IMySampleService {
/**
* @var ISampleDao
*/
private $sampleDao;
public function setSampleDao(ISampleDao $sampleDao){
$this->sampleDao = $sampleDao;
}
}
27. Real World Dependency Injection – Special Edition
Interface Injection
<?php
interface IApplicationContextAware {
public function setCtx(IApplicationContext $ctx);
}
28. Real World Dependency Injection – Special Edition
Interface Injection
<?php
class MySampleService implements IMySampleService,
IApplicationContextAware {
/**
* @var IApplicationContext
*/
private $ctx;
public function setCtx(IApplicationContext $ctx) {
$this->ctx = $ctx;
}
}
37. Real World Dependency Injection – Special Edition
Unittesting einfach gemacht
<?php
require_once 'PHPUnit/Framework.php';
class ServiceTest extends PHPUnit_Framework_TestCase {
public function testSampleService() {
// set up dependencies
$sampleDao = $this->getMock('ISampleDao');
$service = new MySampleService($sampleDao);
// run test case
$return = $service->doWork();
// check assertions
$this->assertTrue($return);
}
}
39. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
Page Exporter
Page Exporter
40. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
Page Exporter
Page Exporter
Released / /Published
Released Published Workingcopy
Workingcopy
Pages
Pages Pages
Pages
41. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
<?php
abstract class PageExporter {
protected function setPageDao(IPageDao $pageDao) {
$this->pageDao = $pageDao;
}
}
42. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
<?php
abstract class PageExporter {
protected function setPageDao(IPageDao $pageDao) {
$this->pageDao = $pageDao;
}
}
Zur Erinnerung:
Der Vertrag!
43. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
<?php
class PublishedPageExporter extends PageExporter {
public function __construct() {
$this->setPageDao(new PublishedPageDao());
}
}
class WorkingCopyPageExporter extends PageExporter {
public function __construct() {
$this->setPageDao(new WorkingCopyPageDao());
}
}
44. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
"Only deleted code is good code!"
Oliver Gierke
45. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
<?php
class PageExporter {
public function __construct(IPageDao $pageDao) {
$this->pageDao = $pageDao;
}
}
46. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id="ExportLive" class="PageExporter">
<constructor-arg ref="PublishedPageDao" />
</bean>
<bean id="ExportWorking" class="PageExporter">
<constructor-arg ref="WorkingCopyPageDao" />
</bean>
</beans>
47. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung
<?php
// create ApplicationContext instance
$ctx = new ApplicationContext();
// retrieve live exporter
$exporter = $ctx->getBean('ExportLive');
// retrieve working copy exporter
$exporter = $ctx->getBean('ExportWorking');
48. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung II
49. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung II
http://editor.loc/page/[id]/headline/
http://editor.loc/page/[id]/content/
http://editor.loc/page/[id]/teaser/
50. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung II
<?php
class EditPart extends Mvc_Action_AFormAction {
private $pagePartsManager;
private $type;
public function __construct(IPagePartsManager $pm) {
$this->pagePartsManager = $pm;
}
public function setType($ptype) {
$this->type = (int) $type;
}
protected function process(Bo_ABo $formBackObj) {
}
}
51. Real World Dependency Injection – Special Edition
Eine Klasse, mehrfache Ausprägung II
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id="EditHeadline" class="EditPart">
<constructor-arg ref="PagePartDao" />
<property name="Type" const="PType::Headline" />
</bean>
<bean id="EditContent" class="EditPart">
<constructor-arg ref="PagePartDao" />
<property name="Type" const="PType::Content" />
</bean>
</beans>
53. Real World Dependency Injection – Special Edition
Externe Services mocken
WS-
WS-
Bookingmanager
Bookingmanager Webservice
Webservice
Connector
Connector
54. Real World Dependency Injection – Special Edition
Externe Services mocken
WS-
WS-
Bookingmanager
Bookingmanager Webservice
Webservice
Connector
Connector
Zur Erinnerung:
Der Vertrag!
55. Real World Dependency Injection – Special Edition
Externe Services mocken
FS-
FS-
Bookingmanager
Bookingmanager Filesystem
Filesystem
Connector
Connector
56. Real World Dependency Injection – Special Edition
Externe Services mocken
FS-
FS-
Bookingmanager
Bookingmanager Filesystem
Filesystem
Connector
Connector
erfüllt den
Vertrag!
60. Real World Dependency Injection – Special Edition
Keine Framework Abhängigkeit
<?php
class MySampleService implements IMySampleService {
private $sampleDao;
public function __construct(ISampleDao $sampleDao) {
$this->sampleDao = $sampleDao;
}
public function getSample($sampleId) {
try {
return $this->sampleDao->readById($sampleId);
}
catch(DaoException $exception) {}
}
}
61. Real World Dependency Injection – Special Edition
Wie sieht das nun in der Praxis aus?
63. Real World Dependency Injection – Special Edition
ZendDi – Erste Schritte
<?php
namespace Acme;
class TalkService {
public function __construct() {
}
public function getTalks() {
}
}
64. Real World Dependency Injection – Special Edition
ZendDi – Erste Schritte
<?php
$di = new ZendDiDi();
$service = $di->get('AcmeTalkService');
$service->getTalks();
65. Real World Dependency Injection – Special Edition
ZendDi – Constructor Injection
<?php
namespace Acme;
interface GenericRepository {
public function readTalks();
}
class TalkRepository implements GenericRepository {
public function readTalks() {
}
}
class TalkService {
public function __construct(TalkRepository $repo) {
}
public function getTalks() {
}
}
66. Real World Dependency Injection – Special Edition
ZendDi – Constructor Injection
<?php
$di = new ZendDiDi();
$service = $di->get('AcmeTalkService');
$service->getTalks();
67. Real World Dependency Injection – Special Edition
ZendDi – Setter Injection
<?php
namespace Acme;
class Logger {
public function doLog($logMsg) {
}
}
class TalkService {
public function __construct(TalkRepository $repo) {
}
public function setLogger(Logger $logger) {
}
public function getTalks() {
}
}
68. Real World Dependency Injection – Special Edition
ZendDi – Setter Injection
<?php
$di = new ZendDiDi();
$di->configure(
new ZendDiConfiguration(
array(
'definition' => array(
'class' => array(
'AcmeTalkService' => array(
'setLogger' => array('required' => true)
)
)
)
)
)
);
$service = $di->get('AcmeTalkService');
var_dump($service);
69. Real World Dependency Injection – Special Edition
ZendDi – Interface Injection
<?php
namespace Acme;
class Logger {
public function doLog($logMsg) {
}
}
interface LoggerAware {
public function setLogger(Logger $logger);
}
class TalkService implements LoggerAware {
public function __construct(TalkRepository $repo) {
}
public function setLogger(Logger $logger) {
}
public function getTalks() {
}
}
70. Real World Dependency Injection – Special Edition
ZendDi – Interface Injection
<?php
$di = new ZendDiDi();
$service = $di->get('AcmeTalkService');
$service->getTalks();
71. Real World Dependency Injection – Special Edition
ZendDi – Grundsätzliches
<?php
$di = new ZendDiDi();
$service = $di->get('AcmeTalkService');
var_dump($service);
$service2 = $di->get('AcmeTalkService');
var_dump($service2); // gleiche Instanz wie $service
$service3 = $di->get(
'AcmeTalkService',
array(
'repo' => new phpbnl12TalkRepository()
)
);
var_dump($service3); // neue Instanz
72. Real World Dependency Injection – Special Edition
ZendDi – Builder Definition
<?php
// Dependency beschreiben
$dep = new ZendDiDefinitionBuilderPhpClass();
$dep->setName('AcmeTalkRepository');
// Klasse beschreiben
$class = new ZendDiDefinitionBuilderPhpClass();
$class->setName('AcmeTalkService');
// Injection Methode beschreiben
$im = new ZendDiDefinitionBuilderInjectionMethod();
$im->setName('__construct');
$im->addParameter('repo', 'AcmeTalkRepository');
$class->addInjectionMethod($im);
// Builder konfigurieren
$builder = new ZendDiDefinitionBuilderDefinition();
$builder->addClass($dep);
$builder->addClass($class);
73. Real World Dependency Injection – Special Edition
ZendDi – Builder Definition
<?php
// zur DefinitionList hinzufügen
$defList = new ZendDiDefinitionList($builder);
$di = new ZendDiDi($defList);
$service = $di->get('AcmeTalkService');
var_dump($service);
75. Real World Dependency Injection – Special Edition
Symfony2
<?php
namespace AcmeTalkBundleController;
use SymfonyBundleFrameworkBundleControllerController;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SensioBundleFrameworkExtraBundleConfigurationTemplate;
class TalkController extends Controller {
/**
* @Route("/", name="_talk")
* @Template()
*/
public function indexAction() {
$service = $this->get('acme.talk.service');
return array();
}
}
76. Real World Dependency Injection – Special Edition
Symfony2 – Konfigurationsdatei
Datei services.xml in src/Acme/DemoBundle/Resources/config
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
</container>