What is this DI and AOP stuff anyway...
Friday, June 7, 13
About Me
Currently BBC Sport (Freelancer)
Lived in Japan for 15 years - love sushi
Love frameworks - not just PHP ones
Involved in Lithium and BEAR.Sunday projects
Richard McIntyre - @mackstar
Friday, June 7, 13
Dependency Injection
Friday, June 7, 13
Most Basic Example - Problem
class Mailer
{
private $transport;
public function __construct()
{
$this->transport = 'sendmail';
}
// ...
}
Friday, June 7, 13
Expensive
new Zend_Config_Ini($path, ENV);
Friday, June 7, 13
Easily becomes an inheritance mess
Sport_Lib_Builder_GenericStatsAbstract
Sport_Lib_Builder_GenericStats_Cricket_Table
Sport_Lib_Builder_GenericStats_Cricket_NarrowTable
Sport_Lib_Builder_GenericStats_CricketAbstract
Sport_Lib_API_GenericStats_Cricket
Sport_Lib_API_GenericStatsAbstract
Sport_Lib_Service_SportsData_Cricket
Sport_Lib_Service_SportsData
Sport_Lib_ServiceAbstract
Friday, June 7, 13
DI for a 5 year old
When you go and get things out of the refrigerator for yourself, you can
cause problems. You might leave the door open, you might get something
Mommy or Daddy doesn't want you to have. You might even be looking
for something we don't even have or which has expired.
What you should be doing is stating a need, "I need something to drink
with lunch," and then we will make sure you have something when you
sit down to eat.
Friday, June 7, 13
Something like this...
Application needs Foo, which needs Bar, which needs Bim, so:
Application creates Bim
Application creates Bar and gives it Bim
Application creates Foo and gives it Bar
Application calls Foo
Foo calls Bar
Bar does something
Friday, June 7, 13
The Service Container
Dependency Injection Round 1
Friday, June 7, 13
class Mailer
{
private $transport;
public function __construct($transport)
{
$this->transport = $transport;
}
// ...
}
Symfony2 Example
Friday, June 7, 13
class Mailer
{
private $transport;
public function __construct($transport)
{
$this->transport = $transport;
}
// ...
}
use SymfonyComponentDependencyInjectionContainerBuilder;
$container = new ContainerBuilder();
$container->register('mailer', 'Mailer');
Symfony2 Example
Friday, June 7, 13
class Mailer
{
private $transport;
public function __construct($transport)
{
$this->transport = $transport;
}
// ...
}
use SymfonyComponentDependencyInjectionContainerBuilder;
$container = new ContainerBuilder();
$container->register('mailer', 'Mailer');
use SymfonyComponentDependencyInjectionContainerBuilder;
$container = new ContainerBuilder();
$container
->register('mailer', 'Mailer')
->addArgument('sendmail');
Symfony2 Example
Friday, June 7, 13
class NewsletterManager
{
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
// ...
}
Friday, June 7, 13
class NewsletterManager
{
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
// ...
}
use SymfonyComponentDependencyInjectionContainerBuilder;
use SymfonyComponentDependencyInjectionReference;
$container = new ContainerBuilder();
$container->setParameter('mailer.transport', 'sendmail');
$container
->register('mailer', 'Mailer')
->addArgument('%mailer.transport%');
$container
->register('newsletter_manager', 'NewsletterManager')
->addArgument(new Reference('mailer'));
Friday, June 7, 13
use SymfonyComponentDependencyInjectionContainerBuilder;
$container = new ContainerBuilder();
$newsletterManager = $container->get('newsletter_manager');
Friday, June 7, 13
Inversion Of Control
Pattern
The control of the dependencies is inversed from one
being called to the one calling.
Friday, June 7, 13
Container
Newsletter Manager
Mailer
transport = sendmail
Friday, June 7, 13
Container
Newsletter Manager
Mailer
# src/Acme/HelloBundle/Resources/config/
services.yml
parameters:
# ...
mailer.transport: sendmail
services:
mailer:
class: Mailer
arguments: [%mailer.transport%]
newsletter_manager:
class: NewsletterManager
calls:
- [ setMailer, [ @mailer ] ]
transport = sendmail
Friday, June 7, 13
Decoupled Code
Friday, June 7, 13
Tips
Test first in isolation
Don’t need to wire together to test (Eg Environment etc)
integration tests are expensive.
When you do wire together with test/mock classes as needed
Duck Type your code / Use interfaces
Friday, June 7, 13
Friday, June 7, 13
$container = new Pimple();
// define some parameters
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';
Friday, June 7, 13
$container = new Pimple();
// define some parameters
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';
// define some objects
$container['session_storage'] = function ($c) {
return new $c['session_storage_class']($c['cookie_name']);
};
$container['session'] = function ($c) {
return new Session($c['session_storage']);
};
Friday, June 7, 13
Clever DI with Bindings
Friday, June 7, 13
Laravel 4 example
class MyConcreteClass implements MyInterface
{
Friday, June 7, 13
Laravel 4 example
class MyConcreteClass implements MyInterface
{
class MyClass
{
public function __construct(MyInterface $my_injection)
{
$this->my_injection = $my_injection;
}
}
App::bind('MyInterface', 'MyConcreteClass');
Friday, June 7, 13
Cleverer DI with Injection
Points & Bindings
Friday, June 7, 13
Google Guice
Guice alleviates the need for factories and the use of ‘new’ in
your ‘Java’a code
Think of Guice's @Inject as the new new. You will still need
to write factories in some cases, but your code will not
depend directly on them. Your code will be easier to change,
unit test and reuse in other contexts.
Ray.DI
Friday, June 7, 13
interface MailerInterface {}
PHP Google Guice Clone
Ray.DI
Friday, June 7, 13
class Mailer implements MailerInterface
{
private $transport;
/**
* @Inject
* @Named("transport_type")
*/
public function __construct($transport)
{
$this->transport = $transport;
}
}
interface MailerInterface {}
PHP Google Guice Clone
Ray.DI
Friday, June 7, 13
class Mailer implements MailerInterface
{
private $transport;
/**
* @Inject
* @Named("transport_type")
*/
public function __construct($transport)
{
$this->transport = $transport;
}
}
interface MailerInterface {}
class NewsletterManager
{
public $mailer;
/**
* @Inject
*/
public function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}
}
PHP Google Guice Clone
Ray.DI
Friday, June 7, 13
class NewsletterModule extends AbstractModule
{
protected function configure()
{
$this->bind()->annotatedWith('transport_type')->toInstance('sendmail');
$this->bind('MailerInterface')->to('Mailer');
}
}
Friday, June 7, 13
class NewsletterModule extends AbstractModule
{
protected function configure()
{
$this->bind()->annotatedWith('transport_type')->toInstance('sendmail');
$this->bind('MailerInterface')->to('Mailer');
}
} $di = Injector::create([new NewsletterModule]);
$newsletterManager = $di->getInstance('NewsletterManager');
Friday, June 7, 13
class NewsletterModule extends AbstractModule
{
protected function configure()
{
$this->bind()->annotatedWith('transport_type')->toInstance('sendmail');
$this->bind('MailerInterface')->to('Mailer');
}
} $di = Injector::create([new NewsletterModule]);
$newsletterManager = $di->getInstance('NewsletterManager');
$works = ($newsletterManager->mailer instanceof MailerInterface);
Friday, June 7, 13
Problems?/Gochas
1. Overly abstracted code
2. Easy to go crazy with it
3. Too many factory classes
4. Easy to loose what data is where
5. Container is like super global variable
Friday, June 7, 13
Many other
implementations
Friday, June 7, 13
Aspect Orientated
Programming
Friday, June 7, 13
The Problem
function addPost() {
$log->writeToLog("Entering addPost");
// Business Logic
$log->writeToLog("Leaving addPost");
}
Friday, June 7, 13
The Problem
function addPost() {
$log->writeToLog("Entering addPost");
// Business Logic
$log->writeToLog("Leaving addPost");
}
function addPost() {
$authentication->validateAuthentication($user);
$authorization->validateAccess($user);
// Business Logic
}
Friday, June 7, 13
Core Class Aspect Class
AOP Framework
Proxy Class
Continue Process
Friday, June 7, 13
Lithium Example -Logging in your model
namespace appmodels;
use lithiumutilcollectionFilters;
use lithiumutilLogger;
Filters::apply('appmodelsPost', 'save', function($self, $params, $chain) {
Logger::write('info', $params['data']['title']);
return $chain->next($self, $params, $chain);
});
Friday, June 7, 13
Dispatcher::applyFilter('run', function($self, $params, $chain) {
$key = md5($params['request']->url);
if($cache = Cache::read('default', $key)) {
return $cache;
}
$result = $chain->next($self, $params, $chain);
Cache::write('default', $key, $result, '+1 day');
return $result;
});
Lithium Example -Caching
Friday, June 7, 13
Friday, June 7, 13
Find
Friday, June 7, 13
Cache
Find
Friday, June 7, 13
Log
Cache
Find
Friday, June 7, 13
Log
Cache
Find
Friday, June 7, 13
Log
Cache
Find
Friday, June 7, 13
Log
Cache
Find
Friday, June 7, 13
Ray.AOP
Guice AOP Clone
Friday, June 7, 13
/**
* NotOnWeekends
*
* @Annotation
* @Target("METHOD")
*/
final class NotOnWeekends
{
}
Create Annotation
Create Real Class - Billing service
Friday, June 7, 13
/**
* NotOnWeekends
*
* @Annotation
* @Target("METHOD")
*/
final class NotOnWeekends
{
}
Create Annotation
class RealBillingService
{
/**
* @NotOnWeekends
*/
chargeOrder(PizzaOrder $order, CreditCard $creditCard)
{
Create Real Class - Billing service
Friday, June 7, 13
class WeekendBlocker implements MethodInterceptor
{
public function invoke(MethodInvocation $invocation)
{
$today = getdate();
if ($today['weekday'][0] === 'S') {
throw new RuntimeException(
$invocation->getMethod()->getName() . " not allowed on weekends!"
);
}
return $invocation->proceed();
}
}
Interception Logic
Friday, June 7, 13
$bind = new Bind;
$matcher = new Matcher(new Reader);
$interceptors = [new WeekendBlocker];
$pointcut = new Pointcut(
$matcher->any(),
$matcher->annotatedWith('RayAopSampleAnnotationNotOnWeekends'),
$interceptors
);
$bind->bind('RayAopSampleAnnotationRealBillingService', [$pointcut]);
$billing = new Weaver(new RealBillingService, $bind);
try {
echo $billing->chargeOrder();
} catch (RuntimeException $e) {
echo $e->getMessage() . "n";
exit(1);
}
Binding on Annotation
Friday, June 7, 13
$bind = new Bind;
$bind->bindInterceptors('chargeOrder', [new WeekendBlocker]);
$billingService = new Weaver(new RealBillingService, $bind);
try {
echo $billingService->chargeOrder();
} catch (RuntimeException $e) {
echo $e->getMessage() . "n";
exit(1);
}
Explicit Method Binding
Friday, June 7, 13
Gochas
Friday, June 7, 13
BEAR.Sunday
Friday, June 7, 13

What is this DI and AOP stuff anyway...

  • 1.
    What is thisDI and AOP stuff anyway... Friday, June 7, 13
  • 2.
    About Me Currently BBCSport (Freelancer) Lived in Japan for 15 years - love sushi Love frameworks - not just PHP ones Involved in Lithium and BEAR.Sunday projects Richard McIntyre - @mackstar Friday, June 7, 13
  • 3.
  • 4.
    Most Basic Example- Problem class Mailer { private $transport; public function __construct() { $this->transport = 'sendmail'; } // ... } Friday, June 7, 13
  • 5.
  • 6.
    Easily becomes aninheritance mess Sport_Lib_Builder_GenericStatsAbstract Sport_Lib_Builder_GenericStats_Cricket_Table Sport_Lib_Builder_GenericStats_Cricket_NarrowTable Sport_Lib_Builder_GenericStats_CricketAbstract Sport_Lib_API_GenericStats_Cricket Sport_Lib_API_GenericStatsAbstract Sport_Lib_Service_SportsData_Cricket Sport_Lib_Service_SportsData Sport_Lib_ServiceAbstract Friday, June 7, 13
  • 7.
    DI for a5 year old When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might even be looking for something we don't even have or which has expired. What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat. Friday, June 7, 13
  • 8.
    Something like this... Applicationneeds Foo, which needs Bar, which needs Bim, so: Application creates Bim Application creates Bar and gives it Bim Application creates Foo and gives it Bar Application calls Foo Foo calls Bar Bar does something Friday, June 7, 13
  • 9.
    The Service Container DependencyInjection Round 1 Friday, June 7, 13
  • 10.
    class Mailer { private $transport; publicfunction __construct($transport) { $this->transport = $transport; } // ... } Symfony2 Example Friday, June 7, 13
  • 11.
    class Mailer { private $transport; publicfunction __construct($transport) { $this->transport = $transport; } // ... } use SymfonyComponentDependencyInjectionContainerBuilder; $container = new ContainerBuilder(); $container->register('mailer', 'Mailer'); Symfony2 Example Friday, June 7, 13
  • 12.
    class Mailer { private $transport; publicfunction __construct($transport) { $this->transport = $transport; } // ... } use SymfonyComponentDependencyInjectionContainerBuilder; $container = new ContainerBuilder(); $container->register('mailer', 'Mailer'); use SymfonyComponentDependencyInjectionContainerBuilder; $container = new ContainerBuilder(); $container ->register('mailer', 'Mailer') ->addArgument('sendmail'); Symfony2 Example Friday, June 7, 13
  • 13.
    class NewsletterManager { private $mailer; publicfunction __construct(Mailer $mailer) { $this->mailer = $mailer; } // ... } Friday, June 7, 13
  • 14.
    class NewsletterManager { private $mailer; publicfunction __construct(Mailer $mailer) { $this->mailer = $mailer; } // ... } use SymfonyComponentDependencyInjectionContainerBuilder; use SymfonyComponentDependencyInjectionReference; $container = new ContainerBuilder(); $container->setParameter('mailer.transport', 'sendmail'); $container ->register('mailer', 'Mailer') ->addArgument('%mailer.transport%'); $container ->register('newsletter_manager', 'NewsletterManager') ->addArgument(new Reference('mailer')); Friday, June 7, 13
  • 15.
    use SymfonyComponentDependencyInjectionContainerBuilder; $container =new ContainerBuilder(); $newsletterManager = $container->get('newsletter_manager'); Friday, June 7, 13
  • 16.
    Inversion Of Control Pattern Thecontrol of the dependencies is inversed from one being called to the one calling. Friday, June 7, 13
  • 17.
  • 18.
    Container Newsletter Manager Mailer # src/Acme/HelloBundle/Resources/config/ services.yml parameters: #... mailer.transport: sendmail services: mailer: class: Mailer arguments: [%mailer.transport%] newsletter_manager: class: NewsletterManager calls: - [ setMailer, [ @mailer ] ] transport = sendmail Friday, June 7, 13
  • 19.
  • 20.
    Tips Test first inisolation Don’t need to wire together to test (Eg Environment etc) integration tests are expensive. When you do wire together with test/mock classes as needed Duck Type your code / Use interfaces Friday, June 7, 13
  • 21.
  • 22.
    $container = newPimple(); // define some parameters $container['cookie_name'] = 'SESSION_ID'; $container['session_storage_class'] = 'SessionStorage'; Friday, June 7, 13
  • 23.
    $container = newPimple(); // define some parameters $container['cookie_name'] = 'SESSION_ID'; $container['session_storage_class'] = 'SessionStorage'; // define some objects $container['session_storage'] = function ($c) { return new $c['session_storage_class']($c['cookie_name']); }; $container['session'] = function ($c) { return new Session($c['session_storage']); }; Friday, June 7, 13
  • 24.
    Clever DI withBindings Friday, June 7, 13
  • 25.
    Laravel 4 example classMyConcreteClass implements MyInterface { Friday, June 7, 13
  • 26.
    Laravel 4 example classMyConcreteClass implements MyInterface { class MyClass { public function __construct(MyInterface $my_injection) { $this->my_injection = $my_injection; } } App::bind('MyInterface', 'MyConcreteClass'); Friday, June 7, 13
  • 27.
    Cleverer DI withInjection Points & Bindings Friday, June 7, 13
  • 28.
    Google Guice Guice alleviatesthe need for factories and the use of ‘new’ in your ‘Java’a code Think of Guice's @Inject as the new new. You will still need to write factories in some cases, but your code will not depend directly on them. Your code will be easier to change, unit test and reuse in other contexts. Ray.DI Friday, June 7, 13
  • 29.
    interface MailerInterface {} PHPGoogle Guice Clone Ray.DI Friday, June 7, 13
  • 30.
    class Mailer implementsMailerInterface { private $transport; /** * @Inject * @Named("transport_type") */ public function __construct($transport) { $this->transport = $transport; } } interface MailerInterface {} PHP Google Guice Clone Ray.DI Friday, June 7, 13
  • 31.
    class Mailer implementsMailerInterface { private $transport; /** * @Inject * @Named("transport_type") */ public function __construct($transport) { $this->transport = $transport; } } interface MailerInterface {} class NewsletterManager { public $mailer; /** * @Inject */ public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } } PHP Google Guice Clone Ray.DI Friday, June 7, 13
  • 32.
    class NewsletterModule extendsAbstractModule { protected function configure() { $this->bind()->annotatedWith('transport_type')->toInstance('sendmail'); $this->bind('MailerInterface')->to('Mailer'); } } Friday, June 7, 13
  • 33.
    class NewsletterModule extendsAbstractModule { protected function configure() { $this->bind()->annotatedWith('transport_type')->toInstance('sendmail'); $this->bind('MailerInterface')->to('Mailer'); } } $di = Injector::create([new NewsletterModule]); $newsletterManager = $di->getInstance('NewsletterManager'); Friday, June 7, 13
  • 34.
    class NewsletterModule extendsAbstractModule { protected function configure() { $this->bind()->annotatedWith('transport_type')->toInstance('sendmail'); $this->bind('MailerInterface')->to('Mailer'); } } $di = Injector::create([new NewsletterModule]); $newsletterManager = $di->getInstance('NewsletterManager'); $works = ($newsletterManager->mailer instanceof MailerInterface); Friday, June 7, 13
  • 35.
    Problems?/Gochas 1. Overly abstractedcode 2. Easy to go crazy with it 3. Too many factory classes 4. Easy to loose what data is where 5. Container is like super global variable Friday, June 7, 13
  • 36.
  • 37.
  • 38.
    The Problem function addPost(){ $log->writeToLog("Entering addPost"); // Business Logic $log->writeToLog("Leaving addPost"); } Friday, June 7, 13
  • 39.
    The Problem function addPost(){ $log->writeToLog("Entering addPost"); // Business Logic $log->writeToLog("Leaving addPost"); } function addPost() { $authentication->validateAuthentication($user); $authorization->validateAccess($user); // Business Logic } Friday, June 7, 13
  • 40.
    Core Class AspectClass AOP Framework Proxy Class Continue Process Friday, June 7, 13
  • 41.
    Lithium Example -Loggingin your model namespace appmodels; use lithiumutilcollectionFilters; use lithiumutilLogger; Filters::apply('appmodelsPost', 'save', function($self, $params, $chain) { Logger::write('info', $params['data']['title']); return $chain->next($self, $params, $chain); }); Friday, June 7, 13
  • 42.
    Dispatcher::applyFilter('run', function($self, $params,$chain) { $key = md5($params['request']->url); if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result, '+1 day'); return $result; }); Lithium Example -Caching Friday, June 7, 13
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
    /** * NotOnWeekends * * @Annotation *@Target("METHOD") */ final class NotOnWeekends { } Create Annotation Create Real Class - Billing service Friday, June 7, 13
  • 52.
    /** * NotOnWeekends * * @Annotation *@Target("METHOD") */ final class NotOnWeekends { } Create Annotation class RealBillingService { /** * @NotOnWeekends */ chargeOrder(PizzaOrder $order, CreditCard $creditCard) { Create Real Class - Billing service Friday, June 7, 13
  • 53.
    class WeekendBlocker implementsMethodInterceptor { public function invoke(MethodInvocation $invocation) { $today = getdate(); if ($today['weekday'][0] === 'S') { throw new RuntimeException( $invocation->getMethod()->getName() . " not allowed on weekends!" ); } return $invocation->proceed(); } } Interception Logic Friday, June 7, 13
  • 54.
    $bind = newBind; $matcher = new Matcher(new Reader); $interceptors = [new WeekendBlocker]; $pointcut = new Pointcut( $matcher->any(), $matcher->annotatedWith('RayAopSampleAnnotationNotOnWeekends'), $interceptors ); $bind->bind('RayAopSampleAnnotationRealBillingService', [$pointcut]); $billing = new Weaver(new RealBillingService, $bind); try { echo $billing->chargeOrder(); } catch (RuntimeException $e) { echo $e->getMessage() . "n"; exit(1); } Binding on Annotation Friday, June 7, 13
  • 55.
    $bind = newBind; $bind->bindInterceptors('chargeOrder', [new WeekendBlocker]); $billingService = new Weaver(new RealBillingService, $bind); try { echo $billingService->chargeOrder(); } catch (RuntimeException $e) { echo $e->getMessage() . "n"; exit(1); } Explicit Method Binding Friday, June 7, 13
  • 56.
  • 57.