Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Commit

Permalink
Merge pull request #4145 from Ocramius/feature/eager-services
Browse files Browse the repository at this point in the history
Delegate factories
  • Loading branch information
weierophinney committed Apr 15, 2013
2 parents 71f4f9f + b531502 commit 8802391
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 5 deletions.
17 changes: 17 additions & 0 deletions library/Zend/ServiceManager/Config.php
Expand Up @@ -106,6 +106,17 @@ public function getShared()
return (isset($this->config['shared'])) ? $this->config['shared'] : array(); return (isset($this->config['shared'])) ? $this->config['shared'] : array();
} }


/**
* 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 * Configure service manager
* *
Expand Down Expand Up @@ -145,5 +156,11 @@ public function configureServiceManager(ServiceManager $serviceManager)
foreach ($this->getShared() as $name => $isShared) { foreach ($this->getShared() as $name => $isShared) {
$serviceManager->setShared($name, $isShared); $serviceManager->setShared($name, $isShared);
} }

foreach ($this->getDelegators() as $originalServiceName => $delegators) {
foreach ($delegators as $delegator) {
$serviceManager->addDelegator($originalServiceName, $delegator);
}
}
} }
} }
28 changes: 28 additions & 0 deletions 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);
}
92 changes: 87 additions & 5 deletions library/Zend/ServiceManager/ServiceManager.php
Expand Up @@ -48,6 +48,11 @@ class ServiceManager implements ServiceLocatorInterface
*/ */
protected $abstractFactories = array(); protected $abstractFactories = array();


/**
* @var array[]
*/
protected $delegators = array();

/** /**
* @var array * @var array
*/ */
Expand Down Expand Up @@ -311,6 +316,25 @@ public function addAbstractFactory($factory, $topOfStack = true)
return $this; return $this;
} }


/**
* 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 * Add initializer
* *
Expand Down Expand Up @@ -475,17 +499,14 @@ public function get($name, $usePeeringServiceManagers = true)
} }


/** /**
* Create an instance * Create an instance of the requested service
* *
* @param string|array $name * @param string|array $name
*
* @return bool|object * @return bool|object
* @throws Exception\ServiceNotFoundException
* @throws Exception\ServiceNotCreatedException
*/ */
public function create($name) public function create($name)
{ {
$instance = false;

if (is_array($name)) { if (is_array($name)) {
list($cName, $rName) = $name; list($cName, $rName) = $name;
} else { } else {
Expand All @@ -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])) { if (isset($this->factories[$cName])) {
$instance = $this->createFromFactory($cName, $rName); $instance = $this->createFromFactory($cName, $rName);
Expand Down
59 changes: 59 additions & 0 deletions tests/ZendTest/ServiceManager/ServiceManagerTest.php
Expand Up @@ -18,6 +18,7 @@
use Zend\ServiceManager\Config; use Zend\ServiceManager\Config;


use ZendTest\ServiceManager\TestAsset\FooCounterAbstractFactory; use ZendTest\ServiceManager\TestAsset\FooCounterAbstractFactory;
use ZendTest\ServiceManager\TestAsset\MockSelfReturningDelegatorFactory;


class ServiceManagerTest extends \PHPUnit_Framework_TestCase class ServiceManagerTest extends \PHPUnit_Framework_TestCase
{ {
Expand Down Expand Up @@ -728,4 +729,62 @@ public function testRetrieveServiceFromPeeringServiceManagerIfretrieveFromPeerin
$this->assertEquals($serviceManagerChild->get($foo1), $boo2); $this->assertEquals($serviceManagerChild->get($foo1), $boo2);
$this->assertEquals($this->serviceManager->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));
}
} }
@@ -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;
}
}

0 comments on commit 8802391

Please sign in to comment.