Долой регулярные выражения или новый способ проверки данных
Безопасность данных, которыми оперируют программисты в своих программах, всегда был предметом для дискуссий и размышлений. Первым и чуть ли не единственным инструментом при проверке и очистке переменных в PHP были регулярные выражения. Некоторые так увлекались этим делом, что составляли огромные выражения в соответствии со стандартами (видел одно рег. выражение для проверки email на 6 тыс. символов). В этой статье рассмотрим некоторую альтернативу регулярным выражениям для проверки и очистки данных.
В PHP, начиная с версии 5.2, присутствует группа функций для фильтрации данных. Это расширение не требует особой настройки и установки.
Фильтры
PHP разных версий может поддерживать различное количество фильтров. Для того, чтобы выяснить какие данные могут отфильтровать функции существует функция filter_list(). Для просмотра идентификатора конкретного фильтра, нужно использовать функцию filter_id().
Выведем список фильтров и их идентификаторы.
filter_list.php
<?php echo "<table><tr><td>Имя фильтра</td><td>Идентификатор</td>"; $filters = filter_list(); foreach($filters as $filter){ echo "<tr><td>$filter</td><td>".filter_id($filter). "</td></tr>"; } echo "</table>"; var_dump(filter_var('http://www.example.loc',filter_id('validate_url'))); ?>
| Имя фильтра | Идентификатор |
| int | 257 |
| boolean | 258 |
| float | 259 |
| validate_regexp | 272 |
| validate_url | 273 |
| validate_email | 274 |
| validate_ip | 275 |
| string | 513 |
| stripped | 513 |
| encoded | 514 |
| special_chars | 515 |
| unsafe_raw | 516 |
| 517 | |
| url | 518 |
| number_int | 519 |
| number_float | 520 |
| magic_quotes | 521 |
| callback | 1024 |
Идентификатор фильтра используется для того, чтобы рассказать функциям фильтрации какой именно фильтр нужно применять.
Функции фильтрации
Для проверки и фильтрации одиночных переменных существует функция filter_var().
mixed filter_var ( mixed $variable [, int $filter [, mixed $options ]] )
Функция принимает переменную $variable, которую нужно отфильтровать, идентификатор фильтра $filter, а также некоторые опции фильтрации.
Идентификатор фильтра можно получить с помощью функции filter_id(), как было показано выше. Кроме того, это расширение предопределяет константы, в которых содержатся идентификаторы фильтров. Вот список констант.
Фильтры проверки данных
- FILTER_VALIDATE_BOOLEAN – проверяет как булевое значение, соответствует фильтру boolean;
- FILTER_VALIDATE_INT – проверяет как целое число, соответствует фильтру int. Опции фильтрации могут быть следующими:
- min_range — минимальное число;
- max_range — максимальное число;
- FILTER_FLAG_ALLOW_OCTAL — разрешает восьмеричные числа;
- FILTER_FLAG_ALLOW_HEX — разрешает шестнадцатеричные числа;
- FILTER_VALIDATE_FLOAT – проверяет как число с плавающей точкой, соответствует фильтру float;
- FILTER_VALIDATE_REGEXP – проверяет на соответствие регулярному выражению, соответствует фильтру validate_regexp;
- FILTER_VALIDATE_URL – проверяет, является ли переменная правильным URL, соответствует фильтру validate_url. Опции фильтрации могут быть следующими:
- FILTER_FLAG_SCHEME_REQUIRED — наличие протокола (http://example);
- FILTER_FLAG_HOST_REQUIRED — наличие хоста (http://www.example.com);
- FILTER_FLAG_PATH_REQUIRED — наличие пути (www.example.com/example1/test2/);
- FILTER_FLAG_QUERY_REQUIRED — наличие строки запроса (example.php?name=Peter&age=37)
- FILTER_VALIDATE_EMAIL – проверяет, является ли переменная правильным email, соответствует фильтру validate_email;
- FILTER_VALIDATE_IP – проверяет, является ли переменная правильным IP, соответствует фильтру validate_ip. Опции фильтрации могут быть следующими:
- FILTER_FLAG_IPV4 — значение должно быть IPv4 (255.255.255.255);
- FILTER_FLAG_IPV6 — значение должно быть IPv6 ( 2001:0db8:85a3:08d3:1319:8a2e:0370:7334);
- FILTER_FLAG_NO_PRIV_RANGE — значение должно соответствовать RFC, и не входить в приватные диапазоны (192.168.0.1, 10.0.0.1, …);
- FILTER_FLAG_NO_RES_RANGE — значение не должно содержать зарезервированные IP адреса, этот флаг принимает и IPv4 и IPv6 (255.255.255.255);
Фильтры обработки данных
- FILTER_SANITIZE_STRING – удаляет потенциально опасные данные из строки, соответствует фильтру string. Опции фильтрации могут быть следующими:
- FILTER_FLAG_NO_ENCODE_QUOTES — не кодировать кавычки;
- FILTER_FLAG_STRIP_LOW — удалить все ASCII-символы с кодом меньше 32;
- FILTER_FLAG_STRIP_HIGH — удалить ASCII-символы с кодом больше 127;
- FILTER_FLAG_ENCODE_LOW — кодировать все ASCII-символы с кодом меньше 32;
- FILTER_FLAG_ENCODE_HIGH — кодировать все ASCII-символы с кодом больше 127;
- FILTER_FLAG_ENCODE_AMP — кодировать символ & в &
- FILTER_SANITIZE_STRIPPED – то же что и FILTER_SANITIZE_STRING;
- FILTER_SANITIZE_ENCODED – удаляет или кодирует нежелательные символы (похоже на urlencode()), соответствует фильтру encoded. Опции фильтрации могут быть следующими:
- FILTER_FLAG_STRIP_LOW — удалить все ASCII-символы с кодом меньше 32
- FILTER_FLAG_STRIP_HIGH — удалить все ASCII-символы с кодом больше 32
- FILTER_FLAG_ENCODE_LOW — кодировать все ASCII-символы с кодом меньше 32
- FILTER_FLAG_ENCODE_HIGH — кодировать все ASCII-символы с кодом больше 32
- FILTER_SANITIZE_SPECIAL_CHARS – преобразовывает спец.символы в HTML-сущности, соответствует фильтру special_chars. Опции фильтрации могут быть следующими:
- FILTER_FLAG_STRIP_LOW — удалить все ASCII-символы с кодом меньше 32
- FILTER_FLAG_STRIP_HIGH — удалить все ASCII-символы с кодом больше 32
- FILTER_FLAG_ENCODE_HIGH — кодировать все ASCII-символы с кодом больше 32
- FILTER_SANITIZE_EMAIL – удаляет все символы, запрещенные в email, соответствует фильтру email;
- FILTER_SANITIZE_URL – удаляет все символы, запрещенные в URL, соответствует фильтру url;
- FILTER_SANITIZE_NUMBER_INT – удаляет все символы, кроме целых чисел, соответствует фильтру number_int;
- FILTER_SANITIZE_NUMBER_FLOAT – удаляет все символы, кроме чисел с плавающей точкой, соответствует фильтру number_float;
- FILTER_SANITIZE_MAGIC_QUOTES – добавляет слэши к спец символам, соответствует фильтру magic_quotes.
Напишем небольшой тестовый код, который продемонстрирует работу фильтров.
filter_var.php
<?php //имя $name = 'web-junior'; //email $email = 'test@email.com'; //URL $homepage='www.web-junior.net'; //возраст - целое число $age=30; //сначала отфильтруем имя //все спец-символы закодируем var_dump(filter_var($name, FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_LOW)); echo '<br/>'; //проверим email. //Проверка, если честно не очень. var_dump(filter_var($email, FILTER_VALIDATE_EMAIL)); echo '<br/>'; //проверим URL. //Должен вернуть false, поскольку протокол не указан var_dump(filter_var($homepage, FILTER_VALIDATE_URL,FILTER_FLAG_SCHEME_REQUIRED)); echo '<br/>'; //проверяем возраст: //должно быть от 18 до 50 var_dump(filter_var($age, FILTER_VALIDATE_INT, array('options'=>array( 'min_range'=>18, 'max_range'=>50) ) ) ); ?>
Выведет:
string(10) “web-junior”
string(14) “test@email.com”
bool(false)
int(30)
Следующая функция для фильтрации данных называется filter_var_array()
mixed filter_var_array ( array $data [, mixed $definition ] )
По сути выполняет те же операции что и filter_var(), но только делает это с данными в массиве. Первый параметр – это массив данных, второй – это массив с указаниями как фильтровать данные. Вот пример.
<strong>filter_var_array.php</strong>
<?php //данные $data = array( 'product_id' => 'libgd<script>', 'component' => '10', 'versions' => '2.0.33', 'testscalar' => array('2', '23', '10', '12'), 'testarray' => '2', ); //указания как фильтровать $args = array( 'product_id' => FILTER_SANITIZE_ENCODED, 'component' => array('filter' => FILTER_VALIDATE_INT, 'flags' => FILTER_FORCE_ARRAY, 'options' => array('min_range' => 1, 'max_range' => 10) ), 'versions' => FILTER_SANITIZE_ENCODED, 'doesnotexist' => FILTER_VALIDATE_INT, 'testscalar' => array( 'filter' => FILTER_VALIDATE_INT, 'flags' => FILTER_REQUIRE_SCALAR, ), 'testarray' => array( 'filter' => FILTER_VALIDATE_INT, 'flags' => FILTER_FORCE_ARRAY, ) ); //применяем фильтры $myinputs = filter_var_array($data, $args); //выводим var_dump($myinputs); echo "\n"; ?>
Этот код выведет
array(6) {
["product_id"]=>
string(17) “libgd%3Cscript%3E”
["component"]=>
array(1) {
[0]=>
int(10)
}
["versions"]=>
string(6) “2.0.33″
["doesnotexist"]=>
NULL
["testscalar"]=>
bool(false)
["testarray"]=>
array(1) {
[0]=>
int(2)
}
}
Судя по всему, разработчики этих функций во время разработки задались вопросом: «Какие данные чаще всего приходится проверять?» И пришли к единственно верному логическому ответу: «Те, которые приходят от пользователя». Придя к такому выводу, они создали функцию, которая проверяет данные в массивах $_GET, $_POST, $_COOKIE, $_SERVER и $_ENV.
mixed filter_input ( int $type , string $variable_name [, int $filter [, mixed $options ]] )
$type может быть одной из констант INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, INPUT_ENV, которая обозначает соответствующий входящий массив. $variable_name – это имя переменной в массиве, а $filter и $options аналогичны filter_var().
Бывает достаточно удобно применить фильтр сразу к данным, которые пришли извне.
Сделаем на скорую руку форму.
form.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xml:lang="en" lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Form</title> <meta http-equiv="Content-Type" content="text/html; charset=windows-1251"> </head> <body> <form action="filter_input.php" method="post"> <label for="name">Имя</label><input id="name" /><br/> <label for="email">Email</label><input id="email" /><br/> <label for="homepage">Домашняя страничка</label><input type="text" /><br/> <label for="age">Возраст</label><input id="age" /><br/> <input type="submit" value="Отправить" /> </form> </body> </html>
Вот форма.
Форма получилась самой простой. Но нам больше и не нужно.
Теперь получим и выведем все что получили, обрабатывая результат функцией filter_input()
filter_input.php
<?php //фильтруем имя //все непечатаемые символы будут закодированы $name = filter_input(INPUT_POST,'name', FILTER_SANITIZE_STRING,FILTER_FLAG_ENCODE_LOW); //проверяем email $email = filter_input(INPUT_POST, 'email',FILTER_VALIDATE_EMAIL); //URL должен содержать протокол $homepage = filter_input(INPUT_POST, 'homepage', FILTER_VALIDATE_URL,FILTER_FLAG_SCHEME_REQUIRED); //возраст //вход разрешен с 18 и до 50 $age = filter_input(INPUT_POST, 'age',FILTER_VALIDATE_INT, array('options'=>array('min_range'=>18,'max_range'=>50))); //посмотрим что получили echo "Ваше имя ".($name!=false?$name: 'введено неверно')."<br/> Ваш email ".($email!=false?$email:'введен неверно')."<br/> Ваша домашняя страничка ". ($homepage!=false?$homepage:'введена неверно')."<br/> Ваш возраст ".($age!==false?$age:'введен неверно')." "; ?>
В результате отправки данных с формы мы получим что-то вроде
Все данные отфильтрованы успешно
Следующая функции для фильтрации
mixed filter_input_array ( int $type [, mixed $definition ] )
Функция filter_input_array() своей функциональностью очень похожа на функции filter_input() и filter_var_array(). Эта функция фильтрует несколько переменных из входящих данных. Приведенный выше пример можно переписать так.
filter_input_array.php
<?php $defs = array( 'name'=>array( 'filter'=>FILTER_SANITIZE_STRING, 'flags'=>FILTER_FLAG_ENCODE_LOW ), 'email'=>FILTER_VALIDATE_EMAIL, 'homepage'=>array( 'filter'=>FILTER_VALIDATE_URL, 'flags'=>FILTER_FLAG_SCHEME_REQUIRED ), 'age'=>array( 'filter'=>FILTER_VALIDATE_INT, 'options'=>array('min_range'=>18,'max_range'=>50) ) ); $res = filter_input_array(INPUT_POST,$defs); //посмотрим что получили echo "Ваше имя ".($res['name']!=false?$res['name']: 'введено неверно')."<br/> Ваш email ".($res['email']!=false?$res['email']: 'введен неверно')."<br/> Ваша домашняя страничка ".($res['homepage']!=false?$res['homepage']: 'введена неверно')."<br/> Ваш возраст ".($res['age']!==false?$res['age']: 'введен неверно')." "; ?>
Результат работы этого скрипта будет таким, как и filter_input.php.
Последняя функция в этом расширении называется filter_has_var().
bool filter_has_var ( int $type , string $variable_name )
Эта функция проверяет существует ли переменная $variable_name во входящих данных типа $type. Тип может быть таким же как и в функции filter_input().
Таким образом, используя эти функции, можно существенно сократить использование регулярных выражений в приложении, и, соответственно, увеличить скорость работы приложения.
Популярность: 15%
Интересное из других блогов:
2leep.comИ не забывайте комментировать статью.
Добавляйся в группу во вконтакте, чтобы самым первым узнавать все новости сайта
Отзывов: 14 на «Долой регулярные выражения или новый способ проверки данных»
RSS-лента комментариев. Адрес для трекбека


Автор: Костян, 23 января 2010 в 20:42
используетсам пишешь эти классы?
Автор: Алексей, 28 января 2010 в 11:19
используетСовсем недавно тоже узнал о такой возможности в новой версии PHP 5.2. Немного упростит проверку данных. Только применяя это, нужно помнить, что это есть только в новых версиях PHP.
Автор: web-junior, 28 января 2010 в 17:59
используетДа конечно вы правы. Помнить нужно обязательно.
Но с другой стороны, года через 2-3, никто уже и не будет помнить не только о версиях ниже 5.2, но и про саму 5.2 все забудут. Прогресс ведь не стоит на месте =)
Автор: voksel, 7 февраля 2010 в 20:31
используетСпасибо за статейку.
Автор: web-junior, 7 февраля 2010 в 20:58
используетПожалуйста. Заходите ещё. На сайте вы сможете найти ещё много интересных материалов
Автор: Hows, 27 февраля 2010 в 13:16
используетПолюбак, в самих же этих функциях используются регулярки
Автор: Андрей, 19 апреля 2010 в 10:42
используетЭто сто процентов, что в них используются регулярки, но вот наверняка они отрабатываются быстрей чем рукописные.
А у уважаемого автора есть наблюдения по этому поводу?
Автор: web-junior, 19 апреля 2010 в 11:07
используетДа, конечно, здесь используются регулярки. За подтверждением ходить далеко не нужно. Достаточно посмотреть в исходниках php:
php-5.2.13/ext/filter/logical_filter.c
Все дело в том, что на C эти регулярки работают намного быстрее, чем на PHP. В этом и есть основной выигрыш.
Автор: Сергей, 26 августа 2010 в 01:02
используетПоходу разработчики забыли, что в домене может встретиться знак “-”! Хотел использовать у себя для проверки правильно ввода URL, но вот незадача…
Автор: web-junior, 26 августа 2010 в 17:39
используетНу почему-же? Вот мой домен, вполне верно обрабатывает. К примеру такой код:
Выводит
string(25) “http://www.web-junior.net”
Т.е. обрабатывает верно.
Если у вас какой-либо код работает не верно, то напишите его здесь и мы вместе попробуем разобраться в причинах его неработоспособности =)
Автор: papay, 23 ноября 2010 в 11:56
используетСпасибо. познавательная статья. Не знал про эти возможности PHP. Действительно может оказать существенную помощь при написании своих веб-приложений
Автор: web-junior, 24 ноября 2010 в 19:53
используетПожалуйста!
Заходите еще, здесь вы сможете найти множество полезных материалов.