gravity/dataverify

A lightweight PHP validation library with fluent interface for validating data structures, supporting batch/fail-fast modes and custom strategies

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 0

Open Issues: 0

pkg:composer/gravity/dataverify

v1.0.1 2025-12-28 18:15 UTC

This package is auto-updated.

Last update: 2025-12-28 18:16:41 UTC


README

A fluent, zero-dependency PHP validation library.

PHP Version License

composer require gravity/dataverify

Quick Start

use 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());
}

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}$/');

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 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 = 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;
}

See full example

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>";
    }
}

See full example

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);

See conditional examples

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');

See translation examples

Why DataVerify?

  • 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

Documentation

Guides:

Examples:

Performance

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

Compatibility

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+ ⚠️ Partial Best for webservices/APIs

Examples

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 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 = 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 verified
How 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();
    }
}

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