I'm building a web application and is trying to make my code reusable. I've decided to create components that can be used outside this project.
Now I'm trying to create a simple DI container, one class, and I would like some help checking it out. I'm thinking to do something like this:
class Container
{
protected static $_services = array();
protected static $_shared = array();
/**
* Create and store a new service in the container.
*
* @param string $service Service identifier name.
* @param mixed $params Service parameters, array or a Closure object.
* @return object Ramverk\Framework\Container.
*/
public function add($service, $params)
{
if (!is_object($params) && !is_array($params)) {
throw new Exception("Service params must be a Closure object or an array.");
}
static::$_services[$service] = $params;
return $this;
}
/**
* Get a shared service object, will return the same object each time.
*
* @param string $service Name of the shared service to get.
* @return object Shared service object.
*/
public function get($service)
{
if (!array_key_exists($service, static::$_shared)) {
static::$_shared[$service] = $this->getNew($service);
}
return static::$_shared[$service];
}
/**
* Get a new instance of an existing service.
*
* @param string $service Name of the service to create.
* @return object Create service object.
*/
public function getNew($service)
{
// Make sure service has been added
if (!array_key_exists($service, static::$_services)) {
throw new Exception("Service '$service' not found");
}
// get container service
$container = static::$_services[$service];
// If service is wrapped in a Closure we invoke it
if (is_object($container)) {
return $container();
}
// Make sure we have a class to work with
if (!isset($container['class'])) {
throw new Exception("A class must be set in $service service");
}
// Get service key and remove key
$class = $container['class'];
unset($container['class']);
// Check if this service uses adapters
if (array_key_exists('adapter', $container)) {
$config = (array) $container['adapter'];
if (!isset($config['class'])) {
throw new Exception("An adapter class must be set in $service service");
}
// Grab adapter name and remove unwanted the keys
$adapter = $config['class'];
unset($config['class'], $container['adapter']);
// Create the instance and return it
return new $class(new $adapter($config));
}
// Create class instance and pass parameters to constructor
return new $class($container);
}
}
Adding a new service must contain a name (id) and either a Closure object or an array. The following will create and return the same thing:
$container = new Container();
$container->add('session', function() {
$config = array(
'name' => 'my_session',
'expires' => '4 hours',
);
$storage = new Session\Adapter\Native($config);
return new Session($storage);
});
// - Equals to:
$container->add('session', array(
'class' => 'Session',
'adapter' => array(
'class' => 'Session\Adapter\Native',
'name' => 'my_session',
'expires' => '4 hours',
),
));
// Now I get the shared object with:
$session = $container->get('session');
// Or create a new instance
$session = $container->getNew('session');
Am I doing it right?