127
The IoC Hydra

The IoC Hydra - Dutch PHP Conference 2016

Embed Size (px)

Citation preview

Page 1: The IoC Hydra - Dutch PHP Conference 2016

The IoC Hydra

Page 2: The IoC Hydra - Dutch PHP Conference 2016

Kacper Gunia @cakper Independent Consultant & Trainer

DDD London Leader @ddd_london

Page 3: The IoC Hydra - Dutch PHP Conference 2016

CHAPTER 1: THE THEORY

Page 4: The IoC Hydra - Dutch PHP Conference 2016

THIS IS NOT YET ANOTHER DEPENDENCY INJECTION TALK ;)

Page 5: The IoC Hydra - Dutch PHP Conference 2016

SO WE NEED SOME CLARIFICATION

Page 6: The IoC Hydra - Dutch PHP Conference 2016

IOC !== DI

Page 7: The IoC Hydra - Dutch PHP Conference 2016

–Pico Container

“Inversion of Control (IoC) is a design pattern that addresses a component’s dependency

resolution, configuration and lifecycle. ”

Page 8: The IoC Hydra - Dutch PHP Conference 2016

–Pico Container

“It suggests that the control of those three should not be the concern of the component

itself. Thus it is inverted back.”

Page 9: The IoC Hydra - Dutch PHP Conference 2016

–Pico Container

“Dependency Injection is where components are given their dependencies through their

constructors, methods, or directly into fields.”

Page 10: The IoC Hydra - Dutch PHP Conference 2016

Inversion of Control

Dependency Injection

Events

AOP

Page 11: The IoC Hydra - Dutch PHP Conference 2016

WHY DO WE IOC?

Page 12: The IoC Hydra - Dutch PHP Conference 2016

PROS• Separation of concerns:

• dependency resolution, configuration and lifecycle

• Enforcing Single Responsibility Principle

• Easier testing

• Modular architecture, loose coupling

Page 13: The IoC Hydra - Dutch PHP Conference 2016

CONS

• Learning curve

• Code is harder do analyse/debug

• Moves complexity somewhere else (doesn’t remove)

• Need for extra tools like Containers / Dispatchers

Page 14: The IoC Hydra - Dutch PHP Conference 2016

HOW DO WE WRITE SOFTWARE WITHOUT IOC?

Page 15: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE

interface FileEraser{ public function erase($filename); }

Page 16: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLEinterface Logger{ public function log($log); } class PrintingLogger implements Logger{ public function log($log) { echo $log; } }

Page 17: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE

class LocalFileEraser implements FileEraser{ public function erase($filename) { $logger = new PrintingLogger(); $logger->log("Attempt to erase file: " . $filename); unlink($filename); $logger->log("File " . $filename . " was erased.”); } }

Page 18: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE

$eraser = new LocalFileEraser(); $eraser->erase('important-passwords.txt');

Page 19: The IoC Hydra - Dutch PHP Conference 2016

HOW CAN WE FIX IT WITH DI?

Page 20: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH DIclass LocalFileEraser implements FileEraser { private $logger; public function __construct(Logger $logger) { $this->logger = $logger; } public function erase($path) { $this->logger->log("Attempt to erase file: " . $path); unlink($path); $this->logger->log("File " . $path . " was erased."); } }

Page 21: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH DI

$logger = new PrintingLogger(); $eraser = new LocalFileEraser($logger); $eraser->erase('important-passwords.txt');

Page 22: The IoC Hydra - Dutch PHP Conference 2016

What (is being executed)Known Unknown

Kno

wn

Unkn

own

Whe

n (i

s be

ing

exec

uted

)

Dependency Injection

Stages of loosening control(from the component point of view)

Page 23: The IoC Hydra - Dutch PHP Conference 2016

HOW CAN WE FIX IT WITH EVENTS?

Page 24: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH EVENTSclass FileEvent implements Event{ private $path; public function __construct($path) { $this->path = $path; } public function getPath() { return $this->path; } }

Page 25: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH EVENTS

class FileEraseWasInitialised extends FileEvent{ } class FileWasErased extends FileEvent{ }

Page 26: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH EVENTS

interface Listener{ public function handle(Event $event); } interface Event{ }

Page 27: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH EVENTSclass LoggingFileEventListener implements Listener{ private $logger; public function __construct(Logger $logger) { $this->logger = $logger; } public function handle(Event $event) { if ($event instanceof FileEvent) { $this->logger->log(get_class($event).' '.$event->getPath()); } }}

Page 28: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH EVENTStrait Observable{ private $listeners = []; public function addListener(Listener $listener) { $this->listeners[] = $listener; } public function dispatch(Event $event) { foreach ($this->listeners as $listener) { $listener->handle($event); } } }

Page 29: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH EVENTSclass LocalFileEraser implements FileEraser{ use Observable; public function erase($filename) { $this->dispatch(new FileEraseWasInitialised($filename)); unlink($filename); $this->dispatch(new FileWasErased($filename)); } }

Page 30: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH EVENTS

$eraser = new LocalFileEraser();

$listener = new LoggingFileEventListener(new PrintingLogger()); $eraser->addListener($listener); $eraser->erase('important-passwords.txt');

Page 31: The IoC Hydra - Dutch PHP Conference 2016

What (is being executed)Known Unknown

Kno

wn

Unkn

own

Whe

n (i

s be

ing

exec

uted

)

Events

Stages of loosening control(from the component point of view)

Page 32: The IoC Hydra - Dutch PHP Conference 2016

HOW CAN WE FIX IT WITH AOP?

Page 33: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH AOP USING DECORATOR

class LocalFileEraser implements FileEraser{ public function erase($filename) { unlink($filename); } }

Page 34: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH AOP USING DECORATORclass LoggingFileEraser implements FileEraser{ private $decorated; private $logger; public function __construct(FileEraser $decorated, Logger $logger) { $this->decorated = $decorated; $this->logger = $logger; } public function erase($filename) { $this->logger->log('File erase was initialised' . $filename); $this->decorated->erase($filename); $this->logger->log('File was erased' . $filename); } }

Page 35: The IoC Hydra - Dutch PHP Conference 2016

EXAMPLE WITH AOP USING DECORATOR

$localFileEraser = new LocalFileEraser(); $logger = new PrintingLogger(); $eraser = new LoggingFileEraser($localFileEraser, $logger); $eraser->erase('important-passwords.txt');

Page 36: The IoC Hydra - Dutch PHP Conference 2016

What (is being executed)Known Unknown

Kno

wn

Unkn

own

Whe

n (i

s be

ing

exec

uted

)

AOP

Stages of loosening control(from the component point of view)

Page 37: The IoC Hydra - Dutch PHP Conference 2016

What (is being executed)Known Unknown

Kno

wn

Unkn

own

Whe

n (i

s be

ing

exec

uted

)

Dependency Injection Events

AOP

Stages of loosening control(from the component point of view)

Page 38: The IoC Hydra - Dutch PHP Conference 2016

FRAMEWORKS & LIBRARIES

Page 39: The IoC Hydra - Dutch PHP Conference 2016

A libraries provide functionality that you decide when to call.

Page 40: The IoC Hydra - Dutch PHP Conference 2016

Frameworks provide an architecture for the application and

decide when to call your code.

Page 41: The IoC Hydra - Dutch PHP Conference 2016

“DON’T CALL US, WE’LL CALL YOU”aka Hollywood Principle

Page 42: The IoC Hydra - Dutch PHP Conference 2016

Frameworks utilise IoC principles and can be seen as one of its manifestations.

Page 43: The IoC Hydra - Dutch PHP Conference 2016

CHAPTER 2: THE PRACTICE

Page 44: The IoC Hydra - Dutch PHP Conference 2016

DEPENDENCY INJECTION CONTAINERS

Page 45: The IoC Hydra - Dutch PHP Conference 2016

WHERE DIC CAN HELP

Page 46: The IoC Hydra - Dutch PHP Conference 2016

RESOLVING GRAPHS OF OBJECTS$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8'; $dbUsername = 'username'; $dbPassword = 'password'; $customerRepository = new CustomerRepository(new PDO($dsn, $dbUsername, $dbPassword)); $smtpHost = 'smtp.example.org'; $smtpPort = 25; $smtpUsername = 'username'; $smtpPassword = 'password'; $transport = new Swift_SmtpTransport($smtpHost, $smtpPort); $transport->setUsername($smtpUsername); $transport->setPassword($smtpPassword); $mailer = new Swift_Mailer($transport); $loggerName = "App"; $logger = new \Monolog\Logger($loggerName); $stream = "app.log"; $logger->pushHandler(new \Monolog\Handler\StreamHandler($stream)); $controller = new RegistrationController($customerRepository, $mailer, $logger);

Page 47: The IoC Hydra - Dutch PHP Conference 2016

RESOLVING GRAPHS OF OBJECTSuse Pimple\Container; $container = new Container(); $container['dsn'] = 'mysql:host=localhost;dbname=testdb;charset=utf8'; $container['db_username'] = 'username'; $container['dsn'] = 'password'; $container['pdo'] = function ($c) { return new PDO($c['dsn'], $c['db_username'], $c['dsn']); }; $container['customer_repository'] = function ($c) { return new CustomerRepository($c['pdo']); };

Page 48: The IoC Hydra - Dutch PHP Conference 2016

RESOLVING GRAPHS OF OBJECTS$container['smtp_host'] = 'smtp.example.org'; $container['smtp_port'] = 25; $container['smtp_username'] = 'username'; $container['smtp_password'] = 'password'; $container['transport'] = function ($c) { return new Swift_SmtpTransport($c['smtp_host'], $c['smtp_port']); }; $container->extend('transport', function ($transport, $c) { $transport->setUsername($c['smtp_username']); $transport->setPassword($c['smtp_password']); return $transport; }); $container['mailer'] = function ($c) { return new Swift_Mailer($c['transport']); };

Page 49: The IoC Hydra - Dutch PHP Conference 2016

RESOLVING GRAPHS OF OBJECTS$container['logger_name'] = "App"; $container['stream_name'] = "app_" . $container['environment'] . ".log"; $container['logger'] = function ($c) { return new \Monolog\Logger($c['logger_name']); }; $container->extend('transport', function ($logger, $c) { $logger->pushHandler( new \Monolog\Handler\StreamHandler($c[‘stream_handler']) ); return $logger; }); $container['stream_handler'] = function ($c) { return new \Monolog\Handler\StreamHandler($c['stream_name']); };

Page 50: The IoC Hydra - Dutch PHP Conference 2016

RESOLVING GRAPHS OF OBJECTS

$container['registration_controller'] = function ($c) { return new RegistrationController( $c['customer_repository'], $c['mailer'], $c[‘logger’] ); };

Page 51: The IoC Hydra - Dutch PHP Conference 2016

LIFECYCLE MANAGEMENT

$container['session_storage'] = function ($c) { return new SessionStorage('SESSION_ID'); };

Page 52: The IoC Hydra - Dutch PHP Conference 2016

LIFECYCLE MANAGEMENT

$container['session_storage'] = $container->factory(function ($c) { return new SessionStorage('SESSION_ID'); });

Page 53: The IoC Hydra - Dutch PHP Conference 2016

WHERE DIC CAN HARM

Page 54: The IoC Hydra - Dutch PHP Conference 2016

SERVICE LOCATOR

class RegistrationController { function resetPasswordAction() { $mailer = Container::instance()['mailer']; //... } }

Page 55: The IoC Hydra - Dutch PHP Conference 2016

SERVICE LOCATOR• Coupled to container

• Responsible for resolving dependencies

• Dependencies are hidden

• Hard to test

• Might be ok when modernising legacy!

Page 56: The IoC Hydra - Dutch PHP Conference 2016

SETTER INJECTION

Page 57: The IoC Hydra - Dutch PHP Conference 2016

SETTER INJECTION

• Forces to be defensive as dependencies are optional

• Dependency is not locked (mutable)

• In some cases can be replaced with events

• We can avoid it by using NullObject pattern

Page 58: The IoC Hydra - Dutch PHP Conference 2016

SETTER INJECTIONclass LocalFileEraser implements FileEraser{ private $logger; public function setLogger(Logger $logger) { $this->logger = $logger; } public function erase($filename) { if ($this->logger instanceof Logger) { $this->logger->log("Attempt to erase file: " . $filename); } unlink($filename); if ($this->logger instanceof Logger) { $this->logger->log("File " . $filename . " was deleted"); } }}

Page 59: The IoC Hydra - Dutch PHP Conference 2016

SETTER INJECTIONclass LocalFileEraser implements FileEraser{ private $logger; public function __construct(Logger $logger) { $this->logger = $logger; } public function erase($path) { $this->logger->log("Attempt to erase file: " . $path); unlink($path); $this->logger->log("File " . $path . " was erased.”); } }

Page 60: The IoC Hydra - Dutch PHP Conference 2016

SETTER INJECTION

class NullLogger implements Logger { public function log($log) { // whateva... } } $eraser = new LocalFileEraser(new NullLogger()); $eraser->erase('important-passwords.txt');

Page 61: The IoC Hydra - Dutch PHP Conference 2016

PARTIAL APPLICATION

Page 62: The IoC Hydra - Dutch PHP Conference 2016

DI WAY OF DOING THINGS

interface Logger{ public function log($log); } class PrintingLogger implements Logger{ public function log($log) { echo $log; } }

Page 63: The IoC Hydra - Dutch PHP Conference 2016

DI WAY OF DOING THINGSclass LocalFileEraser implements FileEraser{ private $logger; public function __construct(Logger $logger) { $this->logger = $logger; } public function erase($path) { $this->logger->log("Attempt to erase file: " . $path); unlink($path); $this->logger->log("File " . $path . " was deleted"); } }

Page 64: The IoC Hydra - Dutch PHP Conference 2016

DI WAY OF DOING THINGS

$logger = new PrintingLogger(); $eraser = new LocalFileEraser($logger); $eraser->erase('important-passwords.txt');

Page 65: The IoC Hydra - Dutch PHP Conference 2016

FUNCTIONAL WAY OF DOING THINGS

$erase = function (Logger $logger, $path) { $logger->log("Attempt to erase file: " . $path); unlink($path); $logger->log("File " . $path . " was deleted"); };

Page 66: The IoC Hydra - Dutch PHP Conference 2016

FUNCTIONAL WAY OF DOING THINGS

use React\Partial; $erase = function (Logger $logger, $path) { $logger->log("Attempt to erase file: " . $path); unlink($path); $logger->log("File " . $path . " was deleted"); }; $erase = Partial\bind($erase, new PrintingLogger()); $erase('important-passwords.txt');

Page 67: The IoC Hydra - Dutch PHP Conference 2016

CHAPTER 3: THE SYMFONY

Page 68: The IoC Hydra - Dutch PHP Conference 2016

SYMFONY/DEPENDENCY-INJECTION

Page 69: The IoC Hydra - Dutch PHP Conference 2016

FEATURES

• Many configurations formats

• Supports Factories/Configurators/Decoration

• Extendable with Compiler Passes

• Supports lazy loading of services

Page 70: The IoC Hydra - Dutch PHP Conference 2016

PERFORMANCE

Page 71: The IoC Hydra - Dutch PHP Conference 2016

PERFORMANCE OF SYMFONY DIC

• Cached/Dumped to PHP code

• In debug mode it checks whether config is fresh

• During Compilation phase container is being optimised

Page 72: The IoC Hydra - Dutch PHP Conference 2016

LARGE NUMBER OF CONFIGURATION FILES

SLOWS CONTAINER BUILDER

Page 73: The IoC Hydra - Dutch PHP Conference 2016
Page 74: The IoC Hydra - Dutch PHP Conference 2016

SLOW COMPILATION

• Minimise number of bundles/config files used

• Try to avoid using extras like JMSDiExtraBundle

• Can be really painful on NFS

Page 75: The IoC Hydra - Dutch PHP Conference 2016

LARGE CONTAINERS ARE SLOW TO LOAD

AND USE A LOT OF MEMORY

Page 76: The IoC Hydra - Dutch PHP Conference 2016

LARGE CONTAINERS

• Review and remove unnecessary services/bundles etc.

• Split application into smaller ones with separate kernels

Page 77: The IoC Hydra - Dutch PHP Conference 2016

PRIVATE SERVICES

Page 78: The IoC Hydra - Dutch PHP Conference 2016
Page 79: The IoC Hydra - Dutch PHP Conference 2016

PRIVATE SERVICES

• It’s only a hint for compiler

• Minor performance gain (inlines instantiation)

• Private services can still be fetched (not recommended)

Page 80: The IoC Hydra - Dutch PHP Conference 2016

LAZY LOADING OF SERVICES

Page 81: The IoC Hydra - Dutch PHP Conference 2016

LAZY LOADING OF SERVICES• Used when instantiation is expensive or not needed

• i.e. event listeners

• Solutions:

• Injecting container directly

• Using proxy objects

Page 82: The IoC Hydra - Dutch PHP Conference 2016

INJECTING CONTAINER DIRECTLY

• As fast as it can be

• Couples service implementation to the container

• Makes testing harder

Page 83: The IoC Hydra - Dutch PHP Conference 2016

USING PROXY OBJECTS• Easy to use (just configuration option)

• Code and test are not affected

• Adds a bit of overhead

• especially when services are called many times

• on proxy generation

Page 84: The IoC Hydra - Dutch PHP Conference 2016

DEFINING CLASS NAMES AS PARAMETERS

Page 85: The IoC Hydra - Dutch PHP Conference 2016

DEFINING CLASS NAMES AS PARAMETERS

• Rare use case (since decoration is supported even less)

• Adds overhead

• Removed in Symfony 3.0

Page 86: The IoC Hydra - Dutch PHP Conference 2016

CIRCULAR REFERENCES

Page 87: The IoC Hydra - Dutch PHP Conference 2016
Page 88: The IoC Hydra - Dutch PHP Conference 2016

Security

Listener

Doctrine

CIRCULAR REFERENCE

Page 89: The IoC Hydra - Dutch PHP Conference 2016

CIRCULAR REFERENCES

• Injecting Container is just a workaround

• Using setter injection after instantiation as well

• Solving design problem is the real challenge

Page 90: The IoC Hydra - Dutch PHP Conference 2016

Doctrine

Security

Listener

TokenStorage

BROKEN CIRCULAR REFERENCE

Page 91: The IoC Hydra - Dutch PHP Conference 2016

SCOPES

Page 92: The IoC Hydra - Dutch PHP Conference 2016

MEANT TO SOLVE “THE REQUEST PROBLEM”

Page 93: The IoC Hydra - Dutch PHP Conference 2016

SCOPE DEFINES STATE OF THE APPLICATION

Page 94: The IoC Hydra - Dutch PHP Conference 2016

PROBLEMS WITH INJECTING REQUEST TO SERVICES

Page 95: The IoC Hydra - Dutch PHP Conference 2016

PROBLEMS WITH INJECTING REQUEST TO SERVICES

• Causes ScopeWideningInjectionException

• Anti-pattern - Request is a Value Object

• Which means Container was managing it’s state

• Replaced with RequestStack

Page 96: The IoC Hydra - Dutch PHP Conference 2016

App

Controller A

Sub Request

Stateful service

Sub Request

Master Request

Stateful service

Master Request

Controller B

REQUESTS MANAGED WITH SCOPES

Page 97: The IoC Hydra - Dutch PHP Conference 2016

App

Controller A

Stateless service

Request Stack

Controller B

SubRequest

MasterRequest

REQUESTS MANAGED WITH STACK

Page 98: The IoC Hydra - Dutch PHP Conference 2016

FETCHING THE REQUEST FROM THE STACK IS AN ANTI PATTERN

Page 99: The IoC Hydra - Dutch PHP Conference 2016

REQUEST INSTANCE SHOULD BE PASSED AROUND

Page 100: The IoC Hydra - Dutch PHP Conference 2016

STATEFUL SERVICES

Page 101: The IoC Hydra - Dutch PHP Conference 2016
Page 102: The IoC Hydra - Dutch PHP Conference 2016

STATEFUL SERVICES

• Refactor & pass the state to the call

• Fetch state explicitly on per need basis

• Use shared = false flag

Page 103: The IoC Hydra - Dutch PHP Conference 2016

PROTOTYPE SCOPE

Page 104: The IoC Hydra - Dutch PHP Conference 2016
Page 105: The IoC Hydra - Dutch PHP Conference 2016

Prototype scope

Prototype-scoped

Service Z

Stateless Service A

Prototype scope

Prototype-scoped

Service Z

Stateless Service A

Prototype scope

Prototype-scoped

Service Z

Stateless Service A

USING PROTOTYPE SCOPE WITH STRICT = TRUE

Page 106: The IoC Hydra - Dutch PHP Conference 2016

New Instances

StatefulService Z

StatefulService Z

StatefulService Z

Stateless Service A

USING PROTOTYPE SCOPE WITH STRICT = FALSE

Page 107: The IoC Hydra - Dutch PHP Conference 2016

FORGET ABOUT SCOPES ANYWAY WERE REMOVED IN SYMFONY 3.0

Page 108: The IoC Hydra - Dutch PHP Conference 2016

USE SHARED=FALSE FLAG

Page 109: The IoC Hydra - Dutch PHP Conference 2016

CONTROLLERS

Page 110: The IoC Hydra - Dutch PHP Conference 2016
Page 111: The IoC Hydra - Dutch PHP Conference 2016

EXTENDING BASE CONTROLLER

• Easy to use by newcomers / low learning curve

• Limits inheritance

• Encourages using DIC as Service Locator

• Hard unit testing

Page 112: The IoC Hydra - Dutch PHP Conference 2016

CONTAINER AWARE INTERFACE• Controller is still coupled to framework

• Enables inheritance

• Lack of convenience methods

• Encourages using DIC as Service Locator

• Testing is still hard

Page 113: The IoC Hydra - Dutch PHP Conference 2016

CONTROLLER AS A SERVICE• Requires additional configuration

• Lack of convenience methods

• Full possibility to inject only relevant dependencies

• Unit testing is easy

• Enables Framework-agnostic controllers

Page 114: The IoC Hydra - Dutch PHP Conference 2016

NONE OF THE ABOVE OPTIONS WILL FORCE YOU TO WRITE GOOD/BAD CODE

Page 115: The IoC Hydra - Dutch PHP Conference 2016

SYMFONY/EVENT-DISPATCHER

Page 116: The IoC Hydra - Dutch PHP Conference 2016

FEATURES

• Implementation of Mediator pattern

• Allows for many-to-many relationships between objects

• Makes your projects extensible

• Supports priorities/stopping event flow

Page 117: The IoC Hydra - Dutch PHP Conference 2016

EVENT DISPATCHER

• Can be (really) hard to debug

• Priorities of events / managing event flow

• Events can be mutable - indirect coupling

• Hard to test

Page 118: The IoC Hydra - Dutch PHP Conference 2016

INDIRECT COUPLING PROBLEM

• Two services listening on kernel.request event:

• Priority 16 - GeoIP detector - sets country code

• Priority 8 - Locale detector - uses country code and user agent

Page 119: The IoC Hydra - Dutch PHP Conference 2016

INDIRECT COUPLING PROBLEM

• Both events indirectly coupled (via Request->attributes)

• Configuration change will change the app logic

• In reality we always want to call one after another

Page 120: The IoC Hydra - Dutch PHP Conference 2016
Page 121: The IoC Hydra - Dutch PHP Conference 2016

WHEN TO USE EVENT DISPATCHER

• Need to extend Framework or other Bundle

• Building reusable Bundle & need to add extension points

• Consider using separate dispatcher for Domain events

Page 122: The IoC Hydra - Dutch PHP Conference 2016

CHAPTER 4: THE END ;)

Page 123: The IoC Hydra - Dutch PHP Conference 2016

BE PRAGMATIC

Page 124: The IoC Hydra - Dutch PHP Conference 2016

BE EXPLICIT

Page 125: The IoC Hydra - Dutch PHP Conference 2016

DON’T RELY ON MAGIC

Page 126: The IoC Hydra - Dutch PHP Conference 2016

Kacper Gunia @cakper Independent Consultant & Trainer

DDD London Leader

Thanks! joind.in/talk/f681d

Page 127: The IoC Hydra - Dutch PHP Conference 2016

REFERENCES• http://martinfowler.com/bliki/InversionOfControl.html

• http://picocontainer.com/introduction.html

• http://www.infoq.com/presentations/8-lines-code-refactoring

• http://richardmiller.co.uk/2014/03/12/avoiding-setter-injection/

• http://art-of-software.blogspot.co.uk/2013/02/cztery-smaki-odwracania-i-utraty.html