fangstar/php-validator
A lightweight, framework-agnostic PHP validation library with a familiar Laravel-style rule syntax. Validate arrays and request data using built-in rules, custom messages, and nested fields, plus support for custom validators—no framework required.
fangstar/php-validator is a small, framework-agnostic PHP parameter-validation component. It predates Laravel's standalone illuminate/validation split being widely used outside Laravel, so the fastest path is to treat it as a single-purpose library rather than a framework component.
composer require fangstar/php-validator:^1.0
vendor/fangstar/php-validator/composer.json and confirm the php requirement. A 2016-era package will typically declare >=5.4 or >=5.6; that is a lower bound only, so it will happily install on PHP 8 and then fail at runtime on removed syntax. Run your test suite on your actual target PHP version before adopting.src/ first, not the README. The README is Chinese-only and the public API surface is small (one validator class plus a rules file). Ten minutes in src/ will tell you more than the docs: look for the entry-point class, the rule registry/array, and how error messages are stored.$validator = new Validator();
$ok = $validator->check($_POST, [
'name' => 'required|max:20',
'email' => 'required|email',
'age' => 'integer|min:18',
]);
Verify the exact entry-point name (check, make, validate, or a static call) in src/ — this is the one thing worth confirming by hand before writing anything else.If you are already on Laravel, stop here and use the framework's Validator instead; this package buys you nothing and costs you a PHP 8 compatibility audit.
Centralize rule sets, don't inline them. The main day-to-day win is having one declarative place per endpoint describing its contract. Put rule arrays in dedicated classes or a config/validation.php map keyed by action name, and have controllers/service methods look them up:
$rules = require __DIR__ . '/../config/validation.php';
$validator->check($payload, $rules['user.create']);
Separate "shape" validation from "business" validation. Use the package to assert types, presence, length, and format; keep uniqueness checks, ownership checks, and state-machine rules in your service layer. Validation libraries of this vintage have no notion of a database context, and bolting one in via custom rules makes failures hard to attribute.
Wrap it once. Because the library is unmaintained, do not let it leak across the codebase. Write a thin adapter — App\Validation\Validator — that exposes validate(array $data, array $rules): array and throws a single ValidationException carrying all messages. That adapter is your escape hatch: when you eventually swap in illuminate/validation or symfony/validator, you change one file.
Error handling. Collect all failures rather than short-circuiting per field; API clients want the full list. Check whether the package stores messages as a flat array or keyed by field — if keyed, array_merge(...array_values($errors)) before returning, and flatten nested/array-field keys yourself.
Custom rules. Every validator in this family has an extension point (a register/extend method or a rules array you can append to). Register custom rules once at bootstrap, not per call, and keep them pure functions of ($value, $param) so they stay testable.
Framework integration. In Slim, Symfony, or a plain front controller, invoke the adapter in middleware so controllers receive only validated input. In Laravel, use a service provider to bind the adapter only if you have a legacy codebase already emitting these rule strings — otherwise migrate them to Laravel rule syntax, which is a near-mechanical translation for the common rules.
each(), create_function(), curly-brace string offsets ($str{0}), and dynamic-property deprecations. If you adopt it, fork it, tag your fork, and point Composer at your VCS. Do not depend on Packagist staying reachable for a package nobody maintains.== and empty() internally. Consequences: "0" is treated as empty for required, and 0 == "" is true in PHP 7 but not PHP 8 — so a rule that "worked" in your dev container can change behavior across runtimes. Write a test per rule with falsy inputs: 0, "0", "", null, [], false.max uses strlen, a 20-character Chinese name fails a max:20 rule. Patch to mb_strlen in your fork and note it in the README diff.items.*.price-style paths only if the package explicitly documents wildcard support. If not, loop over the array yourself and validate each element, prefixing error keys with the index.illuminate/validation as tech debt rather than an open question.How can I help you explore Laravel packages today?