Казалось бы, что наш фреймворк уже достаточно целостный, в какой-то мере так и есть, но давайте все-таки взглянем, что можно было бы улучшить.

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

Обновим определение маршрута соответственно:

Этот шаг очень прост и скоро вы поймете основной замысел, как только вы начнете добавлять страницы. Сейчас вы можете заметит один нежелательный эффект, класс LeapYearController всегда создается, даже если запрашиваемый URL не соответствует маршруту leap_year. Это очень нецелесообразно с точки зрения производительности: теперь на каждый запрос будут инициализированы все контроллеры всех маршрутов. Было бы куда лучше использовать ленивую инициализацию, таким образом инициализация контроллеров будет выполняется «по требованию» соответствующего маршрута.
Чтобы решить эту проблему, и ряд других, давайте установим компонент HttpKernel:

Компонент HttpKernel имеет много интересных функций, но прямо сейчас нам необходима — controller resolver. Она определяет, когда и как выполнятся контроллеру, и какие аргументы ему передать. Все обработчики контроллеров используют следующий интерфейс:

GetController() метод основан на том же соглашении, которое мы приняли ранее: _controller должен содержать контроллер, связанный с запросом. Так же как и встроенные в PHP функции обратного вызова, getController() принимает строки, состоящие из имени класса плюс два двоеточия и имя метода — «класс:: метод»:

Чтобы заставить этот код работать, измените код фреймворка таким образом, чтобы использовать controller resolver от HttpKernel:

Мелочь, а прияно, controller resolver корректно обрабатывает ошибки за вас, например: если вы забыли определить _controller он выдаст соответствующую ошибку.

Теперь давайте посмотрим, как обработчик узнает какие аргументы, необходимы контроллеру. getArguments() разбирает контроллер, чтобы определить, какие аргументы ему передавать используя реверс-инжиниринг PHP Reflection.
Аргументом метода IndexAction() является объект класса Request. Метод getArguments() верно передает аргумент, если корректно определен тип:

Интересный факт то, что в getArguments() так же можно передать любой атрибут класса Request; достаточно чтобы аргумент назывался так же, как и соответствующий атрибут:

Также вы можете одновременно передавать различные атрибуты вместе с объектом класса Request (порядок не важен, так как указывается тип)

И наконец, вы можете определить значения по умолчанию для любого аргумента:

Давайте просто передадим $year в наш контроллер:

controller resolver также заботится о проверке вызова контроллера и его аргументах. В случае возникновения проблемы, он генерирует исключение, с понятным сообщением, объясняющим проблему: контроллер класса не существует, этот метод не определен, аргумент не соответствует атрибуту,…

Если controller resolver настолько гибок из коробки, зачем же придумывать что-то еще, спросите Вы? В Symfony2 есть 2 примера: getController() расширяется для использования методов в качестве сервисов, и в FrameworkExtraBundle, getArguments() расширяется, где атрибуты запроса конвертируются в объект автоматически.

Подведем итоги по нашей новой версии фреймворка:

Задумайтесь на минутку: наш фреймворк стал еще более надежным и гибким, и до сих пор состоит из менее чем 40 строк кода.