SlideShare a Scribd company logo
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

Similar to Dependency Injection in Drupal 8

Dependency injection in CakePHP
Dependency injection in CakePHPDependency injection in CakePHP
Dependency injection in CakePHP
markstory
 
Dependency injection in Drupal 8
Dependency injection in Drupal 8Dependency injection in Drupal 8
Dependency injection in Drupal 8Alexei Gorobets
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
Alena Holligan
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
Jonathan Wage
 
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
Ashwini Kumar
 
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
Diego Lewin
 
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
SWIFTotter Solutions
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
Lukas Smith
 
The IoC Hydra
The IoC HydraThe IoC Hydra
The IoC Hydra
Kacper Gunia
 
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
Kacper Gunia
 
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
 
Dependency Injection and Pimple
Dependency Injection and PimpleDependency Injection and Pimple
Dependency Injection and Pimple
DQNEO
 
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
Matthias Noback
 
Into the ZF2 Service Manager
Into the ZF2 Service ManagerInto the ZF2 Service Manager
Into the ZF2 Service Manager
Chris Tankersley
 
AngularJS
AngularJSAngularJS
AngularJS
LearningTech
 
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
Matthias Noback
 
Angular presentation
Angular presentationAngular presentation
Angular presentation
Matus Szabo
 
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)
guiwoda
 
Design patterns in PHP
Design patterns in PHPDesign patterns in PHP
Design patterns in PHP
Jason Straughan
 
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...
go_oh
 

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

DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
Quotidiano Piemontese
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
Microsoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdfMicrosoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdf
Uni Systems S.M.S.A.
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
mikeeftimakis1
 
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
Neo4j
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
Neo4j
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
Neo4j
 
Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1
DianaGray10
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
Neo4j
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
RinaMondal9
 
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdfUni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems S.M.S.A.
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5
DianaGray10
 
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptxSecstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
nkrafacyberclub
 

Recently uploaded (20)

DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
Microsoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdfMicrosoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdf
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
 
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
 
Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
 
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdfUni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdf
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5UiPath Test Automation using UiPath Test Suite series, part 5
UiPath Test Automation using UiPath Test Suite series, part 5
 
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptxSecstrike : Reverse Engineering & Pwnable tools for CTF.pptx
Secstrike : Reverse Engineering & Pwnable tools for CTF.pptx
 

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/