Проверку, может ли пользователь иметь доступ к тому или иному контроллеру, мы можем реализовать 2-мя способами:
Первый способ, возможно, чуть легче, однако в обучающих целях мы выбрали второй способ.
Взглянем на файл shop/www/index.php. Если вы помните, то именно на этот файл index.php перенаправляются все запросы к нашему приложению.
<?php require_once(dirname(__FILE__) . '/../setup.php'); require_once('limb/web_app/src/lmbWebApplication.class.php'); $application = new lmbWebApplication(); $application->process(); ?>
index.php по сути подключает настроечный файл setup.php и запускает объек класса lmbWebApplication. lmbWebApplication - это центральный класс, само приложение. С него начинается обработка запроса пользователя.
Рассмотрим, что представляет из себя lmbWebApplication:
Файл limb/web_app/src/lmbWebApplication.class.php:
<?php lmb_require('limb/filter_chain/src/lmbFilterChain.class.php'); lmb_require('limb/core/src/lmbHandle.class.php'); class lmbWebApplication extends lmbFilterChain { function __construct() { $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbUncaughtExceptionHandlingFilter')); $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbSessionStartupFilter')); $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbRequestDispatchingFilter', array(new lmbHandle('limb/web_app/src/request/lmbRoutesRequestDispatcher')))); $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbResponseTransactionFilter')); $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbActionPerformingFilter')); $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbViewRenderingFilter')); } } ?>
lmbWebApplication - это цепочка фильтров, содержащая какой-то предопределенный набор фильтров. lmbWebApplication - это класс-пример, годный только для очень простых приложений. В реальных приложениях мы всегда используем свои собственные подобные классы.
Кратко, что такое lmbHandle - это класс, который используется для отложенной инициализации объектов. Это позволяет экономить на включении и парсинге php-кода в случаях, когда до некоторых объектов «не доходит очередь». Подробнее о классе lmbHandle.
Теперь пару слов о том, что такое цепочка фильтров. Если вы знакомы с шаблоном проектирования Intercepting Filter, то понять идею не составит большого труда.
Итак, фильтры являются своеобразными расширениями ядра системы, их них состоит самая «верхняя» (начальная) часть приложения, созданного на базе Limb. Фильтры выполняют действия, характерные для большинства приложений, но обычно не имеющих никакого отношения к предметной области приложения (различные вспомогательные действия). Например, фильтры могут стартовать сессию, разбирать запрос, проверять права доступа, выдавать закешированный вариант страницы, регистрировать посещение страницы в статистике и т.д. То есть фильтры - это своеобразный FrontController, только в Limb он не имеет формы единого класса, а разделен на составляющие, любую из которых можно заменить. Количество фильтров различно от приложения к приложению.
Внутренняя логика каждого фильтра зависит от ситуации. Фильтры самостоятельно принимают решение о том, передавать управление следующим фильтрам или нет. Это позволяет обрывать нормальный ход работы приложения и, например, переносить пользователей на определенные страницы сайта. Этими особенностями мы воспользуемся при реализации нашего фильтра по проверке прав доступа к объектам.
Цепочка фильтров реализована в виде пакета FILTER_CHAIN.
Большинство стандартных фильтров можно найти в пакете WEB_APP limb/web_app/src/filter. Вот список таких фильтров c небольшими описаниями:
Фильтр | Назначение |
---|---|
lmbResponseTransactionFilter | Отсылает данные (заголовки и контент) браузеру после того, как большинство остальных фильтров отработали. Необходим, так как логика приложения может требовать обнуления промежуточно сформированных данных и передачи совсем другого результата браузеру. |
lmbSessionStartupFilter | Настраивает драйвер хранилища сессионных данных и стартует сессию |
lmbUncaughtExceptionHandlingFilter | Позволяет обрабатывать ошибки, возникшие в системе и выводить пользователю более «мягкие варианты» |
lmbRequestDispatchingFilter | В этом фильтре определяется текущий контроллер и действие. |
lmbActionPerformingFilter | Запускает команду, которая соответствует текущему контроллеру и действию |
lmbViewRenderingFilter | Запускает процесс рендеринга шаблона, который был установлен в результате выполнения действия контроллера |
Остался один момент - как это все собирается в единое целое:
За первые три пункта этого списка, как вы наверное уже догадались, отвечают соответствующие фильтры:
Сменив или расширив любой из этих фильтров, мы можем изменить любой компонент Limb-приложения:
Теперь последний вопрос: как различные компоненты знают друг о друге? Для этих целей существует тулкит!
Тулкит содержит:
Так как тулкит может быть получен в любом месте системы - значит все эти объекты также доступны везде в приложении!
Поэтому:
Теперь эти знания мы можем использовать для реализации нашего фильтра AccessFilter, проверяющего права доступа к панели управления нашим магазином.
Класс фильтра должен реализовывать интерфейс lmbInterceptingFilter (limb/filter_chain/src/lmbInterceptingFilter.interface.php).
Приведем код AccessFilter:
Файл shop/src/filter/AccessFilter.class.php:
<?php lmb_require('limb/filter_chain/src/lmbInterceptingFilter.interface.php'); class AccessFilter implements lmbInterceptingFilter { function run($filter_chain) { $toolkit = lmbToolkit :: instance(); $user = $toolkit->getUser(); $controller = $toolkit->getDispatchedController(); if(!$controller) throw new lmbException('Controller is not dispatched yet!'); if(!$this->_isAdminPage($controller) || ($user->getIsLoggedIn() && $user->getIsAdmin())) { $filter_chain->next(); return; } else $toolkit->renderView('not_allowed.html'); } protected function _isAdminPage($controller) { return ($controller->getName() == 'admin') || (strpos($controller->getName(), 'admin_') !== false); } } ?>
Наш AccessFilter реализован следующим образом:
Для отображения сообщения о том, что страница недоступна мы также создали шаблон not_allowed.html.
Файл shop/template/not_allowed.html:
<core:set title='Restricted page'/> <core:wrap file="page.html" in="content"> <dl class='restricted'> <dt> <img src='/images/stop.gif'/> This page is restricted for you.</dt> <dd>Try to login as an administrator if you are not logged in yet.</dd> </dl> </core:wrap>
Так, теперь фильтр и шаблон у нас есть - необходимо внедрить его в наше приложение. Для этого нам придется вмешаться в файл shop/www/index.php
Мы не будем создавать новый класс цепочки фильтров, а просто перепишем файл shop/www/index.php следующим образом:
<?php require_once(dirname(__FILE__) . '/../setup.php'); lmb_require('limb/filter_chain/src/lmbFilterChain.class.php'); lmb_require('limb/core/src/lmbHandle.class.php'); $application = new lmbFilterChain(); $application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbUncaughtExceptionHandlingFilter')); $application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbSessionStartupFilter')); $application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbRequestDispatchingFilter', array(new lmbHandle('limb/web_app/src/request/lmbRoutesRequestDispatcher')))); $application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbResponseTransactionFilter')); $application->registerFilter(new lmbHandle('src/filter/AccessFilter')); $application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbActionPerformingFilter')); $application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbViewRenderingFilter')); $application->process(); ?>
Обратите внимание, что мы поместили наш фильтр AcessFilter после lmbResponseTransactionFilter. Это необходимо, так как lmbResponseTransactionFilter занимается непосредственной отсылкой ответа приложения браузеру. Вернее lmbResponseTransactionFilter завершает так называемую транзакцию класса lmbHttpResponse.
lmbHttpResponse аккумулирует все данные, которые должны были бы поступить браузеру, до самого последнего момента, и отсылает их только если вызывается метод lmbHttpResponse :: commit(). Это позволяет полностью очистить содержимое lmbHttpResponse в любое момент и начать формировать другой ответ.
Попробуйте теперь вновь зайти на страницу /admin_product, не проходя при этом процедуры аутентификации. Если вы все сделали правильно, то увидете следующую страницу:
Что ж, панель управления защищена. Теперь нам необходимо сделать только отображение данных для текущего пользователя в зоне profile слева на страницах сайта.
Итак, следующий шаг: Шаг4.4 Отображение профайла пользователя
Обсуждение