| Server IP : 65.108.144.40 / Your IP : 216.73.217.165 Web Server : Apache/2.4.52 (Ubuntu) System : Linux ubuntu-8gb-hel1-1 5.15.0-173-generic #183-Ubuntu SMP Fri Mar 6 13:29:34 UTC 2026 x86_64 User : dev ( 1000) PHP Version : 8.2.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /var/www/html/project-slim/app/Validators/ |
Upload File : |
<?php
namespace App\Validators;
use App\Exceptions\ValidationException;
/**
* Base validator with comprehensive validation methods
*/
abstract class BaseValidator
{
protected array $data = [];
protected array $errors = [];
/**
* Clear all validation errors
*/
protected function clearErrors(): void
{
$this->errors = [];
}
/**
* Add an error for a specific field
*/
protected function addError(string $field, string $message): void
{
if (!isset($this->errors[$field])) {
$this->errors[$field] = [];
}
$this->errors[$field][] = $message;
}
/**
* Check if there are any validation errors
*/
protected function hasErrors(): bool
{
return !empty($this->errors);
}
/**
* Get all validation errors
*/
protected function getErrors(): array
{
return $this->errors;
}
/**
* Throw ValidationException if there are errors
*/
protected function throwIfErrors(string $message = 'Validation failed'): void
{
if ($this->hasErrors()) {
throw new ValidationException($message, $this->errors);
}
}
/**
* Get error count across all fields
*/
protected function getErrorCount(): int
{
$count = 0;
foreach ($this->errors as $fieldErrors) {
$count += count($fieldErrors);
}
return $count;
}
/**
* Get all errors as a flat array
*/
protected function getAllErrorsFlat(): array
{
$flatErrors = [];
foreach ($this->errors as $field => $fieldErrors) {
foreach ($fieldErrors as $error) {
$flatErrors[] = "{$field}: {$error}";
}
}
return $flatErrors;
}
/**
* Add multiple errors for a field at once
*/
protected function addErrors(string $field, array $messages): void
{
foreach ($messages as $message) {
$this->addError($field, $message);
}
}
/**
* Validate multiple fields with the same rule
*/
public function validateMultiple(array $fields, callable $validator, ?string $message = null): self
{
foreach ($fields as $field) {
$validator($field, $message);
}
return $this;
}
/**
* Batch validate required fields
*/
public function requiredFields(array $fields, ?string $message = null): self
{
foreach ($fields as $field) {
$this->required($field, $message ?? "{$field} is required");
}
return $this;
}
/**
* Validate conditional field (only if condition is met)
*/
public function conditional(string $field, callable $condition, callable $validator): self
{
if ($condition($this->data)) {
$validator($field);
}
return $this;
}
/**
* Validate that a field is required (not empty)
*/
public function required(string $field, ?string $message = null): self
{
if (!isset($this->data[$field]) || empty($this->data[$field])) {
$this->addError($field, $message ?? "{$field} is required");
}
return $this;
}
/**
* Validate email format
*/
public function email(string $field, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (!filter_var($this->data[$field], FILTER_VALIDATE_EMAIL)) {
$this->addError($field, $message ?? 'Please enter a valid email address');
}
}
return $this;
}
/**
* Validate minimum length
*/
public function minLength(string $field, int $length, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (strlen($this->data[$field]) < $length) {
$this->addError($field, $message ?? "{$field} must be at least {$length} characters long");
}
}
return $this;
}
/**
* Validate maximum length
*/
public function maxLength(string $field, int $length, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (strlen($this->data[$field]) > $length) {
$this->addError($field, $message ?? "{$field} must not exceed {$length} characters");
}
}
return $this;
}
/**
* Validate phone number format
*/
public function phone(string $field, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
// Remove all non-digit characters except +
$cleaned = preg_replace('/[^\d+]/', '', $this->data[$field]);
// Check if it's a valid international phone number format
if (!preg_match('/^[\+]?[1-9]\d{9,14}$/', $cleaned)) {
$this->addError($field, $message ?? 'Please enter a valid phone number (10-15 digits)');
}
}
return $this;
}
/**
* Validate numeric value
*/
public function numeric(string $field, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (!is_numeric($this->data[$field])) {
$this->addError($field, $message ?? "{$field} must be a numeric value");
}
}
return $this;
}
/**
* Validate integer value
*/
public function integer(string $field, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (!filter_var($this->data[$field], FILTER_VALIDATE_INT)) {
$this->addError($field, $message ?? "{$field} must be a valid integer");
}
}
return $this;
}
/**
* Validate minimum value
*/
public function min(string $field, float $min, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (is_numeric($this->data[$field]) && (float)$this->data[$field] < $min) {
$this->addError($field, $message ?? "{$field} must be at least {$min}");
}
}
return $this;
}
/**
* Validate maximum value
*/
public function max(string $field, float $max, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (is_numeric($this->data[$field]) && (float)$this->data[$field] > $max) {
$this->addError($field, $message ?? "{$field} must not exceed {$max}");
}
}
return $this;
}
/**
* Validate that two fields match
*/
public function matches(string $field, string $otherField, ?string $message = null): self
{
if (isset($this->data[$field]) && isset($this->data[$otherField])) {
if ($this->data[$field] !== $this->data[$otherField]) {
$this->addError($field, $message ?? "{$field} does not match {$otherField}");
}
}
return $this;
}
/**
* Validate strong password
*/
public function strongPassword(string $field, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
$password = $this->data[$field];
$errors = [];
if (strlen($password) < 8) {
$errors[] = 'at least 8 characters long';
}
if (!preg_match('/[a-z]/', $password)) {
$errors[] = 'contain at least one lowercase letter';
}
if (!preg_match('/[A-Z]/', $password)) {
$errors[] = 'contain at least one uppercase letter';
}
if (!preg_match('/\d/', $password)) {
$errors[] = 'contain at least one number';
}
if (!preg_match('/[^\w\s]/', $password)) {
$errors[] = 'contain at least one special character';
}
if (!empty($errors)) {
$defaultMessage = 'Password must ' . implode(', ', $errors);
$this->addError($field, $message ?? $defaultMessage);
}
}
return $this;
}
/**
* Validate URL format
*/
public function url(string $field, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (!filter_var($this->data[$field], FILTER_VALIDATE_URL)) {
$this->addError($field, $message ?? 'Please enter a valid URL');
}
}
return $this;
}
/**
* Validate that value is in allowed list
*/
public function in(string $field, array $allowed, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (!in_array($this->data[$field], $allowed)) {
$allowedStr = implode(', ', $allowed);
$this->addError($field, $message ?? "{$field} must be one of: {$allowedStr}");
}
}
return $this;
}
/**
* Validate date format
*/
public function date(string $field, string $format = 'Y-m-d', ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
$date = \DateTime::createFromFormat($format, $this->data[$field]);
if (!$date || $date->format($format) !== $this->data[$field]) {
$this->addError($field, $message ?? "{$field} must be a valid date in {$format} format");
}
}
return $this;
}
/**
* Validate boolean value
*/
public function boolean(string $field, ?string $message = null): self
{
if (isset($this->data[$field])) {
if (!is_bool($this->data[$field]) && !in_array($this->data[$field], [0, 1, '0', '1', 'true', 'false'], true)) {
$this->addError($field, $message ?? "{$field} must be a boolean value");
}
}
return $this;
}
/**
* Validate using regular expression
*/
public function regex(string $field, string $pattern, ?string $message = null): self
{
if (isset($this->data[$field]) && !empty($this->data[$field])) {
if (!preg_match($pattern, $this->data[$field])) {
$this->addError($field, $message ?? "{$field} format is invalid");
}
}
return $this;
}
/**
* Comprehensive validation method that runs ALL validations and collects ALL errors
*/
public function validateAll(array $validationRules): self
{
foreach ($validationRules as $field => $rules) {
if (is_array($rules)) {
foreach ($rules as $rule => $params) {
$this->applyValidationRule($field, $rule, $params);
}
}
}
return $this;
}
/**
* Apply a single validation rule to a field
*/
private function applyValidationRule(string $field, string $rule, $params): void
{
switch ($rule) {
case 'required':
$this->required($field, $params['message'] ?? null);
break;
case 'email':
$this->email($field, $params['message'] ?? null);
break;
case 'minLength':
$this->minLength($field, $params['length'], $params['message'] ?? null);
break;
case 'maxLength':
$this->maxLength($field, $params['length'], $params['message'] ?? null);
break;
case 'phone':
$this->phone($field, $params['message'] ?? null);
break;
case 'strongPassword':
$this->strongPassword($field, $params['message'] ?? null);
break;
case 'regex':
$this->regex($field, $params['pattern'], $params['message'] ?? null);
break;
case 'in':
$this->in($field, $params['values'], $params['message'] ?? null);
break;
case 'numeric':
$this->numeric($field, $params['message'] ?? null);
break;
case 'integer':
$this->integer($field, $params['message'] ?? null);
break;
case 'min':
$this->min($field, $params['value'], $params['message'] ?? null);
break;
case 'max':
$this->max($field, $params['value'], $params['message'] ?? null);
break;
case 'url':
$this->url($field, $params['message'] ?? null);
break;
case 'date':
$this->date($field, $params['format'] ?? 'Y-m-d', $params['message'] ?? null);
break;
case 'boolean':
$this->boolean($field, $params['message'] ?? null);
break;
}
}
/**
* Get validation summary with error counts and details
*/
public function getValidationSummary(): array
{
return [
'has_errors' => $this->hasErrors(),
'error_count' => $this->getErrorCount(),
'fields_with_errors' => array_keys($this->errors),
'total_fields_validated' => count($this->data),
'errors' => $this->errors,
'flat_errors' => $this->getAllErrorsFlat()
];
}
}