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?

Dependency Injection in Drupal 8

  • 1.
  • 2.
    Intro ● Rudimentary understandingof 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 asa design pattern ● DI from a framework perspective ● Symfony-style DI ● DI in Drupal 8
  • 4.
    Agenda ● DI asa design pattern ● DI from a framework perspective ● Symfony-style DI ● DI in Drupal 8 Why? How?
  • 5.
  • 6.
    Goal: we wantto 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 wantto write code that is... ✔Clutter-free ✔Reusable ✔Testable
  • 16.
    Goal: we wantto write code that is... ✔Clutter-free ✔Reusable ✔Testable Ignorant
  • 17.
    The less yourcode 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 expressdependencies in the class definition rather than instantiating them in the class itself.
  • 22.
    Constructor Injection isnot the only form of DI
  • 23.
  • 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 setterinjection, except there is an interface for each dependency's setter method. Very verbose Not very common
  • 27.
  • 30.
    “Don't call us, we'llcall 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);?
  • 33.
  • 34.
    Where does injectionhappen? ➔ Manual injection ➔ Use a factory ➔ Use a container / injector
  • 35.
    Using DI ina 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 Aservice 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 Injectionby Dhanji R. Prasanna, published by Manning
  • 41.
    Source: Dependency Injectionby 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 itwork? ➔ 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 keycorresponds 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 ➔ Viaan API ➔ Using XML ➔ Using YAML
  • 46.
    Scope The scope ofa 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 Injectionby Dhanji R. Prasanna, published by Manning
  • 50.
    Source: Dependency Injectionby Dhanji R. Prasanna, published by Manning Service Locator Service Locator
  • 51.
    Getting wired inis not just about configuration, it's about how the application works with the container.
  • 52.
  • 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
  • 55.
  • 56.
    “Compiling” the container It'stoo 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 Classservice_container extends Container { /** * Gets the 'example' service. */ protected function getExampleService() { return $this->services['example'] = new SomeNamespaceSomeClass(); } }
  • 58.
    “Synthetic” Services A syntheticservice 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'sanswer 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 passesgive 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 canadd 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 playsan 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 extendsBundle { public function build(ContainerBuilder $container) { ... // Compiler pass for event subscribers. $container->addCompilerPass(new RegisterKernelListenersPass()); ... } } Register the compiler pass
  • 67.
  • 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 implementsAliasManagerInterface { ... 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 youcan 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
  • 72.
  • 73.
    Get wired inas an event subscriber
  • 74.
  • 75.
    How to getyour 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 thecontainer! Ever. (Unless you absolutely must)
  • 78.
    Where does itall 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 withDI and PHPUnit
  • 81.
    PHPUnit Awesomeness // Createa 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 // Createan 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 anda list of resources on DI and its use in Symfony and Drupal are available at http://katbailey.github.io/
  • 84.