CodeIgniter: добавляем фильтр. Часть 1

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

Область применения - самая разная: различные проверки прав, xss фильтрация, установка каких-либо полей класса, загрузка и инициализация моделей/библиотек/хелперов и прочего.

Итак, задача: ввести метод(назовём его _filter) в контроллер, который будет вызываться сразу после конструктора, перед какими-либо другими. Единственным аргументом _filter будет строка - метод который будет вызван после фильтра фреймворком. При этом вмешиваться в код фреймворка CodeIgniter нельзя.

Как оказывается, есть нечто отдаленно похожее на фильтры в yii и в codeIgniter-е. Это хуки (hooks). В общем это нечто вроде "стадий" - прохождения вэб-приложением цикла жизни с возможностью вызвать свои функции. В доках можно подробнее прочитать, из всех хуков нам интересен только 1 - post_controller_constructor.

В первую очередь надо включить поддержку хуков в главном конфиге фреймворка(system/application/config/config.php):
[cc lang="php" line_numbers="false"]
....
$config['enable_hooks'] = TRUE;
....
[/cc]

Далее, в конфиге хука(system/application/config/hooks.php) описываем его:
[cc lang="php" line_numbers="false"]
$hook['post_controller_constructor'][] = array(
'function' => 'pre_method_filter',
'filename' => 'pre_method_filter.php',
'filepath' => 'hooks',
'params' => array()
);
[/cc]

Теперь создаём файл который указали в конфиге(system/application/hooks/pre_method_filter.php) и описываем хук функцию. Она получает экземпляр объекта CI и вызывает наш метод _filter с аргументом. Аргумент это всего лишь второй кусочек(/first/second/third/) пути после роутинга.
[cc lang="php"]
_filter($CI->uri->rsegment(2, ''));
}
?>
[/cc]

Я предполагаю, что класс стандартного контроллера(Controller) расширен как библиотека(по дефолту это будет называется MY_Controller). Причин расширения стандартного контроллера уйма. Если вы ещё не сделали этого - самое время.

Теперь создаём метод _filter(). У меня он пустой, но, конечно, может содержать код, который будет использоваться во всех контроллерах(например аунтенфикация).
[cc lang="php"]
abstract class MY_Controller extends Controller {
. . . . . . . .
/**
* Фильтр.
* При необходимости метод можно перегрузить в потомке.
* Вызывается до метода класса после конструктора
*/
public function _filter($method) { }
. . . . .
[/cc]
Методу пришлось дать паблик доступ - будет вызываться из функции pre_method_filter.

На этом всё. Если мы хотим чтоб у нашего контроллера был фильтр, просто перегружаем этот метод, например так:
[cc lang="php"]
class Moderate extends MY_Controller {
/**
* Перегруженный фильтр
* @param string $method имя вызываемого метода
*/
public function _filter($method) {
if ($this->isGuest()) {
redirect($this->SiteConfig('redirect', 'need_login'));
}
if ($method == 'post' && $this->checkFlood()) {
die('Замечен флуд!');
}
}
}
[/cc]
Если посетитель - гость, то отправляем его на страницу логина. И если будет вызывается некий post(отправка сообщений) и функция проверки на флуд вернёт истину то скрипт завершиться.

Это - первый пример использования который пришёл в голову, на самом деле же, если понятно для чего фильтр то он без дела не останется :-)

Наконец-то руки дошли разобраться,
когда в очередной раз придумывал костыль
с _remap() и уже собрался патчить CI