Create factory for plugins that resources can be registered with.
This commit is contained in:
parent
f88df400f0
commit
e1d8239e8a
145
PHPCI/Plugin/Util/Factory.php
Normal file
145
PHPCI/Plugin/Util/Factory.php
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCI\Plugin\Util;
|
||||
|
||||
|
||||
|
||||
class Factory {
|
||||
|
||||
const TYPE_Array = "array";
|
||||
const TYPE_Callable = "callable";
|
||||
|
||||
private $currentPluginOptions;
|
||||
|
||||
/**
|
||||
* @var \Pimple
|
||||
*/
|
||||
private $container;
|
||||
|
||||
function __construct(\Pimple $container = null)
|
||||
{
|
||||
if ($container) {
|
||||
$this->container = $container;
|
||||
}
|
||||
else {
|
||||
$this->container = new \Pimple();
|
||||
}
|
||||
|
||||
$self = $this;
|
||||
$this->registerResource(
|
||||
function() use ($self) {
|
||||
return $self->getLastOptions();
|
||||
},
|
||||
'options',
|
||||
'array'
|
||||
);
|
||||
}
|
||||
|
||||
public function getLastOptions() {
|
||||
return $this->currentPluginOptions;
|
||||
}
|
||||
|
||||
public function buildPlugin($className, array $options = array())
|
||||
{
|
||||
$this->currentPluginOptions = $options;
|
||||
|
||||
$reflectedPlugin = new \ReflectionClass($className);
|
||||
|
||||
$constructor = $reflectedPlugin->getConstructor();
|
||||
|
||||
if ($constructor) {
|
||||
$argsToUse = array();
|
||||
foreach($constructor->getParameters() as $param) {
|
||||
$argsToUse = $this->addArgFromParam($argsToUse, $param);
|
||||
}
|
||||
$plugin = $reflectedPlugin->newInstanceArgs($argsToUse);
|
||||
} else {
|
||||
$plugin = $reflectedPlugin->newInstance();
|
||||
}
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $loader
|
||||
* @param string|null $name
|
||||
* @param string|null $type
|
||||
* @throws \InvalidArgumentException
|
||||
* @internal param mixed $resource
|
||||
*/
|
||||
public function registerResource(callable $loader,
|
||||
$name = null,
|
||||
$type = null
|
||||
)
|
||||
{
|
||||
if ($name === null && $type === null) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Type or Name must be specified"
|
||||
);
|
||||
}
|
||||
|
||||
$resourceID = $this->getInternalID($type, $name);
|
||||
|
||||
$this->container[$resourceID] = $loader;
|
||||
}
|
||||
|
||||
private function getInternalID($type = null, $name = null)
|
||||
{
|
||||
$type = $type ? : "";
|
||||
$name = $name ? : "";
|
||||
return $type . "-" . $name;
|
||||
}
|
||||
|
||||
private function getResourceFor($type = null, $name = null)
|
||||
{
|
||||
$fullId = $this->getInternalID($type, $name);
|
||||
if (isset($this->container[$fullId])) {
|
||||
return $this->container[$fullId];
|
||||
}
|
||||
|
||||
$typeOnlyID = $this->getInternalID($type, null);
|
||||
if (isset($this->container[$typeOnlyID])) {
|
||||
return $this->container[$typeOnlyID];
|
||||
}
|
||||
|
||||
$nameOnlyID = $this->getInternalID(null, $name);
|
||||
if (isset($this->container[$nameOnlyID])) {
|
||||
return $this->container[$nameOnlyID];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getParamType(\ReflectionParameter $param)
|
||||
{
|
||||
$class = $param->getClass();
|
||||
if ($class) {
|
||||
return $class->getName();
|
||||
} elseif($param->isArray()) {
|
||||
return self::TYPE_Array;
|
||||
} elseif($param->isCallable()) {
|
||||
return self::TYPE_Callable;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function addArgFromParam($existingArgs, \ReflectionParameter $param)
|
||||
{
|
||||
$name = $param->getName();
|
||||
$type = $this->getParamType($param);
|
||||
$arg = $this->getResourceFor($type, $name);
|
||||
|
||||
if ($arg !== null) {
|
||||
$existingArgs[] = $arg;
|
||||
} elseif ($arg === null && $param->isOptional()) {
|
||||
$existingArgs[] = $param->getDefaultValue();
|
||||
} else {
|
||||
throw new \DomainException(
|
||||
"Unsatisfied dependency: " . $param->getName()
|
||||
);
|
||||
}
|
||||
|
||||
return $existingArgs;
|
||||
}
|
||||
}
|
57
Tests/PHPCI/Plugin/Util/ExamplePlugins.php
Normal file
57
Tests/PHPCI/Plugin/Util/ExamplePlugins.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
namespace PHPCI\Plugin\Tests\Util;
|
||||
|
||||
use PHPCI\Builder;
|
||||
use PHPCI\Model\Build;
|
||||
use PHPCI\Plugin;
|
||||
|
||||
class ExamplePluginWithNoConstructorArgs {
|
||||
|
||||
}
|
||||
|
||||
class ExamplePluginWithSingleOptionalArg {
|
||||
function __construct($optional = null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ExamplePluginWithSingleRequiredArg {
|
||||
|
||||
public $RequiredArgument;
|
||||
|
||||
function __construct($requiredArgument)
|
||||
{
|
||||
$this->RequiredArgument = $requiredArgument;
|
||||
}
|
||||
}
|
||||
|
||||
class ExamplePluginWithSingleTypedRequiredArg {
|
||||
|
||||
public $RequiredArgument;
|
||||
|
||||
function __construct(\stdClass $requiredArgument)
|
||||
{
|
||||
$this->RequiredArgument = $requiredArgument;
|
||||
}
|
||||
}
|
||||
|
||||
class ExamplePluginFull implements Plugin {
|
||||
|
||||
public $Options;
|
||||
|
||||
public function __construct(
|
||||
Builder $phpci,
|
||||
Build $build,
|
||||
array $options = array()
|
||||
)
|
||||
{
|
||||
$this->Options = $options;
|
||||
}
|
||||
|
||||
public function execute()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
174
Tests/PHPCI/Plugin/Util/FactoryTest.php
Normal file
174
Tests/PHPCI/Plugin/Util/FactoryTest.php
Normal file
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCI\Plugin\Tests\Util;
|
||||
|
||||
require_once __DIR__ . "/ExamplePlugins.php";
|
||||
|
||||
use PHPCI\Plugin\Util\Factory;
|
||||
|
||||
class FactoryTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* @var \PHPCI\Plugin\Util\Factory
|
||||
*/
|
||||
protected $testedFactory;
|
||||
|
||||
protected $expectedResource;
|
||||
|
||||
protected $resourceLoader;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->testedFactory = new Factory();
|
||||
|
||||
// Setup a resource that can be returned and asserted against
|
||||
$this->expectedResource = new \stdClass();
|
||||
$resourceLink = $this->expectedResource;
|
||||
$this->resourceLoader = function() use (&$resourceLink) {
|
||||
return $resourceLink;
|
||||
};
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
|
||||
public function testRegisterResourceThrowsExceptionWithoutTypeAndName()
|
||||
{
|
||||
$this->setExpectedException("InvalidArgumentException");
|
||||
$this->testedFactory->registerResource($this->resourceLoader, null, null);
|
||||
}
|
||||
|
||||
public function testBuildPluginWorksWithConstructorlessPlugins()
|
||||
{
|
||||
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
|
||||
$expectedPluginClass = $namespace .'ExamplePluginWithNoConstructorArgs';
|
||||
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
|
||||
$this->assertInstanceOf($expectedPluginClass, $plugin);
|
||||
}
|
||||
|
||||
public function testBuildPluginWorksWithSingleOptionalArgConstructor()
|
||||
{
|
||||
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
|
||||
$expectedPluginClass = $namespace . 'ExamplePluginWithSingleOptionalArg';
|
||||
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
|
||||
$this->assertInstanceOf($expectedPluginClass, $plugin);
|
||||
}
|
||||
|
||||
public function testBuildPluginThrowsExceptionIfMissingResourcesForRequiredArg()
|
||||
{
|
||||
$this->setExpectedException(
|
||||
'DomainException',
|
||||
'Unsatisfied dependency: requiredArgument'
|
||||
);
|
||||
|
||||
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
|
||||
$expectedPluginClass = $namespace . 'ExamplePluginWithSingleRequiredArg';
|
||||
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
|
||||
}
|
||||
|
||||
public function testBuildPluginLoadsArgumentsBasedOnName()
|
||||
{
|
||||
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
|
||||
$expectedPluginClass = $namespace . 'ExamplePluginWithSingleRequiredArg';
|
||||
|
||||
$this->testedFactory->registerResource(
|
||||
$this->resourceLoader,
|
||||
"requiredArgument"
|
||||
);
|
||||
|
||||
/** @var ExamplePluginWithSingleRequiredArg $plugin */
|
||||
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
|
||||
|
||||
$this->assertEquals($this->expectedResource, $plugin->RequiredArgument);
|
||||
}
|
||||
|
||||
public function testBuildPluginLoadsArgumentsBasedOnType()
|
||||
{
|
||||
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
|
||||
$expectedPluginClass = $namespace . 'ExamplePluginWithSingleTypedRequiredArg';
|
||||
|
||||
$this->testedFactory->registerResource(
|
||||
$this->resourceLoader,
|
||||
null,
|
||||
"stdClass"
|
||||
);
|
||||
|
||||
/** @var ExamplePluginWithSingleTypedRequiredArg $plugin */
|
||||
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
|
||||
|
||||
$this->assertEquals($this->expectedResource, $plugin->RequiredArgument);
|
||||
}
|
||||
|
||||
public function testBuildPluginLoadsFullExample()
|
||||
{
|
||||
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
|
||||
$expectedPluginClass = $namespace . 'ExamplePluginFull';
|
||||
|
||||
$this->registerBuildAndBuilder();
|
||||
|
||||
/** @var ExamplePluginFull $plugin */
|
||||
$plugin = $this->testedFactory->buildPlugin($expectedPluginClass);
|
||||
|
||||
$this->assertInstanceOf($expectedPluginClass, $plugin);
|
||||
}
|
||||
|
||||
public function testBuildPluginLoadsFullExampleWithOptions()
|
||||
{
|
||||
$namespace = '\\PHPCI\\Plugin\\Tests\\Util\\';
|
||||
$expectedPluginClass = $namespace . 'ExamplePluginFull';
|
||||
|
||||
$expectedArgs = array(
|
||||
'thing' => "stuff"
|
||||
);
|
||||
|
||||
$this->registerBuildAndBuilder();
|
||||
|
||||
/** @var ExamplePluginFull $plugin */
|
||||
$plugin = $this->testedFactory->buildPlugin(
|
||||
$expectedPluginClass,
|
||||
$expectedArgs
|
||||
);
|
||||
|
||||
$this->assertInternalType('array', $plugin->Options);
|
||||
$this->assertArrayHasKey('thing', $plugin->Options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers mocked Builder and Build classes so that realistic plugins
|
||||
* can be tested.
|
||||
*/
|
||||
private function registerBuildAndBuilder()
|
||||
{
|
||||
$this->testedFactory->registerResource(
|
||||
function () {
|
||||
return $this->getMock(
|
||||
'PHPCI\Builder',
|
||||
array(),
|
||||
array(),
|
||||
'',
|
||||
false
|
||||
);
|
||||
},
|
||||
null,
|
||||
'PHPCI\\Builder'
|
||||
);
|
||||
|
||||
$this->testedFactory->registerResource(
|
||||
function () {
|
||||
return $this->getMock(
|
||||
'PHPCI\Model\Build',
|
||||
array(),
|
||||
array(),
|
||||
'',
|
||||
false
|
||||
);
|
||||
},
|
||||
null,
|
||||
'PHPCI\\Model\Build'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,8 @@
|
|||
"symfony/yaml" : "2.*",
|
||||
"symfony/console" : "2.*",
|
||||
"psr/log": "1.0.0",
|
||||
"monolog/monolog": "1.6.0"
|
||||
"monolog/monolog": "1.6.0",
|
||||
"pimple/pimple": "v1.1.*"
|
||||
},
|
||||
|
||||
"suggest": {
|
||||
|
|
104
composer.lock
generated
104
composer.lock
generated
|
@ -3,7 +3,7 @@
|
|||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
|
||||
],
|
||||
"hash": "534baabecc11275d5cc7f375eecf738d",
|
||||
"hash": "8ad9f4b137f30db71c8dcf45d8347655",
|
||||
"packages": [
|
||||
{
|
||||
"name": "block8/b8framework",
|
||||
|
@ -153,6 +153,52 @@
|
|||
],
|
||||
"time": "2013-07-28 22:38:30"
|
||||
},
|
||||
{
|
||||
"name": "pimple/pimple",
|
||||
"version": "v1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fabpot/Pimple.git",
|
||||
"reference": "471c7d7c52ad6594e17b8ec33efdd1be592b5d83"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fabpot/Pimple/zipball/471c7d7c52ad6594e17b8ec33efdd1be592b5d83",
|
||||
"reference": "471c7d7c52ad6594e17b8ec33efdd1be592b5d83",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Pimple": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
|
||||
"homepage": "http://pimple.sensiolabs.org",
|
||||
"keywords": [
|
||||
"container",
|
||||
"dependency injection"
|
||||
],
|
||||
"time": "2013-09-19 04:53:08"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.0.0",
|
||||
|
@ -293,6 +339,62 @@
|
|||
"homepage": "http://symfony.com",
|
||||
"time": "2013-09-25 06:04:15"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dependency-injection",
|
||||
"version": "v2.3.7",
|
||||
"target-dir": "Symfony/Component/DependencyInjection",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/DependencyInjection.git",
|
||||
"reference": "3ead0b87b455289864d648152e0930629df687d2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/3ead0b87b455289864d648152e0930629df687d2",
|
||||
"reference": "3ead0b87b455289864d648152e0930629df687d2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/config": "~2.2",
|
||||
"symfony/yaml": "~2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/config": "",
|
||||
"symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
|
||||
"symfony/yaml": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\DependencyInjection\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony DependencyInjection Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2013-11-09 15:43:20"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v2.3.6",
|
||||
|
|
Loading…
Reference in a new issue