В последней части моей серии, мы опустошили класс Simplex\\Framework расширив его класс из Symfony HttpKernel. Смотря на этот пустой класс, вы возможно хотели бы перенести код из фронт-контроллера в него:

Фронт-контроллер стал бы более лаконичен:

Имея более лаконичный фронт-контроллер означает, что вы можете использовать более, чем для одного приложения. Почему это могло быть полезным? Это позволило бы вам иметь различные настройки для среды разработки и боевого окружения. В окружении разработки, вы, возможно, хотели бы видеть отчет об ошибках и чтобы они отображались в браузере, для упрощения отладки:

… но в тоже время вы бы не хотели переносить эту конфигурацию на боевое окружение. Имея 2 различный фронт-контроллера дает вам возможность иметь слегка различные конфигурации каждого из них.

Что ж, переместим код из фронт-контроллера в наш фреймворк, сделая фреймворк более настраиваемым, но в тоже время, это добавит нам парочку проблем:

  • Мы не сможем зарегистрировать кастомного слушателя, т.к. диспетчер не доступен вне класса фреймворка (простым решением может быть предоставления метода Framework::getEventDispatcher());
  • Мы потерям нашу гибкость; мы не сможем изменить реализации UrlMatcher или ControllerResolver;
  • По сравнению с предыдущей версией, мы не сможем тестировать наш фреймворк также легко;
  • Мы не сможем изменить кодировку передаваему ResponseListener (временным решением может стать передача в конструктор в виде аргумента).

Предыдущий код не имел тех же проблем, потому что мы использовали “внедрение зависимостей”; все зависимости нашего фрейморка были внедрены в своих конструкторах (например, диспетчер событий был внедрен во фреймворк, таким образом мы имели полный контроль на его созданием и настройкой).

Означает ли это что бы должка выбирать между гибкостью, настраиваемостью, легким тестированием и возможностью не копи-пастить часть кода из фронт-контроллера в каждом приложении? Как вы могли догадываться, есть решение. Мы можем решить все эти проблему, более того несколько других, используя Dependency Injection Container DIC (контейнер внедрений зависимостей):

Создадим новый файл для размещения конфигурации DIC:

Задача этого файла – настройка зависимостей ваших объектов. Не создается ни один объект на этом этапе конфигурации. Это чисто статическое описание объектов, которыми вы манипулируете и создаете. Объекты будут созданы по требованию, когда вы вызовите его из контейнера или когда контейнеру понадобиться создать какой-либо объект.

Например, чтобы создать слушателя маршрутов, мы говорим Symfony что имя класса Symfony\Component\HttpKernel\EventListener\RouterListeners, и что его конструктор принимает объект new Reference(‘matcher’). Как вы могли заметить, каждый объект имеет уникальное имя. Именя позволяют нам получать объекты и ссылать на них в других определения объектах.

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

Теперь фронт-контроллер выглядит так:

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

Если вы хотите использовать легковестный контейнер, например Pimple, простой DIC в 60 строк кода.

(60 строк кода было в 1 версии, сейчас Pimple уже дорос до 3, но объем кода практически не изменился).

Теперь, как мы регестрируем кастомных слушателей во фронт-контроллере:

Кроме описания ваших объектов, DIC можно также настроить используя параметры. Давайте создадим, который так и делаем, если мы находимся в режиме отладки:

Этот параметр, может быть использован при определении объекта. Давайте создадим настройку кодировки:

После этого изменения, вы должны установить кодировку до использования слушателя ответа:

Вместо того, чтобы полагаться на соглашения как определять переменную $routes, давайте используем еще раз параметры:

Это относиться и к фронт-контроллеру:

Мы очевидно только слегка прикоснулись к тому на что способен контейнер: от имени классов в качестве параметров, до переопределиния существующих классов, от поддержки возможности для демпинга контейнер до простого класса PHP, и т.д.

We have obviously barely scratched the surface of what you can do with the container: from class names as parameters, to overriding existing object definitions, from scope support to dumping a container to a plain PHP class, and much more.

DIC это крутая штука, которая может управлять любыми PHP классами.

Не говорите мне, что вы не хотите использовать DIC в вашем фреймворке. Если вы не любите его, не используйте. Это ваш фреймворк, не мой.

Это последняя часть моей серии статей о создании фреймворка используя компоненты Symfony2. Я знаю что многое осталось не рассмотренным, но я надеюсь это дало вам достаточно информации для начала и лучшего понимания, что находиться у Symfony2 под капотом.

Если вы хотете разобраться глубже, я настоятельно рекомендую вам посмотреть исходник мини-фреймворка Silex, особенно класс Application.

Удачи!