Fast, dependency-free data validation for PHP. Describe what each field should
look like with a small rule DSL ("required|integer|range(1...10)"), callbacks,
or custom named rules, and get back localized error messages.
- PHP 8.1 or higher
ext-mbstring
composer require initphp/validationrequire 'vendor/autoload.php';
use InitPHP\Validation\Validation;
// GET /?name=Muhammet&year=2022
$validation = new Validation($_GET);
$validation->rule('name', 'required|string');
$validation->rule('year', 'integer|range(1970...2099)');
if ($validation->validation()) {
// ... the data is valid
} else {
foreach ($validation->getError() as $message) {
echo $message . "\n";
}
}- Give it data. Pass an associative array to the constructor, or use
setData()/mergeData(). - Queue rules. Each
rule()call adds checks for one or more fields. - Validate.
validation()runs every queued rule, returnstruewhen nothing failed, and consumes the queued rules — so the usual flow is queue → validate → read errors, repeated as needed. - Read errors.
getError()returns the messages from the most recent run.
$validation = new Validation([
'email' => 'someone@example.com',
'password' => 'secret',
'confirm' => 'secret',
]);
$valid = $validation
->rule('email', 'required|mail')
->rule('password', 'required|length(8...)')
->rule('confirm', 'again(password)')
->validation();A rule string is a pipe-separated list. Arguments go in parentheses and are
comma-separated; arguments are trimmed, so only(a, b, c) and only(a,b,c) are
equivalent. Rule names are matched case-insensitively.
See docs/rules-reference.md for a per-rule
reference with examples.
Pass a callback as the rule. It receives the field value and returns a boolean.
The third argument to rule() is a custom message.
$validation = new Validation(['number' => 13]);
$validation->rule('number', static function ($value): bool {
return ($value % 2) === 0;
}, '{field} must be an even number.');
$validation->validation(); // false
$validation->getError(); // ["number must be an even number."]You can also mix strings and callbacks in one array:
$validation->rule('number', ['integer', static fn ($v): bool => $v > 0]);Register reusable rules with extend() so they work inside the DSL string,
arguments and all:
$validation->extend(
'divisible',
static fn ($value, $by): bool => ((int) $value % (int) $by) === 0,
'{field} must be divisible by {2}.'
);
$validation->rule('quantity', 'divisible(5)');optional skips a field's rules when it has no value, but still validates it
when present:
$validation->rule('nickname', 'optional|alpha|length(3...20)');The regex rule can reference a named pattern. Built-in names include uri,
slug, url, alpha, words, alphanum, int, float, tel, text,
file, folder, address, date_dmy, date_ymd and email. Register your
own with pattern():
$validation->pattern('product_code', '[A-Z]{2}-[0-9]{4}');
$validation->rule('code', 'regex(product_code)');Messages support {field} and positional placeholders: {1} is the value and
{2} is the first rule argument.
$validation->rule('age', 'min(18)');
// "age must be greater than or equal to 18."Replace raw field names with friendly labels:
$validation->labels(['age' => 'Age']);
// "Age must be greater than or equal to 18."Switch language (the package ships with en and tr), or override individual
messages:
$validation->setLocale('tr');
$validation->setLocaleArray(['integer' => '{field} is not a whole number.']);Point at your own language directory with setLocaleDir(). See
docs/localization-and-messages.md.
Everything thrown by the package implements
InitPHP\Validation\Exception\ExceptionInterface:
UndefinedRuleException— a rule name was used that is neither built-in nor registered withextend(). Unknown rules fail loudly instead of silently passing.InvalidArgumentException— a key or rule entry had an unsupported type.LocaleException— a locale directory or file could not be loaded.
Version 2.0 is a breaking release. The headline changes:
- Requires PHP 8.1+ (was 7.4+).
- Callable rules now work — they previously threw a
TypeError. - Unknown rules now throw
UndefinedRuleExceptioninstead of silently passing validation. - Rules are no longer dispatched to arbitrary global functions (a
surprising and unsafe behaviour). Use
extend()for custom logic. - Error-message keys resolve case-insensitively, the
againmessage now resolves in English, and several edge cases were fixed (argument trimming, open-endedlength/rangebounds, null-safety, looseequals/again).
Full notes: docs/upgrading-from-1.x.md.
composer test # PHPUnit
composer stan # PHPStan (max level)
composer cs-check # PHP-CS-Fixer (dry run)
composer ci # all of the aboveContributions are welcome. Please read the org-wide
Contributing guide
and open a pull request against main. New behaviour should come with tests,
and composer ci should pass.
Released under the MIT License. Copyright © 2022 InitPHP.
