5
\$\begingroup\$

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?

\$\endgroup\$

3 Answers 3

6
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

I think that pimple ( http://pimple-project.org/ ) is the version used in symfony 2. might be worth checking out.

\$\endgroup\$
1
  • 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
    Commented Oct 7, 2011 at 20:39
1
\$\begingroup\$

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/

\$\endgroup\$
1
  • \$\begingroup\$ The dependecy injection container from symfony is PHP 5.3+ \$\endgroup\$ Commented Nov 14, 2012 at 17:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.