siriusphp/validation
SiriusPHP Validation is a standalone PHP validation library with a rich set of built-in rules, nested array support, custom rules and error messages, and a simple API for validating forms and structured input.
Install it with Composer — it has no framework coupling and no Laravel/Symfony service provider to wire up:
composer require siriusphp/validation
The whole API centers on three things: a Validator instance, rules added per field, and getMessages() after validate().
use Sirius\Validation\Validator;
$validator = new Validator();
$validator->add('name', 'required | minlength(2) | maxlength(80)');
$validator->add('email', 'required | email');
$validator->add('age', 'integer | between(18,120)');
$validator->add('password', 'required | minlength(8)');
$validator->add('password_confirm', 'required | match(password)');
if (!$validator->validate($_POST)) {
$errors = $validator->getMessages(); // ['email' => ['required' => '...'], ...]
}
Where to look first:
required | email | minlength(5)).src/Rule/ — every available rule is a class there, and that directory is the real documentation. Rule names map to classes via RuleFactory.RuleFactory for the canonical alias list (e.g. minlength, between, match, in, callback, upload-related rules).First realistic use case: validating a registration/contact form in a controller or small service, then mapping getMessages() into your view or a JSON 422 response. It is a form/input validator, not a full domain-model validator.
One validator per form/request. Build it in a factory method or dedicated RegisterRequestValidator class rather than scattering add() calls in controllers:
function registrationValidator(): Validator
{
$v = new Validator();
$v->add('email', 'required | email');
$v->add('password', 'required | minlength(8)');
$v->add('password_confirm', 'required | match(password)');
return $v;
}
Rule strings vs. options arrays. The pipe syntax is convenient for simple rules; rules that need configuration (min, max, field, format, etc.) can also be added with an options array:
$validator->add('username', 'minlength', ['min' => 3]);
Use the string form for readability and the options form when parameters contain commas or other characters that would confuse the parser.
Custom messages. Keep user-facing copy out of the rule layer by centralizing message overrides per field/rule when you add the rule, or by post-processing getMessages() in a presenter. This makes translation and tone control much easier than editing rule classes.
Conditional and cross-field rules. required_with / required_without cover "email or phone, at least one" style rules, and match/not_match handle password confirmation and similar comparisons against another field in the same data array. Field names must match the keys in the data you pass to validate() exactly.
Callbacks for domain checks. Use the callback rule for things like "username not already taken" or a captcha check, keeping the callback small and injected with a repository/HTTP client. This is the cleanest extension point before writing a full custom rule class.
Custom rules. When logic is reused, write a rule class under your own namespace (following the contract of the classes in src/Rule/) and register its alias with the RuleFactory. Register it before adding rules that reference it.
HTTP integration. Since it is framework-agnostic, adapt at the boundary:
$validator = registrationValidator();
if (!$validator->validate($request->all())) {
return response()->json(['errors' => $validator->getMessages()], 422);
}
For HTML forms, flatten the nested messages into a field => firstMessage array for the template. Do the same for Symfony, Slim, or plain PHP front controllers.
Testing. Validators are cheap to instantiate, so table-driven PHPUnit tests over validate() and getMessages() give excellent coverage of your rule sets without touching HTTP:
$this->assertFalse($validator->validate(['email' => 'nope']));
$this->assertArrayHasKey('email', $validator->getMessages());
File uploads. Use the provided upload/file rule against the $_FILES-shaped array, and only move the uploaded file after validation succeeds. Keep persistence separate from validation.
Validator keeps every rule you add. Never reuse a single instance across multiple forms or requests in a long-running process; construct a fresh one per validation.getMessages() is keyed by field and then by rule, so templates and JSON APIs usually need a flattening helper. Decide on one shape early and stick to it.required semantics. Empty string, null, and empty arrays fail, but "0"/0 generally pass — verify against your version before relying on it for numeric fields.minlength/maxlength use byte length or multibyte-aware counting before using them on user names in non-ASCII languages; if not mb-safe, add a custom rule.RuleFactory actually knows each alias. Unknown aliases are the most common source of confusing failures.add() calls that reference them, typically in the validator factory.respect/validation, Symfony Validator, or your framework's validator if you hit an unsupported edge case.getMessages() before your presenter touches it, then inspect the corresponding rule class in src/Rule/ — its getMessage() and validate() methods tell you exactly why a value failed.How can I help you explore Laravel packages today?