Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #4145 from Ocramius/feature/eager-services

Delegate factories
  • Loading branch information...
commit 074b9fb7b30ab61a250454e0f9e17bbf308f3dfc 2 parents 41ef654 + b0b3745
@weierophinney weierophinney authored
View
17 library/Zend/ServiceManager/Config.php
@@ -107,6 +107,17 @@ public function getShared()
}
/**
+ * Get the delegator services map, with keys being the services acting as delegates,
+ * and values being the delegator factories names
+ *
+ * @return array
+ */
+ public function getDelegators()
+ {
+ return (isset($this->config['delegators'])) ? $this->config['delegators'] : array();
+ }
+
+ /**
* Configure service manager
*
* @param ServiceManager $serviceManager
@@ -145,5 +156,11 @@ public function configureServiceManager(ServiceManager $serviceManager)
foreach ($this->getShared() as $name => $isShared) {
$serviceManager->setShared($name, $isShared);
}
+
+ foreach ($this->getDelegators() as $originalServiceName => $delegators) {
+ foreach ($delegators as $delegator) {
+ $serviceManager->addDelegator($originalServiceName, $delegator);
+ }
+ }
}
}
View
28 library/Zend/ServiceManager/DelegatorFactoryInterface.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+namespace Zend\ServiceManager;
+
+/**
+ * Interface for factories that can create delegators for services
+ */
+interface DelegatorFactoryInterface
+{
+ /**
+ * A factory that creates delegators of a given service
+ *
+ * @param ServiceLocatorInterface $serviceLocator the service locator which requested the service
+ * @param string $name the normalized service name
+ * @param string $requestedName the requested service name
+ * @param callable $callback the callback that is responsible for creating the service
+ *
+ * @return mixed
+ */
+ public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback);
+}
View
92 library/Zend/ServiceManager/ServiceManager.php
@@ -49,6 +49,11 @@ class ServiceManager implements ServiceLocatorInterface
protected $abstractFactories = array();
/**
+ * @var array[]
+ */
+ protected $delegators = array();
+
+ /**
* @var array
*/
protected $pendingAbstractFactoryRequests = array();
@@ -312,6 +317,25 @@ public function addAbstractFactory($factory, $topOfStack = true)
}
/**
+ * Sets the given service name as to be handled by a delegator factory
+ *
+ * @param string $serviceName name of the service being the delegate
+ * @param string $delegatorFactoryName name of the service being the delegator factory
+ *
+ * @return ServiceManager
+ */
+ public function addDelegator($serviceName, $delegatorFactoryName)
+ {
+ if (!isset($this->delegators[$this->canonicalizeName($serviceName)])) {
+ $this->delegators[$this->canonicalizeName($serviceName)] = array();
+ }
+
+ $this->delegators[$this->canonicalizeName($serviceName)][] = $delegatorFactoryName;
+
+ return $this;
+ }
+
+ /**
* Add initializer
*
* @param callable|InitializerInterface $initializer
@@ -475,17 +499,14 @@ public function get($name, $usePeeringServiceManagers = true)
}
/**
- * Create an instance
+ * Create an instance of the requested service
*
* @param string|array $name
+ *
* @return bool|object
- * @throws Exception\ServiceNotFoundException
- * @throws Exception\ServiceNotCreatedException
*/
public function create($name)
{
- $instance = false;
-
if (is_array($name)) {
list($cName, $rName) = $name;
} else {
@@ -499,6 +520,67 @@ public function create($name)
}
}
+ if (isset($this->delegators[$cName])) {
+ $serviceManager = $this;
+ $additionalDelegators = count($this->delegators[$cName]) - 1;
+ $creationCallback = function () use ($serviceManager, $rName, $cName) {
+ return $serviceManager->doCreate($rName, $cName);
+ };
+
+ for ($i = 0; $i < $additionalDelegators; $i += 1) {
+ $creationCallback = $this->createDelegatorCallback(
+ $this->delegators[$cName][$i],
+ $rName,
+ $cName,
+ $creationCallback
+ );
+ }
+
+ /* @var $delegatorFactory DelegatorFactoryInterface */
+ $delegatorFactory = $this->get($this->delegators[$cName][$i]);
+
+ return $delegatorFactory->createDelegatorWithName($this, $cName, $rName, $creationCallback);
+ }
+
+ return $this->doCreate($rName, $cName);
+ }
+
+ /**
+ * Creates a callback that uses a delegator to create a service
+ *
+ * @param string $delegatorFactoryName name of the delegator factory service
+ * @param string $rName requested service name
+ * @param string $cName canonical service name
+ * @param callable $creationCallback callback that is responsible for instantiating the service
+ *
+ * @return callable
+ */
+ private function createDelegatorCallback($delegatorFactoryName, $rName, $cName, $creationCallback)
+ {
+ $serviceManager = $this;
+
+ return function () use ($serviceManager, $delegatorFactoryName, $rName, $cName, $creationCallback) {
+ /* @var $delegatorFactory DelegatorFactoryInterface */
+ $delegatorFactory = $serviceManager->get($delegatorFactoryName);
+
+ return $delegatorFactory->createDelegatorWithName($serviceManager, $cName, $rName, $creationCallback);
+ };
+ }
+
+ /**
+ * Actually creates the service
+ *
+ * @param string $rName real service name
+ * @param string $cName canonicalized service name
+ *
+ * @return bool|mixed|null|object
+ * @throws Exception\ServiceNotFoundException
+ *
+ * @internal this method is internal because of PHP 5.3 compatibility - do not explicitly use it
+ */
+ public function doCreate($rName, $cName)
+ {
+ $instance = false;
if (isset($this->factories[$cName])) {
$instance = $this->createFromFactory($cName, $rName);
View
59 tests/ZendTest/ServiceManager/ServiceManagerTest.php
@@ -18,6 +18,7 @@
use Zend\ServiceManager\Config;
use ZendTest\ServiceManager\TestAsset\FooCounterAbstractFactory;
+use ZendTest\ServiceManager\TestAsset\MockSelfReturningDelegatorFactory;
class ServiceManagerTest extends \PHPUnit_Framework_TestCase
{
@@ -728,4 +729,62 @@ public function testRetrieveServiceFromPeeringServiceManagerIfretrieveFromPeerin
$this->assertEquals($serviceManagerChild->get($foo1), $boo2);
$this->assertEquals($this->serviceManager->get($foo1), $boo2);
}
+
+ /**
+ * @covers Zend\ServiceManager\ServiceManager::create
+ * @covers Zend\ServiceManager\ServiceManager::createDelegatorCallback
+ * @covers Zend\ServiceManager\ServiceManager::addDelegator
+ */
+ public function testUsesDelegatorWhenAvailable()
+ {
+ $delegator = $this->getMock('Zend\\ServiceManager\\DelegatorFactoryInterface');
+
+ $this->serviceManager->setService('foo-delegator', $delegator);
+ $this->serviceManager->addDelegator('foo-service', 'foo-delegator');
+ $this->serviceManager->setInvokableClass('foo-service', 'stdClass');
+
+ $delegator
+ ->expects($this->once())
+ ->method('createDelegatorWithName')
+ ->with(
+ $this->serviceManager,
+ 'fooservice',
+ 'foo-service',
+ $this->callback(function ($callback) {
+ if (!is_callable($callback)) {
+ return false;
+ }
+
+ $service = call_user_func($callback);
+
+ return $service instanceof \stdClass;
+ })
+ )
+ ->will($this->returnValue($delegator));
+
+ $this->assertSame($delegator, $this->serviceManager->create('foo-service'));
+ }
+
+ /**
+ * @covers Zend\ServiceManager\ServiceManager::create
+ * @covers Zend\ServiceManager\ServiceManager::createDelegatorCallback
+ * @covers Zend\ServiceManager\ServiceManager::addDelegator
+ */
+ public function testUsesMultipleDelegates()
+ {
+ $fooDelegator = new MockSelfReturningDelegatorFactory();
+ $barDelegator = new MockSelfReturningDelegatorFactory();
+
+ $this->serviceManager->setService('foo-delegate', $fooDelegator);
+ $this->serviceManager->setService('bar-delegate', $barDelegator);
+ $this->serviceManager->addDelegator('foo-service', 'foo-delegate');
+ $this->serviceManager->addDelegator('foo-service', 'bar-delegate');
+ $this->serviceManager->setInvokableClass('foo-service', 'stdClass');
+
+ $this->assertSame($barDelegator, $this->serviceManager->create('foo-service'));
+ $this->assertCount(1, $barDelegator->instances);
+ $this->assertCount(1, $fooDelegator->instances);
+ $this->assertInstanceOf('stdClass', array_shift($fooDelegator->instances));
+ $this->assertSame($fooDelegator, array_shift($barDelegator->instances));
+ }
}
View
35 tests/ZendTest/ServiceManager/TestAsset/MockSelfReturningDelegatorFactory.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ServiceManager
+ */
+
+namespace ZendTest\ServiceManager\TestAsset;
+
+use Zend\ServiceManager\DelegatorFactoryInterface;
+use Zend\ServiceManager\ServiceLocatorInterface;
+
+/**
+ * Mock factory that logs delegated service instances and returns itself instead of the original service
+ */
+class MockSelfReturningDelegatorFactory implements DelegatorFactoryInterface
+{
+ /**
+ * @var mixed[]
+ */
+ public $instances = array();
+
+ /**
+ * {@inheritDoc}
+ */
+ public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback)
+ {
+ $this->instances[] = call_user_func($callback);
+
+ return $this;
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.