Долой регулярные выражения или новый способ проверки данных
Безопасность данных, которыми оперируют программисты в своих программах, всегда был предметом для дискуссий и размышлений. Первым и чуть ли не единственным инструментом при проверке и очистке переменных в 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().
Таким образом, используя эти функции, можно существенно сократить использование регулярных выражений в приложении, и, соответственно, увеличить скорость работы приложения.
Популярность: 14%
Интересное из других блогов:
2leep.comИ не забывайте комментировать статью.
Добавляйся в группу во вконтакте, чтобы самым первым узнавать все новости сайта


Автор: Griffs, 8 декабря 2010 в 23:43
используетСпасибо автору, очень познавательная статья.
Нашел только один недочет в функции:
filter_input(INPUT_POST, ‘email’,FILTER_VALIDATE_EMAIL);
При вводе такой конструкции в поле мейла: email@email – не фильтрует, а возвращает как корректный.
Автор: Alexander, 22 июля 2011 в 22:57
используетСпасибо. Правда говоря – ну и констант мне придётся учить. Хорошо что есть много знакомых названий.