В света на PHP има два типа програмисти. Първите са любознателни, търсещи познания и възможно най-елегантни и чисти решения на проблеми в разработките, целящи да превърнат кода си в една хармонична картина, която поражда възхищение, а не безконечни душевни терзания. Вторият тип са мързеливите, вечно бързащи паразити, за които програмирането е близко до някакъв вид миньорска дейност. В сорс кода обаче количество != качество. Прези годините съм разбрал, че в тази област няма положение в което си казваш „Аз съм готов! Няма какво повече да уча!“. Непрекъснато излизат нови методики и подходи, които имат за цел да направят живота на програмиста по-лесен.

В последно време все повече и повече се говори за Dependency Injection(DI) в PHP общността – и има защо. По моя лична преценка, разработката на Zend Framework 2 и Symfony 2 популяризира значително Dependency Injection(в PHP) и хвърли светлина върху важността и големите предимства на този design pattern. Въпреки всичките статии в блоговете, PHP програмистите все още се плашат от тази подход. Ще се опитам да обясня как аз разбирам и използвам DI.

Какво точно е Dependency Injection?

Dependency Injection е design pattern. Много хора не правят разлика между Dependency Injection и Dependency Injection Container. За сега ще се опитам да обясня DI, като за DI Container-и ще си поговорим по-надолу.

Както много други шаблони, така и DI може да се обясни най-лесно с пример. Нека имаме два класа – Keyboard и PersonalComputer.


<?php
class Keyboard
{

}


class PersonalComputer
{

protected $keyboard = null;

public function __construct(Keyboard $keyboard)
{
$this->keyboard = $keyboard;
}

public function getKeyboard()
{
return $this->keyboard;
}

}

<?php
$keyboard = new Keyboard;
$pc = new PersonalComputer($keyboard);

Както се вижда от примера, първо инстанцираме класа Keyboard, след което създаваме обект от тип PersonalComputer. При инстанцирането на PersonalComputer, подаваме като аргумент на конструктора инстанцията на Keyboard. От своя страна конструктора на PersonalComputer капсулира получения обект в protected $keyboard.

Забележете, че в конструктора на PersonalComputer уточняваме какъв тип обект точно ни трябва – инстанция на Keyboard. По този начин сме сигурни, че класа PersonalComputer ще бъде успешно инстанциран тогава и само тогава, когато подадения аргумент $keyboard е обект от тип Keyboard.

Разгледания горе пример представя така наречения constructor injection (инжектиране чрез конструктора). В този случей се казва, че PersonalComputer консумира (consumes) Keyboard.

DI контейнери

Dependency Injection контейнерите са библиотеки, които автоматично инжектират зависими инстанции.

Phemto е DI контейнер за PHP. Ето как ще приложим Phemto с горния пример:

<?php
$injector = new Phemto;
$pc = $injector->create('PersonalComputer');

Контейнерът анализира класа PersonalComputer(използвайки Reflection) и автоматично инжектира нова инстанция на Keyboard.

DI конфигурация

Автоматичното създаване на инстанции е най-фундаменталната възможност на DI контейнерите. В много случеи разработчиците имат нужда да контролират тези инстанции. Универсалното решение е използване на конфигурация във външен файл.

Едни от най-главните опции, които влизат в конфигурацията на повечето DI контейнери са следните:

  • Дефиниране на параметри, които да се подадът на конструктура на даден клас при инстанциране
  • Определяне на опцията shared за клас – ако е true това означава, че даден клас ще се инстанцира само веднъж и всяка едно негово създаване чрез контейнера ще връща една и съща инстанция.
  • Настройка (обикновенно наречена call) за автоматично извикване на setter методи след инстанциране на клас (setter injection е подход подобен на горе споменатият constructor injection, но вместо конструктор се използват setter методи)
  • Дефиниране на конфигурации на инстанции, които се създават в контекста на даден клас (йерархично конфигуриране на инстанции)

В следващата част ще си поговорим по-сериозно за DI конфигурации. Също така ще погледнем някои други теми, които са свързани с DI (като шаблона Service Locator например).