Пользовательский ввод в Kohana3 - часть 1

Просмотров: 9433Комментарии: 10
Web frameworks

Сегодня мы посмотрим, как можно обрабатывать пользовательский ввод (данные из форм) в новой версии фреймворка Kohana. Предварительно не забываем обновиться до вышедшего третьего релиз-кандидата.

Сначала разберемся, что нам предоставляет для работы с формами и данными из форм Ko3. Я насчитал четыре штуки хелперов:

•     Kohana_Form
•     Kohana_Security
•     Kohana_Upload
•     Kohana_Validate

В этой части туториала мы не будем рассматривать загрузку файлов на сервер, ограничимся обычными данными из форм. Поэтому Kohana_Upload пока отложим в сторону. Что же мы видим касательно остального? Как минимум, нету знакомой нам по Ko2 библиотеки Input, которая осуществляла XSS-фильтрацию ввода и выдачу в удобном виде суперглобальных массивов данных. Есть хелперы для формирования форм, валидации данных и очистки ввода от потенциально опасных сущностей.

Итак, начнем знакомство. Создадим с помощью хелпера Kohana_Form форму для отправки данных на сервер. Сначала добавим нашему микросайту еще одну страничку:

[code lang="php"]

public function action_form()

{

    $this->template->title = 'Form page';

    $this->template->content = View::factory('elements/form');

    $this->template->navigation = $this->_simple_nav();

       

}

[/code]

Саму форму логично будет сформировать в шаблоне elements/form, но используя (для примера) хелпер Kohana_Form. Его методы банальны до невозможности, они позволяют задать имя элемента, его значение и опционально массив атрибутов (в данном случае обойдемся без них) :

[code lang="php"]

echo 'Write something please:<br />';

echo Form::open();

echo '<div>'.Form::hidden('hidden','form_sent').'</div>';

echo '<div>'.Form::input('text').'</div>';

echo '<div>'.Form::password('pass').'</div>';

echo '<div>'.Form::textarea('area').'</div>';

echo '<div>'.Form::label('chck[0]','item 1');

echo Form::checkbox('chck[0]','1').'<br />';

echo Form::label('chck[1]','item 2');

echo Form::checkbox('chck[1]','2').'</div>';

echo '<div>'.Form::label('radio','Choose:');

echo '<br />';

echo 'A'.Form::radio('radio','A');

echo ' or B'.Form::radio('radio','B').'</div>';

$opts = array (1=>'car', 2=> 'toy', 3=>'ball');

echo Form::select('sel',$opts).'<br />';

echo Form::submit('submit','Send');

echo Form::close();

[/code]

А что мы будем делать с данными из формы? Сначала их нужно прочесть, и займется этим ранее созданный метод контроллера Controller_Welcome::action_form(). Поскольку разработчики не предоставили нам отдельной библиотеки для работы с суперглобальными массивами ввода, придется разбирать их вручную (и это официально благословляется). Вот что мы напишем в начале метода:

[code lang="php"]

$last_input = array();

       

if (Arr::get($_POST, 'hidden') == 'form_sent')

{

    $keys = array ('text','pass','area','chck','radio','sel');

    $last_input = Arr::extract($_POST, $keys, NULL);

           

}

[/code]

Этот кусок кода проверяет, была ли отправлена форма (значение из скрытого поля должно быть выставлено). Если да, то из массива $_POST добываются интересующие нас переменные (массив $keys). Для облегчения нашей участи я использовал два метода весьма полезного хелпера Kohana_Arr: Arr::get(массив, ключ, значение по умолчанию — опционально) для получения одного элемента массива и Arr::extract(массив, массив ключей, значение по умолчанию — опционально) для коллективных разборов. Дальше можно запихать массив полученных переменных в наш шаблон, чтобы иметь возможность на них посмотреть. Заменим

[code lang="php"]

$this->template->content = View::factory('elements/form');

[/code]

на

[code lang="php"]

$this->template->content = View::factory('elements/form')

                                    ->set('last_input', $last_input);

[/code]

А в шаблоне аккуратно выведем этот массив:

[code lang="php"]

if (is_array($last_input) AND ! empty($last_input))

{

    $input = array();

    foreach ($last_input as $var=>$val)

    {

        if ($val)

        {

            $input[] = 'Variable <i>'.$var.'</i> equals: '.$val;   

        }

          

    }

   

    $input = implode('<br />', $input);

   

    echo '<div style="width: 200pt; border-style: solid dotted; border-color: #00ff00;">'.$input.'</div>';        

}

[/code]

Да, при работе с реальным пользовательским вводом крайне желательно выполнять на полученных переменных Security:: xss_clean(строка). Жаль, что метод не принимает массива, но это при необходимости легко исправляется.

Сейчас мы можем полюбоваться тем, что вводим. Чего не хватает? Валидации!

Прикрутим же ее. Пусть нам нужны два обязательных поля: text и radio. Кроме того, поля text и pass должны содержать алфавитно-цифровые символы (не более 10). Пока хватит.

Набросок валидации может быть примерно такой (он срабатывает только когда пришел $_POST):

[code lang="php"]

$valid = Validate::factory($last_input)

                        ->filter('text','trim')

                        ->filter('pass','trim')

                        ->filter('area','trim')

                       

                        ->rules('text',array('not_empty'=>NULL,'alpha_numeric'=>array(TRUE),'max_length'=>array(10)))

                        ->rules('pass',array('alpha_numeric'=>array(TRUE),'max_length'=>array(10)))

                        ->rule('radio','not_empty');

[/code]

Здесь мы инициировали новый «сеанс» валидации, передав в Validate::factory() интересующий нас массив данных. Затем добавили на три поля фильтры, позволяющие отсекать незначащие пробелы (жаль, что нет возможности «повесить» фильтры на избранный массив полей: можно или на одно, или на все имеющиеся — если прописать вместо имени поля TRUE). Кстати, пост-фильтрации в Ko3 нас лишили (меня это не сильно огорчило, поскольку не использовал).

После этого добавили правила для трех полей. Обратите внимание: метод Validate::rule(поле, правило, параметры) принимает параметры в виде массива (если они есть в данной функции). Так что даже если параметр один, придется пихать его массивом. Правилом может быть функция из имеющегося набора (посмотрите класс, там все видно; если нужно, пишите, приведу таблицу).

Несколько более неудобная, на мой взгляд, фишка наблюдается в методе Validate::rules(поле, правила), предназаначенном для назначения полю сразу нескольких правил. В массиве правил ключи должны быть именами функций-правил, а значения — параметрами этих функций. И если функция не принимает параметров (например, правило 'not_empty'), то все равно придется явно прописывать 'not_empty'=>NULL.

Также нужно сказать, что работу с Юникодом в алфавитно-цифровых правилах (alpha*) надо явно включать вот так: 'alpha_numeric'=>array(TRUE). Немного странно, что «заточенная» под UTF Кохана по умолчанию его не валидирует. 

Если валидация не пройдена, запросим массив ошибок:

[code lang="php"]

if ( ! $valid->check())

{

    $errors = $valid->errors();   

}

[/code]

Если вызывается Validate::errors() без параметров, то в массиве будут присутствовать только ошибочные поля и название правила, которое нарушено (по-видимому, первого попавшегося, а не всех нарушенных).

Все накопленные ошибки будем выводить на уже известную нам страницу с формой в специальный блок (в контроллере предварительно добавим к шаблону вывода ->set('errors', $errors)):

[code lang="php"]

if (is_array($errors) AND ! empty($errors))

{

    $input_err = array();

    foreach ($errors as $field=>$val)

    {

        $input_err[] = 'Error in field <i>'.$field.'</i> as: '.$val[0];       

    }

    $input_err = implode('<br />', $input_err);

   

    echo '<div style="width: 200pt; border-style: solid dotted; border-color: #ff0000;">'.$input_err.'</div>';        

}

[/code]

Запустив все это, можем увидеть любопытный и не очень приятный факт: нелатинские символы трактуются в два раза «длиннее», чем латинские (например, на «привет» срабатывает ошибка по 'max_length'=>array(10), а на goodbye она не срабатывает). В общем-то интуитивно это понятно, но осадок остается :( Пока еще не решил, стоит ли как-то править, или оставлять как есть.

Очевидно, сейчас у нас еще далеко не то, что хочется получить (в частности, красивые уведомления об ошибках и заполнение полей формы после неправильного ввода), но пока что остановимся на этом. В следующей части надеюсь продолжить тему форм в Kohana3.

Скачать application

Комментариев: 10 RSS

1 Sezarin 07-09-2009 13:56

...нелатинские символы трактуются в два раза «длиннее», чем латинские...

Кстати, Text::limit_chars() страдает той-же болезнью :(

И когда, наконец, достижения эволюции кодировок освободят нас от этой 'мигрени'?..

2 Slaver 07-09-2009 15:05

И с этим намудрили в KO3 :( Особенно не понятно, почему до сих пор проблемы с символами, если всё на UTF-8.

А для удобной работы с формами в KO2 я использовал модуль Formo. Сейчас остаётся ждать порта в KO3 — надеюсь, автор модуля уже этим занимается smile

3 Brotkin Ivan 07-09-2009 15:46

0. Наверное стоит называть классы Коханы без префикса 'Kohana_', т.к. мы все равно обращаемся к их потомкам с более короткими и удобными именами.

1. TRUE в правила типа alpha надо передавать для включения режима UTF при сравнении. В данном случае вместо ctype_alpha() (которая работает только с латинницей) будет работать регулярка.

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

3. По поводу длины строк. По умолчанию длина строки в Kohana определяется через mb_string(). Для кириллицы длина вдвое больше, чем для латинницы. Чтобы этого избежать, надо в качестве дополнительного параметра указывать кодировку: mb_string('тест', 'UTF-8'); Это недоработка, как мне кажется. Надо бы приаттачить замечание на github.

4. Если правило/callback/фильтр должен отрабатывать для всех полей, можно использовать TRUE вместо имени поля.

5. Меню навигации в контроллере наверное стоит вынести куда-нить в before() или __construct(), чтобы не прописывать в каждом методе.

4 Brotkin Ivan 07-09-2009 16:04

Заглянул в тикеты, создал предложение по поводу длины строк - а там уже есть такое замечание. Более того - в транке уже лежит коммит (http://github.com/kohana/core/commit/ce979e933e538178bdd5be1234fe8fca5b3c60b7), исправляющий данную ситуацию.

5 Александр Купреев 07-09-2009 16:16

Спасибо за дельные комментарии!

2 Slaver

Формо, конечно, хорош, но только до тех пор, увы, пока не нужно перехватывать ввод в контроллере (если там включен плагин mval и он пихает напрямую в модель). Поэтому в моем текущем проекте, где контроллер используется в том числе и для проверки разрешений, я от Формо отказался.

2 Brotkin Ivan

0. Этот туториал рассчитан на не очень искушенных пользователей, поэтому я старался максимально точно называть классы. Но может и откажусь в будущем.

1. Да, но я удивлен почему валидация UTF не стоит по умолчанию.

2. Да. Это, в общем наверное логично, но полезнее было бы выводить сразу все ошибки, чем повторно тыкать юзера в то же поле, но уже указывая на другую ошибку.

3. Я про то же. Можно, конечно, извращаться, но лучше было бы, чтоб поддержка Юникода была даже в этом smile Я задал вопрос на форуме, надеюсь кто-нить из разработчиков прокомментирует

4. Я сожалею только, что нельзя подойти более гибко, задав массив нужных полей. А так -- или все или ничего только одно smile

5. Честно, я сначала (когда только начинал) и вынес. А потом внес обратно, думая что придется работать с разными шаблонами для разных страниц. Поэтому если в ближайшее время не введу набор шаблонов, то скрою с глаз _simple_nav() smile

6 Александр Купреев 07-09-2009 16:18

2 Brotkin Ivan

о, здорово!

Надеюсь, в релизе 09.09.09 все уже будет ОК smile

7 phpdreamer 31-05-2010 11:16

$keys  = array ('text','pass','area','chck','radio','sel');
$last_input = Arr::extract($_POST, $keys, NULL);

а если нам отправят не меньше параметров? чекбокс если не нажать не отправиться, да и хацкеры любопытные попадаются... а ПО должно работать с любыми входящими данными.

9 phpdreamer 31-05-2010 11:17

что то у Вас капча одна и та же все время =)

10 Александр Купреев 31-05-2010 22:26

если отправять меньше, то Arr:extract() должен подставить NULL. Так ведь? В реальном приложении это вполне можно обработать.

что то у Вас капча одна и та же все время =)
сам удивляюсь, но говорят -- так надо smile

Оставьте комментарий!


Используйте нормальные имена.

     

  

Если вы уже зарегистрированы как комментатор или хотите зарегистрироваться, укажите пароль и свой действующий email. При регистрации на указанный адрес придет письмо с кодом активации и ссылкой на ваш персональный аккаунт, где вы сможете изменить свои данные, включая адрес сайта, ник, описание, контакты и т.д., а также подписку на новые комментарии.

MaxSiteAuth. Войти через loginza

(обязательно)