first commit
This commit is contained in:
222
app/Common/Helpers/DataValidator.php
Normal file
222
app/Common/Helpers/DataValidator.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
namespace Workbloom\Helpers;
|
||||
|
||||
class DataValidator {
|
||||
private array $inputs;
|
||||
private array $errors = [];
|
||||
|
||||
public function __construct(array $inputs) {
|
||||
$this->inputs = $inputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida um campo usando uma regra específica.
|
||||
*/
|
||||
public function validate(string $field, callable|string $rule, string $message = '', array $additionalParams = []): static {
|
||||
$value = $this->inputs[$field] ?? null;
|
||||
|
||||
// Se for string, monta callable usando a própria classe
|
||||
if (is_string(value: $rule) && method_exists(object_or_class: self::class, method: $rule)) {
|
||||
$rule = [self::class, $rule];
|
||||
}
|
||||
|
||||
$params = array_merge([$value], $additionalParams);
|
||||
|
||||
$result = call_user_func_array(callback: $rule, args: $params);
|
||||
|
||||
// Caso seja validação de senha (retorna array)
|
||||
if (is_array(value: $result) && isset($result['valid'])) {
|
||||
if (!$result['valid']) {
|
||||
$this->errors[$field] = [
|
||||
'status' => 'is_invalid',
|
||||
'message' => $result['errors']
|
||||
];
|
||||
}
|
||||
}
|
||||
// Para outras validações booleanas
|
||||
elseif ($result === false) {
|
||||
$this->errors[$field] = [
|
||||
'status' => 'is_invalid',
|
||||
'message' => $message
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrors(): array {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function passes(): bool {
|
||||
return empty($this->errors);
|
||||
}
|
||||
|
||||
// ---------- Regras comuns ----------
|
||||
|
||||
public static function validateEmail(string $value): bool {
|
||||
return (bool) filter_var(value: $value, filter: FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
public static function validatePhone(string $value, int $minLength = 10, int $maxLength = 13): bool {
|
||||
$digits = preg_replace(pattern: '/\D+/', replacement: '', subject: $value);
|
||||
$len = strlen(string: $digits);
|
||||
return $len >= $minLength && $len <= $maxLength;
|
||||
}
|
||||
|
||||
public static function validateCpf(string $value): bool {
|
||||
$cpf = preg_replace(pattern: '/\D+/', replacement: '', subject: $value);
|
||||
|
||||
if (strlen(string: $cpf) !== 11 || preg_match(pattern: '/(\d)\1{10}/', subject: $cpf)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($t = 9; $t < 11; $t++) {
|
||||
$d = 0;
|
||||
for ($c = 0; $c < $t; $c++) {
|
||||
$d += $cpf[$c] * (($t + 1) - $c);
|
||||
}
|
||||
$d = ((10 * $d) % 11) % 10;
|
||||
if ($cpf[$c] != $d) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function validateCnpj(string $value): bool {
|
||||
// Remove qualquer máscara (pontos, barras, hífens)
|
||||
$cleanedValue = preg_replace(pattern: '/[.\-\/]/', replacement: '', subject: $value);
|
||||
|
||||
// O CNPJ sem máscara deve ter 14 caracteres (12 alfanuméricos + 2 dígitos)
|
||||
if (strlen(string: $cleanedValue) !== 14) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cnpjBase = strtoupper(string: substr(string: $cleanedValue, offset: 0, length: 12)); // Os 12 caracteres alfanuméricos
|
||||
$dvRecebidos = substr(string: $cleanedValue, offset: 12, length: 2); // Os 2 dígitos verificadores
|
||||
|
||||
// Verifica se os dois últimos caracteres são realmente dígitos
|
||||
if (!ctype_digit(text: $dvRecebidos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mapeamento de Valor para cálculo do DV (do documento) [cite: 19]
|
||||
$mapaValores = [
|
||||
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
|
||||
'A' => 17, 'B' => 18, 'C' => 19, 'D' => 20, 'E' => 21, 'F' => 22, 'G' => 23, 'H' => 24, 'I' => 25,
|
||||
'J' => 26, 'K' => 27, 'L' => 28, 'M' => 29, 'N' => 30, 'O' => 31, 'P' => 32, 'Q' => 33, 'R' => 34,
|
||||
'S' => 35, 'T' => 36, 'U' => 37, 'V' => 38, 'W' => 39, 'X' => 40, 'Y' => 41, 'Z' => 42
|
||||
];
|
||||
|
||||
// Array de pesos de 2 a 9 (repetidos da direita para a esquerda, recomeçando depois do 8º caracter) [cite: 26, 27]
|
||||
// O documento usa [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] para o 1º DV no exemplo de 12 caracteres [cite: 27]
|
||||
$pesos1Dv = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
||||
|
||||
// Para o 2º DV, são 13 caracteres (os 12 de base + o 1º DV), e a contagem de pesos recomeça (2 a 9)
|
||||
// O documento usa [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] para 13 caracteres no exemplo [cite: 40]
|
||||
$pesos2Dv = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
||||
|
||||
$cnpjParaCalculo = $cnpjBase;
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$soma = 0;
|
||||
$pesos = ($i === 0) ? $pesos1Dv : $pesos2Dv;
|
||||
$tamanho = strlen(string: $cnpjParaCalculo);
|
||||
|
||||
// O documento usa os pesos na ordem da esquerda para a direita no cálculo [cite: 27, 40]
|
||||
for ($j = 0; $j < $tamanho; $j++) {
|
||||
$caractere = $cnpjParaCalculo[$j];
|
||||
$valor = $mapaValores[$caractere] ?? null;
|
||||
|
||||
// Se o caractere não for um dos mapeados (ex: um caractere especial ou minúsculo que não foi maiusculizado)
|
||||
if ($valor === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$peso = $pesos[$j];
|
||||
$soma += $valor * $peso;
|
||||
}
|
||||
|
||||
// Obter o resto da divisão do somatório por 11 [cite: 31]
|
||||
$resto = $soma % 11;
|
||||
|
||||
// Se o resto da divisão for igual a 1 ou 0, o primeiro dígito será igual a 0 (zero)[cite: 32].
|
||||
// Senão, o primeiro dígito será igual ao resultado de 11 - resto[cite: 33].
|
||||
$digitoCalculado = ($resto < 2) ? 0 : 11 - $resto;
|
||||
|
||||
$dvRecebido = (int) $dvRecebidos[$i];
|
||||
|
||||
if ($dvRecebido !== $digitoCalculado) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Para o cálculo do segundo DV, acrescentar o primeiro DV (calculado) ao final do CNPJ base [cite: 38]
|
||||
if ($i === 0) {
|
||||
$cnpjParaCalculo .= $digitoCalculado;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function notEmpty(mixed $value): bool {
|
||||
if (is_array(value: $value)) {
|
||||
return !empty(array_filter(array: $value, callback: fn($v) => $v !== null && $v !== ''));
|
||||
}
|
||||
return !empty(trim(string: (string) $value));
|
||||
}
|
||||
|
||||
public static function validatePassword(string $password, array $rules = []): array {
|
||||
$errors = [];
|
||||
|
||||
$minLength = $rules['minLength'] ?? 8;
|
||||
$maxLength = $rules['maxLength'] ?? 64;
|
||||
$requireUpper = $rules['upper'] ?? true;
|
||||
$requireLower = $rules['lower'] ?? true;
|
||||
$requireDigit = $rules['digit'] ?? true;
|
||||
$requireSymbol = $rules['symbol'] ?? true;
|
||||
|
||||
if (strlen(string: $password) < $minLength) {
|
||||
$errors[] = "Senha deve ter no mínimo {$minLength} caracteres.";
|
||||
}
|
||||
|
||||
if (strlen(string: $password) > $maxLength) {
|
||||
$errors[] = "Senha deve ter no máximo {$maxLength} caracteres.";
|
||||
}
|
||||
|
||||
if ($requireUpper && !preg_match(pattern: '/[A-Z]/', subject: $password)) {
|
||||
$errors[] = "Senha deve conter pelo menos uma letra maiúscula.";
|
||||
}
|
||||
|
||||
if ($requireLower && !preg_match(pattern: '/[a-z]/', subject: $password)) {
|
||||
$errors[] = "Senha deve conter pelo menos uma letra minúscula.";
|
||||
}
|
||||
|
||||
if ($requireDigit && !preg_match(pattern: '/\d/', subject: $password)) {
|
||||
$errors[] = "Senha deve conter pelo menos um número.";
|
||||
}
|
||||
|
||||
if ($requireSymbol && !preg_match(pattern: '/[\W_]/', subject: $password)) {
|
||||
$errors[] = "Senha deve conter pelo menos um símbolo.";
|
||||
}
|
||||
|
||||
return [
|
||||
'valid' => empty($errors),
|
||||
'errors' => $errors
|
||||
];
|
||||
}
|
||||
|
||||
public static function validateList(mixed $value, array $validOptions): bool {
|
||||
if (is_array(value: $value)) {
|
||||
foreach ($value as $item) {
|
||||
if (!in_array(needle: $item, haystack: $validOptions, strict: true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array(needle: $value, haystack: $validOptions, strict: true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user