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

IoC container - Laracon EU 2016

IoC container - Laracon EU 2016

Did you know your IoC container can do a whole lot more than just constructor injection? Besides that it is actually packed with features. Inflectors, resolving callbacks, aliasing, method invocation to name a few. In this talk you will learn how to leverage the power of a great container and service providers to write better, loosely coupled code. Well designed code put together by your IoC container will make your applications SOLID, modular, lean and decoupled from the framework!

Feedback here: https://joind.in/event/laracon-eu-2016/ioc-container-beyond-constructor-injection

Hannes Van De Vreken

August 23, 2016
Tweet

More Decks by Hannes Van De Vreken

Other Decks in Technology

Transcript

  1. IOC container
    @hannesvdvreken
    Laracon EU 2016

    View full-size slide

  2. Hi, my name is Hannes.

    View full-size slide

  3. madewithlove.be
    !$%&'

    View full-size slide

  4. IoC Container

    View full-size slide

  5. International
    O
    C

    View full-size slide

  6. International
    Olympic
    C

    View full-size slide

  7. International
    Olympic
    Committee

    View full-size slide

  8. International
    Olympic
    Committee

    View full-size slide

  9. WHAT IS IOC?
    IoC == Inversion of Control

    View full-size slide

  10. WHAT IS IOC?
    But first:
    Design patterns

    View full-size slide

  11. WHAT IS IOC?
    class Service
    {
    public function __construct(Ftp $ftp)
    {
    }
    public function calculate()
    {
    // Get stuff from FTP.
    // Do calculations.
    // Return result.
    }
    }

    View full-size slide

  12. WHAT IS IOC?
    class Service
    {
    public function __construct(Ftp $ftp)
    {
    }
    public function calculate()
    {
    // Get stuff from FTP.
    // Add caching to prevent hitting FTP over and over again
    // Do calculations.
    // Return result.
    }
    }

    View full-size slide

  13. WHAT IS IOC?
    class Service
    {
    public function __construct(Ftp $ftp)
    {
    }
    public function calculate()
    {
    // Get stuff from FTP.
    // Add caching to prevent hitting FTP over and over again
    // Log that FTP is accessed.
    // Do calculations.
    // Return result.
    }
    }

    View full-size slide

  14. WHAT IS IOC?
    class Service
    {
    public function __construct(Ftp $ftp)
    {
    }
    public function calculate()
    {
    // Get stuff from FTP.
    // Add caching to prevent hitting FTP over and over again
    // Log that FTP is accessed.
    // Do calculations.
    // Return result.
    }
    }

    View full-size slide

  15. WHAT IS IOC?
    \
    class FtpStorage
    {
    public function __construct(Ftp $ftp)
    {
    $this->ftp = $ftp;
    }
    public function get($key)
    {
    // Get it.
    // Cache it.
    // Log it.
    }
    }

    View full-size slide

  16. WHAT IS IOC?
    class Service
    {
    public function __construct(FtpStorage $storage)
    {

    $this->storage = $storage;
    }
    }

    View full-size slide

  17. WHAT IS IOC?
    interface Storage
    {
    public function get($key);
    }

    View full-size slide

  18. WHAT IS IOC?
    \
    class FtpStorage implements Storage
    {
    public function __construct(Ftp $ftp)
    {
    $this->ftp = $ftp;
    }
    public function get($key)
    {

    }
    }

    View full-size slide

  19. WHAT IS IOC?
    class Service
    {
    public function __construct(Storage $storage)
    {

    $this->storage = $storage;
    }
    }

    View full-size slide

  20. WHAT IS IOC?
    new Service(new FtpStorage(new Ftp($settings)));

    View full-size slide

  21. WHAT IS IOC?
    Don’t create these instances yourself

    View full-size slide

  22. WHAT IS IOC?
    Inversion of Control
    IoC container to the rescue

    View full-size slide

  23. WHAT IS IOC?
    \
    class CacheDecorator implements Storage
    {
    public function __construct(Cache $cache, Storage $storage)
    {…}
    public function get($key)
    {
    return $this->cache->remember($key, 60, function () {
    return $this->storage->get($key);
    });
    }
    }

    View full-size slide

  24. WHAT IS IOC?
    \
    class LogDecorator implements Storage
    {
    public function __construct(LoggerInterface $log, Storage $storage)
    {…}
    public function get($key)
    {
    $this->log->info('Retrieved value for '.$key);
    return $this->storage->get($key);
    }
    }

    View full-size slide

  25. WHAT IS IOC?
    \
    class FtpStorage implements Storage
    {
    public function __construct(Ftp $ftp)
    {
    $this->ftp = $ftp;
    }
    public function get($key)
    {
    // Get it and return it.
    }
    }

    View full-size slide

  26. WHAT IS IOC?
    FtpStorage
    implements
    Storage
    Service
    depends on
    depends on
    Ftp

    View full-size slide

  27. WHAT IS IOC?
    FtpStorage
    Storage
    CacheDecorator
    Service
    LogDecorator
    S3Storage
    implements
    depends on

    View full-size slide

  28. WHAT IS IOC?
    Dependency inversion:
    Interface + implementation
    Dependency injection:
    Injecting `Storage` into `Service`
    Inversion of Control:
    The container builds your objects and calls them

    View full-size slide

  29. WHAT IS IOC?
    $container->get(Service::class);
    1) Make FtpStorage
    2) Wrap it with LogDecorator
    3) Wrap LogDecorator instance with CacheDecorator
    4) New up Service with CacheDecorator instance.

    View full-size slide

  30. WHAT IS IOC?
    IoC container is here to help you with
    Composition

    View full-size slide

  31. CONSTRUCTOR INJECTION
    illuminate/container
    league/container
    symfony/dependency-injection
    aura/di

    container-interop/container-interop

    View full-size slide

  32. Automatic constructor
    injection

    View full-size slide

  33. class SendInvoice
    {
    public function __construct(Mailer $mailer)
    {
    $this->mailer = $mailer;
    }
    public function listen(InvoiceGeneratedEvent $event)
    {

    }
    }

    View full-size slide

  34. CONSTRUCTOR INJECTION
    Constructor injection

    View full-size slide

  35. $app->make(SendInvoice::class);

    View full-size slide

  36. CONSTRUCTOR INJECTION
    Reflection

    View full-size slide

  37. (new \ReflectionClass($class))
    ->getConstructor()
    ->getParameters();

    View full-size slide

  38. CONSTRUCTOR INJECTION
    Recursively

    View full-size slide

  39. Helping the container

    View full-size slide

  40. REGISTRATION
    Service Providers

    View full-size slide

  41. class ServiceProvider
    {
    public function register()
    {

    }
    public function boot(Dispatcher $events)
    }

    View full-size slide

  42. class ServiceProvider
    {
    public function register() {…}
    public function boot(Dispatcher $events)
    {

    }
    }

    View full-size slide

  43. class ServiceProvider
    {
    protected $defer = true;
    public function register() {…}
    public function provides()
    {
    return [
    Mailer::class,
    SwiftMailer::class,
    ];
    }
    }

    View full-size slide

  44. REGISTRATION
    Register bindings
    in the register method

    View full-size slide

  45. REGISTRATION - BINDING
    $this->app->bind(
    MailerContract::class,
    SwiftMailer::class
    );

    View full-size slide

  46. REGISTRATION - BINDING
    $this->app->bind(Mailer::class, function () {
    $mailer = new SwiftMailer();
    return new LogDecorator($mailer);
    });

    View full-size slide

  47. REGISTRATION - BINDING
    $this->app->singleton(Mailer::class, function () {

    });

    View full-size slide

  48. REGISTRATION - BINDING
    $this->app->alias(
    Mailer::class,
    'mailer'
    );

    View full-size slide

  49. REGISTRATION
    Bind a concretion for every interface

    View full-size slide

  50. REGISTRATION
    Bind everything with its class name

    View full-size slide

  51. REGISTRATION
    Unless you bind multiple versions

    View full-size slide

  52. REGISTRATION
    $this->app->bind('fs.photos', function () {});
    $this->app->bind('fs.files', function () {});

    View full-size slide

  53. REGISTRATION - CONTEXTUAL BINDING
    Contextual binding

    View full-size slide

  54. REGISTRATION - CONTEXTUAL BINDING
    $this->app
    ->when(PhotosController::class)
    ->needs(FilesystemInterface::class)
    ->give('fs.photos');

    View full-size slide

  55. REGISTRATION - CONTEXTUAL BINDING
    No more
    DB::connection('db1')->table($table)->…;

    View full-size slide

  56. $this->app->bind('db.1', function () {
    return $this->app['db']
    ->connection('db1');
    });
    $this->app
    ->when(EventListener::class)
    ->needs(ConnectionInterface::class)
    ->give('db.1');

    View full-size slide

  57. Inflection
    REGISTRATION - CONTAINER EVENTS

    View full-size slide

  58. REGISTRATION - CONTAINER EVENTS
    Inflection (aka “container events”)

    View full-size slide

  59. REGISTRATION - CONTAINER EVENTS
    $this->app->resolving(
    function (Foo $foo) {…}
    );

    View full-size slide

  60. use App\Contracts\EnvironmentAware;
    $this->app->resolving(
    function (EnvironmentAware $object) {
    $object->setEnv(
    $this->app->environment()
    );
    }
    );

    View full-size slide

  61. interface EnvironmentAware
    {
    public function setEnv($env);
    }

    View full-size slide

  62. trait EnvironmentAware
    {
    public function setEnv($env)
    {
    $this->env = $env;
    }
    private function isEnv($envs): bool
    {
    return in_array($this->env, (array) $envs);
    }
    }

    View full-size slide

  63. REGISTRATION - CONTAINER EVENTS
    Method injection

    View full-size slide

  64. REGISTRATION - CONTAINER EVENTS
    Also useful for:
    - Quickly injecting stuff

    View full-size slide

  65. REGISTRATION - CONTAINER EVENTS
    interface CommandBusAware
    trait CommandBusAware
    interface EventDispatcherAware
    trait EventDispatcherAware

    View full-size slide

  66. REGISTRATION - CONTAINER EVENTS
    Also useful for:
    - Quickly injecting stuff

    View full-size slide

  67. REGISTRATION - CONTAINER EVENTS
    Also useful for:
    - Quickly injecting stuff
    - Injecting soft dependencies

    View full-size slide

  68. $this->app->resolving(function (Mailer $mailer)
    {
    $mailer->setQueueResolver(function () {
    return $this->app->make(Queue::class);
    });
    );

    View full-size slide

  69. REGISTRATION - CONTAINER EVENTS
    Also useful for:
    - Quickly injecting stuff
    - Injecting soft dependencies

    View full-size slide

  70. REGISTRATION - CONTAINER EVENTS
    Also useful for:
    - Quickly injecting stuff
    - Injecting soft dependencies
    - Setting configurable values

    View full-size slide

  71. $this->app->resolving(function (Validator)
    {
    $fileTypes = $app['config']->get(…)
    $validator->setAllowedFileTypes($fileTypes);
    );

    View full-size slide

  72. Using the container

    View full-size slide

  73. CALLING THE CONTAINER
    Preferably only for
    - Factories
    - Event Dispatchers
    - Router
    - Command Bus

    View full-size slide

  74. $this->app->make('fs');
    $this->app->make('fs', […]);
    $this->app->bind(
    'fs',
    function ($app, $args) {…}
    );

    View full-size slide

  75. $this->app->call(function (Mailer $mailer) {});
    $this->app->call([$listener, 'handle']);
    $this->app->call('Listener::handle');
    $this->app->call('Listener@handle');

    View full-size slide

  76. $func = $this->app->wrap('Listener@handle');
    $func();

    View full-size slide

  77. public function wrap($callback) {
    return function () {
    $this->call($callback);
    };
    }

    View full-size slide

  78. BONUS - SOFT DEPENDENCIES
    Solving circular dependencies

    View full-size slide

  79. BONUS - CIRCULAR DEPENDENCIES
    Solving circular dependencies

    View full-size slide

  80. BONUS - CIRCULAR DEPENDENCIES
    $mailer = new Mailer(QueueInterface $queue);
    $queue = new Queue(MailerInterface $mailer);

    View full-size slide

  81. BONUS - CIRCULAR DEPENDENCIES
    $mailer = new Mailer();
    $queue = new Queue(MailerInterface $mailer);
    $mailer->setQueueResolver(function () {
    return $this->app->make('queue');
    });

    View full-size slide

  82. BONUS - NOT SUPPORTED
    Contextual binding for methods
    (unsupported)

    View full-size slide

  83. BONUS - METHOD LEVEL CONTEXTUAL BINDING
    $this->app—>when('FooController@store')
    ->needs(QueueContract::class)
    ->give('queue.redis');
    https:/
    /github.com/laravel/framework/pull/12183

    View full-size slide

  84. recap:
    Reflection
    Binding
    Calling code
    Bonus

    View full-size slide

  85. Thank you!
    https://joind.in/talk/xxxxx
    @hannesvdvreken
    Laracon EU 2016

    View full-size slide

  86. Time for questions.
    @hannesvdvreken
    Laracon EU 2016

    View full-size slide

  87. • http:/
    /mwl.be
    REFERENCES

    View full-size slide