Skip to content

Repository files navigation

DataVerify

A fluent, zero-dependency PHP validation library.

Latest VersionTotal DownloadsPHP VersionLicense

Requirements

  • PHP 8.1+
  • Composer (recommended) or manual installation
composer require gravity/dataverify

Quick Start

useGravity\DataVerify;
$data = (object)[
'email' => 'user@example.com',
'age' => 25
];
$dv = newDataVerify($data);
$dv
->field('email')->required->email
->field('age')->required->int->between(18, 100);
if (!$dv->verify()) {
print_r($dv->getErrors());
}

Key Features

Fluent validation

$dv->field('email')->required->email->maxLength(100);

Nested objects

$dv->field('user')->required->object
->subfield('name')->required->string
->subfield('email')->required->email;

Conditional validation

$dv->field('vat_number')
->when('country', '=', 'FR')
->then->required->regex('/^FR\d{11}$/');

Reusable Rules & Schemas

// Register reusable validation rules
DataVerify::registerRules('strongPassword')
->minLength(12)
->containsUpper
->containsLower
->containsSpecialCharacter;
// Apply to any field$dv->field('password')->rule('strongPassword');
// Register complete validation schemas
DataVerify::registerSchema('userApi')
->field('user')->required->object
->subfield('email')->email
->when('user.id', '!=', null)->then->required
->subfield('password')->rule('strongPassword')
->when('user.id', '!=', null)->then->required;
// Apply entire schema$dv = newDataVerify($data); $dv->schema('userApi');
if(!$dv->verify()){
// Get Your Errors
}

Custom validation strategies

class SiretStrategy extends ValidationStrategy {
publicfunctiongetName(): string { return'siret'; }
protectedfunctionhandler(mixed$value): bool { /* validation logic */ }
}
DataVerify::global()->register(newSiretStrategy());
$dv->field('siret')->siret; // Available everywhere

Use Cases

REST APIs & Web Services

Perfect for validating incoming API requests with conditional logic:

// POST /api/users/register$data = json_decode(file_get_contents('php://input'));
$dv = newDataVerify($data);
$dv
->field('email')->required->email->disposableEmail
->field('password')->required->minLength(8)
->containsUpper->containsNumber->containsSpecialCharacter
->field('company_name')
->when('account_type', '=', 'business')
->then->required->string->minLength(2)
->field('vat_number')
->when('account_type', '=', 'business')
->and('country', 'in', ['FR', 'BE', 'DE'])
->then->required->regex('/^[A-Z]{2}\d{9,12}$/');
if (!$dv->verify(batch: false)) { // Fail-fast for better performancehttp_response_code(400);
echojson_encode(['errors' => $dv->getErrors()]);
exit;
}

See full example

Form Validation (WordPress, PrestaShop, Laravel)

Validate complex forms with dependent fields:

// E-commerce checkout form$dv = newDataVerify($_POST);
$dv
->field('shipping_method')->required->in(['standard', 'express', 'pickup'])
->field('shipping_address')
->when('shipping_method', '!=', 'pickup')
->then->required->string->minLength(10)
->field('shipping_city')
->when('shipping_method', '!=', 'pickup')
->then->required->string
->field('pickup_store')
->when('shipping_method', '=', 'pickup')
->then->required->string
->field('gift_message')
->when('is_gift', '=', true)
->then->required->maxLength(500);
if (!$dv->verify()) {
// Display errors in formforeach ($dv->getErrors() as$error) {
echo"<p class='error'>{$error['message']}</p>";
}
}

See full example

Data Processing Pipelines

Validate batches of data with fail-fast for early termination:

// Process CSV import with 10,000 rowsforeach ($csvRowsas$row) {
$dv = newDataVerify($row);
$dv
->field('sku')->required->alphanumeric
->field('price')->required->numeric->greaterThan(0)
->field('stock')->int->between(0, 99999);
if (!$dv->verify(batch: false)) { // Stop at first error per row$failedRows[] = ['row' => $row, 'errors' => $dv->getErrors()];
continue; // Skip invalid row
}
// Process valid row...
}
Conditional Business Rules

Complex validation logic based on multiple conditions:

// SaaS subscription validation$dv = newDataVerify($subscription);
$dv
->field('plan')->required->in(['free', 'pro', 'enterprise'])
->field('payment_method')
->when('plan', '!=', 'free')
->then->required->in(['card', 'invoice'])
->field('card_number')
->when('plan', '!=', 'free')
->and('payment_method', '=', 'card')
->then->required->regex('/^\d{16}$/')
->field('billing_email')
->when('plan', '=', 'enterprise')
->or('payment_method', '=', 'invoice')
->then->required->email
->field('seats')
->when('plan', 'in', ['pro', 'enterprise'])
->then->required->int->between(1, 1000);

See conditional examples

Multi-language Applications

Built-in i18n with automatic locale detection:

$dv = newDataVerify($data);
$dv->setLocale('fr'); // Built-in: EN, FR$dv->field('email')->required->email;
if (!$dv->verify()) {
// Error message in French:// "Le champ email doit être une adresse email valide"echo$dv->getErrors()[0]['message'];
}
// Add custom translations$dv->addTranslations([
'validation.siret' => 'El campo {field} debe ser un SIRET válido'
], 'es');

See translation examples

Why DataVerify?

  • Zero dependencies - Pure PHP 8.1+, no vendor bloat
  • Fluent API - Readable, chainable validations
  • Reusable patterns - Rules & Schemas for DRY validation with auto-documentation
  • Extensible - Custom validation strategies with auto-documentation
  • Fast - ~9.7μs simple validation, ~2MB memory (benchmarks)
  • i18n ready - Built-in translation support (EN, FR)
  • Framework agnostic - Works with WordPress, Laravel, Symfony, vanilla PHP
  • Worker-mode ready - Tested with FrankenPHP, stable memory over 3M+ requests
  • Production tested - 600+ tests, 84% mutation score

Documentation

Guides:

Examples:

Performance

DataVerify is designed for production with predictable sub-millisecond performance:

Simple validation: ~9.7μs (99% < 10μs)
Complex nested: ~19.1μs (99% < 20μs)
Batch mode (100 fields): ~572.9μs
Fail-fast (100 fields): ~256.1μs (2.2x faster)
Memory usage: ~2MB (stable)

See:Full benchmarks

Examples

Basic validation

$dv = newDataVerify($data);
$dv
->field('name')->required->string->minLength(3)
->field('email')->required->email
->field('age')->int->between(18, 120);
if (!$dv->verify()) {
print_r($dv->getErrors());
}

Batch vs fail-fast

$dv->verify(); // Batch mode: all errors$dv->verify(batch: false); // Fail-fast: first error only (2x faster)

Optional fields

$dv->field('phone')->string; // Optional: null OK, if present must be valid$dv->field('email')->required->email; // Required: must exist AND be valid

Custom error messages

$dv->field('password')
->required
->minLength(8)->errorMessage('Password too weak - min 8 characters')
->containsUpper->errorMessage('Password must include uppercase letters');

Nested validation

// Simple object nesting$dv->field('user')->required->object
->subfield('profile')->required->object
->subfield('address')->required->object
->subfield('city')->required->string
->subfield('country')->required->string;
// Deep array nesting with indices// Validates: data.orders[0].items[2].name$dv->field('orders')->required->array
->subfield('0', 'items', '2', 'name')->required->string->minLength(3);
// Complex nested structures (arrays + objects)// Validates: data.warehouses[0].inventory[1].product.sku$dv->field('warehouses')->required->array
->subfield('0', 'inventory', '1', 'product', 'sku')->required->alphanumeric
->subfield('0', 'inventory', '1', 'product', 'stock')->required->int->between(0, 9999);

FAQ

Is sanitization included?

No. DataVerify validates data but does NOT sanitize it. Always sanitize user input before validation:

$data->email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$data->name = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8');
$dv = newDataVerify($data);
$dv->field('email')->required->email;
Can I reuse a DataVerify instance?

No. Each validation requires a new instance to prevent state corruption:

// ✅ Correctforeach ($usersas$user) {
$dv = newDataVerify($user); // New instance$dv->field('email')->required->email;
$dv->verify();
}
// ❌ Wrong - throws LogicException$dv = newDataVerify($user1);
$dv->verify();
$dv->verify(); // Error: already verified
How do I validate arrays of items?

Loop and validate each item individually:

foreach ($data->itemsas$item) {
$dv = newDataVerify($item);
$dv->field('sku')->required->alphanumeric
->field('qty')->required->int->between(1, 100);
if (!$dv->verify()) {
$errors[] = $dv->getErrors();
}
}

Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

Changelog

See CHANGELOG.md for version history.

License

MIT License - see LICENSE file for details.


Made with ❤️ by Romain Feregotto

About

Lightweight PHP validation with fluent API, conditional logic, custom strategies, and zero dependencies

Topics

Resources

Contributing

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages