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

Forwards-compatibility changes #60

Merged
merged 2 commits into from
Jan 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions src/AbstractPluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace Zend\ServiceManager;

use Interop\Container\ContainerInterface;
use Exception as BaseException;

/**
Expand Down Expand Up @@ -57,11 +58,56 @@ abstract class AbstractPluginManager extends ServiceManager implements ServiceLo
* Add a default initializer to ensure the plugin is valid after instance
* creation.
*
* @param null|ConfigInterface $configuration
* Additionally, the constructor provides forwards compatibility with v3 by
* overloading the initial argument. v2 usage expects either null or a
* ConfigInterface instance, and will ignore any other arguments. v3 expects
* a ContainerInterface instance, and will use an array of configuration to
* seed the current instance with services. In most cases, you can ignore the
* constructor unless you are writing a specialized factory for your plugin
* manager or overriding it.
*
* @param null|ConfigInterface|ContainerInterface $configOrContainerInstance
* @param array $v3config If $configOrContainerInstance is a container, this
* value will be passed to the parent constructor.
* @throws Exception\InvalidArgumentException if $configOrContainerInstance
* is neither null, nor a ConfigInterface, nor a ContainerInterface.
*/
public function __construct(ConfigInterface $configuration = null)
public function __construct($configOrContainerInstance = null, array $v3config = [])
{
parent::__construct($configuration);
if (null !== $configOrContainerInstance
&& ! $configOrContainerInstance instanceof ConfigInterface
&& ! $configOrContainerInstance instanceof ContainerInterface
) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a ConfigInterface instance or ContainerInterface instance; received %s',
get_class($this),
(is_object($configOrContainerInstance)
? get_class($configOrContainerInstance)
: gettype($configOrContainerInstance)
)
));
}

if ($configOrContainerInstance instanceof ContainerInterface) {
if (property_exists($this, 'serviceLocator')) {
if (! empty($v3config)) {
parent::__construct(new Config($v3config));
}
$this->serviceLocator = $configOrContainerInstance;
}

if (property_exists($this, 'creationContext')) {
if (! empty($v3config)) {
parent::__construct($v3config);
}
$this->creationContext = $configOrContainerInstance;
}
}

if ($configOrContainerInstance instanceof ConfigInterface) {
parent::__construct($configOrContainerInstance);
}

$this->addInitializer(function ($instance) {
if ($instance instanceof ServiceLocatorAwareInterface) {
$instance->setServiceLocator($this);
Expand Down
67 changes: 67 additions & 0 deletions src/Factory/InvokableFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\ServiceManager\Factory;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Exception\InvalidServiceNameException;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

/**
* Factory for instantiating classes with no dependencies or which accept a single array.
*
* The InvokableFactory can be used for any class that:
*
* - has no constructor arguments;
* - accepts a single array of arguments via the constructor.
*
* It replaces the "invokables" and "invokable class" functionality of the v2
* service manager, and can also be used in v2 code for forwards compatibility
* with v3.
*/
final class InvokableFactory implements FactoryInterface
{
/**
* Create an instance of the requested class name.
*
* @param ContainerInterface $container
* @param string $requestedName
* @param null|array $options
* @return object
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return (null === $options) ? new $requestedName : new $requestedName($options);
}

/**
* Create an instance of the named service.
*
* If `$requestedName` is not provided, raises an exception; otherwise,
* proxies to the `__invoke()` method to create an instance of the
* requested class.
*
* @param ServiceLocatorInterface $serviceLocator
* @param null|string $canonicalName Ignored
* @param null|string $requestedName
* @return object
* @throws InvalidServiceNameException
*/
public function createService(ServiceLocatorInterface $serviceLocator, $canonicalName = null, $requestedName = null)
{
if (! $requestedName) {
throw new InvalidServiceNameException(sprintf(
'%s requires that the requested name is provided on invocation; please update your tests or consuming container',
__CLASS__
));
}
return $this($serviceLocator, $requestedName);
}
}
50 changes: 50 additions & 0 deletions test/AbstractPluginManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@

namespace ZendTest\ServiceManager;

use Interop\Container\ContainerInterface;
use ReflectionClass;
use ReflectionObject;
use Zend\ServiceManager\Config;
use Zend\ServiceManager\Exception\InvalidArgumentException;
use Zend\ServiceManager\Exception\RuntimeException;
use Zend\ServiceManager\ServiceManager;
use ZendTest\ServiceManager\TestAsset\FooPluginManager;
Expand Down Expand Up @@ -283,4 +285,52 @@ public function testWillResetAutoInvokableServiceIfNotValid()
$this->assertTrue($pluginManager->has(__CLASS__));
}
}

/**
* @group migration
*/
public function testConstructorAllowsPassingContainerAsFirstArgument()
{
$container = $this->prophesize(ContainerInterface::class);
$pluginManager = new FooPluginManager($container->reveal());
$this->assertSame($container->reveal(), $pluginManager->getServiceLocator());
}

/**
* @group migration
*/
public function testConstructorAllowsPassingContainerAndConfigurationArrayAsArguments()
{
$container = $this->prophesize(ContainerInterface::class);
$pluginManager = new FooPluginManager($container->reveal(), ['services' => [
__CLASS__ => $this,
]]);
$this->assertSame($container->reveal(), $pluginManager->getServiceLocator());
$this->assertTrue($pluginManager->has(__CLASS__));
}

public function invalidConstructorArguments()
{
return [
'true' => [true],
'false' => [false],
'zero' => [0],
'int' => [1],
'zero-float' => [0.0],
'float' => [1.1],
'string' => ['invalid'],
'array' => [['services' => [__CLASS__ => $this]]],
'object' => [(object) ['services' => [__CLASS__ => $this]]],
];
}

/**
* @group migration
* @dataProvider invalidConstructorArguments
*/
public function testPassingArgumentsOtherThanNullConfigOrContainerAsFirstConstructorArgRaisesException($arg)
{
$this->setExpectedException(InvalidArgumentException::class);
new FooPluginManager($arg);
}
}
32 changes: 32 additions & 0 deletions test/Factory/InvokableFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\ServiceManager\Factory;

use Interop\Container\ContainerInterface;
use PHPUnit_Framework_TestCase as TestCase;
use Zend\ServiceManager\Factory\InvokableFactory;
use ZendTest\ServiceManager\TestAsset\InvokableObject;

/**
* @covers \Zend\ServiceManager\Factory\InvokableFactory
*/
class InvokableFactoryTest extends TestCase
{
public function testCanCreateObject()
{
$container = $this->getMock(ContainerInterface::class);
$factory = new InvokableFactory();

$object = $factory($container, InvokableObject::class, ['foo' => 'bar']);

$this->assertInstanceOf(InvokableObject::class, $object);
$this->assertEquals(['foo' => 'bar'], $object->options);
}
}
34 changes: 34 additions & 0 deletions test/TestAsset/InvokableObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\ServiceManager\TestAsset;

class InvokableObject
{
/**
* @var array
*/
public $options;

/**
* @param array $options
*/
public function __construct(array $options = [])
{
$this->options = $options;
}

/**
* @return array
*/
public function getOptions()
{
return $this->options;
}
}