2. Why talk about DI?
* Drupal 8 is coming…
* DI is a commonly used pattern in software
design
3. The problems in D7
1. Strong dependencies
* Globals:
- users
- database
- language
- paths
* EntityFieldQuery still assumes SQL in
property queries
* Views assumes SQL database.
4. The problems in D7
2. No way to reuse.
* Want to change a small part of contrib code?
Copy the callback entirely and replace it with your code.
5. The problems in D7
3. A lot of clutter happening.
* Use of preprocess to do calculations.
* Excessive use of alter hooks for some major
replacements.
* PHP in template files? Huh =)
6. The problems in D7
4. How can we test something?
* Pass dummy DB connection?
NO.
* Create mock objects?
NO.
* Want a simple test class?
Bootstrap Drupal first.
14. Some procedural code
function foo_bar($foo) {
global $user;
if ($user->uid != 0) {
$nodes = db_query("SELECT * FROM {node}")->fetchAll();
}
// Load module file that has the function.
module_load_include('inc', cool_module, 'cool.admin');
node_make_me_look_cool(NOW());
}
15. Some procedural code
function foo_bar($foo) {
global $user;
if ($user->uid != 0) {
$nodes = db_query("SELECT * FROM {node}")->fetchAll();
}
// Load module file that has the function.
module_load_include('inc', cool_module, 'cool.admin');
node_make_me_look_cool(NOW());
}
* foo_bar knows about the user object.
* node_make_me_look_cool needs a function from another module.
* You need bootstrapped Drupal to use module_load_include, or at least include the file that declares it.
* foo_bar assumes some default database connection.
17. User class uses sessions to store language
class SessionStorage
{
function __construct($cookieName = 'PHP_SESS_ID')
{
session_name($cookieName);
session_start();
}
function set($key, $value)
{
$_SESSION[$key] = $value;
}
function get($key)
{
return $_SESSION[$key];
}
// ...
}
class User
{
protected $storage;
function __construct()
{
$this->storage = new SessionStorage();
}
function setLanguage($language)
{
$this->storage->set('language', $language);
}
function getLanguage()
{
return $this->storage->get('language');
}
// ...
}
18. Working with User
$user = new User();
$user->setLanguage('fr');
$user_language = $user->getLanguage();
Working with such code is damn easy.
What could possibly go wrong here?
20. What if?
What if we want to change a session cookie name?
class User
{
function __construct()
{
$this->storage = new SessionStorage('SESSION_ID');
}
// ...
}
Hardcode the name in User’s constructor
21. What if?
class User
{
function __construct()
{
$this->storage = new SessionStorage
(STORAGE_SESSION_NAME);
}
// ...
}
define('STORAGE_SESSION_NAME', 'SESSION_ID');
Define a constant
What if we want to change a session cookie name?
22. What if?
class User
{
function __construct($sessionName)
{
$this->storage = new SessionStorage($sessionName);
}
// ...
}
$user = new User('SESSION_ID');
Send cookie name as User argument
What if we want to change a session cookie name?
23. What if?
class User
{
function __construct($storageOptions)
{
$this->storage = new SessionStorage($storageOptions
['session_name']);
}
// ...
}
$user = new User(array('session_name' => 'SESSION_ID'));
Send an array of options as User argument
What if we want to change a session cookie name?
24. What if?
What if we want to change a session storage?
For instance to store sessions in DB or files
25. What if?
What if we want to change a session storage?
For instance to store sessions in DB or files
You need to change User class
28. Here it is:
class User
{
function __construct($storage)
{
$this->storage = $storage;
}
// ...
}
Instead of instantiating the storage in User, let’s just
pass it from outside.
29. Here it is:
class User
{
function __construct($storage)
{
$this->storage = $storage;
}
// ...
}
Instead of instantiating the storage in User, let’s just
pass it from outside.
$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
30. Types of injections:
class User
{
function __construct($storage)
{
$this->storage = $storage;
}
}
class User
{
function setSessionStorage($storage)
{
$this->storage = $storage;
}
}
class User
{
public $sessionStorage;
}
$user->sessionStorage = $storage;
Constructor injection
Setter injection
Property injection
31. Ok, where does injection happen?
$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
Where should this code be?
32. Ok, where does injection happen?
$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
Where should this code be?
* Manual injection
* Injection in a factory class
* Using a container/injector
33. How frameworks do that?
Dependency Injection Container (DIC)
or
Service container
44. Compile the container
There is no reason to pull configurations on every
request. We just compile the container in a PHP class
with hardcoded methods for each service.
container->compile();
45. Compiler passes
Compiler passes are classes that process the container,
giving you an opportunity to manipulate existing service
definitions.
Use them to:
● Specify a different class for a given serviceid
● Process“tagged”services
47. Tagged services
There is a way to tag services for further processing in
compiler passes.
This technique is used to register event subscribers to
Symfony’s event dispatcher.
52. 1. Write OO code and get wired into the container.
2. In case of legacy procedural code you can use:
Drupal::service(‘some_service’);
Example:
Drupal 7:
$path = $_GET['q']
Drupal 8:
$request = Drupal::service('request');
$path = $request->attributes->get('_system_path');
Ways to use core’s services
53. ● 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/CoreServiceProvider.
php
● Compiler pass classes are in:
core/lib/Drupal/Core/DependencyInjection/
Compiler/
Where the magic happens?
54. ● Define module services in :
mymodule/mymodule.services.yml
● Compiler passes get added in:
mymodule/lib/Drupal/mymodule/Mymodule
ServiceProvider.php
● All classes including Compiler class
classes live in:
mymodule/lib/Drupal/mymodule
What about modules?
55. Resources:
● Fabien Potencier’s “What is Dependency
Injection” series
● Symfony Service Container
● Symfony Dependency Injection Component
● WSCCI Conversion Guide
● Change notice: Use Dependency Injection to
handle global PHP objects
56. $slide = new Questions();
$current_slide = new Slide($slide);