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?
3 Answers 3
Why are you defining the properties as static? I would absolutely not do that. It means two instantiations of the Container will have the same services.
I think that pimple ( http://pimple-project.org/ ) is the version used in symfony 2. might be worth checking out.
-
1\$\begingroup\$ Nope, it's not used in Symfony2. It is used in Silex. And I agree with you, it's definitely a simple and good existing solution for the problem the OP is trying to solve. \$\endgroup\$igorw– igorw2011年10月07日 20:39:21 +00:00Commented Oct 7, 2011 at 20:39
You should read the following series of articles: http://fabien.potencier.org/article/11/what-is-dependency-injection
Additionally, the resulting component (PHP 5.2+) is there: http://components.symfony-project.org/dependency-injection/
-
\$\begingroup\$ The dependecy injection container from symfony is PHP 5.3+ \$\endgroup\$catalin.costache– catalin.costache2012年11月14日 17:02:24 +00:00Commented Nov 14, 2012 at 17:02