-
Notifications
You must be signed in to change notification settings - Fork 2
From Validator
The Validator class provides a fluent, intuitive interface for validating form data and request inputs in the Webrium framework. It includes comprehensive validation rules, security features, and localization support.
- Installation
- Basic Usage
- Available Validation Rules
- Conditional Validation
- Error Handling
- Security Features
- Localization
- Complete Examples
- API Reference
- Best Practices
The Validator class is included in the Webrium framework. No additional installation is required.
use Webrium\Validator;use Webrium\Validator;
// Create validator instance (uses request data by default)
$validator = new Validator();
// Define validation rules
$validator->field('username')->required()->alphaNum()->min(3)->max(20);
$validator->field('email')->required()->email();
$validator->field('age')->required()->integer()->min(18);
// Execute validation
if ($validator->fails()) {
echo $validator->getFirstErrorMessage();
exit;
}
// Continue with valid data$data = [
'username' => 'john_doe',
'email' => 'john@example.com',
'age' => 25
];
$validator = new Validator($data);
$validator->field('username')->required()->string();
$validator->field('email')->required()->email();
if ($validator->passes()) {
echo "Validation passed!";
}$validator = new Validator();
// Use custom labels for better error messages
$validator->field('user_email', 'Email Address')->required()->email();
$validator->field('user_phone', 'Phone Number')->required()->phone();
// Error message will say: "Email Address is required" instead of "user_email is required"Field must be present and not empty.
$validator->field('name')->required();Allow field to be null or empty. When applied, all other rules are skipped if field is empty.
$validator->field('middle_name')->nullable()->string()->max(50);
// Valid: null, "", "John"Field must be a string.
$validator->field('description')->string();Field must be numeric (integer or float).
$validator->field('price')->numeric();
// Valid: 10, 10.5, "10", "10.5"Field must be an integer (supports negative integers).
$validator->field('quantity')->integer();
// Valid: 10, -5, "10", "-5"Field must be a boolean value.
$validator->field('is_active')->boolean();
// Valid: true, false, 0, 1, "0", "1"Field must be an array.
$validator->field('tags')->array();Field must be an object.
$validator->field('settings')->object();Field must contain only alphabetic characters.
$validator->field('firstname')->alpha();
// Valid: "John", "Mary"
// Invalid: "John123", "John-Doe"Field must contain only alphanumeric characters.
$validator->field('username')->alphaNum();
// Valid: "john123", "Mary456"
// Invalid: "john_doe", "mary-123"Minimum length for strings, minimum value for numbers, minimum count for arrays.
// String: minimum 3 characters
$validator->field('username')->string()->min(3);
// Number: minimum value 18
$validator->field('age')->integer()->min(18);
// Array: minimum 2 items
$validator->field('tags')->array()->min(2);Maximum length for strings, maximum value for numbers, maximum count for arrays.
// String: maximum 50 characters
$validator->field('username')->string()->max(50);
// Number: maximum value 100
$validator->field('percentage')->integer()->max(100);
// Array: maximum 10 items
$validator->field('tags')->array()->max(10);Value must be between min and max (for strings, numbers, or arrays).
// String: 3-20 characters
$validator->field('username')->string()->between(3, 20);
// Number: 18-65
$validator->field('age')->integer()->between(18, 65);
// Array: 2-5 items
$validator->field('tags')->array()->between(2, 5);Field must be a valid email address.
$validator->field('email')->required()->email();
// Valid: user@example.com, user.name@example.co.uk
// Invalid: user@, @example.com, user@.comField must be a valid phone number (supports international formats).
$validator->field('phone')->required()->phone();
// Valid: +1234567890, 1234567890, (123) 456-7890, 123-456-7890
// Invalid: 123, abcd, ++123Field must be a valid URL.
$validator->field('website')->url();
// Valid: https://example.com, http://example.com/path
// Invalid: example.com, htp://example.comField must be a valid domain name.
$validator->field('domain')->domain();
// Valid: example.com, subdomain.example.com
// Invalid: https://example.com, exampleField must be a valid IP address (IPv4 or IPv6).
$validator->field('ip_address')->ip();
// Valid: 192.168.1.1, 2001:0db8:85a3:0000:0000:8a2e:0370:7334
// Invalid: 999.999.999.999, invalid-ipField must be a valid MAC address.
$validator->field('mac_address')->mac();
// Valid: 00:1B:44:11:3A:B7, 00-1B-44-11-3A-B7
// Invalid: 00:1B:44:11, invalid-macField must have exact number of digits.
$validator->field('pin_code')->digits(4);
// Valid: "1234", "0000"
// Invalid: "123", "12345", "abcd"Field must have digits between min and max.
$validator->field('verification_code')->digitsBetween(4, 6);
// Valid: "1234", "12345", "123456"
// Invalid: "123", "1234567"Field must match another field (commonly used for password confirmation).
$validator->field('password')->required()->min(8);
$validator->field('password_confirmation')->required()->confirmed('password');
// Both fields must matchField must be different from another field.
$validator->field('new_email')->required()->email()->different('old_email');Field must match the given regular expression pattern.
Security Note: The validator includes protection against ReDoS (Regular Expression Denial of Service) attacks.
// Username: alphanumeric and underscore only
$validator->field('username')->regex('/^[a-zA-Z0-9_]+$/');
// Hexadecimal color code
$validator->field('color')->regex('/^#[0-9A-Fa-f]{6}$/');Security Restrictions:
- Maximum pattern length: 500 characters
- Nested quantifiers not allowed
- Nested lookahead/lookbehind not allowed
- Recursive patterns not allowed
- Maximum execution time: 2 seconds
// This will throw an exception (security)
$validator->field('test')->regex('/(\w+)*(\w+)*/'); // Nested quantifiersField must be one of the given values.
$validator->field('status')->in(['active', 'inactive', 'pending']);
// Valid: "active", "inactive", "pending"
// Invalid: "deleted", "archived"Field must not be one of the given values.
$validator->field('username')->notIn(['admin', 'root', 'system']);Field must be a valid date in the specified format.
// Default format: Y-m-d
$validator->field('birth_date')->date();
// Valid: "2024-01-15"
// Custom format: d/m/Y
$validator->field('start_date')->date('d/m/Y');
// Valid: "15/01/2024"
// Format: Y-m-d H:i:s
$validator->field('created_at')->date('Y-m-d H:i:s');
// Valid: "2024-01-15 14:30:00"Field must be a valid JSON string.
$validator->field('settings')->json();
// Valid: '{"key":"value"}', '[1,2,3]'
// Invalid: '{key:value}', 'not-json'Apply validation only when field is present in the request data.
// Optional field - only validates if provided
$validator->field('phone')->sometimes()->phone();
// If 'phone' is not in request, no validation
// If 'phone' is in request, must be valid phone numberUse Cases:
- Optional API parameters
- Conditional form fields
- PATCH requests (partial updates)
// nullable: Field can be present but empty
$validator->field('middle_name')->nullable()->string();
// Valid: null, "", "John"
// sometimes: Field may not be present at all
$validator->field('optional_field')->sometimes()->email();
// Valid: not present, or valid email if present
// Invalid: present but empty ""
// Combination
$validator->field('optional_email')->sometimes()->nullable()->email();
// Valid: not present, present but empty, or valid email$validator = new Validator();
$validator->field('email')->required()->email();
// Method 1: fails()
if ($validator->fails()) {
// Validation failed
}
// Method 2: passes()
if ($validator->passes()) {
// Validation passed
}
// Method 3: validate() or isValid()
if (!$validator->validate()) {
// Validation failed
}// Get first error
$error = $validator->getFirstError();
// Returns: ['field' => 'email', 'message' => 'The email field is required']
// Get first error message only
$message = $validator->getFirstErrorMessage();
// Returns: "The email field is required"
// Get all errors
$errors = $validator->getErrors();
// Returns array of all errors
// Get errors for specific field
$emailErrors = $validator->getFieldErrors('email');
// Check if specific field has errors
if ($validator->hasError('email')) {
echo "Email field has validation errors";
}// Per-field custom message
$validator->field('email')
->required('Please provide your email address')
->email('Please enter a valid email address');
$validator->field('age')
->required('Age is required')
->integer('Age must be a number')
->min(18, 'You must be at least 18 years old');The validator includes comprehensive protection against Regular Expression Denial of Service (ReDoS) attacks:
// Security checks on regex patterns
$validator->field('test')->regex('/^[a-z]+$/'); // ✓ Safe
// These will throw exceptions:
$validator->field('test')->regex('/(\w+)*(\w+)*/'); // ✗ Nested quantifiers
$validator->field('test')->regex('/(?!.*(?!.*))/'); // ✗ Nested lookaheadProtection Measures:
- Pattern complexity analysis
- Maximum pattern length (500 chars)
- Execution timeout (2 seconds)
- Dangerous pattern detection
All input values are automatically sanitized:
// Automatic sanitization in getValue()
- Trim whitespace
- Remove null bytes (\0)
- Security against null byte injectionWhile validator doesn't directly interact with database, it helps prevent SQL injection by:
- Type validation (integer, numeric)
- Format validation (email, url)
- Input sanitization (null bytes removal)
Create validation message files in langs/{locale}/validation.php:
langs/en/validation.php:
<?php
return [
'required' => 'The :attribute field is required.',
'email' => 'The :attribute must be a valid email address.',
'min' => [
'string' => 'The :attribute must be at least :min characters.',
'numeric' => 'The :attribute must be at least :min.',
'array' => 'The :attribute must have at least :min items.',
],
'max' => [
'string' => 'The :attribute may not be greater than :max characters.',
'numeric' => 'The :attribute may not be greater than :max.',
'array' => 'The :attribute may not have more than :max items.',
],
'alpha' => 'The :attribute must only contain letters.',
'alpha_num' => 'The :attribute must only contain letters and numbers.',
'phone' => 'The :attribute must be a valid phone number.',
// ... more messages
];langs/fa/validation.php:
<?php
return [
'required' => 'فیلد :attribute الزامی است.',
'email' => 'فیلد :attribute باید یک آدرس ایمیل معتبر باشد.',
'min' => [
'string' => 'فیلد :attribute باید حداقل :min کاراکتر باشد.',
'numeric' => 'فیلد :attribute باید حداقل :min باشد.',
'array' => 'فیلد :attribute باید حداقل :min مورد داشته باشد.',
],
'alpha' => 'فیلد :attribute فقط باید حروف داشته باشد.',
'phone' => 'فیلد :attribute باید یک شماره تلفن معتبر باشد.',
// ... more messages
];use Webrium\App;
// Set locale before validation
App::setLocale('fa'); // Persian
App::setLocale('en'); // English
App::setLocale('es'); // Spanish
$validator = new Validator();
// Validation messages will be in the selected localeuse Webrium\Validator;
$validator = new Validator();
$validator->field('username', 'Username')
->required()
->alphaNum()
->between(3, 20);
$validator->field('email', 'Email Address')
->required()
->email();
$validator->field('password', 'Password')
->required()
->string()
->min(8);
$validator->field('password_confirmation', 'Password Confirmation')
->required()
->confirmed('password');
$validator->field('age', 'Age')
->required()
->integer()
->min(18);
$validator->field('phone', 'Phone Number')
->nullable()
->phone();
if ($validator->fails()) {
// Return errors as JSON
header('Content-Type: application/json');
echo json_encode([
'success' => false,
'errors' => $validator->getErrors()
]);
exit;
}
// Process registration
echo "Registration successful!";$validator = new Validator();
// All fields are optional (sometimes)
$validator->field('username')->sometimes()->alphaNum()->between(3, 20);
$validator->field('email')->sometimes()->email();
$validator->field('phone')->sometimes()->phone();
$validator->field('bio')->sometimes()->string()->max(500);
// Only validate fields that are present in request
if ($validator->passes()) {
// Update only provided fields
$updates = [];
if (isset($_POST['username'])) $updates['username'] = $_POST['username'];
if (isset($_POST['email'])) $updates['email'] = $_POST['email'];
// ...
}use Webrium\Validator;
use Webrium\App;
// JSON request data
$validator = new Validator(App::input());
$validator->field('action')->required()->in(['create', 'update', 'delete']);
$validator->field('id')->sometimes()->integer()->min(1);
$validator->field('data')->required()->json();
if ($validator->fails()) {
App::returnData([
'error' => true,
'message' => $validator->getFirstErrorMessage(),
'errors' => $validator->getErrors()
], 400);
}
// Process API request
$action = App::input('action');
$data = json_decode(App::input('data'), true);
// ...$validator = new Validator();
// Basic required fields
$validator->field('order_type')->required()->in(['delivery', 'pickup']);
$validator->field('customer_name')->required()->string()->min(2);
$validator->field('customer_phone')->required()->phone();
// Conditional: delivery address required only for delivery orders
$orderType = $_POST['order_type'] ?? null;
if ($orderType === 'delivery') {
$validator->field('delivery_address')->required()->string()->min(10);
$validator->field('delivery_city')->required()->string();
$validator->field('delivery_zip')->required()->digits(5);
}
// Optional fields
$validator->field('special_instructions')->nullable()->string()->max(200);
$validator->field('coupon_code')->sometimes()->alphaNum()->between(4, 10);
if ($validator->passes()) {
echo "Order validated successfully!";
}// Step 1: Personal Information
function validateStep1($data) {
$validator = new Validator($data);
$validator->field('firstname')->required()->alpha()->min(2);
$validator->field('lastname')->required()->alpha()->min(2);
$validator->field('email')->required()->email();
return $validator;
}
// Step 2: Account Details
function validateStep2($data) {
$validator = new Validator($data);
$validator->field('username')->required()->alphaNum()->between(3, 20);
$validator->field('password')->required()->min(8);
$validator->field('password_confirmation')->required()->confirmed('password');
return $validator;
}
// Step 3: Additional Info
function validateStep3($data) {
$validator = new Validator($data);
$validator->field('phone')->nullable()->phone();
$validator->field('bio')->nullable()->string()->max(500);
return $validator;
}
// Usage
$step = $_POST['step'] ?? 1;
switch ($step) {
case 1:
$validator = validateStep1($_POST);
break;
case 2:
$validator = validateStep2($_POST);
break;
case 3:
$validator = validateStep3($_POST);
break;
}
if ($validator->fails()) {
// Show errors for current step
}| Method | Parameters | Description |
|---|---|---|
__construct($data) |
array|null $data |
Create validator with optional custom data |
| Method | Parameters | Returns |
|---|---|---|
field($name, $label) |
string $name, string|null $label |
self |
| Method | Description | Returns |
|---|---|---|
nullable() |
Allow field to be null/empty | self |
sometimes() |
Validate only if field present | self |
| Method | Description | Returns |
|---|---|---|
required($message) |
Field must be present | self |
string($message) |
Must be string | self |
numeric($message) |
Must be numeric | self |
integer($message) |
Must be integer | self |
boolean($message) |
Must be boolean | self |
array($message) |
Must be array | self |
object($message) |
Must be object | self |
| Method | Parameters | Returns |
|---|---|---|
alpha($message) |
string|null $message |
self |
alphaNum($message) |
string|null $message |
self |
min($min, $message) |
int|float $min, string|null $message |
self |
max($max, $message) |
int|float $max, string|null $message |
self |
between($min, $max, $message) |
int|float $min, $max, string|null $message |
self |
| Method | Parameters | Returns |
|---|---|---|
email($message) |
string|null $message |
self |
phone($message) |
string|null $message |
self |
url($message) |
string|null $message |
self |
domain($message) |
string|null $message |
self |
ip($message) |
string|null $message |
self |
mac($message) |
string|null $message |
self |
| Method | Parameters | Returns |
|---|---|---|
digits($length, $message) |
int $length, string|null $message |
self |
digitsBetween($min, $max, $message) |
int $min, $max, string|null $message |
self |
| Method | Parameters | Returns |
|---|---|---|
confirmed($field, $message) |
string $field, string|null $message |
self |
different($field, $message) |
string $field, string|null $message |
self |
| Method | Parameters | Returns |
|---|---|---|
regex($pattern, $message) |
string $pattern, string|null $message |
self |
in($values, $message) |
array $values, string|null $message |
self |
notIn($values, $message) |
array $values, string|null $message |
self |
| Method | Parameters | Returns |
|---|---|---|
date($format, $message) |
string $format, string|null $message |
self |
json($message) |
string|null $message |
self |
| Method | Returns | Description |
|---|---|---|
validate() |
bool |
Execute validation |
isValid() |
bool |
Alias for validate() |
fails() |
bool |
Check if validation failed |
passes() |
bool |
Check if validation passed |
| Method | Returns | Description |
|---|---|---|
getErrors() |
array |
Get all errors |
getFirstError() |
array|null |
Get first error |
getFirstErrorMessage() |
string|null |
Get first error message |
getFieldErrors($field) |
array |
Get errors for specific field |
hasError($field) |
bool |
Check if field has errors |
// ✓ GOOD - Clear error messages
$validator->field('user_email', 'Email Address')->required()->email();
// Error: "Email Address is required"
// ✗ BAD - Technical error messages
$validator->field('user_email')->required()->email();
// Error: "user_email is required"// ✓ GOOD - Validate before processing
$validator = new Validator();
$validator->field('email')->required()->email();
if ($validator->fails()) {
return; // Stop here
}
// Process data...// ✓ GOOD - Specific validation
$validator->field('age')->integer()->min(18);
$validator->field('price')->numeric()->min(0);
// ✗ BAD - Too generic
$validator->field('age')->required();
$validator->field('price')->required();// ✓ GOOD - Logical rule order
$validator->field('username')
->required() // First check if present
->string() // Then check type
->alphaNum() // Then check format
->between(3, 20); // Finally check length
// ✗ BAD - Illogical order
$validator->field('username')
->between(3, 20) // Length check before knowing if it's a string
->alphaNum()
->string()
->required();// ✓ GOOD - Clear intent
$validator->field('middle_name')->nullable()->string()->max(50);
// ✗ BAD - Confusing
$validator->field('middle_name')->string()->max(50); // Fails on empty// ✓ GOOD - Update only provided fields
$validator->field('username')->sometimes()->alphaNum();
$validator->field('email')->sometimes()->email();
// ✗ BAD - Requires all fields
$validator->field('username')->required()->alphaNum();
$validator->field('email')->required()->email();// ✓ GOOD - User-friendly messages
$validator->field('credit_card')
->required('Please provide your credit card number')
->digits(16, 'Credit card number must be 16 digits');
// ✗ BAD - Generic messages
$validator->field('credit_card')->required()->digits(16);// ✓ GOOD - Always validate server-side
$validator = new Validator();
$validator->field('email')->required()->email();
if ($validator->fails()) {
// Server-side validation failed
}
// ✗ BAD - Relying only on JavaScript validation
// (Client-side validation can be bypassed)// Set locale based on user preference
App::setLocale($userPreferredLanguage);
// Validator will use localized messages
$validator = new Validator();
$validator->field('email')->required()->email();function validateCreateRequest() {
$validator = new Validator(App::input());
$validator->field('title')->required()->string()->max(100);
$validator->field('content')->required()->string();
$validator->field('status')->required()->in(['draft', 'published']);
return $validator;
}
function validateUpdateRequest() {
$validator = new Validator(App::input());
$validator->field('title')->sometimes()->string()->max(100);
$validator->field('content')->sometimes()->string();
$validator->field('status')->sometimes()->in(['draft', 'published']);
return $validator;
}$data = App::input();
$validator = new Validator($data);
$validator->field('payment_method')
->required()
->in(['credit_card', 'paypal', 'cash']);
// Add conditional validation
if (isset($data['payment_method']) && $data['payment_method'] === 'credit_card') {
$validator->field('card_number')->required()->digits(16);
$validator->field('cvv')->required()->digits(3);
$validator->field('expiry_date')->required()->date('m/Y');
}
if (isset($data['payment_method']) && $data['payment_method'] === 'paypal') {
$validator->field('paypal_email')->required()->email();
}// ✗ Problem
$validator->field('email')->email('Invalid email');
// Still shows default message
// ✓ Solution - Pass message to each rule
$validator->field('email')
->required('Email is required')
->email('Please enter a valid email');// ✗ Problem - Field is nullable
$validator->field('email')->nullable()->email();
// Empty string passes (because nullable)
// ✓ Solution - Use required if field shouldn't be empty
$validator->field('email')->required()->email();// ✓ Fixed in latest version
$validator->field('temperature')->integer();
// Now accepts: -5, -10, etc.// ✗ Problem - Complex pattern triggers security check
$validator->field('test')->regex('/(\w+)*(\w+)*/');
// Exception: Complex regex patterns not allowed
// ✓ Solution - Simplify pattern
$validator->field('test')->regex('/^\w+$/');- Always validate server-side - Never trust client-side validation alone
- Use type validation - Prevents type juggling attacks
- Limit string lengths - Prevents buffer overflow and DoS
-
Use whitelists - Prefer
in()overnotIn()for security - Sanitize regex - Framework protects against ReDoS, but be careful
- Validate numeric ranges - Prevent integer overflow
- Reuse validator instances when validating multiple similar datasets
-
Use
sometimes()to skip unnecessary validations - Validate only required fields in PATCH requests
- Cache locale files (done automatically)
- Stop at first error (done automatically per field)
Part of the Webrium Framework. See framework license for details.