Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing Extensible Code Using Event Dispatcher

John Kary
October 20, 2012

Writing Extensible Code Using Event Dispatcher

Demonstrate refactoring a typical Controller to using Symfony2 EventDispatcher component to make writing decoupled code easier.

John Kary

October 20, 2012
Tweet

More Decks by John Kary

Other Decks in Programming

Transcript

  1. WRITING EXTENSIBLE CODE
    USING EVENT DISPATCHER
    John Kary
    Kansas City PHP User Group, October 2012

    View Slide

  2. ABOUT
    PHP since 2005
    Contract PHP Developer in Lawrence
    Education & Trucking Industry
    Previously of University of Kansas - IT

    View Slide

  3. EventDispatcher
    http://bit.ly/sf2-event-dispatcher
    {
    "require": {
    "symfony/event-dispatcher": "dev-master"
    }
    }
    php composer.phar update

    View Slide

  4. Overview
    1. Procedural PHP
    2. Refactor to Object-Oriented PHP
    3. Extensibility
    4. Controllers in Model View Controller
    5. EventDispatcher concept
    6. Refactor to using an EventDispatcher
    7. EventDispatcher caveats

    View Slide

  5. Maybe you know about...
    JavaScript Events
    Basics of Object-Oriented
    Programming
    Inheritance
    Polymorphism
    Encapsulation
    Abstraction
    Delegation
    Dependency Injection
    Single Responsibility Principle
    Separation of Concerns
    MVC

    View Slide

  6. View Slide

  7. DemCrookidWigitz.com

    View Slide

  8. John,
    Customers are unhappy. Send
    them a “Welcome!” email after
    they register on the web site.
    Tell them we are glad to have
    them as a customer.
    That’ll teach ‘em. Do it now.
    The Boss
    The Boss

    View Slide

  9. 0Before We Begin

    View Slide

  10. 0Before We Begin
    UTION CAUTION CAUTION CAUTIO

    View Slide

  11. The Code
    In This Presentation
    Is Not
    Production-Ready

    View Slide

  12. 1Procedural PHP

    View Slide

  13. Procedural PHP
    $_POST = array(
    'name' => 'John Kary',
    'email' => '[email protected]',
    );
    $to = $_POST['email'];
    $subject = 'New Account';
    $message = "Welcome " . $_POST['name'] . "!
    Your new space has been created! Use this link to login:
    https://activate.demcrookidwidgitz.com/code/Sq9284tWBnWgpjw
    Thanks!
    DemCrookidWigitz";
    $headers = array();
    $headers[] = 'From: [email protected]';
    $headers[] = 'Reply-To: [email protected]';
    $headers[] = 'X-Mailer: PHP/' . phpversion();
    mail($to, $subject, $message, implode($headers, "\r\n"));

    View Slide

  14. 2
    Object-Oriented PHP

    View Slide

  15. Object-Oriented PHP
    $_POST = array(
    'name' => 'John Kary',
    'email' => '[email protected]',
    );
    $to = $_POST['email'];
    $subject = 'New Account';
    $message = "Welcome " . $_POST['name'] . "!
    Your new space has been created! Use this link to login:
    https://activate.demcrookidwidgitz.com/code/Sq9284tWBnWgpjw
    Thanks!
    DemCrookidWigitz";
    $headers = array();
    $headers[] = 'From: [email protected]';
    $headers[] = 'Reply-To: [email protected]';
    $headers[] = 'X-Mailer: PHP/' . phpversion();
    mail($to, $subject, $message, implode($headers, "\r\n"));
    Customer

    View Slide

  16. Object-Oriented PHP
    class Customer
    {
    public function __construct(array $data)
    {
    $this->name = $data['name'];
    $this->email = $data['email'];
    }
    public function getName() {
    return $this->name;
    }
    public function getEmail() {
    return $this->email;
    }
    }

    View Slide

  17. Object-Oriented PHP
    $_POST = array(
    'name' => 'John Kary',
    'email' => '[email protected]',
    );
    $to = $_POST['email'];
    $subject = 'New Account';
    $message = "Welcome " . $_POST['name'] . "!
    Your new space has been created! Use this link to login:
    https://activate.demcrookidwidgitz.com/code/Sq9284tWBnWgpjw
    Thanks!
    DemCrookidWigitz";
    $headers = array();
    $headers[] = 'From: [email protected]';
    $headers[] = 'Reply-To: [email protected]';
    $headers[] = 'X-Mailer: PHP/' . phpversion();
    mail($to, $subject, $message, implode($headers, "\r\n"));
    Customer
    WelcomeEmail

    View Slide

  18. class WelcomeEmail
    {
    public function send($to, $name) {
    return mail($to, $this->getSubject(), $this->getMessage($name), $this->getHeaders());
    }
    protected function getSubject() {
    return 'New Account';
    }
    protected function getMessage($name) {
    return "Welcome " . $name . "!
    Your new space has been created! Use this link to login:
    https://activate.demcrookidwigitz.com/code/Sq9284tWBnWgpjw
    Thanks!
    DemCrookidWigitz";
    }
    protected function getHeaders() {
    $headers = array();
    $headers[] = 'From: [email protected]';
    $headers[] = 'Reply-To: [email protected]';
    $headers[] = 'X-Mailer: PHP/' . phpversion();
    return implode($headers, "\r\n");
    }
    }

    View Slide

  19. Object-Oriented PHP
    $_POST = array(
    'name' => 'John Kary',
    'email' => '[email protected]',
    );
    $to = $_POST['email'];
    $subject = 'New Account';
    $message = "Welcome " . $_POST['name'] . "!
    Your new space has been created! Use this link to login:
    https://activate.demcrookidwidgitz.com/code/Sq9284tWBnWgpjw
    Thanks!
    DemCrookidWigitz";
    $headers = array();
    $headers[] = 'From: [email protected]';
    $headers[] = 'Reply-To: [email protected]';
    $headers[] = 'X-Mailer: PHP/' . phpversion();
    mail($to, $subject, $message, implode($headers, "\r\n"));
    Customer
    WelcomeEmail
    Controller

    View Slide

  20. class Controller
    {
    // POST http://demcrookidwigitz.com/person
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail(), $customer->getName());
    }
    }
    Object-Oriented PHP

    View Slide

  21. John,
    Advertising wants to know
    how many new customers
    we’re getting. Setup some
    logging. I don’t want their
    grubby hands on the database.
    That’ll teach ‘em. Do it now.
    The Boss
    The Boss

    View Slide

  22. 3Disk Logging

    View Slide

  23. Disk Logging
    class Controller
    {
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName().
    ' at '.$customer->getEmail();
    $dLog->log($msg);
    }
    }

    View Slide

  24. View Slide

  25. John,
    Marketing wants to run
    statistics on orders. Keep
    them away from the database
    too. Just add to that other log
    file we setup for Advertising.
    That’ll teach ‘em. Do it now.
    The Boss
    The Boss

    View Slide

  26. 4More Disk Logging

    View Slide

  27. More Disk Logging
    class Controller
    {
    public function newCustomer() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName().
    ' at '.$customer->getEmail();
    $dLog->log($msg);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    }
    }

    View Slide

  28. View Slide

  29. 5Moar Logging!

    View Slide

  30. class Controller
    {
    public function newCustomer() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName().
    ' at '.$customer->getEmail();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  31. class Controller
    {
    public function newCustomer() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName().
    ' at '.$customer->getEmail();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName().' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');

    View Slide

  32. Your Codebase
    Is Growing

    View Slide

  33. ?

    View Slide

  34. class Controller
    {
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  35. class Controller
    {
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  36. class Controller
    {
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  37. class Controller
    {
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  38. Extensibility
    [A] system design principle where the implementation
    takes into consideration future growth.
    Extensibility - Wikipedia
    http://en.wikipedia.org/wiki/Extensibility

    View Slide

  39. class Controller
    {
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  40. class Controller
    {
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }
    NOT EXTENSIBLE!

    View Slide

  41. Model
    View
    Controller

    View Slide

  42. What is the
    Responsibility
    of a Controller?

    View Slide

  43. Controllers should
    contain
    LOGIC

    View Slide

  44. Controllers should
    contain
    LOGIC

    View Slide

  45. Controllers should
    contain
    LOGIC

    View Slide

  46. Controllers as Glue
    Custom
    Application
    Code
    Framework Controller

    View Slide

  47. Controllers as Glue
    Custom
    Application
    Code
    Controller

    View Slide

  48. Controllers as Glue
    Custom
    Application
    Code
    Controller

    View Slide

  49. Event Dispatcher

    View Slide

  50. Event Dispatcher - Concept
    Request
    Response
    kernel.controller
    kernel.request
    kernel.response
    kernel.view

    View Slide

  51. Event Dispatcher - Concept
    Request
    Response
    Your Events
    kernel.controller
    kernel.request
    kernel.response
    kernel.view

    View Slide

  52. Event Dispatcher - Concept
    Request
    Response
    kernel.controller
    kernel.request
    kernel.response
    kernel.view
    user.login

    View Slide

  53. Event Dispatcher - Concept
    Request
    Response
    kernel.controller
    kernel.request
    kernel.response
    kernel.view
    user.login
    user.register

    View Slide

  54. Event Dispatcher - Concept
    Request
    Response
    kernel.controller
    kernel.request
    kernel.response
    kernel.view
    user.login
    user.register
    $user

    View Slide

  55. Event Dispatcher - Concept
    Request
    Response
    kernel.controller
    kernel.request
    kernel.response
    kernel.view
    user.login
    user.register
    $user
    $user

    View Slide

  56. Event Dispatcher - Register Listeners
    EventDispatcher
    AListener
    named.eventA
    BListener
    named.eventB
    CListener
    named.eventC

    View Slide

  57. Let’s use a
    more concrete
    example

    View Slide

  58. Event Dispatcher - Register Listeners
    CartListener::
    onAddToCart()
    EventDispatcher
    cart.add
    CustomerListener::
    onRegister()
    customer.register
    CartListener::
    grantDiscount()
    cart.checkout

    View Slide

  59. Event Dispatcher - Register Listeners
    use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $listener = new CustomerListener();
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    // Register more listeners
    class CustomerListener
    {
    public function onRegister(CustomerEvent $event)
    {
    // Do some work
    }
    }
    Any PHP Callable
    true === is_callable()

    View Slide

  60. Event Dispatcher - Register Listeners
    use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $dispatcher->addListener('customer.register', array('CustomerListener', 'onRegister'));
    // Register more listeners
    class CustomerListener
    {
    public static function onRegister(CustomerEvent $event)
    {
    // Do some work
    }
    }

    View Slide

  61. Event Dispatcher - Register Listeners
    use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    // Do some work
    });

    View Slide

  62. Event Dispatcher - Dispatch an Event

    View Slide

  63. Request
    Response
    Event Dispatcher - Dispatch an Event

    View Slide

  64. Request
    Response
    Event Dispatcher - Dispatch an Event
    EventDispatcher
    “customer.register”
    CustomerListener::
    onRegister()
    “cart.add”
    CartListener::
    onAddToCart()
    “cart.checkout”
    CartListener::
    grantDiscount()

    View Slide

  65. Request
    Response
    CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - Dispatch an Event
    EventDispatcher
    “customer.register”
    CustomerListener::
    onRegister()
    “cart.add”
    CartListener::
    onAddToCart()
    “cart.checkout”
    CartListener::
    grantDiscount()

    View Slide

  66. Request
    Response
    CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - Dispatch an Event
    EventDispatcher
    “customer.register”
    CustomerListener::
    onRegister()
    “cart.add”
    CartListener::
    onAddToCart()
    “cart.checkout”
    CartListener::
    grantDiscount()

    View Slide

  67. Event Dispatcher - Dispatch an Event
    use Symfony\Component\EventDispatcher\EventDispatcher;
    class CustomerController
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer(array(
    'first_name' => 'Bob',
    'last_name' => 'Smith',
    'email' => '[email protected]',
    ));
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    }
    Customer
    Event
    Event Name

    View Slide

  68. Request
    Response
    CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - Dispatch an Event
    EventDispatcher
    “customer.register”
    CustomerListener::
    onRegister()
    “cart.add”
    CartListener::
    onAddToCart()
    “cart.checkout”
    CartListener::
    grantDiscount()

    View Slide

  69. Request
    Response
    CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - Dispatch an Event
    EventDispatcher
    “customer.register”
    CustomerListener::
    onRegister()
    “cart.add”
    CartListener::
    onAddToCart()
    “cart.checkout”
    CartListener::
    grantDiscount()
    $this->dispatcher->dispatch('customer.register', $event);

    View Slide

  70. Request
    Response
    CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - Dispatch an Event
    EventDispatcher
    “customer.register”
    CustomerListener::
    onRegister()
    “cart.add”
    CartListener::
    onAddToCart()
    “cart.checkout”
    CartListener::
    grantDiscount()

    View Slide

  71. Request
    Response
    CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - Dispatch an Event
    EventDispatcher
    “customer.register”
    CustomerListener::
    onRegister()
    “cart.add”
    CartListener::
    onAddToCart()
    “cart.checkout”
    CartListener::
    grantDiscount()
    “customer.register”
    CustomerListener::
    onRegister()

    View Slide

  72. Event Dispatcher - Handle the Event
    class CustomerListener
    {
    public function onRegister(CustomerEvent $event)
    {
    $customer = $event->getCustomer();
    $name = $event->getName();
    $dispatcher = $event->getDispatcher();
    // Do something with Customer object
    }
    }
    CustomerEvent
    “customer.register”
    Customer

    View Slide

  73. Event Dispatcher - Handle the Event
    class CustomerListener
    {
    public function onRegister(CustomerEvent $event)
    {
    $customer = $event->getCustomer();
    $name = $event->getName();
    $dispatcher = $event->getDispatcher();
    // Do something with Customer object
    }
    }

    View Slide

  74. Event Dispatcher - Handle the Event
    use Symfony\Component\EventDispatcher\Event;
    class CustomerEvent extends Event
    {
    protected $customer;
    public function __construct(Customer $customer) {
    $this->customer = $customer;
    }
    public function getCustomer() {
    return $this->customer;
    }
    }

    View Slide

  75. Event Dispatcher - All together now!

    View Slide

  76. Event Dispatcher - All together now!
    EventDispatcher

    View Slide

  77. Event Dispatcher - All together now!
    CartListener::
    onAddToCart()
    CustomerListener::
    onRegister()
    CartListener::
    grantDiscount()
    EventDispatcher
    cart.checkout
    cart.add
    customer.register

    View Slide

  78. Event Dispatcher - All together now!
    CartListener::
    onAddToCart()
    CustomerListener::
    onRegister()
    CartListener::
    grantDiscount()
    EventDispatcher
    “cart.checkout”
    CartListener::
    grantDiscount()
    “cart.add”
    CartListener::
    onAddToCart()
    “customer.register”
    CustomerListener::
    onRegister()

    View Slide

  79. CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - All together now!
    CartListener::
    onAddToCart()
    CustomerListener::
    onRegister()
    CartListener::
    grantDiscount()
    EventDispatcher
    “cart.checkout”
    CartListener::
    grantDiscount()
    “cart.add”
    CartListener::
    onAddToCart()
    “customer.register”
    CustomerListener::
    onRegister()

    View Slide

  80. CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - All together now!
    CartListener::
    onAddToCart()
    CustomerListener::
    onRegister()
    CartListener::
    grantDiscount()
    EventDispatcher
    “cart.checkout”
    CartListener::
    grantDiscount()
    “cart.add”
    CartListener::
    onAddToCart()
    “customer.register”
    CustomerListener::
    onRegister()
    $this->dispatcher->dispatch('customer.register', $event);

    View Slide

  81. CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - All together now!
    CartListener::
    onAddToCart()
    CustomerListener::
    onRegister()
    CartListener::
    grantDiscount()
    EventDispatcher
    “cart.checkout”
    CartListener::
    grantDiscount()
    “cart.add”
    CartListener::
    onAddToCart()
    “customer.register”
    CustomerListener::
    onRegister()

    View Slide

  82. CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - All together now!
    CartListener::
    onAddToCart()
    CustomerListener::
    onRegister()
    CartListener::
    grantDiscount()
    EventDispatcher
    “cart.checkout”
    CartListener::
    grantDiscount()
    “cart.add”
    CartListener::
    onAddToCart()
    “customer.register”
    CustomerListener::
    onRegister()
    “customer.register”
    CustomerListener::
    onRegister()

    View Slide

  83. CustomerEvent
    “customer.register”
    Customer
    Event Dispatcher - All together now!
    CartListener::
    onAddToCart()
    CustomerListener::
    onRegister()
    CartListener::
    grantDiscount()
    EventDispatcher
    “cart.checkout”
    CartListener::
    grantDiscount()
    “cart.add”
    CartListener::
    onAddToCart()
    “customer.register”
    CustomerListener::
    onRegister()
    “customer.register”
    CustomerListener::
    onRegister()
    That’s
    Event Dispatcher!

    View Slide

  84. Let’s refactor
    our Controller

    View Slide

  85. class Controller
    {
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }
    START

    View Slide

  86. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $event = new OrderEvent($order);
    $this->dispatcher->dispatch('order.new', $event);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.new', $event);
    }
    public function cancelOrder() {
    // ...
    $event = new OrderEvent($order);
    $this->dispatcher->dispatch('order.cancel', $event);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.cancel', $event);
    }
    }
    FINISH

    View Slide

  87. 6
    Inject the EventDispatcher

    View Slide

  88. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');

    View Slide

  89. 7Our First Event

    View Slide

  90. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $email = new WelcomeEmail();
    $email->send($customer->getEmail());
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');

    View Slide

  91. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');

    View Slide

  92. use Symfony\Component\EventDispatcher\Event;
    class CustomerEvent extends Event
    {
    protected $customer;
    public function __construct(Customer $customer) {
    $this->customer = $customer;
    }
    public function getCustomer() {
    return $this->customer;
    }
    }

    View Slide

  93. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail());
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class WelcomeEmail
    {
    public function send($to, $name) {
    return mail($to, $this->getSubject(), $this->getMessage($name), $this->getHeaders());
    }
    protected function getSubject() {
    return 'New Account';
    }
    protected function getMessage($name) {
    return "Welcome " . $name . "!
    Your new space has been created! Use this link to login:
    https://activate.demcrookidwigitz.com/code/Sq9284tWBnWgpjw
    Thanks!
    DemCrookidWigitz";
    }
    protected function getHeaders() {
    $headers = array();
    $headers[] = 'From: [email protected]';

    View Slide

  94. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail());
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));

    View Slide

  95. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail());
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));

    View Slide

  96. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail());
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }

    View Slide

  97. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail());
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }

    View Slide

  98. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail());
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }
    class WelcomeEmail
    {
    public function send($to, $name) {
    return mail($to, $this->getSubject(), $this->getMessage($name), $this->getHeaders());
    }
    }

    View Slide

  99. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail());
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }
    class WelcomeEmail
    {
    public function send($to, $name) {
    return mail($to, $this->getSubject(), $this->getMessage($name), $this->getHeaders());
    }
    }

    View Slide

  100. 8
    Move more code to listeners

    View Slide

  101. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');

    View Slide

  102. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail(), $diskLogger);
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));

    View Slide

  103. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail(), $diskLogger);
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));

    View Slide

  104. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail(), $diskLogger);
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }

    View Slide

  105. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail(), $diskLogger);
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email, DiskLogger $logger) {
    $this->email = $email;
    $this->logger = $logger;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }

    View Slide

  106. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail(), $diskLogger);
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email, DiskLogger $logger) {
    $this->email = $email;
    $this->logger = $logger;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }

    View Slide

  107. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail(), $diskLogger);
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email, DiskLogger $logger) {
    $this->email = $email;
    $this->logger = $logger;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email, DiskLogger $logger) {
    $this->email = $email;
    $this->logger = $logger;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    $this->logger->log('Sent welcome email to ' . $customer->getName());
    }
    }

    View Slide

  108. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail(), $diskLogger);
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email) {
    $this->email = $email;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email, DiskLogger $logger) {
    $this->email = $email;
    $this->logger = $logger;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    }
    }
    class CustomerEmailListener
    {
    public function __construct(WelcomeEmail $email, DiskLogger $logger) {
    $this->email = $email;
    $this->logger = $logger;
    }
    public function onRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getEmail(), $customer->getName());
    $this->logger->log('Sent welcome email to ' . $customer->getName());
    }
    }

    View Slide

  109. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');

    View Slide

  110. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  111. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $dLog = new DiskLogger('/logs/prod.log');
    $msg = 'Sent welcome email to '.$customer->getName();
    $dLog->log($msg);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  112. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossNewCustomerEmailListener(new BossNewCustomerEmail($bossEmail));
    $dispatcher->addListener('customer.register', array($listener, 'onCustomerRegister'));

    View Slide

  113. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossNewCustomerEmailListener(new BossNewCustomerEmail($bossEmail));
    $dispatcher->addListener('customer.register', array($listener, 'onCustomerRegister'));

    View Slide

  114. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossNewCustomerEmailListener(new BossNewCustomerEmail($bossEmail));
    $dispatcher->addListener('customer.register', array($listener, 'onCustomerRegister'));

    View Slide

  115. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossNewCustomerEmailListener(new BossNewCustomerEmail($bossEmail));
    $dispatcher->addListener('customer.register', array($listener, 'onCustomerRegister'));
    class BossNewCustomerEmail
    {
    public function __construct($bossEmail) {
    $this->bossEmail = $bossEmail;
    }
    public function send($name) {
    return mail($this->bossEmail, $this->getSubject(), $this->getMessage($name),
    $this->getHeaders());
    }
    protected function getSubject() {
    return 'New Customer Registered!';
    }
    protected function getMessage($name) {
    return "Boss! " . $name . " just registered! Sweet, huh?";
    }
    // ...
    }

    View Slide

  116. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossNewCustomerEmailListener(new BossNewCustomerEmail($bossEmail));
    $dispatcher->addListener('customer.register', array($listener, 'onCustomerRegister'));

    View Slide

  117. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossNewCustomerEmailListener(new BossNewCustomerEmail($bossEmail));
    $dispatcher->addListener('customer.register', array($listener, 'onCustomerRegister'));
    class BossNewCustomerEmailListener
    {
    public function __construct(BossNewCustomerEmail $email) {
    $this->email = $email;
    }
    public function onCustomerRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getName());
    }
    }

    View Slide

  118. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossNewCustomerEmailListener(new BossNewCustomerEmail($bossEmail));
    $dispatcher->addListener('customer.register', array($listener, 'onCustomerRegister'));
    class BossNewCustomerEmailListener
    {
    public function __construct(BossNewCustomerEmail $email) {
    $this->email = $email;
    }
    public function onCustomerRegister(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getName());
    }
    }

    View Slide

  119. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  120. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' has registered!');
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }
    class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  121. 9
    Clean Up Other Controllers

    View Slide

  122. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  123. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $twitter->tweet($customer->getName() . ' just ordered from us!');
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }
    class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.new', $event);
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  124. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    // Wire listeners
    $listener = new TwitterListener($twitter);
    $dispatcher->addListener('order.new', array($listener, 'tweetAtOrderer'));

    View Slide

  125. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    // Wire listeners
    $listener = new TwitterListener($twitter);
    $dispatcher->addListener('order.new', array($listener, 'tweetAtOrderer'));
    class TwitterListener
    {
    public function __construct(Twitter $twitter) {
    $this->twitter = $twitter;
    }
    public function tweetAtOrderer(CustomerEvent $event) {
    $customer = $event->getOrder();
    $msg = $customer->getTwitterHandle().' Thanks for ordering from us!';
    $this->twitter->tweet($msg);
    }
    }

    View Slide

  126. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.new', $event);
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $eLog = new EmailLogger('[email protected]');
    $eLog->log($customer->getName() . ' cancelled their order...');
    }
    }

    View Slide

  127. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossCancelOrderEmailListener(new BossCancelOrderEmail($bossEmail));
    $dispatcher->addListener('customer.order.cancel', array($listener, 'onCancelOrder'));
    class BossCancelOrderEmail
    {
    public function __construct($bossEmail) {
    $this->bossEmail = $bossEmail;
    }
    public function send($name) {
    return mail($this->bossEmail, $this->getSubject(), $this->getMessage($name),
    $this->getHeaders());
    }
    protected function getSubject() {
    return 'Customer Order Cancelled';
    }
    protected function getMessage($name) {
    return "Boss! " . $name . " cancelled their order. Bummer!";
    }
    // ...
    }

    View Slide

  128. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $bossEmail = '[email protected]';
    // Wire listeners
    // ...
    $listener = new BossCancelOrderEmailListener(new BossCancelOrderEmail($bossEmail));
    $dispatcher->addListener('customer.order.cancel', array($listener, 'onCancelOrder'));
    class BossCancelOrderEmailListener
    {
    public function __construct(BossCancelOrderEmail $email) {
    $this->email = $email;
    }
    public function onCancelOrder(CustomerEvent $event) {
    $customer = $event->getCustomer();
    $this->email->send($customer->getName());
    }
    }

    View Slide

  129. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.new', $event);
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.cancel', $event);
    }
    }

    View Slide

  130. 1
    0
    Our Second Event

    View Slide

  131. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.new', $event);
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.cancel', $event);
    }
    }

    View Slide

  132. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Created order ' . $number);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.new', $event);
    }
    public function cancelOrder() {
    // ...
    $dLog = new DiskLogger('/logs/prod.log');
    $dLog->log('Cancelled order number ' . $number);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.cancel', $event);
    }
    }
    class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $event = new OrderEvent($order);
    $this->dispatcher->dispatch('order.new', $event);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.new', $event);
    }
    public function cancelOrder() {
    // ...
    $event = new OrderEvent($order);
    $this->dispatcher->dispatch('order.cancel', $event);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.cancel', $event);
    }
    }

    View Slide

  133. use Symfony\Component\EventDispatcher\Event;
    class OrderEvent extends Event
    {
    protected $order;
    public function __construct(Order $order) {
    $this->order = $order;
    }
    public function getOrder() {
    return $this->order;
    }
    }

    View Slide

  134. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new OrderLoggerListener($diskLogger);
    $dispatcher->addListener('order.new', array($listener, 'onNewOrder'));
    class OrderLoggerListener
    {
    public function __construct(DiskLogger $logger) {
    $this->logger = $logger;
    }
    public function onNewOrder(OrderEvent $event) {
    $order = $event->getOrder();
    $this->logger->log('Created order number ' . $order->getNumber());
    }
    }

    View Slide

  135. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    // Wire listeners
    $listener = new OrderLoggerListener($diskLogger);
    $dispatcher->addListener('order.new', array($listener, 'onNewOrder'));
    $dispatcher->addListener('order.cancel', array($listener, 'onCancelOrder'));
    class OrderLoggerListener
    {
    public function __construct(DiskLogger $logger) {
    $this->logger = $logger;
    }
    public function onNewOrder(OrderEvent $event) {
    $order = $event->getOrder();
    $this->logger->log('Created order number ' . $order->getNumber());
    }
    public function onCancelOrder(OrderEvent $event) {
    $order = $event->getOrder();
    $this->logger->log('Cancelled order number ' . $order->getNumber());
    }
    }

    View Slide

  136. class Controller
    {
    public function __construct(EventDispatcher $dispatcher) {
    $this->dispatcher = $dispatcher;
    }
    public function newCustomer() {
    $customer = new Customer($_POST);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.register', $event);
    }
    public function createOrder() {
    // ...
    $event = new OrderEvent($order);
    $this->dispatcher->dispatch('order.new', $event);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.new', $event);
    }
    public function cancelOrder() {
    // ...
    $event = new OrderEvent($order);
    $this->dispatcher->dispatch('order.cancel', $event);
    $event = new CustomerEvent($customer);
    $this->dispatcher->dispatch('customer.order.cancel', $event);
    }
    }
    FINISH

    View Slide

  137. use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $diskLogger = new DiskLogger('/logs/prod.log');
    $twitter = new Twitter('demcrookidwigitz', 'b3@tb0Xpr0$');
    $bossEmail = '[email protected]';
    // Wire listeners
    $listener = new CustomerEmailListener(new WelcomeEmail());
    $dispatcher->addListener('customer.register', array($listener, 'onRegister'));
    $listener = new BossNewCustomerEmailListener(new BossNewCustomerEmail($bossEmail));
    $dispatcher->addListener('customer.register', array($listener, 'onCustomerRegister'));
    $listener = new BossCancelOrderEmailListener(new BossCancelOrderEmail($bossEmail));
    $dispatcher->addListener('customer.order.cancel', array($listener, 'onCancelOrder'));
    $listener = new OrderLoggerListener($diskLogger);
    $dispatcher->addListener('order.new', array($listener, 'onNewOrder'));
    $dispatcher->addListener('order.cancel', array($listener, 'onCancelOrder'));
    $listener = new TwitterListener($twitter);
    $dispatcher->addListener('order.new', array($listener, 'tweetAtOrderer'));
    FINISH

    View Slide

  138. Caveats

    View Slide

  139. Listeners are notified
    in the order registered

    View Slide

  140. Caveat - Priority
    use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    // First
    });
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    // Second
    });
    $customer = new Customer();
    $customer->setEmail('[email protected]');
    $event = new CustomerEvent($customer);
    $dispatcher->dispatch('customer.register', $event);
    First
    Second

    View Slide

  141. unless
    a Priority integer
    is given when
    registering a listener

    View Slide

  142. Caveat - Priority
    use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    // Second
    }, 100);
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    // First
    }, 200);
    $customer = new Customer();
    $customer->setEmail('[email protected]');
    $event = new CustomerEvent($customer);
    $dispatcher->dispatch('customer.register', $event);
    First
    Second

    View Slide

  143. Caveat - Priority
    use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    // Third
    }, 100);
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    // First
    }, 200);
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    // Second
    }, 200);
    $customer = new Customer();
    $customer->setEmail('[email protected]');
    $event = new CustomerEvent($customer);
    $dispatcher->dispatch('customer.register', $event);
    First
    Third
    Second

    View Slide

  144. Don’t rely on priority

    View Slide

  145. Objects attached to
    Events are passed
    by reference

    View Slide

  146. which may lead to
    side effects
    if you’re not careful

    View Slide

  147. Caveat - Side Effects
    use Symfony\Component\EventDispatcher\EventDispatcher;
    $dispatcher = new EventDispatcher();
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    echo $event->getCustomer()->getEmail(); // [email protected]
    $event->getCustomer()->setEmail('[email protected]');
    });
    $dispatcher->addListener('customer.register', function(CustomerEvent $event) {
    echo $event->getCustomer()->getEmail(); // [email protected]
    });
    $customer = new Customer();
    $customer->setEmail('[email protected]');
    $event = new CustomerEvent($customer);
    $dispatcher->dispatch('customer.register', $event);
    1. Set Email
    2. Get Email
    3. Update Email
    4. Email not the same

    View Slide

  148. Can make
    debugging
    difficult

    View Slide

  149. Just ask a
    developer

    View Slide

  150. Just ask a
    developer

    View Slide

  151. Adobe Desktop Application Codebase
    Source: http://stlab.adobe.com/wiki/index.php/File:2008_07_25_google.pdf
    Event Handling logic
    All other logic
    33%
    Event Handling bugs
    All other bugs
    50%

    View Slide

  152. Ideas for Utilizing
    Events

    View Slide

  153. Ideas for Utilizing Events
    • Logging
    • Offloading expensive operations to a message queue
    • Email, PDF generation, file system access
    • Setting “created_at” or “updated_at” on an entity
    • Updating a search index when persisting an entity
    • Allowing users of your plug-ins to extend their
    functionality without modifying your code
    • What ideas have you come up with?

    View Slide

  154. Further Reading
    • Introduction to Symfony2 EventDispatcher
    http://bit.ly/sf2-event-dispatcher
    • An Introduction to Zend Framework 2 Event Manager
    http://bit.ly/zf2-event-manater
    • The S.O.L.I.D. Principles of OO and Agile Design
    http://bit.ly/uncle-bob-oo-insights

    View Slide

  155. Sources
    • http://lampwww.epfl.ch/~imaier/pub/DeprecatingObserversTR2010.pdf
    • http://stlab.adobe.com/wiki/index.php/File:2008_07_25_google.pdf
    Photo Credits
    • http://www.flickr.com/photos/pedromourapinheiro/2456410959/
    • http://www.flickr.com/photos/seat850/3893504473/
    • http://www.flickr.com/photos/slworking/5170065748/
    • http://www.flickr.com/photos/33037982@N04/3727397261/
    • http://www.flickr.com/photos/factoryjoe/26967479/

    View Slide

  156. John Kary
    johnkary@
    gmail.com
    @
    johnkary
    Slides @ http://johnkary.net/talks
    WRITING EXTENSIBLE CODE
    USING EVENT DISPATCHER
    https://joind.in/7551

    View Slide