Validação PHP

Resumo : neste tutorial, você aprenderá a construir uma biblioteca de validação PHP reutilizável do zero.

Nos tutoriais anteriores, você aprendeu como validar um campo de formulário usando as funções filter_input()e filter_var(). Por exemplo, o seguinte limpa e valida o emailcampo na POSTsolicitação:

// validate email
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$inputs['email'] = $email;
if ($email) {
    $email = filter_var($email, FILTER_SANITIZE_EMAIL);
    if (!$email) {
        $errors['email'] = 'Please enter a valid email';
    }
} else {
    $errors['email'] = 'Please enter an email';
}Linguagem de código:  PHP  ( php )

Este código funciona bem. No entanto, é bastante demorado. Além disso, não é reutilizável. Então, vamos construir uma biblioteca de validação reutilizável.

Definir regras de validação

Cada campo possui uma ou mais regras de validação. Por exemplo, o emailcampo é obrigatório e deve ser um endereço de e-mail válido, portanto o campo de e-mail tem duas regras:

  • obrigatório
  • e-mail

O usernameé obrigatório e tem entre 3 e 255 caracteres. Além disso, deve conter apenas letras e números. Portanto, o usernamecampo possui três regras:

  • obrigatório
  • entre 3 e 20 caracteres
  • alfanumérico

Para definir regras para campos, você pode definir um $fieldsarray como este:

$fields = [
    'email'=> 'required | email',
    'username' => 'required | alphanumeric | between: 3,255
];Linguagem de código:  PHP  ( php )

Cada elemento da $fieldsmatriz tem o keynome do campo e o valor como regras. Você usa o | personagem para separar duas regras.

Se uma regra tiver parâmetros, por exemplo, between: 3, 25, você utiliza o :caractere para separar o nome da regra e seus parâmetros. Além disso, usamos a vírgula ,para separar dois parâmetros.

Você criará as seguintes regras:

Regra Nome da regra Parâmetro Significado
obrigatório obrigatório Não O campo está definido e não vazio
alfanumérico alfanumérico Não O campo contém apenas letras e números
e-mail e-mail Não O campo é um endereço de e-mail válido
seguro seguro Não O campo deve ter entre 8 e 64 caracteres e conter pelo menos um número, uma letra maiúscula, uma letra minúscula e um caractere especial. Esta regra é para o campo de senha.
min: 3 min Um número inteiro especifica o comprimento mínimo do campo O comprimento do campo deve ser maior ou igual ao comprimento mínimo, por exemplo, 3
máximo: 255 máx. Um número inteiro especifica o comprimento máximo do campo O comprimento do campo deve ser menor ou igual ao comprimento mínimo, por exemplo, 255
mesmo: outro_campo mesmo O nome de outro campo O valor do campo deve ser igual ao valor de another_field
entre: mínimo, máximo entre min e max são números inteiros que especificam o comprimento mínimo e máximo do campo O comprimento do campo deve estar entre mínimo e máximo.
único: tabela, coluna exclusivo coluna e tabela em um banco de dados relacional. Ou campo e coleção em um banco de dados NoSQL O valor do campo deve ser exclusivo na coluna da tabela em um banco de dados

O exemplo a seguir define os campos com regras:

$fields = [
    'firstname' => 'required, max:255',
    'lastname' => 'required, max: 255',
    'address' => 'required | min: 10, max:255',
    'zipcode' => 'between: 5,6',
    'username' => 'required | alphanumeric | between: 3,255 | unique: users,username',
    'email' => 'required | email | unique: users,email',
    'password' => 'required | secure',
    'password2' => 'required | same:password'
];Linguagem de código:  PHP  ( php )

A função validar()

A biblioteca de validação deve ter a validate()função que aceita um array associativo de dados para validar $datae o $fieldsarray que contém as regras de validação:

function validate(array $data, array $fields)Linguagem de código:  PHP  ( php )

Deve retornar um array que contém os erros de validação, em que a chave de cada elemento é o nome do campo e o valor é a mensagem de erro:

function validate(array $data, array $fields): array
{
    // implementation
}Linguagem de código:  PHP  ( php )

A validate()função precisará iterar sobre o $fields. Para cada campo, ele percorre as regras e valida o valor em relação a cada regra. Se a validação falhar, uma mensagem de erro será adicionada a um $errorsarray:

function validate(array $data, array $fields) : array
{
    $errors = [];

    foreach ($fields as $field => $option) {
        // get the rules of the field
        $rules = split($option);

        foreach ($rules as $rule) {
            // run a validation rule for each field
        }
    }

    return $errors;
}Linguagem de código:  PHP  ( php )

Como as regras de um campo são uma string, você precisa separar as regras usando o |caractere. Além disso, você precisa remover todos os espaços em branco. Para fazer isso, você pode definir uma função de seta para:

  • Primeiro, divida uma string por um separador.
  • Em seguida, corte cada item na matriz de resultados e retorne-o.

A função de seta ficará assim:

$split = fn($str, $separator) => array_map('trim', explode($separator, $str));Linguagem de código:  PHP  ( php )

A função de seta aceita uma string e um separador. A explode()função divide o $strpor $separator. O resultado da explode()função é um array de strings.

A array_map()função executa a trim()função em cada item e retorna um novo array de itens com os espaços em branco removidos.

A função validar() é atualizada para incluir a função de seta da seguinte forma:

function validate(array $data, array $fields)
{

    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    $errors = [];

    foreach ($fields as $field => $option) {
        // get the rules of the field
        $rules =  $split($option, '|');

        foreach ($rules as $rule) {
            // run a validation on each rule
        }
    }

    return $errors;
}Linguagem de código:  PHP  ( php )

Podem $ruleou não ter parâmetros. Se tiver parâmetros, conterá o :caractere. O código a seguir extrai o nome da regra e seus parâmetros:

if (strpos($rule, ':')) {
    [$rule_name, $param_str] = $split($rule, ':');
    $params = $split($$param_str, ',');
} else {
    $rule_name = trim($rule);
}Linguagem de código:  PHP  ( php )

Você usa a $splitfunção de seta neste código para dividir a regra pelo :caractere. Por exemplo:

between: 3,255Linguagem de código:  PHP  ( php )

se tornará duas strings:

'between'
'3,255'Linguagem de código:  PHP  ( php )

O seguinte usa a desestruturação do array para atribuir o primeiro e o segundo elementos do resultado da $splitfunção ao $rule_nameand $param_str:

[$rule_name, $param_str] = $split($rule, ':');Linguagem de código:  PHP  ( php )

Por exemplo, a regra "between : 3, 255"resultará em:

$rule_name = 'between';
$param_str = '3, 255'Linguagem de código:  PHP  ( php )

Para obter a lista de parâmetros, você passa o $param_strpara a $split()função:

$params = $split($param_str, ',');Linguagem de código:  PHP  ( php )

A validate()função ficará parecida com a seguinte:

function validate(array $data, array $fields): array
{
    $errors = [];

    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    foreach ($fields as $field => $option) {

        $rules = $split($option, '|');

        foreach ($rules as $rule) {
            $params = [];
            if (strpos($rule, ':')) {
                [$rule_name, $param_str] = $split($rule, ':');
                $params = $split($param_str, ',');
            } else {
                $rule_name = trim($rule);
            }
        }
    }

    return $errors;
}Linguagem de código:  PHP  ( php )

Agora você tem campos e seus nomes de regras com parâmetros.

Para executar a validação para cada regra, você pode ter uma if-elseifdeclaração grande como esta:

if($rule_name === 'required') {

} elseif($rule_name === 'email') {

} ...Linguagem de código:  PHP  ( php )

No entanto, esta abordagem tem dois problemas:

  • Primeiro, é difícil manter um grande if-elseif.
  • Segundo, se quiser adicionar uma nova regra, você precisará alterar a função activate(), o que não é o ideal.

Você deseja que a função validar() seja mais dinâmica para que não precisemos alterar a função quando você adicionar uma nova regra posteriormente. Para fazer isso, você pode usar a variável funções .

Para cada regra, você chama a função de validação dinamicamente assim:

$rule_name($data, $field, ...$params);Linguagem de código:  PHP  ( php )

O primeiro e o segundo argumentos da função de validação são $datae $field. O terceiro argumento é uma lista de argumentos espalhados pelo array $params.

Por exemplo, se $paramsfor [3,255], ...$paramsretornará 3 e 255.

Portanto, a regra 'between: 3, 255'do usernamecom o valor "john"resultará na seguinte chamada de função:

between($data, 'username', 3, 255)Linguagem de código:  PHP  ( php )

Para evitar que a função de validação colida com a função padrão como min, você pode prefixar nossa função de validação com a string is_. Por exemplo, a betweenregra executará a is_betweenfunção de validação.

O seguinte mostra a validate()função atualizada:

function validate(array $data, array $fields): array
{
    $errors = [];

    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    foreach ($fields as $field => $option) {

        $rules = $split($option, '|');

        foreach ($rules as $rule) {
            $params = [];
            if (strpos($rule, ':')) {
                [$rule_name, $param_str] = $split($rule, ':');
                $params = $split($param_str, ',');
            } else {
                $rule_name = trim($rule);
            }

            $fn = 'is_' . $rule_name;

            if (is_callable($fn)) {
                $pass = $fn($data, $field, ...$params);
            }
        }
    }

    return $errors;
}Linguagem de código:  PHP  ( php )

Nesta validate()função, você usa a is_callable()função para verificar se is_rule_namepode ser chamada antes de chamá-la.

Retornando mensagens de erro

Cada regra deve ter uma mensagem de erro específica. Se a validação falhar, você precisará armazenar a mensagem de erro no $errorarray. Além disso, você poderá retornar uma mensagem de erro com os parâmetros.

Por exemplo, se a seguinte regra falhar:

'username' => 'between: 3, 255'Linguagem de código:  PHP  ( php )

Você deve retornar a mensagem de erro:

The username should be between 3 and 255 characters.Linguagem de código:  PHP  ( php )

Para fazer isso, primeiro você define as mensagens de erro de validação padrão:

const DEFAULT_VALIDATION_ERRORS = [
    'required' => 'Please enter the %s',
    'email' => 'The %s is not a valid email address',
    'min' => 'The %s must have at least %s characters',
    'max' => 'The %s must have at most %s characters',
    'between' => 'The %s must have between %d and %d characters',
    'same' => 'The %s must match with %s',
    'alphanumeric' => 'The %s should have only letters and numbers',
    'secure' => 'The %s must have between 8 and 64 characters and contain at least one number, one upper case letter, one lower case letter and one special character',
    'unique' => 'The %s already exists',
];Linguagem de código:  PHP  ( php )

Na DEFAULT_VALIDATION_ERRORSmatriz, a chave é o nome da regra, enquanto o valor é a mensagem de erro. A mensagem de erro possui %sespaços %dreservados para que você possa usar a sprintf()função para formatar a mensagem de erro com parâmetros como este:

sprintf(DEFAULT_VALIDATION_ERRORS[$rule_name], $field, ...$params);Linguagem de código:  PHP  ( php )

Aqui está a validate()função com mensagens de erro:

function validate(array $data, array $fields): array
{
    $errors = [];

    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    foreach ($fields as $field => $option) {

        $rules = $split($option, '|');

        foreach ($rules as $rule) {
            $params = [];
            if (strpos($rule, ':')) {
                [$rule_name, $param_str] = $split($rule, ':');
                $params = $split($param_str, ',');
            } else {
                $rule_name = trim($rule);
            }

            $fn = 'is_' . $rule_name;

            if (is_callable($fn)) {
                $pass = $fn($data, $field , ...$params);
                if (!$pass) {
                    $errors[$field] = sprintf(DEFAULT_VALIDATION_ERRORS[$rule_name], $field, ...$params);
                }
            }
        }
    }

    return $errors;
}Linguagem de código:  PHP  ( php )

Adicionando mensagens de erro personalizadas

A validate()função usa as mensagens de erro padrão. No entanto, às vezes, você precisa passar nossa mensagem de erro personalizada.

Por exemplo, você pode querer usar uma mensagem como 'The username is required'a requiredregra em vez de usar a padrão.

Para fazer isso, você pode adicionar um terceiro parâmetro à validate()função para armazenar as mensagens de erro personalizadas. Este parâmetro é uma matriz de itens cuja chave é o nome do campo e o valor é a mensagem de erro. Por exemplo:

['required' => 'The %s is required']Linguagem de código:  PHP  ( php )

Se você passar o array de mensagens de erro personalizado para a validate()função, ele será usado em vez do padrão.

Aqui está a validate()função com as mensagens de erro personalizadas:

function validate(array $data, array $fields, array $messages = []): array
{

    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    // set the validation messages
    $validation_errors = array_merge(DEFAULT_VALIDATION_ERRORS, $messages);

    $errors = [];

    foreach ($fields as $field => $option) {

        $rules = $split($option, '|');

        foreach ($rules as $rule) {

            $params = [];

            if (strpos($rule, ':')) {
                [$rule_name, $param_str] = $split($rule, ':');
                $params = $split($param_str, ',');
            } else {
                $rule_name = trim($rule);
            }

            $fn = 'is_' . $rule_name;

            if (is_callable($fn)) {
                $pass = $fn($data, $field, ...$params);
                if (!$pass) {
                    $errors[$field] = sprintf($validation_errors[$rule_name], $field, ...$params);
                }
            }
        }
    }

    return $errors;
}Linguagem de código:  PHP  ( php )

Na validate()função:

Primeiro, mescle as mensagens de erro padrão com as mensagens de erro personalizadas. A mensagem de erro personalizada substituirá as mensagens de erro padrão:

$validation_errors = array_merge(DEFAULT_VALIDATION_ERRORS, $messages);Linguagem de código:  PHP  ( php )

Segundo, obtenha as mensagens de erro do array $validation_errorsem vez do DEFAULT_VALIDATION_ERRORSarray:

$errors[$field] = sprintf($validation_errors[$rule_name], $field, ...$params);Linguagem de código:  PHP  ( php )

Adicione uma mensagem de erro personalizada para cada campo e regra

Até agora, uma mensagem de erro personalizada é aplicada a uma regra específica de todos os campos. Por exemplo:

['required' => 'The % is required']Linguagem de código:  CSS  ( css )

E você tem dois campos:

[
   'username' => 'required',
   'password' => 'required'
]Linguagem de código:  PHP  ( php )

A mensagem de erro personalizada será aplicada aos campos usernamee password.

Às vezes, você deseja aplicar uma mensagem de erro personalizada para um campo específico. Por exemplo, você deseja que a regra obrigatória do nome de usuário tenha uma mensagem como:

Please use your username to sign inLinguagem de código:  PHP  ( php )

E a mensagem de erro personalizada para o campo de senha é:

Use your password to sign inLinguagem de código:  PHP  ( php )

Para fazer isso, você precisa aprimorar a validate()função:

function validate(array $data, array $fields, array $messages = []): arrayLinguagem de código:  PHP  ( php )

A $messagesvariável terá a seguinte aparência para oferecer suporte a uma mensagem personalizada para uma regra e um campo específicos:

[
    'required' => 'The %s is required',
    'username' => ['required'=> 'Please use your username to sign in']
]Linguagem de código:  PHP  ( php )

O primeiro elemento definirá a mensagem de erro para a regra obrigatória de todos os campos:

'required' => 'The %s is required'Linguagem de código:  PHP  ( php )

E o segundo elemento definirá a mensagem de erro para a regra necessária do nome de usuário:

'username' => ['required'=> 'Please use your username to sign in']Linguagem de código:  PHP  ( php )

Se o valor do elemento for uma string, será a mensagem de erro de uma regra de todos os campos. A chave é o nome da regra e o valor é uma mensagem de erro.

Entretanto, se o valor do elemento for um array, a chave será o nome do campo e o valor será um array de regras e mensagens de erro.

O seguinte retorna uma matriz que contém uma lista de regras e mensagens de erro:

$rule_messages = array_filter($messages, fn($message) =>  is_string($message));Linguagem de código:  PHP  ( php )

Depois de ter esse $rule_messagesarray, você pode mesclá-lo com as mensagens de erro padrão:

$validation_errors = array_merge(DEFAULT_VALIDATION_ERRORS, $rule_messages);Linguagem de código:  PHP  ( php )

A seguir, obtém-se a mensagem de erro para um campo e uma regra, se existir:

$message[$field][$rule] Linguagem de código:  PHP  ( php )

Caso contrário, você pode receber a mensagem de erro acima $validation_errors:

$validation_errors[$rule_name]Linguagem de código:  PHP  ( php )

Veja a seguir a validate()função com a nova lógica para mensagens de erro personalizadas:

/**
 * Validate
 * @param array $data
 * @param array $fields
 * @param array $messages
 * @return array
 */
function validate(array $data, array $fields, array $messages = []): array
{
    // Split the array by a separator, trim each element
    // and return the array
    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    // get the message rules
    $rule_messages = array_filter($messages, fn($message) =>  is_string($message));
    
    // overwrite the default message
    $validation_errors = array_merge(DEFAULT_VALIDATION_ERRORS, $rule_messages);

    $errors = [];

    foreach ($fields as $field => $option) {

        $rules = $split($option, '|');

        foreach ($rules as $rule) {
            // get rule name params
            $params = [];
            // if the rule has parameters e.g., min: 1
            if (strpos($rule, ':')) {
                [$rule_name, $param_str] = $split($rule, ':');
                $params = $split($param_str, ',');
            } else {
                $rule_name = trim($rule);
            }
            // by convention, the callback should be is_<rule> e.g.,is_required
            $fn = 'is_' . $rule_name;

            if (is_callable($fn)) {
                $pass = $fn($data, $field, ...$params);
                if (!$pass) {
                    // get the error message for a specific field and rule if exists
                    // otherwise get the error message from the $validation_errors
                    $errors[$field] = sprintf(
                        $messages[$field][$rule_name] ?? $validation_errors[$rule_name],
                        $field,
                        ...$params
                    );
                }
            }
        }
    }

    return $errors;
}
Linguagem de código:  PHP  ( php )

Até agora, você completou a validate()função. Vamos definir a função de validação para cada regra.

Funções de validação

Cada função de validação deve ter a seguinte assinatura:

function validation_function(array $data, string $field, ....$params) : boolLinguagem de código:  PHP  ( php )

regra obrigatória

A is_required()função a seguir retorna verdadeiro se o valor estiver definido e não vazio:

function is_required(array $data, string $field): bool
{
    return isset($data[$field]) && trim($data[$field]) !== '';
}Linguagem de código:  PHP  ( php )

regra de e-mail

A is_email()função a seguir retorna verdadeiro se o valor for um endereço de e-mail válido:

function is_email(array $data, string $field): bool
{
    if (empty($data[$field])) {
        return true;
    }

    return filter_var($data[$field], FILTER_VALIDATE_EMAIL);
}Linguagem de código:  PHP  ( php )

Se $data[$field]não estiver definido ou vazio, a is_email()função também retornará true.

regra mínima

A is_min()função a seguir retorna verdadeiro se o comprimento do valor do campo for maior ou igual a $min:

function is_min(array $data, string $field, int $min): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return mb_strlen($data[$field]) >= $min;
}
Linguagem de código:  PHP  ( php )

regra máxima

A is_max()função a seguir retorna verdadeiro se o comprimento do valor do campo for menor ou igual a $max:

function is_max(array $data, string $field, int $max): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return mb_strlen($data[$field]) <= $max;
}
Linguagem de código:  PHP  ( php )

entre regra

A is_between()função a seguir retorna truese o comprimento do valor do campo estiver entre $mine $max:

function is_between(array $data, string $field, int $min, int $max): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    $len = mb_strlen($data[$field]);
    return $len >= $min && $len <= $max;
}Linguagem de código:  PHP  ( php )

regra alfanumérica

O is_alphanumeric()retorna verdadeiro se o valor do campo contiver apenas letras ou caracteres:

function is_alphanumeric(array $data, string $field): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return ctype_alnum($data[$field]);
}Linguagem de código:  PHP  ( php )

mesma regra

A is_same()função retorna truese o valor do campo for igual ao valor do outro campo:

function is_same(array $data, string $field, string $other): bool
{
    if (isset($data[$field], $data[$other])) {
        return $data[$field] === $data[$other];
    }

    if (!isset($data[$field]) && !isset($data[$other])) {
        return true;
    }

    return false;
}Linguagem de código:  PHP  ( php )

regra segura

O is_secure()retorno é verdadeiro se o valor do campo deve ter entre 8 e 64 caracteres e conter pelo menos um número, uma letra maiúscula, uma letra minúscula e um caractere especial:

function is_secure(array $data, string $field): bool
{
    if (!isset($data[$field])) {
        return false;
    }

    $pattern = "#.*^(?=.{8,64})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*\W).*$#";
    return preg_match($pattern, $data[$field]);
}
Linguagem de código:  PHP  ( php )

regra única

Para a uniqueregra, você precisa de uma conexão com o banco de dados e selecionar o valor $columnespecificado $tablenos parâmetros da regra.

Primeiro, crie um novo arquivo chamado database.phpna configpasta e adicione as seguintes constantes:

<?php

const DB_HOST = 'localhost';
const DB_NAME = 'auth';
const DB_USER = 'root';
const DB_PASSWORD = '';Linguagem de código:  PHP  ( php )

Segundo, carregue config/database.phpe defina uma função para conectar-se ao banco de dados :

function db()
{
    static $pdo;
    if (!$pdo) {
        $pdo = connect(DB_HOST, DB_NAME, DB_USER, DB_PASSWORD);
    }
    return $pdo;
}Linguagem de código:  PHP  ( php )

A is_unique()função a seguir retorna verdadeiro se o valor for $columnúnico $table:

function is_unique(array $data, string $field, string $table, string $column): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    $sql = "SELECT $column FROM $table WHERE $column = :value";

    $stmt = db()->prepare($sql);
    $stmt->bindValue(":value", $data[$field]);

    $stmt->execute();

    return $stmt->fetchColumn() === false;
}Linguagem de código:  PHP  ( php )

Juntando tudo

O seguinte mostra todas as funções na biblioteca de validação:

<?php

require __DIR__ . '/../config/database.php';

const DEFAULT_VALIDATION_ERRORS = [
    'required' => 'Please enter the %s',
    'email' => 'The %s is not a valid email address',
    'min' => 'The %s must have at least %s characters',
    'max' => 'The %s must have at most %s characters',
    'between' => 'The %s must have between %d and %d characters',
    'same' => 'The %s must match with %s',
    'alphanumeric' => 'The %s should have only letters and numbers',
    'secure' => 'The %s must have between 8 and 64 characters and contain at least one number, one upper case letter, one lower case letter and one special character',
    'unique' => 'The %s already exists',
];


/**
 * Validate
 * @param array $data
 * @param array $fields
 * @param array $messages
 * @return array
 */
function validate(array $data, array $fields, array $messages = []): array
{
    // Split the array by a separator, trim each element
    // and return the array
    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    // get the message rules
    $rule_messages = array_filter($messages, fn($message) => is_string($message));
    // overwrite the default message
    $validation_errors = array_merge(DEFAULT_VALIDATION_ERRORS, $rule_messages);

    $errors = [];

    foreach ($fields as $field => $option) {

        $rules = $split($option, '|');

        foreach ($rules as $rule) {
            // get rule name params
            $params = [];
            // if the rule has parameters e.g., min: 1
            if (strpos($rule, ':')) {
                [$rule_name, $param_str] = $split($rule, ':');
                $params = $split($param_str, ',');
            } else {
                $rule_name = trim($rule);
            }
            // by convention, the callback should be is_<rule> e.g.,is_required
            $fn = 'is_' . $rule_name;

            if (is_callable($fn)) {
                $pass = $fn($data, $field, ...$params);
                if (!$pass) {
                    // get the error message for a specific field and rule if exists
                    // otherwise get the error message from the $validation_errors
                    $errors[$field] = sprintf(
                        $messages[$field][$rule_name] ?? $validation_errors[$rule_name],
                        $field,
                        ...$params
                    );
                }
            }
        }
    }

    return $errors;
}

/**
 * Return true if a string is not empty
 * @param array $data
 * @param string $field
 * @return bool
 */
function is_required(array $data, string $field): bool
{
    return isset($data[$field]) && trim($data[$field]) !== '';
}

/**
 * Return true if the value is a valid email
 * @param array $data
 * @param string $field
 * @return bool
 */
function is_email(array $data, string $field): bool
{
    if (empty($data[$field])) {
        return true;
    }

    return filter_var($data[$field], FILTER_VALIDATE_EMAIL);
}

/**
 * Return true if a string has at least min length
 * @param array $data
 * @param string $field
 * @param int $min
 * @return bool
 */
function is_min(array $data, string $field, int $min): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return mb_strlen($data[$field]) >= $min;
}

/**
 * Return true if a string cannot exceed max length
 * @param array $data
 * @param string $field
 * @param int $max
 * @return bool
 */
function is_max(array $data, string $field, int $max): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return mb_strlen($data[$field]) <= $max;
}

/**
 * @param array $data
 * @param string $field
 * @param int $min
 * @param int $max
 * @return bool
 */
function is_between(array $data, string $field, int $min, int $max): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    $len = mb_strlen($data[$field]);
    return $len >= $min && $len <= $max;
}

/**
 * Return true if a string equals the other
 * @param array $data
 * @param string $field
 * @param string $other
 * @return bool
 */
function is_same(array $data, string $field, string $other): bool
{
    if (isset($data[$field], $data[$other])) {
        return $data[$field] === $data[$other];
    }

    if (!isset($data[$field]) && !isset($data[$other])) {
        return true;
    }

    return false;
}

/**
 * Return true if a string is alphanumeric
 * @param array $data
 * @param string $field
 * @return bool
 */
function is_alphanumeric(array $data, string $field): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return ctype_alnum($data[$field]);
}

/**
 * Return true if a password is secure
 * @param array $data
 * @param string $field
 * @return bool
 */
function is_secure(array $data, string $field): bool
{
    if (!isset($data[$field])) {
        return false;
    }

    $pattern = "#.*^(?=.{8,64})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*\W).*$#";
    return preg_match($pattern, $data[$field]);
}


/**
 * Connect to the database and returns an instance of PDO class
 * or false if the connection fails
 *
 * @return PDO
 */
function db(): PDO
{
    static $pdo;
    // if the connection is not initialized
    // connect to the database
    if (!$pdo) {
        $pdo = new PDO(
            sprintf("mysql:host=%s;dbname=%s;charset=UTF8", DB_HOST, DB_NAME),
            DB_USER,
            DB_PASSWORD,
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    }
    return $pdo;
}

/**
 * Return true if the $value is unique in the column of a table
 * @param array $data
 * @param string $field
 * @param string $table
 * @param string $column
 * @return bool
 */
function is_unique(array $data, string $field, string $table, string $column): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    $sql = "SELECT $column FROM $table WHERE $column = :value";

    $stmt = db()->prepare($sql);
    $stmt->bindValue(":value", $data[$field]);

    $stmt->execute();

    return $stmt->fetchColumn() === false;
}
Linguagem de código:  PHP  ( php )

Testando a biblioteca de validação

Para testar a uniqueregra, é necessário criar um novo banco de dados e uma tabela com o usernamecampo, por exemplo.

Primeiro, crie um novo banco de dados chamado auth:

CREATE DATABASE auth;Linguagem de código:  SQL (linguagem de consulta estruturada)  ( sql )

Segundo, crie uma nova userstabela:

CREATE TABLE users(
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL UNIQUE,
    email VARCHAR(300) NOT NULL UNIQUE
);Linguagem de código:  SQL (linguagem de consulta estruturada)  ( sql )

Observe que para fins de teste, não precisamos ter o passwordcampo.

Terceiro, insira uma linha na userstabela:

INSERT INTO users(username, email)
VALUES('bob', '[email protected]');Linguagem de código:  SQL (linguagem de consulta estruturada)  ( sql )

O exemplo a seguir usa a validate()função para executar um teste:

<?php

require __DIR__ . '/validation.php';

$data = [
    'firstname' => '',
    'username' => 'bob',
    'address' => 'This is my address',
    'zipcode' => '999',
    'email' => 'jo@',
    'password' => 'test123',
    'password2' => 'test',
];



$fields = [
    'firstname' => 'required, max:255',
    'lastname' => 'required, max: 255',
    'address' => 'required | min: 10, max:255',
    'zipcode' => 'between: 5,6',
    'username' => 'required | alphanumeric | between: 3,255 | unique: users,username',
    'email' => 'required | email | unique: users,email',
    'password' => 'required | secure',
    'password2' => 'required | same:password'
];


$errors = validate($data, $fields, [
    'required' => 'The %s is required',
    'password2' => ['same'=> 'Please enter the same password again']]
);

print_r($errors);Linguagem de código:  PHP  ( php )

Saída:

Array
(
    [firstname] => The firstname is required
    [lastname] => The lastname is required
    [zipcode] => The zipcode must have between 5 and 6 characters
    [username] => The username already exists
    [email] => The email is not a valid email address
    [password] => The password must have between 8 and 64 characters and contain at least one number, one upper case letter, one lower case letter and o
ne special character
    [password2] => Please enter the same password again
)
Linguagem de código:  PHP  ( php )

Adicionando uma nova regra de validação

Para adicionar uma nova regra, você precisa fazer duas coisas:

Primeiro, adicione uma mensagem de erro padrão ao DEFAULT_VALIDATION_ERRORSarray.

const DEFAULT_VALIDATION_ERRORS = [
    // ...
    new_rule => "Error message for the new rule"

];Linguagem de código:  JavaScript  ( javascript )

Segundo, defina uma função de validação com a seguinte assinatura:

function is_new_rule(array $data, string $field, ....$params) : boolLinguagem de código:  PHP  ( php )

Neste tutorial, você aprendeu como construir uma biblioteca de validação reutilizável em PHP.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *