Pimple? No, have not heard

It is surprising that there are still no articles on Habré about this ingenious DI container for PHP.
Why brilliant? Because the whole code of this creation fits into 80 lines - a small object with great capabilities.
The container is one class, and its connection to the project is as follows:

require_once '/path/to/Pimple.php';

Creating a container is just as easy:

$container = new Pimple();

Like many other DI containers, Pimple supports two types of data: services and parameters.


Declare Parameters


Declaring parameters in Pimple is very simple: use the container as a simple array:

// Объявляем параметр
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';


Service Announcement


A service is an object, part of a system that performs its specific task.
For example, services can be: an object that provides a connection to a database, which is responsible for sending mail, template data output, etc.
In Pimple, services are defined as anonymous functions that return a service object:

// Объявление сервисов
$container['session_storage'] = function ($c) {
  return new $c['session_storage_class']($c['cookie_name']);
};
$container['session'] = function ($c) {
  return new Session($c['session_storage']);
};

Please note that an anonymous function has access to the current container and this allows you to use other parameters or services in it.
Service objects are created only when accessing them, so the order of the announcement does not matter.
Using created services is just as easy:

// Получение объекта сервиса
$session = $container['session'];
// Предыдущая строка равносильна следующему коду
// $storage = new SessionStorage('SESSION_ID');
// $session = new Session($storage);


Singleton Services Announcement


By default, every time Pimple is called, it returns a new service object. If you need one instance for the entire application, all you need to do is wrap the declaration in the share () method:

$container['session'] = $container->share(function ($c) {
  return new Session($c['session_storage']);
});


Function Declaration


Since Pimple considers all anonymous functions as a declaration of services, to declare exactly functions in a container, you just need to wrap this whole thing in the protect () method:

$container['random'] = $container->protect(function () { return rand(); });


Change of services after their announcement


In some cases, you may need to change the behavior of an already advertised service. Then you can use the extend () method to register additional code that will be executed immediately after the service is created:

$container['mail'] = function ($c) {
  return new \Zend_Mail();
};
$container['mail'] = $container->extend('mail', function($mail, $c) {
  $mail->setFrom($c['mail.default_from']);
  return $mail;
});

The first parameter to this function is the name of the service that needs to be supplemented, and the second is the function that takes the service object and the current container as arguments. As a result, when accessing the service, the object returned by this function is obtained.
If the service was “Singleton”, you must re-wrap the code of the service add-on with the share () method, otherwise the add-ons will be called every time you access the service:

$container['twig'] = $container->share(function ($c) {
  return new Twig_Environment($c['twig.loader'], $c['twig.options']);
});
$container['twig'] = $container->share($container->extend('twig', function ($twig, $c) {
  $twig->addExtension(new MyTwigExtension());
  return $twig;
}));


Access function returning service


Each time you access the service, Pimple automatically calls the function of its announcement. If you want to get direct access to the declaration function , you can use the raw () method:

$container['session'] = $container->share(function ($c) {
  return new Session($c['session_storage']);
});
$sessionFunction = $container->raw('session');


Reuse of the finished container


If you use the same libraries from project to project, you can create ready-made containers for reuse. All you need to do is extend the Pimple class:

class SomeContainer extends Pimple
{
  public function __construct()
  {
    $this['parameter'] = 'foo';
    $this['object'] = function () { return stdClass(); };
  }
}

And you can easily use this ready-made container inside another container:

$container = new Pimple();
// Объявление сервисов и параметров основного контейнера
// ...
// Вставка другого контейнера
$container['embedded'] = $container->share(function () { return new SomeContainer(); });
// Конфигурация встроенного контейнера
$container['embedded']['parameter'] = 'bar';
// И его использование
$container['embedded']['object']->...;


Conclusion


Dependency management is one of the most important and at the same time difficult tasks in developing web applications. Most frameworks offer their own solutions to this problem. However, in the case of using frameworks without a dependency manager or designing an application architecture without frameworks, I would definitely choose Pimple as a simple and small DI container.

PS Usage examples - translation of the official readme Pimple .

Also popular now: