one234ru / form-validator
HTML forms validation tool, both client- and server-side (PHP)
Requires
- php: >=7.3
This package is auto-updated.
Last update: 2024-11-20 01:28:13 UTC
README
Результатом проверки является массив данных об ошибках, предназначенный для отправки в браузер.
Клиентская часть библиотеке описана здесь.
Массив ошибок имеет вид:
[ { "name": "имя поля (атрибут name в HTML-коде)", "value": "значение", "messages": [ "текст ошибки", "текст другой ошибки" ] }, { ... } ]
В случае корректно заполненной формы массив пуст.
Проверка осуществляется на основании специальной конфигурации и параметров HTTP-запроса.
require 'Validator.php'; $config = [...]; $values = $_GET; $obj = new One234ru\FormValidator($config, $values); $obj->validate($values); // $obj->errors будет содержать массив ошибок
Стандартные проверки
Предположим, что в форме есть три поля — имя, телефона и электронная почта с именами
name
, phone
и email
соответственно. Тогда конфигурация для проверки примет вид:
$config = [ 'name' => ..., // здесь инструкции для проверки 'phone' => ..., 'email' => ... ];
Ключи массива должны совпадать со значениями атрибута name
полей в HTML-коде.
Простая проверка на заполненность
Единственный параметр этой проверки — текст ошибки, который будет показан
пользователю в случае, если поле не заполнено или вообще отсутствует среди параметров
HTTP-запроса. Проверка не будет пройдена, если значением поля является 0
, а не пустая строка. Пробельные символы в
начале и в конце перед проверкой отбрасываются.
Существует два вида объявления проверки: краткий — в виде строки и полный — в
виде массива с ключом *
.
Предположим, мы требуем указать имя и телефон, а электронную почту — по желанию.
В примере ниже для поля name
использован сокращённый вид, а для phone
- полный:
$config = [ 'name' => 'Укажите имя.', 'phone' => [ '*' => 'Укажите телефон.' ], 'email' => [] ];
Полю email
в данном примере не назначены инструкции для проверки, и его содержимое никак не
скажется на её результатах.
Посмотрим, что будет, если на проверку отправить пустой HTTP-запрос:
$obj = new One234ru\FormValidator($config); $values = []; // имитируем пустой запрос $obj->validate($values); echo json_encode($obj->errors, JSON_UNESCAPED_UNICODE);
Результат (отформатирован для удобочитаемости):
[ { "name": "name", "value": null, "messages": [ "Укажите имя." ] }, { "name": "phone", "value": null, "messages": [ "Укажите телефон." ] } ]
Как видим, каждому из полей соответствует массив со списком сообщений об ошибках. value
во всех случаях содержит null
, поскольку соответствующий элемент в HTTP-запросе отсутствовал.
Краткая и полная форма записи действуют совершенно одинаково. Как правило, если нет других проверок, удобнее использовать краткую форму.
Проверка содержимого с помощью регулярного выражения
Теперь предположим, что мы не только хотим проверить, что поля заполнены, но и убедиться в том, что они содержат корректные по формату значения. Например, телефон должен состоять строго из десяти цифр, а e-mail — соответствовать определенному формату.
В этом нам поможет проверка регулярным выражением. Чтобы задать такую, нужно добавить в массив
проверок элемент, ключом которого является регулярное выражение
(ограничитель — /
), а значением — текст ошибки:
$config = [ // проверку name здесь и далее опустим 'phone' => [ '*' => 'Укажите телефон.', '/^\d{10}$/' => 'Телефон следует указывать в виде 10 цифр.', ], 'email' => [ '/^[\.\-\w]+@(\w+\-)*\.[A-z]{2}$/' => '"{*value*}" не является адресом электронной почты.', ] ];
Метка {*value*}
в тесте ошибки будет заменена на значение поля (с кодированием функцией
htmlspecialchars()
).
Посмотрим, как будут выглядеть ошибки:
$values = [ 'phone' => '1234', 'email' => 'somebody@', ];
[ { "name": "phone", "value": "1234", "messages": [ "Телефон следует указывать в виде 10 цифр." ] }, { "name": "email", "value": "somebody@", "messages": [ "\"somebody@\" не является адресом электронной почты." ] } ]
Проверка регулярным выражением будет выполняться только в том случае, если пройдена проверка на заполненность. В этом легко убедиться:
$values = [];
[ { "name": "phone", "value": null, "messages": [ "Укажите телефон." ] } ]
Таким образом, поле email
по-прежнему не является обязательным и проверяться будет только в
случае, если содержит непустое значение.
Проверки с помощью произвольных функций
Такие проверки делятся на два типа — первичные и вторичные.
Вторичные проверки выполняются только в случае, если успешно пройдены все остальные проверки для этого поля.
Предположим, введенный в форму телефон нужно проверить на наличие в некой базе данных. В этом случае инструкции проверки будут выглядеть так:
'phone' => [ '*' => 'Укажите телефон.', '/^\d{10}$/' => 'Телефон следует указывать в виде 10 цифр.', function($value) { $found = ...; // тут ищем в базе данных return (!$found) ? "Телефон $value не значится в наших списках." : ""; // Т.к. телефон уже проверялся регулярным выражением, // обрабатывать его htmlspecialchars() // перед вставкой в текст ошибки ни к чему. } ]
Как видно из примера, в качестве аргумента функции передается значение поля. В ответ она должна вернуть строку-сообщение об ошибке либо пустое значение, если ошибок нет.
$values = [ 'phone' => '1234567890' ];
[ { "name": "phone", "value": "1234567890", "messages": [ "Телефон 1234567890 не значится в наших списках." ] } ]
Первичные проверки выполняются всегда, независимо от заполненности поля и наличия других проверок.
Их декларация отличается лишь тем, что в массиве вместо безымянного ключа элемента
используется '*'
. Есть и сокращенная форма записи:
// вторичная проверка 'phone' => [ function($value) {...} ] // первичная проверка, полная форма 'phone' => [ '*' => function($value) {...} ] // первичная проверка, сокращенная форма 'phone' => function($value) {...}
При записи в полной форме первичную проверку можно сочетать с другими проверками.
Допустим, мы решили локализовать все проверки телефона внутри одной функции. Вот как это будет выглядеть:
'phone' => function($value) { if (empty($value)) { $message = "Укажите телефон."; } elseif (!preg_match('/^\d{10}$/', $value)) { $message = "Телефон следует указывать в виде 10 цифр."; } elseif (!($found = ...)) { $message = "Телефон $value не значится в наших списках."; } else { $message = ""; } return $message; }
Формат данных результатов проверки остаётся прежним. Не зависит он и от формы записи — краткой или полной.
Сводные проверки
Проверку можно построить на основании значений нескольких полей.
Объявляются такие проверки с помощью функций, которые помещаются внутри безымянного элемента на верхнем уровне конфигурации. В качестве аргумента такие функции принимают полный массив параметров HTTP-запроса.
Результатом работы такой функции является список выявленных ошибок. Каждая ошибка в нём представлена в виде массива со следующими элементами:
name
— имя поляvalue
— значение поляmessage
— текст сообщения об ошибке
Предположим, в нашем примере каждому номеру телефона в некой базе данных соответствует определенный e-mail, и при заполнении формы это соответствие нужно проверять. Вот как будет выглядеть подобная проверка:
$config = [ ... [ function ($query) { if (...) { // тут проверяем телефон и email $errors[] = [ 'name' => 'email', 'value' => $query['email'], 'message' => "К телефону $query[phone] привязан другой email." ]; } return $errors ?? []; } ] ]; $values = [ 'phone' => '1234567890', 'email' => 'someone@somewhere.ru', ];
Результат:
[ { "name": "email", "value": "someone@somewhere.ru", "message": "К телефону 1234567890 привязан другой email." } ]
Общие проверки также бывают первичными и вторичными, по аналогии с проверками отдельных полей: первичные выполняются всегда, вторичные — только при успешном результате всех остальных проверок.
Декларация их отличается так же, как и в случае отдельных полей: первичные объявляются с
ключом '*'
, вторичные — с безымянным числовым ключом:
$config = [ ..., [ '*' => function($query) { ... }, // первичная проверка function($query) { ... }, // вторичная проверка function($query) { ... }, // еще одна вторичная проверка ] ]
Элементов, содержащих сводные проверки, может быть несколько:
$config = [ ..., [ '*' => function($query) { ... }, function($query) { ... }, ], [ '*' => function($query) { ... }, function($query) { ... }, ], ... ]
Ошибки без отношения к конкретному полю
В ряде случаев ошибки не относятся к какому-то конкретному полю (например, в случае сбоя при обращении к какой-то внешней системе).
Добавить их в список можно с помощью общих проверок, не указывая name
и value
или вообще
вернув вместо массива строку — текст ошибки.
$config = [ ..., function($query) { if (...) { // Полная форма: return [ [ 'message' => "Произошёл сбой связи с внешней системой." ] ]; // Краткая форма, вложенность на ДВА уровня ниже return "Произошёл сбой связи с внешней системой."; } } ];
Оформление ошибки в таком виде повлияет на её отображение на клиенте: она будет расположена в какой-то общей области формы (как правило, рядом с кнопкой отправки), а не у конкретного поля.
Другой способ добавить общую ошибку в список — вызов метода addError()
, см. ниже.
Проверки однородных полей — []
Под однородными понимаются поля с одинаковым атрибутом name
, содержащим на конце []
, каждое
из которых подчиняется одинаковой логике проверки.
Хороший пример таких полей — промо-коды, которые пользователь может ввести в форму в
произвольном количестве. Для ввода каждого промо-кода в форме будет текстовое поле с именем
promo_codes[]
, с помощью кнопки типа «Ввести ещё один промо-код» пользователь может
самостоятельно добавлять поля новых промо-кодов.
В HTTP-запросе этим полям будет соответствовать ключ promo_codes
, содержащий массив строк:
$values = [ 'promo_codes' => [ "ABC", "", // могут быть и пустые поля "123" ] ] ];
В конфигурации проверки однородных полей указываются с помощью ключа []
:
$config = [ 'promo_codes' => [ '[]' => [ '/^[A-z]+$/' => 'Промо-коды могут состоять только из латинских букв.', function ($value) { if (...) { return "Промо-код $value не распознан."; } } ] ] ]
Результат:
[ { "name": "promo_codes[]", "value": "abc", "messages": [ "Промо-код abc не распознан." ] }, { "name": "promo_codes[]", "value": "134", "messages": [ "Промо-коды могут состоять только из латинских букв." ] } ]
Проверки, указанные в ключе []
, осуществляются по тем же правилам, что и обычные проверки
одиночных полей.
Сокращенная форма записи вида
'[]' => function() {...}
соответствует полной
'[]' => [ '*' => function() {...} ]
Наряду с []
возможна общая проверка на наличие полей в запросе как таковое. Её нужно
размещать на одном уровне с []
:
$config = [ 'promo_codes' => [ '*' => 'Нужно указать хотя бы один промо-код', '[]' => ... ] ]
Если при этом ключ promo_codes
в запросе отсутствует или содержит только пустые значения
(подробней об этом см. ниже), результатом будет сообщение об ошибке:
$values = [ 'promo_codes' => [ "", "" ] ];
[ { "name": "promo_codes", "value": [], "messages": [ "Нужно ввести хотя бы один промо-код." ] } ]
Обратите внимание, что name
в данном случае не содержит []
в конце, т.к. проверка
проводилась на уровне общего ключа HTTP-запроса, а не отдельных полей. Это будет важно при
размещении ошибок на клиенте.
Важно отметить, что содержимое HTTP-запроса перед проверками очищается от пустых однородных полей (с предварительным отбрасыванием пробелов по краям). Пустые значения из массива будут исключены.
Подключение конфигурации проверок в качестве дочерней — children
Бывают случаи, когда форма включается в качестве подраздела в другую, более крупную форму с сохранением всех правил по заполнению полей.
Например, в интернет-магазине форма для редактирования личной информации клиента может входить
в форму заказа в виде раздела клиентских полей. При этом имена полей будут изменены так, чтобы
в HTTP-запрос их значения вошли не на верхнем уровне, а внутри некоторого ключа.
Например, <input name="phone">
может превратиться в <input name="client[phone]">
,
<input name="email">
— в <input name="client[email]">
и так далее.
Чтобы при этом продолжать пользоваться уже имеющейся конфигурацей проверки этих полей, нужно
прибегнуть к использованию ключа children
:
$clients_config = [ 'name' => 'Укажите имя.', 'phone' => 'Укажите телефон.', ]; $full_config = [ 'client' => [ 'chlidren' => $clients_config ] ]; // Пример HTTP-запроса из формы с незаполненными полями $http_query = [ 'client' => [ 'name' => '', 'phone' => '' ] ]; $obj = new One234ru\FormValidator($full_config); $obj->validate($http_query);
Результат:
[ { "name": "client[name]", "value": "", "messages": [ "Укажите имя." ] }, { "name": "client[phone]", "value": "", "messages": [ "Укажите телефон." ] } ]
Дочерняя конфигурация не имеет никаких ограничений по функционалу и может содержать любые типы проверок, описанные выше.
addError()
— ручное дополнение списка ошибок
Метод позволяет вручную дополнить список в обход общей логики проверок.
Это бывает нужно, например, при взаимодействии с внешними системами, запрос к которым строится
на основании данных формы. Перед его отправкой нужно убедиться в корректности введённых данных,
поэтому проверки проводятся до того, как отправляется запрос. Однако результат запроса также
может содержать сообщения об ошибках, которые в таком случае необходимо включить в список. Для
этого и служит addError()
.
В качестве аргументов ему передаются, именно в таком порядке:
- текст сообщения об ошибке или их список в виде массива
- имя поля (необязательно)
- значение (необязательно)
Пример:
$obj = new One234ru\FormValidator($config); $obj->addError('Какая-то ошибка с телефоном', 'phone'); $obj->addError('И с email тоже ошибка', 'email');
Результат по виду такой же, как при обычной проверке:
[ { "name": "phone", "messages": [ "Какая-то ошибка с телефоном" ] }, { "name": "email", "messages": [ "И с email тоже ошибка" ] } ]
Порядок аргументов addError()
делает особенно удобным добавление общей ошибки, позволяя
указать её текст в качестве единственного аргумента:
$obj->addError('Произошёл сбой связи с внешней системой.');
[ { "messages": [ "Произошёл сбой связи с внешней системой." ] } ]
Изменение конфигурации проверки, очистка списка ошибок
В объект можно загрузить другую конфигурацию для проверки с помощью метода setConfigTo()
.
Если после этого запустить проверку, вновь выявленные ошибки заменят существующие. Чтобы этого не произошло, нужно передать методу getErrors()
второй аргумент со значением false
:
// Проверяем какие-то внешние условия и добавляем в список // выявленные ошибки в качестве общих. $some_errors = ...; $obj = new One234ru\FormValidator([]); $obj->addError($some_errors); // Теперь генерируем конфигурацию для проверки $config = ...; $obj->setConfigTo($config); $obj->validate($http_query, false);
Это может пригодиться, если конфигурация для проверки генерируется динамически, и на момент определения некоторых ошибок ясна не до конца:
Прочие возможности
После проведения проверок можно определять по имени поля, корректно ли оно заполнено:
$obj->validate($http_query, false); ... $obj->isFieldValid('email');