A fluent, zero-dependency PHP validation library.
composer require gravity/dataverifyuse Gravity\DataVerify;
$data = (object)[
'email' => 'user@example.com',
'age' => 25
];
$dv = new DataVerify($data);
$dv
->field('email')->required->email
->field('age')->required->int->between(18, 100);
if (!$dv->verify()) {
print_r($dv->getErrors());
}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}$/');Custom strategies
class SiretStrategy extends ValidationStrategy {
public function getName(): string { return 'siret'; }
protected function handler(mixed $value): bool { /* validation logic */ }
}
DataVerify::global()->register(new SiretStrategy());
$dv->field('siret')->siret; // Available everywhereREST 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 = new DataVerify($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 performance
http_response_code(400);
echo json_encode(['errors' => $dv->getErrors()]);
exit;
}Form Validation (WordPress, PrestaShop, Laravel)
Validate complex forms with dependent fields:
// E-commerce checkout form
$dv = new DataVerify($_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 form
foreach ($dv->getErrors() as $error) {
echo "<p class='error'>{$error['message']}</p>";
}
}Data Processing Pipelines
Validate batches of data with fail-fast for early termination:
// Process CSV import with 10,000 rows
foreach ($csvRows as $row) {
$dv = new DataVerify($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 = new DataVerify($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);Multi-language Applications
Built-in i18n with automatic locale detection:
$dv = new DataVerify($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');- ✅ Zero dependencies - Pure PHP 8.1+, no vendor bloat
- ✅ Fluent API - Readable, chainable validations
- ✅ Extensible - Custom strategies with auto-documentation
- ✅ Fast - ~50μs simple validation, ~4.9MB memory (benchmarks)
- ✅ i18n ready - Built-in translation support (EN, FR)
- ✅ Framework agnostic - Works with WordPress, Laravel, Symfony, vanilla PHP
- ✅ Production tested - 346 tests, 72% mutation score
Guides:
- Conditional Validation -
when/and/or/thensyntax - Custom Strategies - Extend with your own rules
- Internationalization - Multi-language error messages
- Error Handling - Working with validation errors
- Validation Rules - All 40+ built-in rules
Examples:
- API Request Validation
- Basic Usage
- Conditional Validation
- Custom Validation Rules
- Translation (PHP)
- Translation (YAML)
DataVerify is designed for production with predictable sub-millisecond performance:
Simple validation: ~50μs (99% < 50μs)
Complex nested: ~72μs (99% < 72μs)
Batch mode (100 fields): 1.5ms
Fail-fast (100 fields): 0.7ms (2x faster)
Memory usage: ~4.9MB (stable)
See: Full benchmarks
| Platform | Status | Notes |
|---|---|---|
| PHP 8.1+ | ✅ Required | Minimum version |
| WordPress 6.0+ | ✅ Compatible | Use in plugins/themes |
| PrestaShop 8.0+ | ✅ Excellent fit | Native Composer support |
| Laravel/Symfony | ✅ Compatible | Use as alternative validator |
| Moodle 4.3+ | Best for webservices/APIs |
Basic validation
$dv = new DataVerify($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 validCustom 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);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 = new DataVerify($data);
$dv->field('email')->required->email;Can I reuse a DataVerify instance?
No. Each validation requires a new instance to prevent state corruption:
// ✅ Correct
foreach ($users as $user) {
$dv = new DataVerify($user); // New instance
$dv->field('email')->required->email;
$dv->verify();
}
// ❌ Wrong - throws LogicException
$dv = new DataVerify($user1);
$dv->verify();
$dv->verify(); // Error: already verifiedHow do I validate arrays of items?
Loop and validate each item individually:
foreach ($data->items as $item) {
$dv = new DataVerify($item);
$dv->field('sku')->required->alphanumeric
->field('qty')->required->int->between(1, 100);
if (!$dv->verify()) {
$errors[] = $dv->getErrors();
}
}Contributions welcome! Please see CONTRIBUTING.md for guidelines.
See CHANGELOG.md for version history.
MIT License - see LICENSE file for details.
Made with ❤️ by Romain Feregotto