Download - Dependency Injection in PHP
Dependency Injection in PHP
PHPCon Poland Szczyrk 25/10/13
!by @cakper
flickr.com/limowreck666/223731385/
Kacper Gunia // @cakper
Software Engineer @SensioLabsUK
Symfony Certified Developer
#Symfony-PL // SymfonyLab.pl
Silesian PHP User Group // SPUG.pl
Inversion of Control
Dependency Injection
Events
Aspect Oriented Programming
flickr.com/goetter/4559773791/
Diving into Dependency Injection
flickr.com/haniamir/651043585/
Dependency Injection!!class DvdPlayer { public function playDisc(Disc $disc) { echo 'Now playing: '.$disc-‐>getMovieTitle(); ! $tv = new Tv('Samsung 40"'); $tv-‐>sendToOutput($disc-‐>getResource()); } }
Dependency Injectionclass DvdPlayer { private $tv; ! public function __construct() { $this-‐>tv = new Tv('Samsung 40"');; } ! public function playDisc(Disc $disc) { echo 'Now playing: ' . $disc-‐>getMovieTitle(); $this-‐>tv-‐>sendToOutput($disc-‐>getResource()); } }
Dependency Injectionclass DvdPlayer { private $tv; ! public function __construct(Tv $tv) { $this-‐>tv = $tv; } // (...) } !$tv = new Tv('Samsung 40"'); $dvdPlayer = new DvdPlayer($tv);
Dependency Injectionclass DvdPlayer { private $device; ! public function __construct(HdmiDevice $device) { $this-‐>device = $device; } // (...) } !$projector = new Projector('Epson EB'); $dvdPlayer = new DvdPlayer($tv);
Nailed it!
“Dependency Injection is where components are given
their dependencies through their constructors, methods, or
directly into fields.”
!
“(...) it is a 25-dollar term for a 5-cent concept”
source: picocontainer.codehaus.org jamesshore.com
Types of Injection
Constructor
Setter
Property
Reflection
flickr.com/jensfinke/4417602702/
Constructor Injection
class Tv implements HdmiDevice{} class DvdPlayer { private $device; ! public function __construct(HdmiDevice $device) { $this-‐>device = $device; } } !$tv = new Tv('Samsung 40"'); $dvdPlayer = new DvdPlayer($tv);
Setter Injection
class BlurayDisc implements Disc{} class DvdPlayer { private $disc; ! public function setDisc(Disc $disc) { $this-‐>disc = $disc; } } !$disc = new BlurayDisc('Pulp Fiction'); $dvdPlayer = new DvdPlayer(); $dvdPlayer-‐>setDisc($disc);
Property Injection
class DvdPlayer { public $remote; } !$dvdPlayer = new DvdPlayer(); $dvdPlayer-‐>remote = new Remote();
Reflection Injection
class DvdPlayer { private $powerSupply; } !$dvdPlayer = new DvdPlayer(); $reflector = new ReflectionClass($dvdPlayer); $powerSupply = $reflector-‐>getProperty('powerSupply'); $powerSupply-‐>setAccessible(true); !$powerSupply-‐>setValue($dvdPlayer, new PowerSupply());
Pros & Cons
flickr.com/paperpariah/3589690234/
Decoupling
Advantage #1
flickr.com/roonster/3387705446/
Dependency Inversion Principle
Advantage #2
flickr.com/antonsiniorg/1876733029/
Reusability
Advantage #3
flickr.com/ejpphoto/2314610838/
Clean code
Advantage #4
flickr.com/38659937@N06/4020413080/
Easier testing
Advantage #5
flickr.com/nattu/895220635/
Instance management
Advantage #6
flickr.com/34094515@N00/3817086937/
IDE support
Advantage #7
flickr.com/gavin_fordham/9407339641/
Complex initialisation
Disadvantage
flickr.com/toniblay/52445415/
Factory
Simple Solution
flickr.com/lenscrack/5577431429/
Dependency Injection Container
Proper Solution
flickr.com/thomashawk/24089964/
“Dependency Injection Container is simply a PHP
object that manages the instantiation of other objects”
!
We call them “Services”
source: symfony.com
Our DvdPlayer
!!$tv = new Tv('Samsung 40"'); $dvdPlayer = new DvdPlayer($tv); !$disc = new Disc('Pulp Fiction'); $dvdPlayer-‐>playDisc($disc);
Simple Container class
class Container { public function getTv() { return new Tv('Samsung 40"'); } ! public function getDvdPlayer() { return new DvdPlayer($this-‐>getTv()); } }
Container use
!!$container = new Container(); !$dvdPlayer = $container-‐>getDvdPlayer(); !$disc = new Disc('Pulp Fiction'); $dvdPlayer-‐>playDisc($disc);
DIC ImplementationsTwitteePimplePHP-DI Zend\DiIlluminate\ContainerOrno\DiLeague\DiSymfony\DependencyInjection
flickr.com/smb_flickr/313116995/
DI & DIC Patterns
flickr.com/thelearningcurvedotca/8645721482/
Expect Interfaces, not Implementations
Pattern #1
flickr.com/deapeajay/2969264395/
Don’t check null values
Pattern #2
flickr.com/g-ratphotos/3328593629/
class DvdPlayer { public $disc; ! public function play() { if (!is_null($this-‐>disc)) { $this-‐>sendToOutput($this-‐>disc-‐>getResource()); } } ! public function play() { if ($this-‐>disc instanceof Disc) { $this-‐>sendToOutput($this-‐>disc-‐>getResource()); } } }
Avoid ‘Service Locator’
Pattern #3
flickr.com/mateus27_24-25/2224073271/
class DvdPlayer { private $locator; ! public function __construct() { $this-‐>locator = ServiceLocator::getInstance(); } !! public function playDisc(Disc $disc) { $this-‐>locator -‐>getTv() -‐>sendToOutput($disc-‐>getResource()); } }
Don’t inject whole container
Pattern #4
flickr.com/ucumari/580865728/
In DIC store Services and Properties,
but not Entities
Pattern #5
flickr.com/madlyinlovewithlife/8674850717/
Don’t be afraid to create plenty of small Classes
Pattern #6
flickr.com/geoki/2691420977/
Symfony DIC in action
Services
new Tv('Samsung 40”’) !services: tv: class: Tv arguments: [“Samsung 40"”]
Parameters
new Tv('Samsung 40”’) !parameters: tv_name: Samsung 40"
Parameters & Services
new Tv('Samsung 40”’) !parameters: tv_name: Samsung 40" !services: tv: class: Tv arguments: [“%tv_name%”] !$container-‐>get('tv');
Compiled Container
!!!!protected function getTvService() { return $this-‐>services['tv'] = new \Tv('Samsung'); }
Static Factory
services: tv class: Tv factory_class: TvFactory factory_method: get
Factory
services: tv_factory: class: TvFactory tv class: Tv factory_service: tv_factory factory_method: get
Optional Dependencies
class DvdPlayer { function __construct(HdmiDevice $device = null){} } !services: tv: class: Tv arguments: [%tv_name%] dvd_player: class: DvdPlayer arguments ["@?tv"]
Lazy-loaded Services
services: tv: class: Tv arguments: [“%tv_name%”] lazy: true
Private Services
services: drm_decryptor: class: DrmDecryptor public: false dvd_player: class: DvdPlayer arguments [“@drm_decryptor”]
Scopes
services: tv: class: Tv arguments: [“%tv_name%”] scope: prototype
PHPStorm support
sxc.hu/photo/228094/
Adrien Brault