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 {
|
||||
|
||||
/**
|
||||
* Array with class instances
|
||||
* Array of container Generator functions
|
||||
*
|
||||
* @var Callable[]
|
||||
*/
|
||||
protected $container = [];
|
||||
|
||||
/**
|
||||
* Array of object instances
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $container = [];
|
||||
protected $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.
|
||||
*
|
||||
* @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 ContainerException Error while retrieving the entry.
|
||||
* @throws NotFoundException - No entry was found for this identifier.
|
||||
* @throws ContainerException - Error while retrieving the entry.
|
||||
*
|
||||
* @return mixed Entry.
|
||||
*/
|
||||
@ -71,40 +78,88 @@ class Container implements ContainerInterface {
|
||||
|
||||
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))
|
||||
{
|
||||
return $this->applyContainer($item($this));
|
||||
}
|
||||
else
|
||||
{
|
||||
return $item;
|
||||
}
|
||||
// If there isn't already an instance, create one
|
||||
$obj = $this->getNew($id);
|
||||
$this->instances[$id] = $obj;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
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 mixed $value
|
||||
* @param Callable $value - a factory callable for the item
|
||||
* @return ContainerInterface
|
||||
*/
|
||||
public function set($id, $value)
|
||||
public function set($id, Callable $value)
|
||||
{
|
||||
$this->container[$id] = $value;
|
||||
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 false otherwise.
|
||||
*
|
||||
* @param string $id Identifier of the entry to look for.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has($id)
|
||||
@ -114,37 +169,38 @@ class Container implements ContainerInterface {
|
||||
|
||||
/**
|
||||
* Determine whether a logger channel is registered
|
||||
* @param string $key The logger channel
|
||||
*
|
||||
* @param string $id The logger channel
|
||||
* @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
|
||||
*
|
||||
* @param LoggerInterface $logger
|
||||
* @param string $key The logger 'channel'
|
||||
* @param string $id The logger 'channel'
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a logger for the selected channel
|
||||
*
|
||||
* @param string $key The logger to retrieve
|
||||
* @param string $id The logger to retrieve
|
||||
* @return LoggerInterface|null
|
||||
*/
|
||||
public function getLogger($key = 'default')
|
||||
public function getLogger($id = 'default')
|
||||
{
|
||||
return ($this->hasLogger($key))
|
||||
? $this->loggers[$key]
|
||||
return ($this->hasLogger($id))
|
||||
? $this->loggers[$id]
|
||||
: NULL;
|
||||
}
|
||||
|
||||
|
@ -16,43 +16,61 @@
|
||||
|
||||
namespace Aviat\Ion\Di;
|
||||
|
||||
use Interop\Container\ContainerInterface as InteropInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* 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 mixed $value
|
||||
* @param string $id
|
||||
* @param Callable $value - a factory callable for the item
|
||||
* @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
|
||||
* @param string $key The logger channel
|
||||
* @param string $id The logger channel
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasLogger($key = 'default');
|
||||
public function hasLogger($id = 'default');
|
||||
|
||||
/**
|
||||
* Add a logger to the Container
|
||||
*
|
||||
* @param LoggerInterface $logger
|
||||
* @param string $key The logger 'channel'
|
||||
* @param string $id The logger 'channel'
|
||||
* @return Container
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger, $key = 'default');
|
||||
public function setLogger(LoggerInterface $logger, $id = 'default');
|
||||
|
||||
/**
|
||||
* Retrieve a logger for the selected channel
|
||||
*
|
||||
* @param string $key The logger to retreive
|
||||
* @param string $id The logger to retreive
|
||||
* @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\NullHandler;
|
||||
|
||||
class FooTest {
|
||||
|
||||
public $item;
|
||||
|
||||
public function __construct($item) {
|
||||
$this->item = $item;
|
||||
}
|
||||
}
|
||||
|
||||
class FooTest2 {
|
||||
use \Aviat\Ion\Di\ContainerAware;
|
||||
}
|
||||
|
||||
class ContainerTest extends \Ion_TestCase {
|
||||
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$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()
|
||||
{
|
||||
$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\ContainerInterface', $container);
|
||||
|
||||
// The factory returns a callable
|
||||
$this->assertTrue(is_callable($container->get('foo')));
|
||||
}
|
||||
|
||||
|
@ -29,13 +29,13 @@ class Ion_TestCase extends PHPUnit_Framework_TestCase {
|
||||
protected static $staticContainer;
|
||||
protected static $session_handler;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
/*public static function setUpBeforeClass()
|
||||
{
|
||||
// Use mock session handler
|
||||
$session_handler = new TestSessionHandler();
|
||||
session_set_save_handler($session_handler, TRUE);
|
||||
self::$session_handler = $session_handler;
|
||||
}
|
||||
}*/
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
@ -86,7 +86,12 @@ class Ion_TestCase extends PHPUnit_Framework_TestCase {
|
||||
// Set up DI container
|
||||
$di = require('di.php');
|
||||
$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;
|
||||
}
|
||||
@ -111,7 +116,7 @@ class Ion_TestCase extends PHPUnit_Framework_TestCase {
|
||||
['Zend\Diactoros\ServerRequestFactory', 'fromGlobals'],
|
||||
array_merge($default, $supers)
|
||||
);
|
||||
$this->container->set('request', $request);
|
||||
$this->container->setInstance('request', $request);
|
||||
}
|
||||
}
|
||||
// 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
|
||||
// -----------------------------------------------------------------------------
|
||||
return function(array $config_array = []) {
|
||||
$container = new Container([
|
||||
'config' => new Config($config_array),
|
||||
]);
|
||||
$container = new Container();
|
||||
|
||||
// Create Request/Response Objects
|
||||
$request = ServerRequestFactory::fromGlobals(
|
||||
$_SERVER,
|
||||
$_GET,
|
||||
$_POST,
|
||||
$_COOKIE,
|
||||
$_FILES
|
||||
);
|
||||
$container->set('request', $request);
|
||||
$container->set('response', new Response());
|
||||
$container->set('config', function() {
|
||||
return new Config([]);
|
||||
});
|
||||
|
||||
$container->setInstance('config', new Config($config_array));
|
||||
|
||||
$container->set('request', function() {
|
||||
return ServerRequestFactory::fromGlobals(
|
||||
$_SERVER,
|
||||
$_GET,
|
||||
$_POST,
|
||||
$_COOKIE,
|
||||
$_FILES
|
||||
);
|
||||
});
|
||||
|
||||
$container->set('response', function() {
|
||||
return new Response();
|
||||
});
|
||||
|
||||
// Create session Object
|
||||
$container->set('session', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user