В этом руководстве я расскажу вам, как создать пользовательский валидатор данных в PHP, шаг за шагом создавая нашу собственную библиотеку валидации. Валидаторы данных являются важными инструментами для любого разработчика, которому необходимо обеспечить достоверность и безопасность данных, передаваемых пользователем. К концу этого урока вы будете хорошо понимать, как создавать пользовательские валидаторы данных в PHP, что позволит вам лучше обрабатывать пользовательские данные и обеспечивать безопасность ваших приложений.
Оглавление
Шаг 1: Создание класса валидации
Первым шагом является создание класса, который будет обрабатывать валидацию. Этот класс должен быть способен хранить правила проверки для каждого поля, которое мы хотим проверить, а также проверять эти правила при вызове.
Вот пример простого класса для проверки:
<?php
namespace DevCoder\Validator;
use DevCoder\Validator\Assert\ValidatorInterface;
use InvalidArgumentException;
use function get_class;
use function gettype;
use function is_array;
use function is_object;
use function sprintf;
class Validation
{
/**
* @var array<string,array>
*/
private $validators;
/**
* @var array<string,string>
*/
private $errors = [];
/**
* @var array
*/
private $data = [];
public function __construct(array $fieldValidators)
{
foreach ($fieldValidators as $field => $validators) {
if (!is_array($validators)) {
$validators = [$validators];
}
$this->addValidator($field, $validators);
}
}
public function validate(array $data): bool
{
$this->data = $data;
/**
* @var $validators array<ValidatorInterface>
*/
foreach ($this->validators as $field => $validators) {
if (!isset($this->data[$field])) {
$this->data[$field] = null;
}
foreach ($validators as $validator) {
if ($validator->validate($this->data[$field]) === false) {
$this->addError($field, (string)$validator->getError());
}
}
}
return $this->getErrors() === [];
}
/**
* @return array<string,string>
*/
public function getErrors(): array
{
return $this->errors;
}
/**
* @return array
*/
public function getData(): array
{
return $this->data;
}
private function addError(string $field, string $message): void
{
$this->errors[$field][] = $message;
}
/**
* @param string $field
* @param array<ValidatorInterface> $validators
* @return void
*/
private function addValidator(string $field, array $validators): void
{
foreach ($validators as $validator) {
if (!$validator instanceof ValidatorInterface) {
throw new InvalidArgumentException(sprintf(
$field . ' validator must be an instance of ValidatorInterface, "%s" given.',
is_object($validator) ? get_class($validator) : gettype($validator)
));
}
$this->validators[$field][] = $validator;
}
}
}
Шаг 2: Создание классов правил для валидации данных
Теперь, когда мы создали класс Validator, следующим шагом будет создание собственных правил валидации. Эти правила будут использоваться для проверки того, являются ли предоставленные данные действительными или нет. Мы создадим их в отдельных файлах, по одному для каждого правила валидации. Каждый файл правила валидации должен содержать класс, названный в честь правила, которое он реализует. Например, если у нас есть правило валидации, проверяющее, является ли значение целым числом, мы назовем класс Integer.
ValidatorInterface
<?php
namespace DevCoder\Validator\Assert;
interface ValidatorInterface
{
public function validate($value): bool;
public function getError(): ?string;
}
AbstractValidator
<?php
namespace DevCoder\Validator\Assert;
abstract class AbstractValidator implements ValidatorInterface
{
/**
* @var string|null
*/
protected $error;
public function getError(): ?string
{
return $this->error;
}
protected function error(string $message, array $context): void
{
$replace = [];
foreach ($context as $key => $value) {
if (is_object($value)) {
$value = method_exists($value, '__toString') ? (string)$value : get_class($value);
} elseif (is_array($value)) {
$value = json_encode($value);
} else {
$value = (string)$value;
}
$replace['{{ ' . $key . ' }}'] = $value;
}
$this->error = strtr($message, $replace);
}
}
Integer
<?php
declare(strict_types=1);
namespace DevCoder\Validator\Assert;
use function ctype_digit;
use function is_int;
use function strval;
class Integer extends AbstractValidator
{
/**
* @var string
*/
private $invalidMessage = 'This value should be of type {{ type }}.';
private $minMessage = '{{ value }} should be {{ limit }} or more.';
private $maxMessage = '{{ value }} should be {{ limit }} or less.';
/**
* @var int|null
*/
private $min;
/**
* @var int|null
*/
private $max;
public function validate($value): bool
{
if ($value === null) {
return true;
}
if (ctype_digit(strval($value)) === false) {
$this->error($this->invalidMessage, ['value' => $value, 'type' => 'integer']);
return false;
}
if (is_int($this->min) && $value < $this->min) {
$this->error($this->minMessage, ['value' => $value, 'limit' => $this->min]);
return false;
}
if (is_int($this->max) && $value > $this->max) {
$this->error($this->maxMessage, ['value' => $value, 'limit' => $this->max]);
return false;
}
return true;
}
public function invalidMessage(string $invalidMessage): self
{
$this->invalidMessage = $invalidMessage;
return $this;
}
public function minMessage(string $minMessage): self
{
$this->minMessage = $minMessage;
return $this;
}
public function maxMessage(string $maxMessage): self
{
$this->maxMessage = $maxMessage;
return $this;
}
public function min(int $min): self
{
$this->min = $min;
return $this;
}
public function max(int $max): self
{
$this->max = $max;
return $this;
}
}
NotNull
<?php
declare(strict_types=1);
namespace DevCoder\Validator\Assert;
class NotNull extends AbstractValidator
{
private $message = 'This value should not be null.';
public function validate($value): bool
{
if ($value === null) {
$this->error($this->message, ['value' => $value]);
return false;
}
return true;
}
public function message(string $message): self
{
$this->message = $message;
return $this;
}
}
Шаг 3: Создание экземпляра класса Validation
Этот объект принимает массив вариантов валидации в качестве входных данных. Ключами массива являются имена полей, а значениями — массивы валидаторов.
<?php
$validation = new Validator([
'age' => [(new Integer())->min(18)->max(99), new NotNull()],
'number_of_children' => [new NotNull(), new Integer()],
'salary' => [new NotNull(), new Integer()],
]);
Шаг 4: Валидация данных
После создания экземпляра класса Validation вы можете проверить данные, вызвав метод validate() класса Validation. Этот метод вернет true, если все правила валидации выполнены, и false в противном случае.
<?php
if ($validation->validate($_POST) === true) {
$data = $validation->getData();
// save in database
// redirect in another page
}
return render('template.html.php', [
'errors' => $validation->getErrors()
]);
Пример других правил, которые могут быть добавлены
$validation = new Validation([
'email' => [new NotNull(), new Email()],
'password' => new NotNull(),
'firstname' => [new NotNull(), (new StringLength())->min(3), new Alphabetic()],
'lastname' => [(new StringLength())->min(3)],
'gender' => new Choice(['Mme', 'Mr', null]),
'website' => [new NotNull(), new Url()],
'age' => [new NotNull(), (new Integer())->min(18)],
'invoice_total' => [new NotNull(), new Numeric()],
'active' => [new NotNull(), new Custom(function ($value) {
return is_bool($value);
})]
]);
Чтобы увидеть другие правила валидации, которые можно добавить, посмотрите мой репозиторий на GitHub по следующему URL: https://github.com/devcoder-xyz/php-validator/tree/main/src/Assert.
Идеально подходит для небольшого проекта
Просто и легко!
https://github.com/devcoder-xyz/php-validator