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' => '[email protected]',
'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.
| Rule | Description |
|---|---|
required |
The value is present and not a blank string (numbers and non-empty arrays pass). |
optional |
Pseudo-rule. If the field is absent, its other rules are skipped instead of failing. |
empty |
The value is empty once trimmed. |
integer |
An integer or an integer-looking string. |
float |
A float/int or a float-looking string. |
numeric |
A numeric value. |
string |
A string. |
boolean |
A real boolean or one of true, false, 1, 0. |
array |
An array. |
alpha |
Letters only (Unicode-aware). |
alphanum / alphanumeric |
Letters and digits only. |
mail |
A valid e-mail address. |
mailHost(host, ...) |
A valid e-mail at one of the given hosts. |
url |
A valid URL. |
urlHost(domain, ...) |
A URL whose host equals, or is a subdomain of, one of the domains. |
ip / ipv4 / ipv6 |
A valid IP address of the given family. |
min(n) |
Numbers >= n; for strings/arrays the length/count >= n. |
max(n) |
Numbers <= n; for strings/arrays the length/count <= n. |
range(min...max) |
A number within the range. Also accepts min-max, ...max, min.... |
length(min...max) |
String length / array count within range. A single number is the maximum; open bounds (...max, min...) are allowed. |
regex(name|body) |
Matches a named pattern or an inline regex body. |
date |
A DateTimeInterface, or any string strtotime() understands. |
dateFormat(format) |
A date string in the given date() format, e.g. dateFormat(Y/m/d). |
again(field) |
Loosely equals the value of another field. |
equals(value) |
Loosely equals the given value. |
startWith(value) |
A string starting with the value, or an array whose first element equals it. |
endWith(value) |
A string ending with the value, or an array whose last element equals it. |
in(needle) |
Case-insensitive substring (strings) or strict membership (arrays). |
notIn(needle) |
The inverse of in. |
contains(needle) |
Case-sensitive substring. |
notContains(needle) |
The inverse of contains. |
only(a, b, ...) |
Case-insensitively equals one of the options. |
strictOnly(a, b, ...) |
Case-sensitively equals one of the options. |
creditCard(type?) |
A credit card number. Optional type: amex, visa, mastercard, maestro, jcb, solo, switch. |
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.