2014-10-22 12:47:56 +02:00
title: Injector
summary: Introduction to using Dependency Injection within SilverStripe.
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
# Injector
2012-05-09 14:26:29 +02:00
2017-07-03 03:22:12 +02:00
The [Injector ](api:SilverStripe\Core\Injector\Injector ) class is the central manager of inter-class dependencies in SilverStripe. It offers developers the
2014-10-22 12:47:56 +02:00
ability to declare the dependencies a class type has, or to change the nature of the dependencies defined by other
developers.
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
Some of the goals of dependency injection are:
2012-05-09 14:26:29 +02:00
* Simplified instantiation of objects
* Providing a uniform way of declaring and managing inter-object dependencies
* Making class dependencies configurable
* Simplifying the process of overriding or replacing core behaviour
* Improve testability of code
* Promoting abstraction of logic
2016-02-18 01:48:17 +01:00
The following sums up the simplest usage of the `Injector` it creates a new object of type `MyClassName` through `create`
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
:::php
$object = Injector::inst()->create('MyClassName');
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
The benefit of constructing objects through this syntax is `ClassName` can be swapped out using the
[Configuration API ](../configuration ) by developers.
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
**mysite/_config/app.yml**
:::yml
Injector:
MyClassName:
class: MyBetterClassName
2012-05-09 14:26:29 +02:00
2015-08-03 02:50:24 +02:00
Repeated calls to `create()` create a new object each time.
2012-05-09 14:26:29 +02:00
:::php
2014-10-22 12:47:56 +02:00
$object = Injector::inst()->create('MyClassName');
$object2 = Injector::inst()->create('MyClassName');
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
echo $object !== $object2;
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
// returns true;
## Singleton Pattern
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
The `Injector` API can be used for the singleton pattern through `get()` . Subsequent calls to `get` return the same
object instance as the first call.
2012-05-09 14:26:29 +02:00
:::php
2014-10-22 12:47:56 +02:00
// sets up MyClassName as a singleton
$object = Injector::inst()->get('MyClassName');
$object2 = Injector::inst()->get('MyClassName');
echo ($object === $object2);
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
// returns true;
## Dependencies
2016-02-18 01:48:17 +01:00
The `Injector` API can be used to define the types of `$dependencies` that an object requires.
2012-05-09 14:26:29 +02:00
:::php
2014-10-22 12:47:56 +02:00
< ?php
2012-05-09 14:26:29 +02:00
class MyController extends Controller {
2014-10-22 12:47:56 +02:00
2012-05-09 14:26:29 +02:00
// both of these properties will be automatically
// set by the injector on object creation
public $permissions;
public $textProperty;
2014-10-22 12:47:56 +02:00
// we declare the types for each of the properties on the object. Anything we pass in via the Injector API must
// match these data types.
2012-05-09 14:26:29 +02:00
static $dependencies = array(
'textProperty' => 'a string value',
'permissions' => '%$PermissionService',
);
}
2014-10-22 12:47:56 +02:00
When creating a new instance of `MyController` the dependencies on that class will be met.
:::php
2012-05-09 14:26:29 +02:00
$object = Injector::inst()->get('MyController');
2014-10-22 12:47:56 +02:00
echo ($object->permissions instanceof PermissionService);
// returns true;
echo (is_string($object->textProperty));
// returns true;
2012-05-09 14:26:29 +02:00
2016-02-18 01:48:17 +01:00
The [Configuration YAML ](../configuration ) does the hard work of configuring those `$dependencies` for us.
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
**mysite/_config/app.yml**
:::yml
Injector:
PermissionService:
class: MyCustomPermissionService
MyController
properties:
textProperty: 'My Text Value'
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
Now the dependencies will be replaced with our configuration.
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
:::php
$object = Injector::inst()->get('MyController');
echo ($object->permissions instanceof MyCustomPermissionService);
// returns true;
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
echo ($object->textProperty == 'My Text Value');
// returns true;
2012-05-09 14:26:29 +02:00
2015-07-29 13:06:25 +02:00
As well as properties, method calls can also be specified:
:::yml
Injector:
Logger:
class: Monolog\Logger
calls:
- [ pushHandler, [ %$DefaultHandler ] ]
2017-01-18 04:58:48 +01:00
## Using constants as variables
2017-02-27 08:05:56 +01:00
Any of the core constants can be used as a service argument by quoting with back ticks "`". Please ensure you also quote the entire value (see below).
2017-01-18 04:58:48 +01:00
2017-02-27 08:05:56 +01:00
```yaml
CachingService:
class: SilverStripe\Cache\CacheProvider
properties:
CacheDir: '`TEMP_DIR`'
```
2017-01-18 04:58:48 +01:00
2017-02-27 08:05:56 +01:00
Note: undefined variables will be replaced with null.
2017-01-18 04:58:48 +01:00
2014-10-22 12:47:56 +02:00
## Factories
2014-02-03 01:30:22 +01:00
Some services require non-trivial construction which means they must be created by a factory class. To do this, create
2017-07-03 03:22:12 +02:00
a factory class which implements the [Factory ](api:SilverStripe\Framework\Injector\Factory ) interface. You can then specify
2014-02-03 01:30:22 +01:00
the `factory` key in the service definition, and the factory service will be used.
An example using the `MyFactory` service to create instances of the `MyService` service is shown below:
2014-10-22 12:47:56 +02:00
**mysite/_config/app.yml**
2014-02-03 01:30:22 +01:00
:::yml
Injector:
MyService:
factory: MyFactory
2015-01-08 08:53:45 +01:00
**mysite/code/MyFactory.php**
2014-10-22 12:47:56 +02:00
2014-02-03 01:30:22 +01:00
:::php
2014-10-22 12:47:56 +02:00
< ?php
2017-04-21 00:54:21 +02:00
class MyFactory implements SilverStripe\Core\Injector\Factory {
2014-10-22 12:47:56 +02:00
2014-02-03 01:30:22 +01:00
public function create($service, array $params = array()) {
return new MyServiceImplementation();
}
}
// Will use MyFactoryImplementation::create() to create the service instance.
$instance = Injector::inst()->get('MyService');
2014-10-22 12:47:56 +02:00
## Dependency overrides
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
To override the `$dependency` declaration for a class, define the following configuration file.
**mysite/_config/app.yml**
2012-05-09 14:26:29 +02:00
MyController:
dependencies:
textProperty: a string value
permissions: %$PermissionService
2014-10-22 12:47:56 +02:00
## Managed objects
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
Simple dependencies can be specified by the `$dependencies` , but more complex configurations are possible by specifying
constructor arguments, or by specifying more complex properties such as lists.
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
These more complex configurations are defined in `Injector` configuration blocks and are read by the `Injector` at
runtime.
2012-05-09 14:26:29 +02:00
Assuming a class structure such as
:::php
2014-10-22 12:47:56 +02:00
< ?php
2012-05-09 14:26:29 +02:00
class RestrictivePermissionService {
private $database;
2014-10-22 12:47:56 +02:00
2012-05-09 14:26:29 +02:00
public function setDatabase($d) {
$this->database = $d;
}
}
2012-10-02 11:38:16 +02:00
2012-05-09 14:26:29 +02:00
class MySQLDatabase {
private $username;
private $password;
2012-10-02 11:38:16 +02:00
2012-05-09 14:26:29 +02:00
public function __construct($username, $password) {
$this->username = $username;
$this->password = $password;
}
}
2014-10-22 12:47:56 +02:00
And the following configuration..
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
:::yml
2012-05-09 14:26:29 +02:00
name: MyController
---
MyController:
dependencies:
2014-10-22 12:47:56 +02:00
permissions: %$PermissionService
Injector:
PermissionService:
class: RestrictivePermissionService
properties:
database: %$MySQLDatabase
MySQLDatabase
constructor:
0: 'dbusername'
1: 'dbpassword'
Calling..
2012-05-09 14:26:29 +02:00
:::php
// sets up ClassName as a singleton
$controller = Injector::inst()->get('MyController');
2014-10-22 12:47:56 +02:00
Would setup the following
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
* Create an object of type `MyController`
2012-05-09 14:26:29 +02:00
* Look through the **dependencies** and call get('PermissionService')
2014-10-22 12:47:56 +02:00
* Load the configuration for PermissionService, and create an object of type `RestrictivePermissionService`
* Look at the properties to be injected and look for the config for `MySQLDatabase`
* Create a MySQLDatabase class, passing dbusername and dbpassword as the parameters to the constructor.
2012-05-09 14:26:29 +02:00
2016-05-03 08:38:49 +02:00
## Service inheritance
By default, services registered with Injector do not inherit from one another; This is because it registers
named services, which may not be actual classes, and thus should not behave as though they were.
Thus if you want an object to have the injected dependencies of a service of another name, you must
assign a reference to that service.
:::yaml
Injector:
JSONServiceDefinition:
properties:
Serialiser: JSONSerialiser
GZIPJSONProvider: %$JSONServiceDefinition
2014-04-22 05:21:12 +02:00
2014-10-22 12:47:56 +02:00
## Testing with Injector
2014-04-22 05:21:12 +02:00
2014-10-22 12:47:56 +02:00
In situations where injector states must be temporarily overridden, it is possible to create nested Injector instances
which may be later discarded, reverting the application to the original state. This is done through `nest` and `unnest` .
2014-04-22 05:21:12 +02:00
2014-10-22 12:47:56 +02:00
This is useful when writing test cases, as certain services may be necessary to override for a single method call.
2014-04-22 05:21:12 +02:00
:::php
// Setup default service
Injector::inst()->registerService(new LiveService(), 'ServiceName');
// Test substitute service temporarily
Injector::nest();
2014-10-22 12:47:56 +02:00
2014-04-22 05:21:12 +02:00
Injector::inst()->registerService(new TestingService(), 'ServiceName');
$service = Injector::inst()->get('ServiceName');
// ... do something with $service
2014-10-22 12:47:56 +02:00
// revert changes
Injector::unnest();
2012-05-09 14:26:29 +02:00
2014-10-22 12:47:56 +02:00
## API Documentation
2012-05-09 14:26:29 +02:00
2017-07-03 03:22:12 +02:00
* [Injector ](api:SilverStripe\Core\Injector\Injector )
* [Factory ](api:SilverStripe\Core\Injector\Factory )