introduction to di(c)

Post on 11-Nov-2014

1.974 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

Quick introduction to Dependency Injection (Container).

TRANSCRIPT

Introduction to DI(C)

meet.php #! "#.$!.%$"%http://meetphp.pl/

$speaker = new Speaker;

$speaker->name = "Radosław Benkel";$speaker->twitter = "@singlespl";$speaker->blog = "http://blog.rbenkel.me";

$speaker->givePresentation();

About me

What?

DependencyInjectionContainer

What?

Dependency

What?

Dependency

class TwitterAPIClient{ protected $httpClient; public function __construct() { $this->httpClient = new SomeCurlWrapper(); } /* ... */}

$client = new TwitterApiClient;

Dependency

class TwitterAPIClient{ protected $httpClient; public function __construct() { $this->httpClient = new SomeCurlWrapper(); } /* ... */}

$client = new TwitterApiClient;

This����������� ������������������  is����������� ������������������  your����������� ������������������  dependency.

“What’s����������� ������������������  wrong����������� ������������������  with����������� ������������������  that”?

Dependency

Try testing it...

Dependency

...or change client implementation

Dependency

Dependency

So let’s use Injection

Dependency

Injection

What?

Injection

class TwitterAPIClient{ protected $httpClient; public function __construct($httpClient) { $this->httpClient = $httpClient; } /* ... */}

$client = new TwitterApiClient(new SomeCurlWrapper);

And����������� ������������������  here����������� ������������������  you����������� ������������������  inject����������� ������������������  dependency

So...

Injection

Injection

public function __construct(){ $this->httpClient = new SomeCurlWrapper();}

public function __construct($httpClient){ $this->httpClient = $httpClient;}

VS

it’s just like...

Injection

Injection

VS

Injection

VS

Try����������� ������������������  replacing����������� ������������������  battery,����������� ������������������  and����������� ������������������  you����������� ������������������  will����������� ������������������  now����������� ������������������  what����������� ������������������  I’m����������� ������������������  talking����������� ������������������  about.����������� ������������������  

Injection types:

Injection

Injection types:

Injection

• constructor injection

Injection types:

Injection

• constructor injection• setter injection

Injection types:

Injection

• constructor injection• setter injection• interface injection

Constructor injection

Injection

class TwitterAPIClient{ protected $httpClient; public function __construct($httpClient) { $this->httpClient = $httpClient; } /* ... */}$client = new TwitterApiClient(new SomeCurlWrapper);

Setter injection

Injection

class TwitterAPIClient{ protected $httpClient; public function __construct() {} public function setHttpClient($httpClient) { $this->httpClient = $httpClient; } /* ... */}$client = new TwitterApiClient;$client->setHttpClient(new SomeCurlWrapper);

Interface injection

Injection

interface HttpClientInterface { public function setHttpClient($httpClient);}

class TwitterAPIClient implements HttpClientInterface { protected $httpClient; public function __construct() {} public function setHttpClient($httpClient) { $this->httpClient = $httpClient; } /* ... */}

$client = new TwitterApiClient;$client->setHttpClient(new SomeCurlWrapper);

So far so good...

Injection

...until you don’t have to do something like

that:

Injection

Injection$mapper = new UserMapperEncrypted( new UserMapperCached( new UserMapperDB( new PDO( 'mysql:host=127.0.0.1', 'user', 'password' ) ), new RedisCacheAdapter( '127.0.0.1:6379' ) ), 'YourSuperSecretPass');

$mapper->save(new User('John', 'Doe'));

Injection

Injection

“How����������� ������������������  to����������� ������������������  solve����������� ������������������  that”?

Just use...

Injection

Container

What?

Container

require_once "container_prod.php";

$mapper = $container->get('mapper.user');

/* mapper is UserMapperEncrypted, which uses UserMapperCached, which uses UserMapperDB, which uses PDO. */$mapper->save(new User('John', 'Doe'));

Container

require_once "container_dev.php";

$mapper = $container->get('mapper.user');

/* mapper is UserMapperDB, with different PDO configuration. */$mapper->save(new User('John', 'Doe'));

Container

require_once "container_prod.php";$mapper = $container->get('mapper.user');$mapper->save(new User('John', 'Doe'));

require_once "container_dev.php";$mapper = $container->get('mapper.user');$mapper->save(new User('John', 'Doe'));

Find����������� ������������������  the����������� ������������������  difference

Container

require_once "container_prod.php";$mapper = $container->get('mapper.user');$mapper->save(new User('John', 'Doe'));

require_once "container_dev.php";$mapper = $container->get('mapper.user');$mapper->save(new User('John', 'Doe'));

Container

require_once "container_prod.php";$mapper = $container->get('mapper.user');$mapper->save(new User('John', 'Doe'));

require_once "container_dev.php";$mapper = $container->get('mapper.user');$mapper->save(new User('John', 'Doe'));

Find����������� ������������������  the����������� ������������������  differenceConfigures����������� ������������������  services����������� ������������������  in����������� ������������������  different����������� ������������������  way,����������� ������������������  but����������� ������������������  for����������� ������������������  you,����������� ������������������  API����������� ������������������  for����������� ������������������  mapper����������� ������������������  is����������� ������������������  the����������� ������������������  same.

Container

require_once "container_prod.php";$mapper = $container->get('mapper.user');$mapper->save(new User('John', 'Doe'));

require_once "container_dev.php";$mapper = $container->get('mapper.user');$mapper->save(new User('John', 'Doe'));

Find����������� ������������������  the����������� ������������������  difference*����������� ������������������  Assuming,����������� ������������������  that����������� ������������������  all����������� ������������������  mappers����������� ������������������  share����������� ������������������  the����������� ������������������  same����������� ������������������  interface,����������� ������������������  which����������� ������������������  they����������� ������������������  rather����������� ������������������  should.

So, DIC it’s something like:

Container

Container

What it does:

Container

• injects object dependencies• creates objects on demand• objects could be shared• (and sometimes other stuff, like tagging, XML/

JSON/YAML config etc. )

So. Lets write simple one.

Container

Containerclass Container{ protected $items = array();

public function get($key) { $item = $this->items[$key]; return is_callable($item) ? $item($this) : $item; }

public function set($key, $value, $shared = false) { if ($shared === true && is_callable($value)) { $this->items[$key] = function($c) use ($value) { static $obj; if (!$obj) { $obj = $value($c); } return $obj; }; } else { $this->items[$key] = $value; } }}

Containerclass Container{ protected $items = array();

public function get($key) { $item = $this->items[$key]; return is_callable($item) ? $item($this) : $item; }

public function set($key, $value, $shared = false) { if ($shared === true && is_callable($value)) { $this->items[$key] = function($c) use ($value) { static $obj; if (!$obj) { $obj = $value($c); } return $obj; }; } else { $this->items[$key] = $value; } }}

*����������� ������������������  Inspired����������� ������������������  by����������� ������������������  Pimple

Lets use that.

Injection

Replacing this:

Injection

Injection$mapper = new UserMapperEncrypted( new UserMapperCached( new UserMapperDB( new PDO( 'mysql:host=127.0.0.1', 'user', 'password' ) ), new RedisCacheAdapter( '127.0.0.1:6379' ) ), 'YourSuperSecretPass');

$mapper->save(new User('John', 'Doe'));

with this:

Injection

Injection

require_once "container_prod.php";

$mapper = $container->get('mapper.user');

/* mapper is UserMapperEncrypted, which uses UserMapperCached (using Redis for cache), which uses UserMapperDB, which uses PDO. */$mapper->save(new User('John', 'Doe'));

Because everything is configured in container...

Injection

Injection//container_prod.php$c = new Container();$c->set('pdo.dsn', 'mysql:host=127.0.0.1');$c->set('pdo.user', 'user');$c->set('pdo.pass', 'password');$c->set('redis.host', '127.0.0.1:6379');$c->set('mcrypt.key', 'YourSuperSecretPass');$c->set('pdo', function(Container $c) { return new PDO( $c->get('pdo.dsn'), $c->get('pdo.user'), $c->get('pdo.pass'), );}, true);$c->set('cache.adapter', function(Container $c) { return new RedisCacheAdapter($c->get('redis.host'));});

$c->set('mapper.user', function(Container $c) { return new UserMapperEncrypted(

new UserMapperCached( new UserMapperDB($c->get('pdo')), $c->get('cache.adapter')

), $c->get('mcrypt.key')

);});

...you can change e.g cache adapter...

Injection

Injection//container_prod.php$c = new Container();$c->set('pdo.dsn', 'mysql:host=127.0.0.1');$c->set('pdo.user', 'user');$c->set('pdo.pass', 'password');$c->set('mcrypt.key', 'YourSuperSecretPass');$c->set('pdo', function(Container $c) { return new PDO( $c->get('pdo.dsn'), $c->get('pdo.user'), $c->get('pdo.pass'), );}, true);$c->set('cache.adapter', function(Container $c) { return new ApcCacheAdapter();});

$c->set('mapper.user', function(Container $c) { return new UserMapperEncrypted( new UserMapperCached( new UserMapperDB($c->get('pdo')), $c->get('cache.adapter') ), $c->get('mcrypt.key') );});

Injection//container_prod.php$c = new Container();$c->set('pdo.dsn', 'mysql:host=127.0.0.1');$c->set('pdo.user', 'user');$c->set('pdo.pass', 'password');$c->set('mcrypt.key', 'YourSuperSecretPass');$c->set('pdo', function(Container $c) { return new PDO( $c->get('pdo.dsn'), $c->get('pdo.user'), $c->get('pdo.pass'), );}, true);$c->set('cache.adapter', function(Container $c) { return new ApcCacheAdapter();});

$c->set('mapper.user', function(Container $c) { return new UserMapperEncrypted( new UserMapperCached( new UserMapperDB($c->get('pdo')), $c->get('cache.adapter') ), $c->get('mcrypt.key') );});

...and your your code hasn’t changed at all.

Injection

Injection

require_once "container_prod.php";

$mapper = $container->get('mapper.user');

/* mapper is UserMapperEncrypted, which uses UserMapperCached (using Apc for cache), which uses UserMapperDB, which uses PDO. */$mapper->save(new User('John', 'Doe'));

But probably, you should use another

DIC:

Container

• AuraDIhttp://auraphp.github.com/Aura.Di/

• Pimplehttp://pimple.sensiolabs.org/

• Symfony % Dependency Injection Componenthttp://symfony.com/doc/current/components/dependency_injection/introduction.html

• ZF% Dependency Injectionhttp://framework.zend.com/wiki/display/ZFDEV%/Zend+DI+QuickStart

• Twitteehttp://twittee.org/

Container

PHP����������� ������������������  5.4����������� ������������������  only

• AuraDIhttp://auraphp.github.com/Aura.Di/

• Pimplehttp://pimple.sensiolabs.org/

• Symfony % Dependency Injection Componenthttp://symfony.com/doc/current/components/dependency_injection/introduction.html

• ZF% Dependency Injectionhttp://framework.zend.com/wiki/display/ZFDEV%/Zend+DI+QuickStart

• Twitteehttp://twittee.org/

Container

Small,����������� ������������������  basic

• AuraDIhttp://auraphp.github.com/Aura.Di/

• Pimplehttp://pimple.sensiolabs.org/

• Symfony % Dependency Injection Componenthttp://symfony.com/doc/current/components/dependency_injection/introduction.html

• ZF% Dependency Injectionhttp://framework.zend.com/wiki/display/ZFDEV%/Zend+DI+QuickStart

• Twitteehttp://twittee.org/

Container

Lot’s����������� ������������������  of����������� ������������������  features

• AuraDIhttp://auraphp.github.com/Aura.Di/

• Pimplehttp://pimple.sensiolabs.org/

• Symfony % Dependency Injection Componenthttp://symfony.com/doc/current/components/dependency_injection/introduction.html

• ZF% Dependency Injectionhttp://framework.zend.com/wiki/display/ZFDEV%/Zend+DI+QuickStart

• Twitteehttp://twittee.org/

Container

Fits����������� ������������������  into����������� ������������������  tweet!

Thank you!

top related