mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
NEW: Allow specifying a factory to use for creating services.
A service factory can be used for creating instances where a non-trivial construction process is required. This is done by adding a `factory` key to the service definition.
This commit is contained in:
parent
b7b041b435
commit
2f817ba177
@ -1,20 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Framework\Injector\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for creating new objects by the injector.
|
* A class for creating new objects by the injector.
|
||||||
*
|
*
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage injector
|
* @subpackage injector
|
||||||
*/
|
*/
|
||||||
class InjectionCreator {
|
class InjectionCreator implements Factory {
|
||||||
|
|
||||||
/**
|
public function create($class, array $params = array()) {
|
||||||
* @param string $object
|
|
||||||
* A string representation of the class to create
|
|
||||||
* @param array $params
|
|
||||||
* An array of parameters to be passed to the constructor
|
|
||||||
*/
|
|
||||||
public function create($class, $params = array()) {
|
|
||||||
$reflector = new ReflectionClass($class);
|
$reflector = new ReflectionClass($class);
|
||||||
|
|
||||||
if (count($params)) {
|
if (count($params)) {
|
||||||
@ -23,4 +19,5 @@ class InjectionCreator {
|
|||||||
|
|
||||||
return $reflector->newInstance();
|
return $reflector->newInstance();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once dirname(__FILE__) . '/InjectionCreator.php';
|
require_once FRAMEWORK_PATH . '/src/SilverStripe/Framework/Injector/Factory.php';
|
||||||
require_once dirname(__FILE__) . '/SilverStripeInjectionCreator.php';
|
|
||||||
require_once dirname(__FILE__) . '/ServiceConfigurationLocator.php';
|
require_once __DIR__ . '/InjectionCreator.php';
|
||||||
require_once dirname(__FILE__) . '/SilverStripeServiceConfigurationLocator.php';
|
require_once __DIR__ . '/SilverStripeInjectionCreator.php';
|
||||||
|
require_once __DIR__ . '/ServiceConfigurationLocator.php';
|
||||||
|
require_once __DIR__ . '/SilverStripeServiceConfigurationLocator.php';
|
||||||
|
|
||||||
|
use SilverStripe\Framework\Injector\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple injection manager that manages creating objects and injecting
|
* A simple injection manager that manages creating objects and injecting
|
||||||
@ -71,6 +75,7 @@ require_once dirname(__FILE__) . '/SilverStripeServiceConfigurationLocator.php';
|
|||||||
* // type
|
* // type
|
||||||
* // By default, singleton is assumed
|
* // By default, singleton is assumed
|
||||||
*
|
*
|
||||||
|
* 'factory' => 'FactoryService' // A factory service to use to create instances.
|
||||||
* 'construct' => array( // properties to set at construction
|
* 'construct' => array( // properties to set at construction
|
||||||
* 'scalar',
|
* 'scalar',
|
||||||
* '%$BeanId',
|
* '%$BeanId',
|
||||||
@ -94,25 +99,25 @@ require_once dirname(__FILE__) . '/SilverStripeServiceConfigurationLocator.php';
|
|||||||
*
|
*
|
||||||
* In addition to specifying the bindings directly in the configuration,
|
* In addition to specifying the bindings directly in the configuration,
|
||||||
* you can simply create a publicly accessible property on the target
|
* you can simply create a publicly accessible property on the target
|
||||||
* class which will automatically be injected if the autoScanProperties
|
* class which will automatically be injected if the autoScanProperties
|
||||||
* option is set to true. This means a class defined as
|
* option is set to true. This means a class defined as
|
||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* class MyController extends Controller {
|
* class MyController extends Controller {
|
||||||
*
|
*
|
||||||
* private $permissionService;
|
* private $permissionService;
|
||||||
*
|
*
|
||||||
* public setPermissionService($p) {
|
* public setPermissionService($p) {
|
||||||
* $this->permissionService = $p;
|
* $this->permissionService = $p;
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* will have setPermissionService called if
|
* will have setPermissionService called if
|
||||||
*
|
*
|
||||||
* * Injector::inst()->setAutoScanProperties(true) is called and
|
* * Injector::inst()->setAutoScanProperties(true) is called and
|
||||||
* * A service named 'PermissionService' has been configured
|
* * A service named 'PermissionService' has been configured
|
||||||
*
|
*
|
||||||
* @author marcus@silverstripe.com.au
|
* @author marcus@silverstripe.com.au
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage injector
|
* @subpackage injector
|
||||||
@ -161,16 +166,18 @@ class Injector {
|
|||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
private $autoScanProperties = false;
|
private $autoScanProperties = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object used to create new class instances
|
* The default factory used to create new instances.
|
||||||
*
|
|
||||||
* Use a custom class here to change the way classes are created to use
|
|
||||||
* a custom creation method. By default the InjectionCreator class is used,
|
|
||||||
* which simply creates a new class via 'new', however this could be overridden
|
|
||||||
* to use, for example, SilverStripe's Object::create() method.
|
|
||||||
*
|
*
|
||||||
* @var InjectionCreator
|
* The {@link InjectionCreator} is used by default, which simply directly
|
||||||
|
* creates objects. This can be changed to use a different default creation
|
||||||
|
* method if desired.
|
||||||
|
*
|
||||||
|
* Each individual component can also specify a custom factory to use by
|
||||||
|
* using the `factory` parameter.
|
||||||
|
*
|
||||||
|
* @var Factory
|
||||||
*/
|
*/
|
||||||
protected $objectCreator;
|
protected $objectCreator;
|
||||||
|
|
||||||
@ -190,7 +197,7 @@ class Injector {
|
|||||||
);
|
);
|
||||||
|
|
||||||
$this->autoProperties = array();
|
$this->autoProperties = array();
|
||||||
|
|
||||||
|
|
||||||
$creatorClass = isset($config['creator']) ? $config['creator'] : 'InjectionCreator';
|
$creatorClass = isset($config['creator']) ? $config['creator'] : 'InjectionCreator';
|
||||||
$locatorClass = isset($config['locator']) ? $config['locator'] : 'ServiceConfigurationLocator';
|
$locatorClass = isset($config['locator']) ? $config['locator'] : 'ServiceConfigurationLocator';
|
||||||
@ -215,7 +222,16 @@ class Injector {
|
|||||||
}
|
}
|
||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default global injector instance.
|
||||||
|
*
|
||||||
|
* @param Injector $instance
|
||||||
|
*/
|
||||||
|
public static function set_inst(Injector $instance) {
|
||||||
|
self::$instance = $instance;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicate whether we auto scan injected objects for properties to set.
|
* Indicate whether we auto scan injected objects for properties to set.
|
||||||
*
|
*
|
||||||
@ -226,17 +242,16 @@ class Injector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the object to use for creating new objects
|
* Sets the default factory to use for creating new objects.
|
||||||
*
|
*
|
||||||
* @param InjectionCreator $obj
|
* @param Factory $obj
|
||||||
*/
|
*/
|
||||||
public function setObjectCreator($obj) {
|
public function setObjectCreator(Factory $obj) {
|
||||||
$this->objectCreator = $obj;
|
$this->objectCreator = $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accessor (for testing purposes)
|
* @return Factory
|
||||||
* @return InjectionCreator
|
|
||||||
*/
|
*/
|
||||||
public function getObjectCreator() {
|
public function getObjectCreator() {
|
||||||
return $this->objectCreator;
|
return $this->objectCreator;
|
||||||
@ -486,8 +501,9 @@ class Injector {
|
|||||||
$constructorParams = $spec['constructor'];
|
$constructorParams = $spec['constructor'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$object = $this->objectCreator->create($class, $constructorParams);
|
$factory = isset($spec['factory']) ? $this->get($spec['factory']) : $this->getObjectCreator();
|
||||||
|
$object = $factory->create($class, $constructorParams);
|
||||||
|
|
||||||
// figure out if we have a specific id set or not. In some cases, we might be instantiating objects
|
// figure out if we have a specific id set or not. In some cases, we might be instantiating objects
|
||||||
// that we don't manage directly; we don't want to store these in the service cache below
|
// that we don't manage directly; we don't want to store these in the service cache below
|
||||||
if (!$id) {
|
if (!$id) {
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Framework\Injector\Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage injector
|
* @subpackage injector
|
||||||
*/
|
*/
|
||||||
|
class SilverStripeInjectionCreator implements Factory {
|
||||||
|
|
||||||
class SilverStripeInjectionCreator {
|
public function create($class, array $params = array()) {
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param string $object
|
|
||||||
* A string representation of the class to create
|
|
||||||
* @param array $params
|
|
||||||
* An array of parameters to be passed to the constructor
|
|
||||||
*/
|
|
||||||
public function create($class, $params = array()) {
|
|
||||||
$class = Object::getCustomClass($class);
|
$class = Object::getCustomClass($class);
|
||||||
$reflector = new ReflectionClass($class);
|
$reflector = new ReflectionClass($class);
|
||||||
|
|
||||||
return $reflector->newInstanceArgs($params);
|
return $params ? $reflector->newInstanceArgs($params) : $reflector->newInstance();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
@ -76,8 +76,7 @@ The subsequent call returns the SAME object as the first call.
|
|||||||
|
|
||||||
In this case, on creation of the MyController object, the injector will
|
In this case, on creation of the MyController object, the injector will
|
||||||
automatically instantiate the PermissionService object and set it as
|
automatically instantiate the PermissionService object and set it as
|
||||||
the **permissions** property.
|
the **permissions** property.
|
||||||
|
|
||||||
|
|
||||||
## Configuring objects managed by the dependency injector
|
## Configuring objects managed by the dependency injector
|
||||||
|
|
||||||
@ -90,6 +89,31 @@ Configuration can be specified for two areas of dependency management
|
|||||||
* Defining dependency overrides for individual classes
|
* Defining dependency overrides for individual classes
|
||||||
* Injector managed 'services'
|
* Injector managed 'services'
|
||||||
|
|
||||||
|
### Factories
|
||||||
|
|
||||||
|
Some services require non-trivial construction which means they must be created by a factory class. To do this, create
|
||||||
|
a factory class which implements the `[api:SilverStripe\Framework\Injector\Factory]` interface. You can then specify
|
||||||
|
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:
|
||||||
|
|
||||||
|
:::yml
|
||||||
|
Injector:
|
||||||
|
MyService:
|
||||||
|
factory: MyFactory
|
||||||
|
MyFactory:
|
||||||
|
class: MyFactoryImplementation
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class MyFactoryImplementation implements SilverStripe\Framework\Injector\Factory {
|
||||||
|
public function create($service, array $params = array()) {
|
||||||
|
return new MyServiceImplementation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will use MyFactoryImplementation::create() to create the service instance.
|
||||||
|
$instance = Injector::inst()->get('MyService');
|
||||||
|
|
||||||
### Dependency overrides
|
### Dependency overrides
|
||||||
|
|
||||||
To override the **static $dependency;** declaration for a class, you could
|
To override the **static $dependency;** declaration for a class, you could
|
||||||
|
19
src/SilverStripe/Framework/Injector/Factory.php
Normal file
19
src/SilverStripe/Framework/Injector/Factory.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Framework\Injector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory which is used for creating service instances.
|
||||||
|
*/
|
||||||
|
interface Factory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new service instance.
|
||||||
|
*
|
||||||
|
* @param string $service The class name of the service.
|
||||||
|
* @param array $params The constructor parameters.
|
||||||
|
* @return object The created service instances.
|
||||||
|
*/
|
||||||
|
public function create($service, array $params = array());
|
||||||
|
|
||||||
|
}
|
@ -541,6 +541,28 @@ class InjectorTest extends SapphireTest {
|
|||||||
$this->assertEquals($item->property, 'othervalue');
|
$this->assertEquals($item->property, 'othervalue');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests creating a service with a custom factory.
|
||||||
|
*/
|
||||||
|
public function testCustomFactory() {
|
||||||
|
$injector = new Injector(array(
|
||||||
|
'service' => array('factory' => 'factory', 'constructor' => array(1, 2, 3))
|
||||||
|
));
|
||||||
|
|
||||||
|
$factory = $this->getMock('SilverStripe\\Framework\\Injector\\Factory');
|
||||||
|
$factory
|
||||||
|
->expects($this->once())
|
||||||
|
->method('create')
|
||||||
|
->with($this->equalTo('service'), $this->equalTo(array(1, 2, 3)))
|
||||||
|
->will($this->returnCallback(function($args) {
|
||||||
|
return new TestObject();
|
||||||
|
}));
|
||||||
|
|
||||||
|
$injector->registerService($factory, 'factory');
|
||||||
|
|
||||||
|
$this->assertInstanceOf('TestObject', $injector->get('service'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class InjectorTestConfigLocator extends SilverStripeServiceConfigurationLocator implements TestOnly {
|
class InjectorTestConfigLocator extends SilverStripeServiceConfigurationLocator implements TestOnly {
|
||||||
@ -667,7 +689,7 @@ class SSObjectCreator extends InjectionCreator {
|
|||||||
$this->injector = $injector;
|
$this->injector = $injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create($class, $params = array()) {
|
public function create($class, array $params = array()) {
|
||||||
if (strpos($class, '(') === false) {
|
if (strpos($class, '(') === false) {
|
||||||
return parent::create($class, $params);
|
return parent::create($class, $params);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user