Refactor container to be more flexible
This commit is contained in:
parent
3c782af86b
commit
3db58c7066
@ -28,11 +28,18 @@ use Aviat\Ion\Di\Exception\NotFoundException;
|
|||||||
class Container implements ContainerInterface {
|
class Container implements ContainerInterface {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array with class instances
|
* Array of container Generator functions
|
||||||
|
*
|
||||||
|
* @var Callable[]
|
||||||
|
*/
|
||||||
|
protected $container = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of object instances
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $container = [];
|
protected $instances = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of logger instances
|
* Map of logger instances
|
||||||
@ -55,10 +62,10 @@ class Container implements ContainerInterface {
|
|||||||
/**
|
/**
|
||||||
* Finds an entry of the container by its identifier and returns it.
|
* Finds an entry of the container by its identifier and returns it.
|
||||||
*
|
*
|
||||||
* @param string $id Identifier of the entry to look for.
|
* @param string $id - Identifier of the entry to look for.
|
||||||
*
|
*
|
||||||
* @throws NotFoundException No entry was found for this identifier.
|
* @throws NotFoundException - No entry was found for this identifier.
|
||||||
* @throws ContainerException Error while retrieving the entry.
|
* @throws ContainerException - Error while retrieving the entry.
|
||||||
*
|
*
|
||||||
* @return mixed Entry.
|
* @return mixed Entry.
|
||||||
*/
|
*/
|
||||||
@ -71,40 +78,88 @@ class Container implements ContainerInterface {
|
|||||||
|
|
||||||
if ($this->has($id))
|
if ($this->has($id))
|
||||||
{
|
{
|
||||||
$item = $this->container[$id];
|
// Return an object instance, if it already exists
|
||||||
|
if (array_key_exists($id, $this->instances))
|
||||||
|
{
|
||||||
|
return $this->instances[$id];
|
||||||
|
}
|
||||||
|
|
||||||
if (is_callable($item))
|
// If there isn't already an instance, create one
|
||||||
{
|
$obj = $this->getNew($id);
|
||||||
return $this->applyContainer($item($this));
|
$this->instances[$id] = $obj;
|
||||||
}
|
return $obj;
|
||||||
else
|
|
||||||
{
|
|
||||||
return $item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotFoundException("Item '{$id}' does not exist in container.");
|
throw new NotFoundException("Item '{$id}' does not exist in container.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a value to the container
|
* Get a new instance of the specified item
|
||||||
|
*
|
||||||
|
* @param string $id - Identifier of the entry to look for.
|
||||||
|
* @param array [$args] - Optional arguments for the factory callable
|
||||||
|
* @throws NotFoundException - No entry was found for this identifier.
|
||||||
|
* @throws ContainerException - Error while retrieving the entry.
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getNew($id, array $args = NULL)
|
||||||
|
{
|
||||||
|
if ( ! is_string($id))
|
||||||
|
{
|
||||||
|
throw new ContainerException("Id must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->has($id))
|
||||||
|
{
|
||||||
|
// By default, call a factory with the Container
|
||||||
|
$args = (is_array($args)) ? $args : [$this];
|
||||||
|
$obj = call_user_func_array($this->container[$id], $args);
|
||||||
|
|
||||||
|
// Check for container interface, and apply the container to the object
|
||||||
|
// if applicable
|
||||||
|
return $this->applyContainer($obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotFoundException("Item '{$id}' does not exist in container.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a factory to the container
|
||||||
*
|
*
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @param mixed $value
|
* @param Callable $value - a factory callable for the item
|
||||||
* @return ContainerInterface
|
* @return ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function set($id, $value)
|
public function set($id, Callable $value)
|
||||||
{
|
{
|
||||||
$this->container[$id] = $value;
|
$this->container[$id] = $value;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a specific instance in the container for an existing factory
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @param mixed $value
|
||||||
|
* @throws NotFoundException - No entry was found for this identifier.
|
||||||
|
* @return ContainerInterface
|
||||||
|
*/
|
||||||
|
public function setInstance($id, $value)
|
||||||
|
{
|
||||||
|
if ( ! $this->has($id))
|
||||||
|
{
|
||||||
|
throw new NotFoundException("Factory '{$id}' does not exist in container. Set that first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->instances[$id] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the container can return an entry for the given identifier.
|
* Returns true if the container can return an entry for the given identifier.
|
||||||
* Returns false otherwise.
|
* Returns false otherwise.
|
||||||
*
|
*
|
||||||
* @param string $id Identifier of the entry to look for.
|
* @param string $id Identifier of the entry to look for.
|
||||||
*
|
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function has($id)
|
public function has($id)
|
||||||
@ -114,37 +169,38 @@ class Container implements ContainerInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether a logger channel is registered
|
* Determine whether a logger channel is registered
|
||||||
* @param string $key The logger channel
|
*
|
||||||
|
* @param string $id The logger channel
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function hasLogger($key = 'default')
|
public function hasLogger($id = 'default')
|
||||||
{
|
{
|
||||||
return array_key_exists($key, $this->loggers);
|
return array_key_exists($id, $this->loggers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a logger to the Container
|
* Add a logger to the Container
|
||||||
*
|
*
|
||||||
* @param LoggerInterface $logger
|
* @param LoggerInterface $logger
|
||||||
* @param string $key The logger 'channel'
|
* @param string $id The logger 'channel'
|
||||||
* @return ContainerInterface
|
* @return ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function setLogger(LoggerInterface $logger, $key = 'default')
|
public function setLogger(LoggerInterface $logger, $id = 'default')
|
||||||
{
|
{
|
||||||
$this->loggers[$key] = $logger;
|
$this->loggers[$id] = $logger;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a logger for the selected channel
|
* Retrieve a logger for the selected channel
|
||||||
*
|
*
|
||||||
* @param string $key The logger to retrieve
|
* @param string $id The logger to retrieve
|
||||||
* @return LoggerInterface|null
|
* @return LoggerInterface|null
|
||||||
*/
|
*/
|
||||||
public function getLogger($key = 'default')
|
public function getLogger($id = 'default')
|
||||||
{
|
{
|
||||||
return ($this->hasLogger($key))
|
return ($this->hasLogger($id))
|
||||||
? $this->loggers[$key]
|
? $this->loggers[$id]
|
||||||
: NULL;
|
: NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,43 +16,61 @@
|
|||||||
|
|
||||||
namespace Aviat\Ion\Di;
|
namespace Aviat\Ion\Di;
|
||||||
|
|
||||||
|
use Interop\Container\ContainerInterface as InteropInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for the Dependency Injection Container
|
* Interface for the Dependency Injection Container
|
||||||
*/
|
*/
|
||||||
interface ContainerInterface extends \Interop\Container\ContainerInterface {
|
interface ContainerInterface extends InteropInterface {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a value to the container
|
* Add a factory to the container
|
||||||
*
|
*
|
||||||
* @param string $key
|
* @param string $id
|
||||||
* @param mixed $value
|
* @param Callable $value - a factory callable for the item
|
||||||
* @return ContainerInterface
|
* @return ContainerInterface
|
||||||
*/
|
*/
|
||||||
public function set($key, $value);
|
public function set($id, Callable $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a specific instance in the container for an existing factory
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @param mixed $value
|
||||||
|
* @return ContainerInterface
|
||||||
|
*/
|
||||||
|
public function setInstance($id, $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a new instance of the specified item
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getNew($id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether a logger channel is registered
|
* Determine whether a logger channel is registered
|
||||||
* @param string $key The logger channel
|
* @param string $id The logger channel
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function hasLogger($key = 'default');
|
public function hasLogger($id = 'default');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a logger to the Container
|
* Add a logger to the Container
|
||||||
*
|
*
|
||||||
* @param LoggerInterface $logger
|
* @param LoggerInterface $logger
|
||||||
* @param string $key The logger 'channel'
|
* @param string $id The logger 'channel'
|
||||||
* @return Container
|
* @return Container
|
||||||
*/
|
*/
|
||||||
public function setLogger(LoggerInterface $logger, $key = 'default');
|
public function setLogger(LoggerInterface $logger, $id = 'default');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a logger for the selected channel
|
* Retrieve a logger for the selected channel
|
||||||
*
|
*
|
||||||
* @param string $key The logger to retreive
|
* @param string $id The logger to retreive
|
||||||
* @return LoggerInterface|null
|
* @return LoggerInterface|null
|
||||||
*/
|
*/
|
||||||
public function getLogger($key = 'default');
|
public function getLogger($id = 'default');
|
||||||
}
|
}
|
@ -8,9 +8,22 @@ use Monolog\Logger;
|
|||||||
use Monolog\Handler\TestHandler;
|
use Monolog\Handler\TestHandler;
|
||||||
use Monolog\Handler\NullHandler;
|
use Monolog\Handler\NullHandler;
|
||||||
|
|
||||||
|
class FooTest {
|
||||||
|
|
||||||
|
public $item;
|
||||||
|
|
||||||
|
public function __construct($item) {
|
||||||
|
$this->item = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FooTest2 {
|
||||||
|
use \Aviat\Ion\Di\ContainerAware;
|
||||||
|
}
|
||||||
|
|
||||||
class ContainerTest extends \Ion_TestCase {
|
class ContainerTest extends \Ion_TestCase {
|
||||||
|
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$this->container = new Container();
|
$this->container = new Container();
|
||||||
@ -53,6 +66,94 @@ class ContainerTest extends \Ion_TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataGetWithException
|
||||||
|
*/
|
||||||
|
public function testGetNewWithException($id, $exception, $message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->container->getNew($id);
|
||||||
|
}
|
||||||
|
catch(ContainerException $e)
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf($exception, $e);
|
||||||
|
$this->assertEquals($message, $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataSetInstanceWithException()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Non-existent id' => [
|
||||||
|
'id' => 'foo',
|
||||||
|
'exception' => 'Aviat\Ion\Di\Exception\NotFoundException',
|
||||||
|
'message' => "Factory 'foo' does not exist in container. Set that first.",
|
||||||
|
],
|
||||||
|
'Non-existent id 2' => [
|
||||||
|
'id' => 'foobarbaz',
|
||||||
|
'exception' => 'Aviat\Ion\Di\Exception\NotFoundException',
|
||||||
|
'message' => "Factory 'foobarbaz' does not exist in container. Set that first.",
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataSetInstanceWithException
|
||||||
|
*/
|
||||||
|
public function testSetInstanceWithException($id, $exception, $message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->container->setInstance($id, NULL);
|
||||||
|
}
|
||||||
|
catch(ContainerException $e)
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf($exception, $e);
|
||||||
|
$this->assertEquals($message, $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetNew()
|
||||||
|
{
|
||||||
|
$this->container->set('footest', function($item) {
|
||||||
|
return new FooTest($item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check that the item is the container, if called without arguments
|
||||||
|
$footest1 = $this->container->getNew('footest');
|
||||||
|
$this->assertInstanceOf('Aviat\Ion\Di\ContainerInterface', $footest1->item);
|
||||||
|
|
||||||
|
$footest2 = $this->container->getNew('footest', ['Test String']);
|
||||||
|
$this->assertEquals('Test String', $footest2->item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetContainerInInstance()
|
||||||
|
{
|
||||||
|
$this->container->set('footest2', function() {
|
||||||
|
return new FooTest2();
|
||||||
|
});
|
||||||
|
|
||||||
|
$footest2 = $this->container->get('footest2');
|
||||||
|
$this->assertEquals($this->container, $footest2->getContainer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetNewReturnCallable()
|
||||||
|
{
|
||||||
|
$this->container->set('footest', function($item) {
|
||||||
|
return function() use ($item) {
|
||||||
|
return $item;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check that the item is the container, if called without arguments
|
||||||
|
$footest1 = $this->container->getNew('footest');
|
||||||
|
$this->assertInstanceOf('Aviat\Ion\Di\ContainerInterface', $footest1());
|
||||||
|
|
||||||
|
$footest2 = $this->container->getNew('footest', ['Test String']);
|
||||||
|
$this->assertEquals('Test String', $footest2());
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetSet()
|
public function testGetSet()
|
||||||
{
|
{
|
||||||
$container = $this->container->set('foo', function() {
|
$container = $this->container->set('foo', function() {
|
||||||
@ -61,6 +162,8 @@ class ContainerTest extends \Ion_TestCase {
|
|||||||
|
|
||||||
$this->assertInstanceOf('Aviat\Ion\Di\Container', $container);
|
$this->assertInstanceOf('Aviat\Ion\Di\Container', $container);
|
||||||
$this->assertInstanceOf('Aviat\Ion\Di\ContainerInterface', $container);
|
$this->assertInstanceOf('Aviat\Ion\Di\ContainerInterface', $container);
|
||||||
|
|
||||||
|
// The factory returns a callable
|
||||||
$this->assertTrue(is_callable($container->get('foo')));
|
$this->assertTrue(is_callable($container->get('foo')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,13 +29,13 @@ class Ion_TestCase extends PHPUnit_Framework_TestCase {
|
|||||||
protected static $staticContainer;
|
protected static $staticContainer;
|
||||||
protected static $session_handler;
|
protected static $session_handler;
|
||||||
|
|
||||||
public static function setUpBeforeClass()
|
/*public static function setUpBeforeClass()
|
||||||
{
|
{
|
||||||
// Use mock session handler
|
// Use mock session handler
|
||||||
$session_handler = new TestSessionHandler();
|
$session_handler = new TestSessionHandler();
|
||||||
session_set_save_handler($session_handler, TRUE);
|
session_set_save_handler($session_handler, TRUE);
|
||||||
self::$session_handler = $session_handler;
|
self::$session_handler = $session_handler;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
@ -86,7 +86,12 @@ class Ion_TestCase extends PHPUnit_Framework_TestCase {
|
|||||||
// Set up DI container
|
// Set up DI container
|
||||||
$di = require('di.php');
|
$di = require('di.php');
|
||||||
$container = $di($config_array);
|
$container = $di($config_array);
|
||||||
$container->set('session-handler', self::$session_handler);
|
$container->set('session-handler', function() {
|
||||||
|
// Use mock session handler
|
||||||
|
$session_handler = new TestSessionHandler();
|
||||||
|
session_set_save_handler($session_handler, TRUE);
|
||||||
|
return $session_handler;
|
||||||
|
});
|
||||||
|
|
||||||
$this->container = $container;
|
$this->container = $container;
|
||||||
}
|
}
|
||||||
@ -111,7 +116,7 @@ class Ion_TestCase extends PHPUnit_Framework_TestCase {
|
|||||||
['Zend\Diactoros\ServerRequestFactory', 'fromGlobals'],
|
['Zend\Diactoros\ServerRequestFactory', 'fromGlobals'],
|
||||||
array_merge($default, $supers)
|
array_merge($default, $supers)
|
||||||
);
|
);
|
||||||
$this->container->set('request', $request);
|
$this->container->setInstance('request', $request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End of Ion_TestCase.php
|
// End of Ion_TestCase.php
|
33
tests/di.php
33
tests/di.php
@ -12,20 +12,27 @@ use Aviat\Ion\Di\Container;
|
|||||||
// Setup DI container
|
// Setup DI container
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
return function(array $config_array = []) {
|
return function(array $config_array = []) {
|
||||||
$container = new Container([
|
$container = new Container();
|
||||||
'config' => new Config($config_array),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create Request/Response Objects
|
$container->set('config', function() {
|
||||||
$request = ServerRequestFactory::fromGlobals(
|
return new Config([]);
|
||||||
$_SERVER,
|
});
|
||||||
$_GET,
|
|
||||||
$_POST,
|
$container->setInstance('config', new Config($config_array));
|
||||||
$_COOKIE,
|
|
||||||
$_FILES
|
$container->set('request', function() {
|
||||||
);
|
return ServerRequestFactory::fromGlobals(
|
||||||
$container->set('request', $request);
|
$_SERVER,
|
||||||
$container->set('response', new Response());
|
$_GET,
|
||||||
|
$_POST,
|
||||||
|
$_COOKIE,
|
||||||
|
$_FILES
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$container->set('response', function() {
|
||||||
|
return new Response();
|
||||||
|
});
|
||||||
|
|
||||||
// Create session Object
|
// Create session Object
|
||||||
$container->set('session', function() {
|
$container->set('session', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user