Skip to content

Commit

Permalink
Adding support for lazy services
Browse files Browse the repository at this point in the history
  • Loading branch information
Ocramius committed Apr 12, 2013
1 parent 755c33d commit 6594369
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 0 deletions.
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@
"require": {
"php": ">=5.3.3"
},
"minimum-stability": "dev",
"require-dev": {
"doctrine/common": ">=2.1",
"ircmaxell/random-lib": "dev-master",
"ircmaxell/security-lib": "dev-master",
"ocramius/proxy-manager": "0.2.*",
"phpunit/PHPUnit": "3.7.*"
},
"suggest": {
"doctrine/common": "Doctrine\\Common >=2.1 for annotation features",
"ext-intl": "ext/intl for i18n features",
"ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if OpenSSL/Mcrypt extensions are unavailable",
"ocramius/proxy-manager": "ProxyManager to handle lazy initialization of services",
"pecl-weakref": "Implementation of weak references for Zend\\Stdlib\\CallbackHandler",
"zendframework/zendpdf": "ZendPdf for creating PDF representations of barcodes",
"zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha for rendering ReCaptchas in Zend\\Captcha and/or Zend\\Form"
Expand Down
64 changes: 64 additions & 0 deletions library/Zend/ServiceManager/Proxy/LazyServiceFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?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\Proxy;

use ProxyManager\Factory\LazyLoadingValueHolderFactory;

use Zend\ServiceManager\DelegateFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\Exception;

/**
* Delegate responsible of instantiating lazy loading value holder proxies of
* given services
*
* @link https://github.com/Ocramius/ProxyManager/blob/master/docs/lazy-loading-value-holder.md
*/
class LazyServiceFactory implements DelegateFactoryInterface
{
/**
* @var \ProxyManager\Factory\LazyLoadingValueHolderFactory
*/
protected $proxyFactory;

/**
* @var string[] map of service names to class names
*/
protected $servicesMap;

/**
* @param LazyLoadingValueHolderFactory $proxyFactory
* @param string[] $servicesMap a map of service names to class names of their
* respective classes
*/
public function __construct(LazyLoadingValueHolderFactory $proxyFactory, array $servicesMap)
{
$this->proxyFactory = $proxyFactory;
$this->servicesMap = $servicesMap;
}

/**
* {@inheritDoc}
*
* @return object|\ProxyManager\Proxy\LazyLoadingInterface|\ProxyManager\Proxy\ValueHolderInterface
*/
public function createDelegateWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback)
{
if (isset($this->servicesMap[$requestedName])) {
return $this->proxyFactory->createProxy($this->servicesMap[$requestedName], $callback);
} elseif (isset($this->servicesMap[$name])) {
return $this->proxyFactory->createProxy($this->servicesMap[$name], $callback);
}

throw new Exception\InvalidServiceNameException(
sprintf('The requested service "%s" was not found in the provided services map', $requestedName)
);
}
}
68 changes: 68 additions & 0 deletions library/Zend/ServiceManager/Proxy/LazyServiceFactoryFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?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\Proxy;

use ProxyManager\Configuration;
use ProxyManager\Factory\LazyLoadingValueHolderFactory;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\Exception;

/**
* Service factory responsible of instantiating {@see \Zend\ServiceManager\Proxy\LazyServiceFactory}
* and configuring it starting from application configuration
*/
class LazyServiceFactoryFactory implements FactoryInterface
{
/**
* {@inheritDoc}
*
* @return \Zend\ServiceManager\Proxy\LazyServiceFactory
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('Config');

if (!isset($config['lazy_services'])) {
throw new Exception\InvalidArgumentException('Missing "lazy_services" config key');
}

$lazyServicesConfig = $config['lazy_services'];

if (!isset($lazyServicesConfig['map'])) {
throw new Exception\InvalidArgumentException('Missing "map" config key in "lazy_services"');
}

$factoryConfig = new Configuration();

if (isset($lazyServicesConfig['proxies_target_dir'])) {
$factoryConfig->setProxiesTargetDir($lazyServicesConfig['proxies_target_dir']);
}

if (isset($lazyServicesConfig['auto_generate_proxies'])) {
$factoryConfig->setAutoGenerateProxies($lazyServicesConfig['auto_generate_proxies']);

// register the proxy autoloader if the proxies already exist
if (!$lazyServicesConfig['auto_generate_proxies']) {
spl_autoload_register($factoryConfig->getProxyAutoloader());
}
}

if (isset($lazyServicesConfig['proxies_namespace'])) {
$factoryConfig->setProxiesNamespace($lazyServicesConfig['proxies_namespace']);
}

return new LazyServiceFactory(
new LazyLoadingValueHolderFactory($factoryConfig),
$lazyServicesConfig['map']
);
}
}
118 changes: 118 additions & 0 deletions tests/ZendTest/ServiceManager/Proxy/LazyServiceFactoryFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?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\Proxy;

use Zend\ServiceManager\Proxy\LazyServiceFactoryFactory;
use Zend\ServiceManager\ServiceManager;

/**
* Tests for {@see \Zend\ServiceManager\Proxy\LazyServiceFactoryFactory}
*
* @covers \Zend\ServiceManager\Proxy\LazyServiceFactoryFactory
*/
class LazyServiceFactoryFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* {@inheritDoc}
*/
public function setUp()
{
if (!interface_exists('ProxyManager\\Proxy\\ProxyInterface')) {
$this->markTestSkipped('Please install `ocramius/proxy-manager` to run these tests');
}
}

/**
* @dataProvider invalidConfigProvider
*/
public function testInvalidConfiguration($config)
{
$locator = $this->getMock('Zend\\ServiceManager\\ServiceLocatorInterface');
$factory = new LazyServiceFactoryFactory();

$locator->expects($this->any())->method('get')->with('Config')->will($this->returnValue($config));
$this->setExpectedException('Zend\\ServiceManager\\Exception\\InvalidArgumentException');

$factory->createService($locator);
}

public function testAutoGenerateProxies()
{
$serviceManager = new ServiceManager();
$namespace = 'ZendTestProxy' . uniqid();

$serviceManager->setService(
'Config',
array(
'lazy_services' => array(
'map' => array('foo' => __CLASS__),
'proxies_namespace' => $namespace,
),
)
);
$serviceManager->setFactory('foo-delegate', 'Zend\ServiceManager\Proxy\LazyServiceFactoryFactory');
$serviceManager->setInvokableClass('foo', __CLASS__);
$serviceManager->addDelegate('foo', 'foo-delegate');

$proxy = $serviceManager->create('foo');

$this->assertInstanceOf('ProxyManager\\Proxy\\LazyLoadingInterface', $proxy);
$this->assertInstanceOf(__CLASS__, $proxy);
$this->assertSame(
$namespace . '\__CG__\ZendTest\ServiceManager\Proxy\LazyServiceFactoryFactoryTest',
get_class($proxy)
);
$this->assertFileExists(
sys_get_temp_dir() . '/' . $namespace . '__CG__ZendTestServiceManagerProxyLazyServiceFactoryFactoryTest.php'
);
}

public function testRegistersAutoloader()
{
$autoloaders = spl_autoload_functions();
$serviceManager = new ServiceManager();
$namespace = 'ZendTestProxy' . uniqid();

$serviceManager->setService(
'Config',
array(
'lazy_services' => array(
'map' => array('foo' => __CLASS__),
'proxies_namespace' => $namespace,
'auto_generate_proxies' => false,
),
)
);
$serviceManager->setFactory('foo-delegate', 'Zend\ServiceManager\Proxy\LazyServiceFactoryFactory');
$serviceManager->create('foo-delegate');

$currentAutoloaders = spl_autoload_functions();
$proxyAutoloader = end($currentAutoloaders);

$this->assertCount(count($autoloaders) + 1, $currentAutoloaders);
$this->assertInstanceOf('ProxyManager\\Autoloader\\AutoloaderInterface', $proxyAutoloader);

spl_autoload_unregister($proxyAutoloader);
}

/**
* Provides invalid configuration
*
* @return array
*/
public function invalidConfigProvider()
{
return array(
array(array()),
array(array('lazy_services' => array()))
);
}
}
85 changes: 85 additions & 0 deletions tests/ZendTest/ServiceManager/Proxy/LazyServiceFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?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\Proxy;

use Zend\ServiceManager\Proxy\LazyServiceFactory;

/**
* Tests for {@see \Zend\ServiceManager\Proxy\LazyServiceFactory}
*
* @covers \Zend\ServiceManager\Proxy\LazyServiceFactory
*/
class LazyServiceFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \ProxyManager\Factory\LazyLoadingValueHolderFactory|\PHPUnit_Framework_MockObject_MockObject
*/
protected $proxyFactory;

protected $locator;

/**
* {@inheritDoc}
*/
public function setUp()
{
if (!interface_exists('ProxyManager\\Proxy\\ProxyInterface')) {
$this->markTestSkipped('Please install `ocramius/proxy-manager` to run these tests');
}

$this->locator = $this->getMock('Zend\\ServiceManager\\ServiceLocatorInterface');
$this->proxyFactory = $this
->getMockBuilder('ProxyManager\\Factory\\LazyLoadingValueHolderFactory')
->disableOriginalConstructor()
->getMock();
}

public function testCreateDelegateWithRequestedName()
{
$instance = new \stdClass();
$callback = function () {};
$factory = new LazyServiceFactory($this->proxyFactory, array('foo' => 'bar'));

$this
->proxyFactory
->expects($this->once())
->method('createProxy')
->with('bar', $callback)
->will($this->returnValue($instance));

$this->assertSame($instance, $factory->createDelegateWithName($this->locator, 'baz', 'foo', $callback));
}

public function testCreateDelegateWithCanonicalName()
{
$instance = new \stdClass();
$callback = function () {};
$factory = new LazyServiceFactory($this->proxyFactory, array('foo' => 'bar'));

$this
->proxyFactory
->expects($this->once())
->method('createProxy')
->with('bar', $callback)
->will($this->returnValue($instance));

$this->assertSame($instance, $factory->createDelegateWithName($this->locator, 'foo', 'baz', $callback));
}

public function testCannotCreateDelegateWithNoMappedServiceClass()
{
$factory = new LazyServiceFactory($this->proxyFactory, array());

$this->setExpectedException('Zend\\ServiceManager\\Exception\\InvalidServiceNameException');

$factory->createDelegateWithName($this->locator, 'foo', 'baz', function () {});
}
}

0 comments on commit 6594369

Please sign in to comment.