Upload
stephan-hochdoerfer
View
1.010
Download
0
Tags:
Embed Size (px)
DESCRIPTION
Citation preview
The state of DI in PHP
The state of DI in PHP
About me
Stephan Hochdörfer, bitExpert AG
Department Manager Research Labs
enjoying PHP since 1999
7 years of DI experience (in PHP)
@shochdoerfer
The state of DI in PHP
What are Dependencies?
Are Dependencies bad?
The state of DI in PHP
Are Dependencies bad?
Not at all!
The state of DI in PHP
Are Dependencies bad?
Hard-coded dependencies are bad!
The state of DI in PHP
Tightly coupled code
The state of DI in PHP
No reuse of components
The state of DI in PHP
No isolation, not testable!
The state of DI in PHP
The state of DI in PHP
Dependency madness!
What`s DI about?
The state of DI in PHP
What`s DI about?
The state of DI in PHP
new TalkService(new TalkRepository());
What`s DI about?
Consumer
The state of DI in PHP
What`s DI about?
Consumer Dependencies
The state of DI in PHP
What`s DI about?
Consumer Dependencies Container
The state of DI in PHP
What`s DI about?
Consumer Dependencies Container
The state of DI in PHP
A little bit of history...
The state of DI in PHP
1988
The state of DI in PHP
„Designing Reusable Classes“Ralph E. Johnson & Brian Foote
1996
The state of DI in PHP
„The Dependency Inversion Principle“Robert C. Martin
2004
The state of DI in PHP
„Inversion of Control Containers and the Dependency Injection pattern“
Martin Fowler
2005 / 2006
The state of DI in PHP
Garden, a lightweight DI container for PHP.
2006
The state of DI in PHP
First PHP5 framework with DI support
2007
The state of DI in PHP
International PHP Conference 2007 features 2 talks about DI.
2011
The state of DI in PHP
Zend Framework 2 (beta),Symfony2, Flow3, ...
Choose wisely!
Simple Container vs. Full stacked DI Framework
The state of DI in PHP
The state of DI in PHP
Pimple
Pimple – First steps
The state of DI in PHP
<?php
class TalkService {public function __construct() {}
public function getTalks() {}
}
Pimple – First steps
The state of DI in PHP
<?phprequire_once '/path/to/Pimple.php';require_once '/path/to/TalkService.php';
// create the Container$container = new Pimple();
// define talkService object in container$container['talkService'] = function ($c) { return new TalkService();};
// instantiate talkService from container$talkService = $container['talkService'];
Pimple – Constructor Injection
The state of DI in PHP
<?php
interface GenericRepository {public function readTalks();
}
class TalkRepository implements GenericRepository {public function readTalks() {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function getTalks() {}
}
Pimple – Constructor Injection
The state of DI in PHP
<?phprequire_once '/path/to/Pimple.php';require_once '/path/to/TalkService.php';
// create the Container$container = new Pimple();
// define services in container$container['talkRepository'] = function ($c) { return new TalkRepository();};$container['talkService'] = function ($c) { return new TalkService($c['talkRepository']);};
// instantiate talkService from container$talkService = $container['talkService'];
Pimple – Setter Injection
The state of DI in PHP
<?php
class Logger {public function doLog($logMsg) {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function setLogger(Logger $logger) {}
public function getTalks() {}
}
Pimple – Setter Injection
The state of DI in PHP
<?phprequire_once '/path/to/Pimple.php';require_once '/path/to/TalkService.php';
// create the Container$container = new Pimple();
// define services in container$container['logger'] = function ($c) { return new Logger();};$container['talkRepository'] = function ($c) { return new TalkRepository();};$container['talkService'] = function ($c) { $service = new TalkService($c['talkRepository']); $service->setLogger($c['logger']); return $service;};
// instantiate talkService from container$talkService = $container['talkService'];
Pimple – General usage
The state of DI in PHP
<?phprequire_once '/path/to/Pimple.php';require_once '/path/to/TalkService.php';
// create the Container$container = new Pimple();
// define services in container$container['loggerShared'] = $c->share(function ($c) { return new Logger();)};$container['logger'] = function ($c) { return new Logger();};
// instantiate logger from container$logger = $container['logger'];
// instantiate shared logger from container (same instance!)$logger2 = $container['loggerShared'];$logger3 = $container['loggerShared'];
The state of DI in PHP
Bucket
Bucket – Constructor Injection
The state of DI in PHP
<?php
interface GenericRepository {public function readTalks();
}
class TalkRepository implements GenericRepository {public function readTalks() {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function getTalks() {}
}
Bucket – Constructor Injection
The state of DI in PHP
<?phprequire_once '/path/to/bucket.inc.php';require_once '/path/to/TalkService.php';
// create the Container$container = new bucket_Container();
// instantiate talkService from container$talkService = $container->create('TalkService');
// instantiate shared instances from container$talkService2 = $container->get('TalkService');$talkService3 = $container->get('TalkService');
The state of DI in PHP
Zend\Di – First steps
The state of DI in PHP
<?phpnamespace Acme;
class TalkService {public function __construct() {}
public function getTalks() {}
}
Zend\Di – First steps
The state of DI in PHP
<?php
$di = new \Zend\Di\Di();
$service = $di->get('Acme\TalkService');$service->getTalks();
Zend\Di – Constructor Injection
The state of DI in PHP
<?phpnamespace Acme;
interface GenericRepository {public function readTalks();
}
class TalkRepository implements GenericRepository {public function readTalks() {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function getTalks() {}
}
Zend\Di – Constructor Injection
The state of DI in PHP
<?php
$di = new \Zend\Di\Di();
$service = $di->get('Acme\TalkService');$service->getTalks();
Zend\Di – Setter Injection
The state of DI in PHP
<?phpnamespace Acme;
class Logger {public function doLog($logMsg) {}
}
class TalkService {public function __construct(TalkRepository $repo) {}
public function setLogger(Logger $logger) {}
public function getTalks() {}
}
Zend\Di – Setter Injection
The state of DI in PHP
<?php$di = new \Zend\Di\Di();$di->configure(
new Zend\Di\Configuration(array(
'definition' => array('class' => array(
'Acme\TalkService' => array('setLogger' => array('required' => true)
))
))
));
$service = $di->get('Acme\TalkService');var_dump($service);
Zend\Di – Interface Injection
The state of DI in PHP
<?phpnamespace 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() {}
}
Zend\Di – Interface Injection
The state of DI in PHP
<?php
$di = new \Zend\Di\Di();
$service = $di->get('Acme\TalkService');$service->getTalks();
Zend\Di – General usage
The state of DI in PHP
<?php
$di = new \Zend\Di\Di();
$service = $di->get('Acme\TalkService');var_dump($service);
$service2 = $di->get('Acme\TalkService');var_dump($service2); // same instance as $service
$service3 = $di->get('Acme\TalkService',array(
'repo' => new \phpbnl12\TalkRepository())
);var_dump($service3); // new instance
Zend\Di – Builder Definition
The state of DI in PHP
<?php// describe dependency$dep = new \Zend\Di\Definition\Builder\PhpClass();$dep->setName('Acme\TalkRepository');
// describe class$class = new \Zend\Di\Definition\Builder\PhpClass();$class->setName('Acme\TalkService');
// add injection method$im = new \Zend\Di\Definition\Builder\InjectionMethod();$im->setName('__construct');$im->addParameter('repo', 'Acme\TalkRepository');$class->addInjectionMethod($im);
// configure builder$builder = new \Zend\Di\Definition\BuilderDefinition();$builder->addClass($dep);$builder->addClass($class);
Zend\Di – Builder Definition
The state of DI in PHP
<?php
// add to Di$defList = new \Zend\Di\DefinitionList($builder);$di = new \Zend\Di\Di($defList);
$service = $di->get('Acme\TalkService');var_dump($service);
The state of DI in PHP
Symfony2
The state of DI in PHP
<?phpnamespace Acme\TalkBundle\Controller;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class TalkController extends Controller { /** * @Route("/", name="_talk") * @Template() */ public function indexAction() { $service = $this->get('acme.talk.service'); return array(); }}
Symfony2 – Configuration file
The state of DI in PHP
<?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/serviceshttp://symfony.com/schema/dic/services/services-1.0.xsd">
</container>
File services.xml in src/Acme/DemoBundle/Resources/config
Symfony2 – Constructor Injection
The state of DI in PHP
<?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">
<services><service id="acme.talk.repo"
class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<argument type="service" id="acme.talk.repo" /></service>
</services></container>
Symfony2 – Setter Injection
The state of DI in PHP
<?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">
<services><service id="acme.talk.logger"
class="Acme\TalkBundle\Service\Logger" />
<service id="acme.talk.repo" class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<argument type="service" id="acme.talk.repo" /><call method="setLogger">
<argument type="service" id="acme.talk.logger" /></call>
</service></services>
</container>
Symfony2 – Setter Injection (optional)
The state of DI in PHP
<?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">
<services><service id="acme.talk.logger"
class="Acme\TalkBundle\Service\Logger" />
<service id="acme.talk.repo" class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<argument type="service" id="acme.talk.repo" /><call method="setLogger">
<argument type="service" id="acme.talk.logger" on-invalid="ignore" />
</call></service>
</services></container>
Symfony2 – Property Injection
The state of DI in PHP
<?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">
<services><service id="acme.talk.repo"
class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<property name="talkRepository" type="service" id="acme.talk.repo" />
</service></services>
</container>
Symfony2 – Interface Injection
The state of DI in PHP
Not supported!
Symfony2 – private/public Services
The state of DI in PHP
<?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">
<services><service id="acme.talk.logger"
class="Acme\TalkBundle\Service\Logger" public="false" />
<service id="acme.talk.repo" class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service" class="Acme\TalkBundle\Service\TalkService">
<argument type="service" id="acme.talk.repo" /><call method="setLogger">
<argument type="service" id="acme.talk.logger" /></call>
</service></services>
</container>
Symfony2 – Service inheritance
The state of DI in PHP
<?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">
<services><service id="acme.talk.serviceparent"
class="Acme\TalkBundle\Service\TalkService" abstract="true"><property name="talkRepository" type="service"
id="acme.talk.repo" /></service>
<service id="acme.talk.service" parent="acme.talk.serviceparent" />
<service id="acme.talk.service2" parent="acme.talk.serviceparent" /></services>
</container>
Symfony2 – Service scoping
The state of DI in PHP
<?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">
<services><service id="acme.talk.repo"
class="Acme\TalkBundle\Service\TalkRepository" />
<service id="acme.talk.service"class="Acme\TalkBundle\Service\TalkService" scope="prototype"><property name="talkRepository" type="service"
id="acme.talk.repo" /></service>
</services></container>
The state of DI in PHP
Flow3 – Constructor Injection
The state of DI in PHP
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface */protected $talkService;
public function __construct(\Acme\Demo\Service\TalkService $talkService) {$this->talkService = $talkService;
}
public function indexAction() {}
}
Flow3 – Setter Injection (manually)
The state of DI in PHP
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface */protected $talkService;
public function setTalkService(\Acme\Demo\Service\TalkService $talkService) {$this->talkService = $talkService;
}
public function indexAction() {}
}
Flow3 – Setter Injection (manually)
The state of DI in PHP
File Objects.yaml in Packages/Application/Acme.Demo/Configuration# @package AcmeAcme\Demo\Controller\StandardController: properties: talkService: object: Acme\Demo\Service\TalkService
Flow3 – Setter Injection (Automagic)
The state of DI in PHP
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface */protected $talkService;
public function injectTalkService(\Acme\Demo\Service\TalkService $talkService) {$this->talkService = $talkService;
}
public function indexAction() {}
}
Flow3 – Setter Injection (Automagic)
The state of DI in PHP
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface */protected $talkService;
public function injectSomethingElse(\Acme\Demo\Service\TalkService $talkService) {$this->talkService = $talkService;
}
public function indexAction() {}
}
Flow3 – Property Injection
The state of DI in PHP
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkService * @FLOW3\Inject */protected $talkService;
public function indexAction() {}
}
Flow3 – Property Injection (with Interface)
The state of DI in PHP
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface * @FLOW3\Inject */protected $talkService;
public function indexAction() {}
}
Flow3 – Property Injection (with Interface)
The state of DI in PHP
# @package AcmeAcme\Demo\Service\TalkServiceInterface: className: 'Acme\Demo\Service\TalkService'
File Objects.yaml in Packages/Application/Acme.Demo/Configuration
Flow3 – Scoping
The state of DI in PHP
<?phpnamespace Acme\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;
/** * @FLOW3\Scope("session") */class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
/** * @var \Acme\Demo\Service\TalkServiceInterface * @FLOW3\Inject */protected $talkService;
public function indexAction() {}
}
Benefits
Real World Dependency Injection
Benefits
Loose coupling, reuse of components!
Real World Dependency Injection
Benefits
Can reduce the amount of code!
Real World Dependency Injection
Benefits
Helps developers to understand the code!
Real World Dependency Injection
Cons – Developers need mindshift
Configuration ↔ Runtime
Real World Dependency Injection
The state of DI in PHP
Cons - PSR for DI container missing!
The state of DI in PHP
Lack of IDE support
Thank you!
http://joind.in/6250