Создаем свой Фреймворк на компонентах symfony2. Часть 9

У нас все еще отсутствует очень важное качество любого хорошего фреймворка: расширяемость. Расширяемость — значит, что любой программист может без проблем внедрить свой функционал в наш фреймворк и изменить способ обработки запроса.

О каком внедрении функционала я говорю? Например аутентификация, или кэширование инстансов. Причем внедрение функционала должно быть plug-and-play. По такому принципу работают например Drupal и WordPress. Часто эта возможность даже предусмотрена на уровне языка программирования, например WSGY в Python или Rack в Ruby.

Так как в PHP ничего подобного на уровне языка не предусмотрено, мы будем использовать хорошо известный шаблон Наблюдатель (Observer). В Symfony2 компонент EventDispatcher реализует упрощенную версию этого шаблона.

Как это работает? Диспетчер (dispatcher) — центральный объект системы, который уведомляет объекты-слушатели о произошедших событиях. Другими словами, ваш код сообщает диспетчеру о событии, диспетчер оповещает о событии всех слушателей, и каждый слушатель делает с этим событием что хочет.

Например, давайте создадим слушателя, который добавляет код Google Analytics ко всем ответам. Что бы сделать это, наш фреймворк должен передать событие прямо перед возвратом объекта response.

Каждый раз как фреймворк обрабатывает Запрос, передается событие ResponseEvent.

Последний шаг — создание Диспетчера во фронт-контроллере, и регистрация слушателя для события response.

Как видите, addListener() ассоциирует PHP callback объект с событием (response). Название события должно быть тем же самым, что использовалось в вызове dispatch().

В Слушателе мы добавляем код Google Analytics только если ответ не переадресация, запрошенный формат HTML и тип данных ответа HTML (эти условия отлично демонстрируют насколько легко работать с данными Запроса и Ответа в коде).

Пока все хорошо, но давайте добавим еще одного слушателя к этому событию. Допустим, я хочу задать заголовок Content-Length в ответе, если он еще не задан.

Правильная работа этого кода зависит от того, добавите вы его до регистрации предыдущего слушателя или после. Иногда приоритет слушателей очень важен, однако по-умолчанию все слушатели имеют приоритет — 0. Для того, что бы Диспетчер запустил слушателя раньше — измените приоритет на положительное число. Отрицательные числа используются для задания низкого приоритета. Так как мы хотим чтобы слушатель Content-Length запускался последним, изменим его приоритет на -255.

Давайте немного порефакторим код. Вынесем слушателя Google в отдельный класс:

Сделаем тоже самое с другим слушателем:

Наш Front Controller должен выглядеть так:

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

Подписчики знают обо всех событиях, которые им интересны и передают эту информацию Диспетчеру методом getSubscribedEvents(). Посмотрим на новую версию GoogleListener:

Соответственно новая версия ContentLengthListener:

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

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

К содержанию >>
Оригинал статьи на английском языке >>
Исходный код из статьи >>

Не забудьте поделиться статьей с друзьями

Подписывайтесь на меня в соц. сетях

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

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