Установить можно через packagist.org: granula/inversion либо скачав и добавив к PRS-0 совместимому загрузчику.
$container = new Inversion\Container(); $container['foo'] = 'My\Class\Foo'; // ... $foo = $container('foo');
В вышеприведённом примере показана базовая функциональность контейнера. Разберем что там происходит.
В первой строчки создаем экземпляр контейнера. Во второй создаем ассоциацию между «foo» и сервисом создающим экземпляр класса «My\Class\Foo». Что по другому можно записать так:
$container->addService(new Service('My\Class\Foo'), 'foo');
Имя «foo» идёт вторым, т.к. его вообще можно опустить. Подробнее ниже.
В третей строчке мы получаем экземпляр объекта. Что по другому можно записать так:
$foo = $container('foo'); // или $foo = $container->get('foo'); // или $foo = $container['foo']->get(); // или $foo = $container->getService('foo')->get();
Однако, рекомендую использовать сокращённый вариант, хотя все они допустимы.
Описание зависимостей
По умолчанию когда в контейнер передаётся строка она понимается как имя класса и подставляется в сервис Inversion\Servise.
У данного сервиса есть несколько особенностей и функций.
Первое это отложенная загрузка. Пока вы не будите использовать его, класс не будет загружен.
Второе, вы можете указать зависимость от других сервисов и параметров. Объясню на примере.
Пусть у нас есть класс Bar, который зависит от классов One и Two:
namespace My\Space; class One {} class Two {} class Bar { public function __construct(One $one, Two $two) { } }
Опишем эту зависимость в Inversion:
use Inversion\Service; //... $container['one'] = 'My\Space\One'; $container['two'] = 'My\Space\Two'; $container['bar'] = new Service('My\Space\Bar', array($container['one'], $container['two']));
Теперь при вызове «bar», они будут созданы и подставлены в конструктор. На самом деле можно ещё проще. Если вместо «one» и «two» указать их имена классов:
$container['My\Space\One'] = 'My\Space\One'; $container['My\Space\Two'] = 'My\Space\Two'; $container['My\Space\Bar'] = new Service('My\Space\Bar'); // "new Service" можно опустить
Это удобный способ описывать зависимости при использовании интерфейсов:
namespace My\Space; class One implements OneInterface {} class Two implements TwoInterface {} class Bar implements BarInterface { public function __construct(OneInterface $one, TwoInterface $two) { } }
$container['My\Space\OneInterface'] = 'My\Space\One'; $container['My\Space\TwoInterface'] = 'My\Space\Two'; $container['My\Space\BarInterface'] = 'My\Space\Bar';
Вообще имена интерфейсов, можно опустить. Они будут автоматически получены из классов:
$container[] = 'My\Space\One'; $container[] = 'My\Space\Two'; $container[] = 'My\Space\Bar';
Вот так вот просто.
Однако, нужно понимать что в таком случае классы будут сразу же загружены чтобы получить список интерфейсов через рефлексию. Поэтому лучше указывать имя интерфейса вручную.
Другие виды сервисов
В библиотеке идет несколько сервисов, однако вы можете создать свой имплементировав Inversion\ServiceInterface.
Closure
Класс: Inversion\Service\Closure
Использование:
$container['closure'] = function () use ($container) { return new My\Class(); };
Можно также указать зависимости:
$container['closure'] = function (One $foo, Two $foo) use ($container) { return new My\Class(); };
Так же как и с Inversion\Service можно указать их явно:
$container['closure'] = new Closure(function (One $foo, Two $foo) use ($container) { return new My\Class(); }, array($container['one'], $container['two']));
Factory
Класс: Inversion\Service\Factory
Использование:
$container['factory'] = new Factory('My\ClassFactory', 'create');
Так же можно указать зависимости для конструктора явно третьим параметром.
Object
Класс: Inversion\Service\Object
Использование:
$container['object'] = new My\Class();
или
$container['object'] = new Object(new My\Class());
Prototype
Класс: Inversion\Service\Prototype
Использование:
$container['prototype'] = new Prototype($object);
При каждом вызове будет создана новая копия: clone $object.
Data
Класс: Inversion\Service\Data
Использование:
$container['data'] = new Data('what you want');
По умолчанию все массивы преобразуется в Data сервисы.
$container['data'] = array(...);
Эквивалентно:
$container['data'] = new Data(array(...));
Ссылки на сервисы
Inversion поддерживает ссылки. Что бы получить ссылку обратитесь к контейнеру как к массиву:
$container['foo'] = new Service(...); $ref = $container['foo']; // Ссылка на сервис.
Таким образом можно создать алиас к любому сервису:
$container['My\Class\FooInterface'] = new Service('My\Class\Foo'); $container['foo'] = $container['My\Class\FooInterface']; //... $foo = $container('foo');
Теперь если кто-нибудь перезапишет «My\Class\FooInterface», то «foo» будет по прежнему ссылаться на этот сервис:
//... $container['My\Class\FooInterface'] = new Service('Another\FooImpl'); //... $foo = $container('foo'); // $foo instanseof Another\FooImpl
Можно даже создавать ссылки на ссылки:
$container['foo'] = 'My\Class\Foo'; $container['ref'] = $container['foo']; $container['ref2'] = $container['ref']; $container['ref3'] = $container['ref2']; //... $foo = $container('ref3'); // $foo instanseof My\Class\Foo $name = $container->getRealName('ref3'); // $name == 'foo'
Расширение сервисов
Например если мы хотим расширить какой-нибудь сервис, то такой способ не подойдет т.к. он перезапишет первый:
$container['My\Class\FooInterface'] = 'My\Class\Foo'; //... $container['My\Class\FooInterface'] = function (FooInterface $foo) { $foo->extendSome(...); return $foo; };
В результате будет зацикливание, что бы этого избежать, для расширения используйте следующую функцию:
$container['My\Class\FooInterface'] = 'My\Class\Foo'; //... $container->extend('My\Class\FooInterface'], function (FooInterface $foo) { return new FooDecorator($foo); });
Тесты
Библиотека Inversion полностью тестирована. Тесты находятся в отдельном репозитории (granula/test) вместе с другими тестами гранулы.
Как Singleton
Inversion спроектирована полностью без использования статических методов и синглетонов, однако редко бывает полезно иметь контейнер как синглетон:
$container = Inversion\Container::getInstanse();
Другие реализации
- Symfony Dependency Injection — мощная и тяжёлая библиотека внедрения зависимости. Имеет хорошую документацию.
- Pimple — простой и очень лёгкий (всего один файл) «контейнер» от создателя Symfony.
ссылка на оригинал статьи http://habrahabr.ru/post/169187/
Добавить комментарий