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
    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\$ Nov 14, 2012 at 17:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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