h2lsoft/validator

Fluent data validator with chainable rules and i18n support

Maintainers

Package info

github.com/h2lsoft/validator

pkg:composer/h2lsoft/validator

Statistics

Installs: 62

Dependents: 1

Suggesters: 0

Stars: 1

Open Issues: 0

v2.0.0 2026-02-23 09:30 UTC

This package is auto-updated.

Last update: 2026-02-23 11:46:55 UTC


README

A fluent, chainable PHP data validator with built-in internationalization support.

Version

Requirements

  • PHP >= 7.4 (PHP 8+ recommended)
  • ext-mbstring

Installation

Install via Composer:

composer require h2lsoft/validator

Quick Start

$validator = new \h2lsoft\Data\Validator();

$validator->input('name')->required()->alpha(' ');
$validator->input('email', 'email address')->required()->email();
$validator->input('age')->required()->integer()->between(18, 99);

if($validator->fails())
{
    print_r($validator->result());
}

Usage

Creating a validator

// default: locale 'en', data from $_POST
$v = new \h2lsoft\Data\Validator();

// with locale
$v = new \h2lsoft\Data\Validator('fr');

// with custom data
$v = new \h2lsoft\Data\Validator('en', ['name' => 'John', 'age' => 25]);

Defining rules

Chain rules fluently after selecting an input with input():

$v->input('email', 'Email address')->required()->email();
$v->input('zip_code', 'Zip code')->required()->mask('99999');
$v->input('password')->required()->minLength(8);
$v->input('password_confirm')->required()->sameAs('password');
$v->input('age')->required()->integer()->between(18, 120);
$v->input('website')->url();
$v->input('start_date')->date('Y-m-d');
$v->input('config')->json();

Checking results

if($v->success())
{
    // validation passed
}

if($v->fails())
{
    $result = $v->result();
    // $result['error']            => true/false
    // $result['error_count']      => int
    // $result['error_stack']      => ['message 1', 'message 2', ...]
    // $result['error_stack_html'] => HTML formatted errors
    // $result['error_stack_deep'] => ['field_name' => ['message 1', ...]]
    // $result['error_fields']     => ['field1', 'field2', ...]
}

Custom error messages

Every rule accepts an optional $message parameter:

$v->input('name')->required('Please enter your name');
$v->input('age')->integer(true, 'Age must be a positive number');

Use [FIELD] placeholder to reference the field label:

$v->input('email', 'Email')->required('`[FIELD]` cannot be empty');
// => "`Email` cannot be empty"

Custom errors

$v->addError('Something went wrong', [], 'field_name');

Custom validation with callbacks

Use custom() for business logic that doesn't fit standard rules:

// check that a promo code exists in database
$v->input('promo_code')->required()->custom(function($value) {
    return PromoCode::findOne(['code' => $value]) !== null;
}, 'Invalid promo code');

// check that an email is not already taken
$v->input('email')->required()->email()->custom(function($value) {
    return User::count(['email' => $value]) === 0;
}, '`[FIELD]` is already taken');

// business rule: minimum amount depends on country
$v->input('amount')->required()->float()->custom(function($value) {
    $minimums = ['FR' => 10, 'US' => 5, 'UK' => 8];
    return $value >= ($minimums[$_POST['country']] ?? 0);
}, '`[FIELD]` is below the minimum for your country');

Available Rules

String / Value Rules

Rule Description Example
required($msg) Must be present and not empty ->required()
email($msg) Must be a valid email ->email()
mask($mask, $msg) Must match a mask (9=digit, a=letter, *=any) ->mask('99-aaa')
in($list, $msg) Value must be in the list ->in(['a', 'b', 'c'])
notIn($list, $msg) Value must not be in the list ->notIn(['x', 'y'])
integer($unsigned, $msg) Must be an integer ->integer()
float($unsigned, $msg) Must be a float ->float(false)
min($min, $msg) Value >= min (or array count) ->min(1)
max($max, $msg) Value <= max (or array count) ->max(100)
between($min, $max, $msg) Value between min and max ->between(1, 10)
length($len, $msg) Exact character length ->length(5)
minLength($len, $msg) Minimum character length ->minLength(3)
maxLength($len, $msg) Maximum character length ->maxLength(255)
equal($value, $msg) Must equal a given value ->equal('yes')
accepted($msg) Must be 'yes', 'YES' or 1 ->accepted()
boolean($msg) Must be a boolean-like value (true/false/1/0/yes/no) ->boolean()
password($min, $upper, $digit, $special, $msg) Password strength validation ->password(8, true, true, true)
url($flags, $msg) Must be a valid URL ->url()
alpha($exc, $latin, $min, $cap, $msg) Alphabetic only ->alpha(' -')
alphaNumeric($exc, $latin, $min, $cap, $msg) Alphanumeric only ->alphaNumeric('_')
date($format, $msg) Valid date ->date('d/m/Y')
datetime($format, $msg) Valid datetime ->datetime('d/m/Y H:i')
dateBefore($date, $format, $msg) Must be before a given date ->dateBefore('2025-12-31', 'Y-m-d')
dateAfter($date, $format, $msg) Must be after a given date ->dateAfter(date('Y-m-d'), 'Y-m-d')
time($format, $msg) Valid time ->time('H:i')
timezone($msg) Valid timezone identifier ->timezone()
country($msg) Valid ISO 3166-1 alpha-2 country code ->country()
language($msg) Valid ISO 639-1 language code ->language()
currency($msg) Valid ISO 4217 currency code ->currency()
iban($msg) Valid IBAN with MOD-97 checksum ->iban()
bic($msg) Valid BIC/SWIFT code (8 or 11 chars) ->bic()
uuid($msg) Valid UUID (RFC 4122, v1-v5) ->uuid()
macAddress($msg) Valid MAC address (colon or hyphen) ->macAddress()
cssColor($msg) Valid CSS color (hex, rgb, hsl, named) ->cssColor()
regex($pattern, $msg) Must match a regex ->regex('/^\d+$/', 'Digits only')
notRegex($pattern, $msg) Must not match a regex ->notRegex('/admin/i', 'Forbidden')
ip($flags, $msg) Valid IP address ->ip()
ipV4($msg) Valid IPv4 address ->ipV4()
ipV6($msg) Valid IPv6 address ->ipV6()
json($msg) Valid JSON string ->json()
jsonArray($msg) Valid JSON array ([...]) ->jsonArray()
jsonObject($msg) Valid JSON object ({...}) ->jsonObject()
sameAs($field, $msg) Must equal another field's value ->sameAs('password')
different($field, $msg) Must differ from another field's value ->different('old_password')
requiredIf($field, $value, $msg) Required only when another field matches ->requiredIf('type', 'pro')
csrfToken($token, $msg, $regenerate) CSRF token validation (auto-regenerates on failure by default) ->csrfToken()
custom($callback, $msg) Custom validation via callback ->custom(fn($v) => $v > 10, 'Too low')

Array Rules (checkboxes, multi-select)

Rule Description Example
multiple($msg) Must be an array ->multiple()
min($min, $msg) Minimum selections ->min(2)
max($max, $msg) Maximum selections ->max(5)
between($min, $max, $msg) Selections between min and max ->between(1, 3)

File Rules

Rule Description Example
fileRequired($msg) File must be uploaded ->fileRequired()
fileExtension($ext, $msg) Allowed extensions ->fileExtension(['jpg', 'png'])
fileMaxSize($size, $msg) Max file size (kb, mb, gb) ->fileMaxSize('2mb')
fileImage($ext) Must be an image ->fileImage()
fileMime($mimes, $msg) Allowed MIME types ->fileMime(['application/pdf'])
fileUploaded($msg) Must be a real upload ->fileUploaded()
fileImageWidth($w, $strict, $msg) Image width constraint ->fileImageWidth(800, true)
fileImageHeight($h, $strict, $msg) Image height constraint ->fileImageHeight(600, true)
fileImageBase64($ext, $create, $msg) Validate base64 image ->fileImageBase64('png')

Internationalization

Supported locales

Code Language
en English (default)
fr French
es Spanish
pt Portuguese
de German
it Italian

Setting the locale

$v = new \h2lsoft\Data\Validator('fr');

// or after creation
$v->setLocale('es');

Adding custom translations

$v->addLocaleMessages('fr', [
    "`[FIELD]` is required" => "`[FIELD]` est obligatoire",
]);

Utility Methods

Method Description
setData(array $data) Replace the data to validate
inputGet($name, $default) Get a value from the data
inputSet($name, $value) Set a value in the data
inputGetAll() Get all data values
setInputNames(array $names) Set display labels for multiple fields
setInputName($name, $label) Set a display label for one field
hasErrors() Get the error count
toJson($flags) Get result as JSON (auto-sets Content-Type header)
reset() Reset errors and state for reuse with new data

Full Example

$_POST['name'] = 'the king !';
$_POST['email'] = 'bad@email';
$_POST['zip_code'] = 'a2345';
$_POST['choices'] = ['apple'];
$_POST['job'] = 'webdesigner';
$_POST['days'] = '-50';
$_POST['date'] = '31/20/2020';
$_POST['website'] = 'text.com';
$_POST['config'] = '{"valid": true}';
$_POST['ip'] = '192.168.1.1';
$_POST['conditions'] = 0;

$v = new \h2lsoft\Data\Validator('en');

$v->input('name')->required()->alpha(' ');
$v->input('email', 'email address')->required()->email();
$v->input('zip_code', 'zip code')->required()->mask('99999');
$v->input('choices')->required()->multiple()->in(['banana', 'pear'])->minLength(2);
$v->input('job')->required()->equal('CEO');
$v->input('days')->required()->integer()->between(1, 60);
$v->input('date')->date('m/d/Y');
$v->input('website')->url(FILTER_FLAG_PATH_REQUIRED);
$v->input('config')->json();
$v->input('ip', 'IP address')->ipV4();
$v->input('job')->custom(fn($v) => strlen($v) >= 2, '`[FIELD]` is too short');
$v->input('conditions')->required()->accepted();

if($v->fails())
{
    print_r($v->result());
}

License

MIT. See full license.