SlideShare a Scribd company logo
1 of 84
Download to read offline
Dependency Injection in Drupal 8
Intro
● Rudimentary understanding of OOP assumed
● Big changes in D8
● Not enough time to explain ALL THE THINGS
but I'll provide a list of resources for a deeper
dive into DI
Agenda
● DI as a design pattern
● DI from a framework perspective
● Symfony-style DI
● DI in Drupal 8
Agenda
● DI as a design pattern
● DI from a framework perspective
● Symfony-style DI
● DI in Drupal 8
Why?
How?
Why Dependency Injection?
Goal: we want to write code
that is...
✔Clutter-free
✔Reusable
✔Testable
Doing It Wrong
1. An example in procedural code
function my_module_func($val1, $val2) {
module_load_include('module_x', 'inc');
$val1 = module_x_process_val($val1);
return $val1 + $val2;
}
function my_module_func($val1, $val2) {
module_load_include('module_x', 'inc');
$val1 = module_x_process_val($val1);
return $val1 + $val2;
}
✗ Clutter-free
✗ Reusable
✗ Testable
Doing It Wrong
1. An example in procedural code
2. An example in Object Oriented code
class Notifier {
private $mailer;
public function __construct() {
$this->mailer = new Mailer();
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
class Notifier {
private $mailer;
public function __construct() {
$this->mailer = new Mailer('sendmail');
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
class Notifier {
private $mailer;
public function __construct(Mailer $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
class Notifier {
private $mailer;
public function __construct(Mailer $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
$mailer = new Mailer();
$notifier = new Notifier($mailer);
Goal: we want to write code
that is...
✔Clutter-free
✔Reusable
✔Testable
Goal: we want to write code
that is...
✔Clutter-free
✔Reusable
✔Testable
Ignorant
The less your code knows, the more
reusable it is.
class Notifier {
private $mailer;
public function __construct(Mailer $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
$mailer = new Mailer();
$notifier = new Notifier($mailer);
class Notifier {
private $mailer;
public function
__construct(MailerInterface $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
$mailer = new SpecialMailer();
$notifier = new Notifier($mailer);
class Notifier {
private $mailer;
public function
__construct(MailerInterface $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
$mailer = new SpecialMailer();
$notifier = new Notifier($mailer);
Constructor Injection
Dependency Injection
Declaratively express dependencies in the
class definition rather than instantiating them
in the class itself.
Constructor Injection is not
the only form of DI
Setter Injection
class Notifier {
private $mailer;
public function
setMailer(MailerInterface $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($msg);
}
}
$mailer = new Mailer();
$notifier = new Notifier();
$notifier->setMailer($mailer);
class Notifier {
private $mailer;
public function
setMailer(MailerInterface $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($msg);
}
}
$mailer = new Mailer();
$notifier = new Notifier();
$notifier->setMailer($mailer);
Setter Injection
Interface Injection
Like setter injection, except there is an
interface for each dependency's setter
method.
Very verbose
Not very common
Dependency Injection
==
Inversion of Control
“Don't call us,
we'll call you!”
(The Hollywood Principle)
class Notifier {
private $mailer;
public function
__construct(MailerInterface $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
$mailer = new Mailer();
$notifier = new Notifier($mailer);
class Notifier {
private $mailer;
public function
__construct(MailerInterface $m) {
$this->mailer = $m;
}
public function notify() {
...
$this->mailer->send($from, $to, $msg);
...
}
}
$mailer = new Mailer();
$notifier = new Notifier($mailer);?
Where does injection happen?
Where does injection happen?
➔ Manual injection
➔ Use a factory
➔ Use a container / injector
Using DI in a Framework
Dependency Injector
==
Dependency Injection Container (DIC)
==
IoC Container
==
Service Container
The Service Container
➔ Assumes responsibility for constructing
object graphs (i.e. instantiating your
classes with their dependencies)
➔ Uses configuration data to know how to
do this
➔ Allows infrastructure logic to be kept
separate from application logic
Objects as Services
A service is an object that provides some kind of
globally useful functionality
Examples of Services
➔ Cache Backend
➔ Logger
➔ Mailer
➔ URL Generator
Examples of Non-Services
➔ Product
➔ Blog post
➔ Email message
Source: Dependency Injection by Dhanji R. Prasanna, published by Manning
Source: Dependency Injection by Dhanji R. Prasanna, published by Manning
Sample configuration
<services...>
<service id=”notifier” class=”Notifier”>
<constructor-arg ref=”emailer” />
</service>
<service id=”emailer” class=”Mailer”>
<constructor-arg ref=”spell_checker” />
</service>
<service id=”spell_checker”
class=”SpellChecker” />
</services>
How does it work?
➔ Service keys map to service definitions
➔ Definitions specify which class to
instantiate and what its dependencies
are
➔ Dependencies are specified as
references to other services (using
service keys)
➔ Keys are unique per object graph
➔ $container->getService('some_service')
Service Keys
Each key corresponds to a unique object graph, e.g.
'english_mailer' corresponds to
new Mailer(new EnglishSpellchecker())
'french_mailer' corresponds to
new Mailer(new FrenchSpellchecker())
Container Configuration
➔ Via an API
➔ Using XML
➔ Using YAML
Scope
The scope of a service is the context under which
the service key refers to the same instance.
Common Scopes
Container scope (aka Singleton scope)
The container returns the same instance every time
a client requests that service
Prototype scope (aka no scope)
The container returns a new instance every time a
client requests that service
Request scope
Every request for the service within the context of
an HTTP request will get the same instance.
Service Locator vs. DI
class SomeClass {
public function __construct(Locator $sl) {
$this->myDep = $sl->get('some_service');
}
}
In DI, calls like
$container->get('some_service')
should only happen at the entry point to the
application
Source: Dependency Injection by Dhanji R. Prasanna, published by Manning
Source: Dependency Injection by Dhanji R. Prasanna, published by Manning
Service Locator
Service Locator
Getting wired in is not just about
configuration, it's about how the
application works with the
container.
Symfony's Dependency Injection
Component
Symfony's DI Component
➔ Service keys are strings, e.g. 'some_service'
➔ Service definitions, in addition to specifying
which class to instantiate and what
constructor arguments to pass in, allow you to
specify additional methods to call on the
object after instantiation
Symfony's DI Component
➔ Default scope: container
➔ Can be configured in PHP, XML or YAML
➔ Can be “compiled” down to plain PHP
Some Symfony Terminology
“Compiling” the container
It's too expensive to parse configuration on
every request.
Parse once and put the result into a PHP class
that hardcodes a method for each service.
“Compiling” the container
Class service_container extends Container {
/**
* Gets the 'example' service.
*/
protected function getExampleService()
{
return $this->services['example'] = new
SomeNamespaceSomeClass();
}
}
“Synthetic” Services
A synthetic service is one that is not instantiated
by the container – the container just gets told
about it so it can then treat it as a service when
anything has a dependency on it.
Bundles
Bundles are Symfony's answer to plugins or
modules, i.e. prepackaged sets of functionality
implementing a particular feature, e.g. a blog.
Bundles need to be able to interact with the
container so they provide a class implementing
the BundleInterace which allows them to do so.
Compiler passes
Compiler passes give you an opportunity to
manipulate other service definitions that have
been registered with the service container.
Use them to:
● Specify a different class for a given service id
● Process “tagged” services
Tagged Services
You can add tags to your services when you
define them. This flags them for some kind of
special processing (in a compiler pass).
For example, this mechanism is used to register
event subscribers (services tagged with
'event_subscriber') to Symfony's event
dispatcher
Symfony's Event Dispatcher
plays an important role in
container wiring
Symfony's Event Dispatcher
➔ Dispatcher dispatches events such as
Kernel::Request
➔ Can be used to dispatch any kind of custom
event
➔ Event listeners are registered to the
dispatcher and notified when an event fires
➔ Event subscribers are classes that provide
multiple event listeners for different events
Symfony's Event Dispatcher
➔ A compiler pass registers all subscribers to
the dispatcher, using their service IDs
➔ The dispatcher holds a reference to the
service container
➔ Can therefore instantiate “subscriber
services” with their dependencies
class RegisterKernelListenersPass implements
CompilerPassInterface {
public function process(ContainerBuilder $container) {
$definition = $container
->getDefinition('event_dispatcher');
$services = $container
->findTaggedServiceIds('event_subscriber');
foreach ($services as $id => $attributes) {
$definition->addMethodCall('addSubscriberService',
array($id, $class));
}
}
}
A compiler pass iterates over the
tagged services
class CoreBundle extends Bundle {
public function build(ContainerBuilder $container)
{
...
// Compiler pass for event subscribers.
$container->addCompilerPass(new
RegisterKernelListenersPass());
...
}
}
Register the compiler pass
Dependency Injection in Drupal 8
Some D8 Services
➔ The default DB connection ('database')
➔ The module handler ('module_handler')
➔ The HTTP request object ('request')
Services:
database:
class: DrupalCoreDatabaseConnection
factory_class: DrupalCoreDatabaseDatabase
factory_method: getConnection
arguments: [default]
path.alias_whitelist:
class: DrupalCorePathAliasWhitelist
tags:
- { name: needs_destruction }
language_manager:
class: DrupalCoreLanguageLanguageManager
path.alias_manager:
class: DrupalCorePathAliasManager
arguments: ['@database',
'@path.alias_whitelist', '@language_manager']
class AliasManager implements AliasManagerInterface {
...
public function __construct(Connection $connection,
AliasWhitelist $whitelist, LanguageManager
$language_manager) {
$this->connection = $connection;
$this->languageManager = $language_manager;
$this->whitelist = $whitelist;
}
...
}
AliasManager's Constructor
2 ways you can use core's
services
1. From procedural code, using a helper
(essentially a service locator)
2. Write OO code and get wired into the
container
Drupal's Application Flow
Get wired in as an event
subscriber
Services:
...
path_subscriber:
class: DrupalCoreEventSubscriberPathSubscriber
tags:
- { name: event_subscriber }
...
Tagged Services
How to get your controller
wired in?
Controllers as Services?
➔ Controllers have dependencies on services
➔ Whether they should be directly wired into
the container is a hotly debated topic in the
Symfony community
➔ Recommended way in D8 is not to make
controllers themselves services but to
implement a special interface that has a
factory method which accepts the container
➔ See book module for an example!
Don't inject the container!
Ever.
(Unless you absolutely must)
Where does it all happen?
➔ The Drupal Kernel:
core/lib/Drupal/Core/DrupalKernel.php
➔ Services are defined in:
core/core.services.yml
➔ Compiler passes get added in:
core/lib/Drupal/Core/CoreBundle.php
➔ Compiler pass classes live here:
core/lib/Drupal/Core/DependencyInjection/
Compiler/...
What about modules?
➔ Services are defined in:
mymodule/mymodule.services.yml
➔ Compiler passes get added in:
mymodule/lib/Drupal/mymodule/MymoduleB
undle.php
➔ All classes, including compiler pass classes,
must live in
mymodule/lib/Drupal/mymodule/
Easy testability with DI and
PHPUnit
PHPUnit Awesomeness
// Create a language manager stub.
$language_manager = $this
->getMock('DrupalCoreLanguageLanguageManager');
$language_manager->expects($this->any())
->method('getLanguage')
->will($this->returnValue((object) array(
'langcode' => 'en',
'name' => 'English')));
PHPUnit Awesomeness
// Create an alias manager stub.
$alias_manager = $this
->getMockBuilder('DrupalCorePathAliasManager')
->disableOriginalConstructor()
->getMock();
$alias_manager->expects($this->any())
->method('getSystemPath')
->will($this->returnValueMap(array(
'foo' => 'user/1',
'bar' => 'node/1',
)));
Resources
These slides and a list of resources on DI and
its use in Symfony and Drupal are available at
http://katbailey.github.io/
Questions?

More Related Content

What's hot

บทที่ 1 ความหมายของการบริหารราชการ
บทที่ 1 ความหมายของการบริหารราชการบทที่ 1 ความหมายของการบริหารราชการ
บทที่ 1 ความหมายของการบริหารราชการ
Saiiew
 
แผนการสอน Reading
แผนการสอน Readingแผนการสอน Reading
แผนการสอน Reading
Success SC Slac
 
Pla d'acció tutorial (pat) - curs acadèmic 2014-2015 - versió de setembre de...
 Pla d'acció tutorial (pat) - curs acadèmic 2014-2015 - versió de setembre de... Pla d'acció tutorial (pat) - curs acadèmic 2014-2015 - versió de setembre de...
Pla d'acció tutorial (pat) - curs acadèmic 2014-2015 - versió de setembre de...
rpujol1
 

What's hot (11)

ระบบปฏิบัติการ Linux
ระบบปฏิบัติการ Linuxระบบปฏิบัติการ Linux
ระบบปฏิบัติการ Linux
 
บทที่ 1 ความหมายของการบริหารราชการ
บทที่ 1 ความหมายของการบริหารราชการบทที่ 1 ความหมายของการบริหารราชการ
บทที่ 1 ความหมายของการบริหารราชการ
 
อาญาภาค 1 บทที่ 8 ผู้กระทำความผิดหลายคน
อาญาภาค 1 บทที่ 8 ผู้กระทำความผิดหลายคนอาญาภาค 1 บทที่ 8 ผู้กระทำความผิดหลายคน
อาญาภาค 1 บทที่ 8 ผู้กระทำความผิดหลายคน
 
รายงานเรื่อง สมุนไพรไทย
รายงานเรื่อง สมุนไพรไทยรายงานเรื่อง สมุนไพรไทย
รายงานเรื่อง สมุนไพรไทย
 
PGDCA fundamentals of Computer os hindi
PGDCA fundamentals of Computer os hindiPGDCA fundamentals of Computer os hindi
PGDCA fundamentals of Computer os hindi
 
แผนการสอน Reading
แผนการสอน Readingแผนการสอน Reading
แผนการสอน Reading
 
ภาษาจีน สถานที่
ภาษาจีน สถานที่ภาษาจีน สถานที่
ภาษาจีน สถานที่
 
ไวรัสคอมพิวเตอร์ เวิร์ม ม้าโทรจัน สแปม
ไวรัสคอมพิวเตอร์ เวิร์ม ม้าโทรจัน สแปมไวรัสคอมพิวเตอร์ เวิร์ม ม้าโทรจัน สแปม
ไวรัสคอมพิวเตอร์ เวิร์ม ม้าโทรจัน สแปม
 
แบบรายงานความคืบหน้าโครงการโรงเรียนสีขาว
แบบรายงานความคืบหน้าโครงการโรงเรียนสีขาวแบบรายงานความคืบหน้าโครงการโรงเรียนสีขาว
แบบรายงานความคืบหน้าโครงการโรงเรียนสีขาว
 
แผนผังความคิดเรื่องอินเตอร์เน็ต
แผนผังความคิดเรื่องอินเตอร์เน็ตแผนผังความคิดเรื่องอินเตอร์เน็ต
แผนผังความคิดเรื่องอินเตอร์เน็ต
 
Pla d'acció tutorial (pat) - curs acadèmic 2014-2015 - versió de setembre de...
 Pla d'acció tutorial (pat) - curs acadèmic 2014-2015 - versió de setembre de... Pla d'acció tutorial (pat) - curs acadèmic 2014-2015 - versió de setembre de...
Pla d'acció tutorial (pat) - curs acadèmic 2014-2015 - versió de setembre de...
 

Similar to Dependency Injection in Drupal 8

Dependency injection in Drupal 8
Dependency injection in Drupal 8Dependency injection in Drupal 8
Dependency injection in Drupal 8
Alexei Gorobets
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf Conference
 

Similar to Dependency Injection in Drupal 8 (20)

Dependency injection in CakePHP
Dependency injection in CakePHPDependency injection in CakePHP
Dependency injection in CakePHP
 
Dependency injection in Drupal 8
Dependency injection in Drupal 8Dependency injection in Drupal 8
Dependency injection in Drupal 8
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
Dependency injection in Drupal 8 : DrupalCon NOLA
Dependency injection in Drupal 8 : DrupalCon NOLADependency injection in Drupal 8 : DrupalCon NOLA
Dependency injection in Drupal 8 : DrupalCon NOLA
 
Dependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony ContainerDependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony Container
 
PHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodePHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better Code
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
 
The IoC Hydra
The IoC HydraThe IoC Hydra
The IoC Hydra
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
 
Dependency Injection and Pimple
Dependency Injection and PimpleDependency Injection and Pimple
Dependency Injection and Pimple
 
A Series of Fortunate Events - Symfony Camp Sweden 2014
A Series of Fortunate Events - Symfony Camp Sweden 2014A Series of Fortunate Events - Symfony Camp Sweden 2014
A Series of Fortunate Events - Symfony Camp Sweden 2014
 
Into the ZF2 Service Manager
Into the ZF2 Service ManagerInto the ZF2 Service Manager
Into the ZF2 Service Manager
 
AngularJS
AngularJSAngularJS
AngularJS
 
A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015
 
Angular presentation
Angular presentationAngular presentation
Angular presentation
 
Introduction to Domain driven design (LaravelBA #5)
Introduction to Domain driven design (LaravelBA #5)Introduction to Domain driven design (LaravelBA #5)
Introduction to Domain driven design (LaravelBA #5)
 
Design patterns in PHP
Design patterns in PHPDesign patterns in PHP
Design patterns in PHP
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...
 

Recently uploaded

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Recently uploaded (20)

Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 

Dependency Injection in Drupal 8

  • 2. Intro ● Rudimentary understanding of OOP assumed ● Big changes in D8 ● Not enough time to explain ALL THE THINGS but I'll provide a list of resources for a deeper dive into DI
  • 3. Agenda ● DI as a design pattern ● DI from a framework perspective ● Symfony-style DI ● DI in Drupal 8
  • 4. Agenda ● DI as a design pattern ● DI from a framework perspective ● Symfony-style DI ● DI in Drupal 8 Why? How?
  • 6. Goal: we want to write code that is... ✔Clutter-free ✔Reusable ✔Testable
  • 7. Doing It Wrong 1. An example in procedural code
  • 8. function my_module_func($val1, $val2) { module_load_include('module_x', 'inc'); $val1 = module_x_process_val($val1); return $val1 + $val2; }
  • 9. function my_module_func($val1, $val2) { module_load_include('module_x', 'inc'); $val1 = module_x_process_val($val1); return $val1 + $val2; } ✗ Clutter-free ✗ Reusable ✗ Testable
  • 10. Doing It Wrong 1. An example in procedural code 2. An example in Object Oriented code
  • 11. class Notifier { private $mailer; public function __construct() { $this->mailer = new Mailer(); } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } }
  • 12. class Notifier { private $mailer; public function __construct() { $this->mailer = new Mailer('sendmail'); } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } }
  • 13. class Notifier { private $mailer; public function __construct(Mailer $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } }
  • 14. class Notifier { private $mailer; public function __construct(Mailer $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } } $mailer = new Mailer(); $notifier = new Notifier($mailer);
  • 15. Goal: we want to write code that is... ✔Clutter-free ✔Reusable ✔Testable
  • 16. Goal: we want to write code that is... ✔Clutter-free ✔Reusable ✔Testable Ignorant
  • 17. The less your code knows, the more reusable it is.
  • 18. class Notifier { private $mailer; public function __construct(Mailer $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } } $mailer = new Mailer(); $notifier = new Notifier($mailer);
  • 19. class Notifier { private $mailer; public function __construct(MailerInterface $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } } $mailer = new SpecialMailer(); $notifier = new Notifier($mailer);
  • 20. class Notifier { private $mailer; public function __construct(MailerInterface $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } } $mailer = new SpecialMailer(); $notifier = new Notifier($mailer); Constructor Injection
  • 21. Dependency Injection Declaratively express dependencies in the class definition rather than instantiating them in the class itself.
  • 22. Constructor Injection is not the only form of DI
  • 24. class Notifier { private $mailer; public function setMailer(MailerInterface $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($msg); } } $mailer = new Mailer(); $notifier = new Notifier(); $notifier->setMailer($mailer);
  • 25. class Notifier { private $mailer; public function setMailer(MailerInterface $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($msg); } } $mailer = new Mailer(); $notifier = new Notifier(); $notifier->setMailer($mailer); Setter Injection
  • 26. Interface Injection Like setter injection, except there is an interface for each dependency's setter method. Very verbose Not very common
  • 28.
  • 29.
  • 30. “Don't call us, we'll call you!” (The Hollywood Principle)
  • 31. class Notifier { private $mailer; public function __construct(MailerInterface $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } } $mailer = new Mailer(); $notifier = new Notifier($mailer);
  • 32. class Notifier { private $mailer; public function __construct(MailerInterface $m) { $this->mailer = $m; } public function notify() { ... $this->mailer->send($from, $to, $msg); ... } } $mailer = new Mailer(); $notifier = new Notifier($mailer);?
  • 34. Where does injection happen? ➔ Manual injection ➔ Use a factory ➔ Use a container / injector
  • 35. Using DI in a Framework Dependency Injector == Dependency Injection Container (DIC) == IoC Container == Service Container
  • 36. The Service Container ➔ Assumes responsibility for constructing object graphs (i.e. instantiating your classes with their dependencies) ➔ Uses configuration data to know how to do this ➔ Allows infrastructure logic to be kept separate from application logic
  • 37. Objects as Services A service is an object that provides some kind of globally useful functionality
  • 38. Examples of Services ➔ Cache Backend ➔ Logger ➔ Mailer ➔ URL Generator
  • 39. Examples of Non-Services ➔ Product ➔ Blog post ➔ Email message
  • 40. Source: Dependency Injection by Dhanji R. Prasanna, published by Manning
  • 41. Source: Dependency Injection by Dhanji R. Prasanna, published by Manning
  • 42. Sample configuration <services...> <service id=”notifier” class=”Notifier”> <constructor-arg ref=”emailer” /> </service> <service id=”emailer” class=”Mailer”> <constructor-arg ref=”spell_checker” /> </service> <service id=”spell_checker” class=”SpellChecker” /> </services>
  • 43. How does it work? ➔ Service keys map to service definitions ➔ Definitions specify which class to instantiate and what its dependencies are ➔ Dependencies are specified as references to other services (using service keys) ➔ Keys are unique per object graph ➔ $container->getService('some_service')
  • 44. Service Keys Each key corresponds to a unique object graph, e.g. 'english_mailer' corresponds to new Mailer(new EnglishSpellchecker()) 'french_mailer' corresponds to new Mailer(new FrenchSpellchecker())
  • 45. Container Configuration ➔ Via an API ➔ Using XML ➔ Using YAML
  • 46. Scope The scope of a service is the context under which the service key refers to the same instance.
  • 47. Common Scopes Container scope (aka Singleton scope) The container returns the same instance every time a client requests that service Prototype scope (aka no scope) The container returns a new instance every time a client requests that service Request scope Every request for the service within the context of an HTTP request will get the same instance.
  • 48. Service Locator vs. DI class SomeClass { public function __construct(Locator $sl) { $this->myDep = $sl->get('some_service'); } } In DI, calls like $container->get('some_service') should only happen at the entry point to the application
  • 49. Source: Dependency Injection by Dhanji R. Prasanna, published by Manning
  • 50. Source: Dependency Injection by Dhanji R. Prasanna, published by Manning Service Locator Service Locator
  • 51. Getting wired in is not just about configuration, it's about how the application works with the container.
  • 53. Symfony's DI Component ➔ Service keys are strings, e.g. 'some_service' ➔ Service definitions, in addition to specifying which class to instantiate and what constructor arguments to pass in, allow you to specify additional methods to call on the object after instantiation
  • 54. Symfony's DI Component ➔ Default scope: container ➔ Can be configured in PHP, XML or YAML ➔ Can be “compiled” down to plain PHP
  • 56. “Compiling” the container It's too expensive to parse configuration on every request. Parse once and put the result into a PHP class that hardcodes a method for each service.
  • 57. “Compiling” the container Class service_container extends Container { /** * Gets the 'example' service. */ protected function getExampleService() { return $this->services['example'] = new SomeNamespaceSomeClass(); } }
  • 58. “Synthetic” Services A synthetic service is one that is not instantiated by the container – the container just gets told about it so it can then treat it as a service when anything has a dependency on it.
  • 59. Bundles Bundles are Symfony's answer to plugins or modules, i.e. prepackaged sets of functionality implementing a particular feature, e.g. a blog. Bundles need to be able to interact with the container so they provide a class implementing the BundleInterace which allows them to do so.
  • 60. Compiler passes Compiler passes give you an opportunity to manipulate other service definitions that have been registered with the service container. Use them to: ● Specify a different class for a given service id ● Process “tagged” services
  • 61. Tagged Services You can add tags to your services when you define them. This flags them for some kind of special processing (in a compiler pass). For example, this mechanism is used to register event subscribers (services tagged with 'event_subscriber') to Symfony's event dispatcher
  • 62. Symfony's Event Dispatcher plays an important role in container wiring
  • 63. Symfony's Event Dispatcher ➔ Dispatcher dispatches events such as Kernel::Request ➔ Can be used to dispatch any kind of custom event ➔ Event listeners are registered to the dispatcher and notified when an event fires ➔ Event subscribers are classes that provide multiple event listeners for different events
  • 64. Symfony's Event Dispatcher ➔ A compiler pass registers all subscribers to the dispatcher, using their service IDs ➔ The dispatcher holds a reference to the service container ➔ Can therefore instantiate “subscriber services” with their dependencies
  • 65. class RegisterKernelListenersPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { $definition = $container ->getDefinition('event_dispatcher'); $services = $container ->findTaggedServiceIds('event_subscriber'); foreach ($services as $id => $attributes) { $definition->addMethodCall('addSubscriberService', array($id, $class)); } } } A compiler pass iterates over the tagged services
  • 66. class CoreBundle extends Bundle { public function build(ContainerBuilder $container) { ... // Compiler pass for event subscribers. $container->addCompilerPass(new RegisterKernelListenersPass()); ... } } Register the compiler pass
  • 68. Some D8 Services ➔ The default DB connection ('database') ➔ The module handler ('module_handler') ➔ The HTTP request object ('request')
  • 69. Services: database: class: DrupalCoreDatabaseConnection factory_class: DrupalCoreDatabaseDatabase factory_method: getConnection arguments: [default] path.alias_whitelist: class: DrupalCorePathAliasWhitelist tags: - { name: needs_destruction } language_manager: class: DrupalCoreLanguageLanguageManager path.alias_manager: class: DrupalCorePathAliasManager arguments: ['@database', '@path.alias_whitelist', '@language_manager']
  • 70. class AliasManager implements AliasManagerInterface { ... public function __construct(Connection $connection, AliasWhitelist $whitelist, LanguageManager $language_manager) { $this->connection = $connection; $this->languageManager = $language_manager; $this->whitelist = $whitelist; } ... } AliasManager's Constructor
  • 71. 2 ways you can use core's services 1. From procedural code, using a helper (essentially a service locator) 2. Write OO code and get wired into the container
  • 73. Get wired in as an event subscriber
  • 75. How to get your controller wired in?
  • 76. Controllers as Services? ➔ Controllers have dependencies on services ➔ Whether they should be directly wired into the container is a hotly debated topic in the Symfony community ➔ Recommended way in D8 is not to make controllers themselves services but to implement a special interface that has a factory method which accepts the container ➔ See book module for an example!
  • 77. Don't inject the container! Ever. (Unless you absolutely must)
  • 78. Where does it all happen? ➔ The Drupal Kernel: core/lib/Drupal/Core/DrupalKernel.php ➔ Services are defined in: core/core.services.yml ➔ Compiler passes get added in: core/lib/Drupal/Core/CoreBundle.php ➔ Compiler pass classes live here: core/lib/Drupal/Core/DependencyInjection/ Compiler/...
  • 79. What about modules? ➔ Services are defined in: mymodule/mymodule.services.yml ➔ Compiler passes get added in: mymodule/lib/Drupal/mymodule/MymoduleB undle.php ➔ All classes, including compiler pass classes, must live in mymodule/lib/Drupal/mymodule/
  • 80. Easy testability with DI and PHPUnit
  • 81. PHPUnit Awesomeness // Create a language manager stub. $language_manager = $this ->getMock('DrupalCoreLanguageLanguageManager'); $language_manager->expects($this->any()) ->method('getLanguage') ->will($this->returnValue((object) array( 'langcode' => 'en', 'name' => 'English')));
  • 82. PHPUnit Awesomeness // Create an alias manager stub. $alias_manager = $this ->getMockBuilder('DrupalCorePathAliasManager') ->disableOriginalConstructor() ->getMock(); $alias_manager->expects($this->any()) ->method('getSystemPath') ->will($this->returnValueMap(array( 'foo' => 'user/1', 'bar' => 'node/1', )));
  • 83. Resources These slides and a list of resources on DI and its use in Symfony and Drupal are available at http://katbailey.github.io/