| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- <?php
- namespace Libs;
- /**
- * Validador de payloads de entrada.
- *
- * Padroniza as validações de input em todos os controllers, evitando
- * casting solto e verificações manuais espalhadas. Uso encadeado:
- *
- * $validator = (new Validator($body))
- * ->required('email')->email('email')
- * ->required('password')->minLength('password', 8);
- *
- * if ($validator->fails()) {
- * return Payload::fail($validator->firstError(), [], 'E_VALIDATE', 400);
- * }
- *
- * Regras só são aplicadas se o campo não já tiver acumulado erro, evitando
- * mensagens redundantes para o mesmo campo.
- */
- final class Validator
- {
- private array $data;
- /** @var array<string, string> erro por campo (primeiro erro encontrado) */
- private array $errors = [];
- public function __construct(array $data)
- {
- $this->data = $data;
- }
- public function required(string $field, ?string $label = null): self
- {
- if ($this->hasError($field)) {
- return $this;
- }
- $value = $this->data[$field] ?? null;
- if ($value === null || (is_string($value) && trim($value) === '') || $value === []) {
- $this->addError($field, sprintf('%s is required', $label ?? $field));
- }
- return $this;
- }
- public function email(string $field, ?string $label = null): self
- {
- if ($this->hasError($field) || !isset($this->data[$field])) {
- return $this;
- }
- $value = trim((string) $this->data[$field]);
- if (filter_var($value, FILTER_VALIDATE_EMAIL) === false) {
- $this->addError($field, sprintf('%s must be a valid email', $label ?? $field));
- }
- return $this;
- }
- /**
- * Telefone aceita apenas dígitos (após remover espaços, +, -, () ),
- * com comprimento entre $min e $max.
- */
- public function phone(string $field, int $min = 8, int $max = 15, ?string $label = null): self
- {
- if ($this->hasError($field) || !isset($this->data[$field])) {
- return $this;
- }
- $digits = preg_replace('/\D+/', '', (string) $this->data[$field]);
- $length = strlen((string) $digits);
- if ($length < $min || $length > $max) {
- $this->addError($field, sprintf('%s must have between %d and %d digits', $label ?? $field, $min, $max));
- }
- return $this;
- }
- public function minLength(string $field, int $min, ?string $label = null): self
- {
- if ($this->hasError($field) || !isset($this->data[$field])) {
- return $this;
- }
- if (mb_strlen((string) $this->data[$field]) < $min) {
- $this->addError($field, sprintf('%s must be at least %d characters', $label ?? $field, $min));
- }
- return $this;
- }
- public function maxLength(string $field, int $max, ?string $label = null): self
- {
- if ($this->hasError($field) || !isset($this->data[$field])) {
- return $this;
- }
- if (mb_strlen((string) $this->data[$field]) > $max) {
- $this->addError($field, sprintf('%s must be at most %d characters', $label ?? $field, $max));
- }
- return $this;
- }
- /**
- * Garante que o valor é um inteiro dentro de [$min, $max] (limites opcionais).
- */
- public function intRange(string $field, ?int $min = null, ?int $max = null, ?string $label = null): self
- {
- if ($this->hasError($field) || !isset($this->data[$field])) {
- return $this;
- }
- $value = filter_var($this->data[$field], FILTER_VALIDATE_INT);
- if ($value === false) {
- $this->addError($field, sprintf('%s must be an integer', $label ?? $field));
- return $this;
- }
- if (($min !== null && $value < $min) || ($max !== null && $value > $max)) {
- $this->addError($field, sprintf('%s is out of allowed range', $label ?? $field));
- }
- return $this;
- }
- /**
- * Garante que o valor está em uma lista de valores permitidos.
- */
- public function in(string $field, array $allowed, ?string $label = null): self
- {
- if ($this->hasError($field) || !isset($this->data[$field])) {
- return $this;
- }
- if (!in_array($this->data[$field], $allowed, true)) {
- $this->addError($field, sprintf('%s has an invalid value', $label ?? $field));
- }
- return $this;
- }
- public function fails(): bool
- {
- return !empty($this->errors);
- }
- /**
- * @return array<string, string>
- */
- public function errors(): array
- {
- return $this->errors;
- }
- public function firstError(): ?string
- {
- if (empty($this->errors)) {
- return null;
- }
- return reset($this->errors);
- }
- private function hasError(string $field): bool
- {
- return isset($this->errors[$field]);
- }
- private function addError(string $field, string $message): void
- {
- if (!isset($this->errors[$field])) {
- $this->errors[$field] = $message;
- }
- }
- }
|