Pimple? Не… Не слышал

от автора

Удивительно, что на Хабре всё ещё нет статей об этом гениальном DI контейнере для PHP.
Почему гениальном? Потому, что весь код этого творения укладывается в 80 строк – маленький объект с большими возможностями.
Контейнер представляет из себя один класс, и его подключение в проект выглядит следующим образом:

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

Создание контейнера так же просто:

$container = new Pimple(); 

Как и многие другие DI контейнеры, Pimple поддерживает два вида данных: сервисы и параметры.

Объявление параметров

Объявить параметры в Pimple очень просто: используем контейнер как простой массив:

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

Объявление сервисов

Сервис — некий объект, часть системы, которая выполняет свою конкретную задачу.
Например, сервисами могут являться: объект, предоставляющий соединение с базой данных, отвечающий за отправку почты, шаблонизацию выводимых данных и т.д.
В Pimple сервисы определяются как анонимные функции, возвращающие объект сервиса:

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

Обратите внимание, что анонимная функция имеет доступ к текущему контейнеру и это позволяет использовать в ней другие параметры или сервисы.
Объекты сервиса создаются только при обращении к ним, так что порядок объявления не имеет никакого значения.
Пользоваться созданными сервисами так же просто:

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

Объявление сервисов «Синглтонов»

По умолчанию при каждом вызове Pimple возвращает новый объект сервиса. Если же требуется один экземпляр на всё приложение, всё, что вам необходимо сделать – обернуть объявление в метод share():

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

Объявление функций

Так как Pimple рассматривает все анонимные функции как объявление сервисов, то для объявления именно функций в контейнере необходимо лишь обернуть всё это дело в метод protect():

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

Изменение сервисов после их объявления

В некоторых случаях может понадобиться изменение поведения уже объявленного сервиса. Тогда можно использовать метод extend() для регистрации дополнительного кода, который будет выполнен сразу же после создания сервиса:

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

Первым параметром в данную функцию передается имя сервиса, которое нужно дополнить, а вторым – функция, принимающая в качестве аргументов объект сервиса и текущий контейнер. В итоге при обращении к сервису получается объект, возвращаемый данной функцией.
Если же сервис был «Синглтоном», необходимо повторно обернуть код дополнения сервиса методом share(), иначе дополнения будут вызываться каждый раз при обращении к сервису:

$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; })); 

Доступ к функции, возвращающей сервис

Каждый раз, когда вы обращаетесь к сервису, Pimple автоматически вызывает функцию его объявления. Если же требуется получить прямой доступ именно к функции объявления, можно использовать метод raw():

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

Повторное использование готового контейнера

Если вы от проекта к проекту используете одни и те же библиотеки, вы можете создать готовые контейнеры для повторного использования. Всё, что нужно сделать – это расширить класс Pimple:

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

И вы можете с лёгкостью использовать данный готовый контейнер внутри другого контейнера:

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

Заключение

Управление зависимостями — одна из важнейших и в то же время трудных задач в разработке веб-приложений. Большинство фреймворков предлагают собственные решения данной проблемы. Однако в случае использования фреймворков без менеджера зависимостей или проектирования архитектуры приложения без фреймворков, в качестве DI контейнера я бы однозначно выбрал Pimple.

P.S. Примеры использования — перевод официального readme Pimple.

ссылка на оригинал статьи http://habrahabr.ru/post/199296/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *