PHP Classes

File: README.md

Recommend this page to a friend!
  Packages of AKONO Metsam Nathan   Contextual Validator - Cross-Field Coherence Checker   README.md   Download  
File: README.md
Role: Documentation
Content type: text/markdown
Description: Package documentation: problem statement, usage, full API reference, limitations.
Class: Contextual Validator - Cross-Field Coherence Checker
Validate multiple form fields that are related
Author: By
Last change:
Date: 11 days ago
Size: 3,380 bytes
 

Contents

Class file image Download

Contextual Validator ? Cross-Field Coherence Checker for PHP

Most PHP validation libraries check one field at a time: is this a valid email, is this string non-empty, is this number in range. That catches typos, but it misses an entire class of bugs where each field is individually valid, yet the form as a whole is inconsistent.

This class validates relationships between fields instead of fields in isolation.

The problem

All of these pass every ordinary field-by-field validator, yet every one of them is wrong:

  • postal code `90210` submitted with country `France`
  • an `end_date` earlier than `start_date`
  • a `birthdate` of 1990 combined with a declared `age` of 15
  • a phone number with a US-length number paired with `country = Germany`
  • a `password_confirmation` that doesn't match `password`
  • a `min_price` filter greater than the `max_price` filter

Installation

Copy src/ContextualValidator.php into your project, or require it directly. No dependencies.

Usage

require 'src/ContextualValidator.php';

use ContextValidate\ContextualValidator;

$validator = new ContextualValidator($_POST);

$validator
    ->postalCodeMatchesCountry('zip', 'country')
    ->dateRange('start_date', 'end_date')
    ->ageMatchesBirthdate('age', 'birthdate')
    ->phoneMatchesCountry('phone', 'country')
    ->fieldsMatch('password', 'password_confirmation')
    ->numericRange('min_price', 'max_price');

if ($validator->fails()) {
    foreach ($validator->errors() as $field => $messages) {
        echo "$field: " . implode(', ', $messages) . "\n";
    }
}

See examples/example.php for a full runnable demo with a form that fails every single rule at once.

Built-in rules

| Method | Checks | |---|---| | postalCodeMatchesCountry($postalField, $countryField) | Postal code format matches the ISO country code (25+ countries built in) | | dateRange($startField, $endField, $allowEqual = true) | Start date is not after end date | | ageMatchesBirthdate($ageField, $birthdateField, $toleranceYears = 0, $referenceDate = null) | Declared age matches computed age from birthdate | | phoneMatchesCountry($phoneField, $countryField) | Phone number digit count is plausible for the given country | | fieldsMatch($fieldA, $fieldB, $message = null) | Two fields hold identical values (e.g. password confirmation) | | numericRange($minField, $maxField) | A "min" field is not greater than a "max" field | | custom($field, $callable) | Any custom cross-field rule; return true or an error string |

Every rule skips silently (does not add an error) when a required field is empty or when the country isn't in the built-in list - it's not this class's job to require fields or judge unknown countries; pair it with your usual field-level validator for that.

Extending country coverage

ContextualValidator::addPostalCodePattern('XX', '/^\d{5}$/');

Limitations

  • Phone validation checks digit count plausibility only, not real numbering-plan rules. For full phone number validation, pair this with a dedicated library (e.g. a libphonenumber port) and use `custom()` to plug it in as a cross-field rule.
  • Postal code coverage is currently ~25 countries; unknown countries are skipped rather than flagged, by design.

Tests

php tests/run-tests.php

License

MIT ? see LICENSE.