Перейти к содержимому
Теория · PHP и веб · урок 6 из 7

Проверка ввода

Вернёмся к бланку заказа. Его мог заполнить кто угодно: в графе «количество» написано «много», а в графе «блюдо» — то, чего нет в меню. Хороший повар сначала сверяет бланк с меню и только потом готовит.

Программе нужна такая же сверка. Каждое значение из запроса выбрал посетитель, и (int) его не проверяет: (int) '15kg' молча даёт 15. Проверка ввода — сравнение значения с правилом до того, как программа его использует. Значение, которое не прошло проверку, не используют, а посетителю показывают ошибку.

filter_var

Функция filter_var проверяет строку по готовому правилу. Правило передают вторым аргументом: константа FILTER_VALIDATE_INT означает целое число, FILTER_VALIDATE_EMAIL — адрес почты.

PHP
<?php

var_dump(filter_var('15', FILTER_VALIDATE_INT)); // int(15)
var_dump(filter_var('15kg', FILTER_VALIDATE_INT)); // bool(false)
var_dump((int) '15kg'); // int(15)
var_dump(filter_var('bob@mail.com', FILTER_VALIDATE_EMAIL)); // string(12) "bob@mail.com"
var_dump(filter_var('bob@', FILTER_VALIDATE_EMAIL)); // bool(false)

Целое число

FILTER_VALIDATE_INT проверяет всю строку. '15' проходит и возвращается уже числом int(15). В '15kg' после цифр идут буквы, поэтому результат — false.

Приведение (int) в той же ситуации нашло бы цифры в начале и молча дало 15.

Адрес почты

FILTER_VALIDATE_EMAIL возвращает сам адрес, если он правильный, и false, если нет. В 'bob@' нет части после @, и проверка не проходит.

Общее правило filter_var: при успехе она возвращает значение, при неудаче — false. Поэтому результат сравнивают строго: === false.

Белый список

Некоторые значения не нужно проверять по виду: их выбирают из короткого списка. Сортировка бывает по имени или по цене, и любое другое слово в ?sort= — ошибка или попытка взлома. Белый список — список разрешённых значений: всё, чего в нём нет, отвергают.

PHP
$_GET = ['sort' => 'date'];
$sort = $_GET['sort'] ?? 'name';
if (!in_array($sort, ['name', 'price'], true)) {
    $sort = 'name';
}
echo $sort, "\n"; // name

Значения 'date' в списке нет, поэтому программа берёт 'name'. Как вы уже знаете, третий аргумент true включает у in_array строгое сравнение. Белый список надёжнее перечня запрещённого: всё опасное не перечислить, а разрешённое умещается в одну строку.

Здесь картинка с меню неточна: у повара одно меню на все заказы. Программе же для каждого поля нужно своё правило — число, адрес почты или список.

Проверка формы

Посетитель часто копирует адрес почты вместе с пробелом, и FILTER_VALIDATE_EMAIL такой адрес отвергает. Поэтому перед проверкой пробелы по краям убирают функцией trim. Ошибки удобно собирать в массив, чтобы показать посетителю все сразу:

PHP
$_POST = ['email' => ' ann@mail.ru ', 'age' => '15kg'];
$errors = [];
$email = filter_var(trim($_POST['email'] ?? ''), FILTER_VALIDATE_EMAIL);
if ($email === false) {
    $errors[] = 'Check the email';
}
$age = filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT);
if ($age === false) {
    $errors[] = 'Age must be a whole number';
}
echo $email, "\n"; // ann@mail.ru
echo count($errors), ' ', $errors[0], "\n"; // 1 Age must be a whole number

Давайте разберём по шагам:

  1. trim убирает пробелы по краям, и адрес проходит проверку.
  2. Возраст '15kg' проверку не проходит, и в $errors появляется сообщение.
  3. Пустой $errors значит, что данные можно использовать. Иначе страница снова показывает форму, а над ней — ошибки.

Проверка не заменяет экранирование. Фамилия O'Brien пройдёт любую разумную проверку, но при выводе в HTML её всё равно пропускают через htmlspecialchars. Чем проверка ввода отличается от экранирования вывода, любят спрашивать на собеседовании: первая решает, принять ли значение, второе — как безопасно его напечатать.

Частая ошибка

Результат filter_var проверяют через !:

PHP
<?php

$qty = filter_var('0', FILTER_VALIDATE_INT);
if (!$qty) { // ✗
    echo 'Not a number', "\n";
}

Программа печатает Not a number, хотя '0' — правильное целое число. filter_var вернула int(0), а ноль — ложное значение, поэтому !$qty истинно. Чтобы исправить, сравните строго: if ($qty === false).

После курса

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

Дальше проверьте себя на симуляторе собеседования по PHP на этом сайте. Он задаёт вопросы по темам курса и по тем, которых курс не касался, и оценивает ответы.

Главное

  • Каждое значение из запроса выбрал посетитель, поэтому перед использованием его проверяют.
  • filter_var возвращает проверенное значение или false: '15kg' с FILTER_VALIDATE_INT даёт false, хотя (int) дал бы 15.
  • Значение из короткого набора сверяют с белым списком через in_array со строгим сравнением.
  • Результат filter_var сравнивают строго, === false, потому что 0 — правильное число.