Fast, modern PHP request validation and sanitization. Schema-based rules, fail-fast execution, intelligent batching, and 108 built-in validation rules.
$validator = Validator::make([
'email' => 'required|email|max:255',
'age' => 'required|integer|min:18',
])->setSanitizers([
'email' => ['trim', 'lowercase'],
])->setCasts([
'age' => 'integer',
]);
$result = $validator->validate($data);
if ($result->passes()) {
$clean = $result->typed();
// All good!
}- 108 Built-in Rules - Basic types, conditional rules, files, database checks, enums, and more
- 46 Built-in Sanitizers - Manual sanitization or built-in sanitize+validate pipeline
- Intelligent Batching - Expensive DB checks are batched automatically
- Native DBLayer 5.1 Bridge - Optional resolver-first
exists/uniqueintegration - Frozen Compiled Validators - Reusable snapshots for persistent runtimes and Fiber-interleaved execution
- Schema Registry + Validator Profiles - Instance-owned frozen schema topology and immutable reusable configuration
- Fail-Fast + Full Collection Modes - Per-field fail-fast with configurable behavior
- Nested + Wildcard Validation - Dot notation with wildcard expansion
- Custom Messages + Placeholders -
:field,:rule,:min, and more - Locale Packs - Per-rule localized message templates with fallback
- Failure Metadata - Structured failures (
field,rule,message,value) - Schema Fragments + Composition - Reuse validation contracts across endpoints
- Conditional Closures -
sometimes()andwhen()for dynamic rule activation - Schema Export - JSON Schema, OpenAPI shape, and introspection metadata
- Typed Output + DTO Mapping - Cast map +
toDTO()support - Uploaded File Object Support - Array-style uploads and PSR-7 style objects
- ️ Upload Hardening Rules -
safe_filename,upload_meta,upload_id,secure_file - ️ PHP 8.4+ - Built with modern PHP features
composer require infocyph/reqshieldRequirements: PHP 8.4+, ext-hash with xxh3 support, ext-mbstring, and ext-fileinfo
use Infocyph\ReqShield\Validator;
$validator = Validator::make([
'email' => [
'rules' => 'required|email|max:255',
'sanitize' => ['trim', 'lowercase'],
'alias' => 'Email Address',
],
'password' => 'required|string|min:8|confirmed',
'age' => 'required|integer|min:18',
])->setCasts([
'age' => 'integer',
])->setCustomMessages([
'email.required' => ':field is required.',
'*.min' => ':field must be at least :min.',
]);
$result = $validator->validate($data);
if ($result->passes()) {
$validated = $result->typed();
// Process your data...
} else {
$errors = $result->errors();
$failures = $result->failures();
// Handle validation errors...
}Manual sanitization:
use Infocyph\ReqShield\Sanitizer;
$clean = [
'email' => Sanitizer::email($input['email']), // '[email protected]'
'username' => Sanitizer::alphaDash($input['username']), // 'john_doe'
'age' => Sanitizer::integer($input['age']), // 25
'bio' => Sanitizer::string($input['bio']), // Strips HTML tags
];
$result = $validator->validate($clean);Or built-in sanitize+validate pipeline:
$validator = Validator::make([
'email' => 'required|email',
'contacts.*.email' => 'required|email',
])->setSanitizers([
'email' => ['trim', 'lowercase'],
'contacts.*.email' => ['trim', 'lowercase'],
]);Or import the namespaced helper:
use function Infocyph\ReqShield\sanitize;
$clean = sanitize(' [email protected] ', 'email'); // '[email protected]'
$clean = sanitize('<b>TEXT</b>', ['string', 'lowercase']); // 'text'ReqShield includes 108 validation rules covering several common scenarios:
- Basic Types
- Formats
- Strings
- Numbers
- Dates
- Conditionals
- Database
- Files
- Arrays
- Comparison
- Patterns
- Additional
Use upload-focused rules for request metadata and filename safety:
$validator = Validator::make([
'upload' => 'required|secure_file',
'filename' => 'required|safe_filename',
'upload_id' => 'required|upload_id',
]);secure_file combines file and upload_meta so you can enforce
payload validity and safe upload metadata in one rule.
ReqShield includes 46 built-in sanitizers covering several common scenarios:
- Basic Types
- Case Conversions
- Text Processing
- Special Formats
- Alphanumeric Filters
- HTML and escaping utilities
- Encoding
- Array Operations
View Complete Sanitizer Reference
$result = Validator::fromArray($rules, $data);
$result = Validator::fromQuery($rules, $_GET);
$result = Validator::fromBody($rules, $body);
$result = Validator::fromFiles($rules, $_FILES);PSR-style request objects are supported through fromServerRequest() when compatible accessors are available.
$result = Validator::fromServerRequest($rules, $request);$validator = Validator::make($rules)
->strict(); // same as allowUnknown(false)
$validator = Validator::make($rules)
->stripUnknown(); // remove unknown fields instead of failing'status' => 'required|enum:App\\Enums\\OrderStatus''status' => [
'rules' => 'required|enum:App\\Enums\\OrderStatus',
'cast' => App\\Enums\\OrderStatus::class,
]use Infocyph\ReqShield\Support\ValidationContext;
$validator->after(function (ValidationContext $ctx): void {
if ((string) $ctx->get('start_date') > (string) $ctx->get('end_date')) {
$ctx->addError('end_date', 'End date must be after start date.');
}
});$result->toProblemJson();
$result->toJsonApiErrors();
$result->toApiErrors();
$result->toFlatErrors();$input = $result->input();
$input->string('email');
$input->int('age');
$input->only(['email', 'age']);Validate deeply nested arrays using dot notation:
$validator = Validator::make([
'user.email' => 'required|email',
'user.name' => 'required|min:3',
'user.profile.age' => 'required|integer|min:18',
'user.profile.bio' => 'string|max:500',
]);
$data = [
'user' => [
'email' => '[email protected]',
'name' => 'John Doe',
'profile' => [
'age' => 25,
'bio' => 'Software developer',
],
],
];
$result = $validator->validate($data);Nested paths are detected automatically and optimized targeted traversal is the default.
Use setNestedFlattenMode('all') only when full flattening is required.
Make error messages user-friendly:
$validator->setFieldAliases([
'user_email' => 'Email Address',
'contacts.*.email' => 'Contact Email',
]);$validator
->setCustomMessages([
'email.required' => ':field is required.',
'*.min' => ':field must be at least :min.',
'contacts.*.email.email' => 'Each :field must be valid.',
])
->addLocalePack('es', [
'required' => 'El campo :field es obligatorio.',
'*' => 'El campo :field no es valido.',
])
->setLocale('es');use Infocyph\ReqShield\Exceptions\ValidationException;
$validator = Validator::make($rules)->throwOnFailure();
try {
$result = $validator->validate($data);
$validated = $result->validated();
} catch (ValidationException $e) {
echo $e->getMessage(); // "Validation failed"
print_r($e->getErrors()); // All errors
echo $e->getErrorCount(); // Number of failed fields
echo $e->getFirstFieldError('email'); // First error for specific field
echo $e->getCode(); // 0 by default; transport status belongs to your application
}$result = $validator->validate($data);
if ($result->fails()) {
return [
'errors' => $result->errors(),
'failures' => $result->failures(), // field, rule, message, value
];
}$validator
->sometimes('vat', 'required', fn(array $data) => ($data['type'] ?? null) === 'business')
->when(
fn(array $data) => ($data['country'] ?? null) === 'US',
fn() => ['state' => 'required|string'],
);Validator::defineFragment('address', [
'line1' => 'required|string|max:120',
'zip' => 'required|digits:5',
]);
$validator = Validator::make([
'name' => 'required|string',
])->useFragment('address', 'billing');$validator = Validator::make([
'age' => 'required|integer',
'active' => 'required|boolean',
])->setCasts([
'age' => 'integer',
'active' => 'boolean',
])->setDtoClass(App\DTO\UserInput::class);
$result = $validator->validate($data);
$typed = $result->typed();
$dto = $result->toDTO();Validator::compile() returns a reusable frozen execution snapshot. Later mutation of the source builder cannot change the compiled validator.
$compiled = Validator::compile([
'email' => 'required|email',
'age' => 'required|integer|min:18',
]);
$result = $compiled->validate($data);Use callbacks for quick custom validation:
use Infocyph\ReqShield\Rules\Callback;
$validator = Validator::make([
'code' => [
'required',
new Callback(
callback: fn($value, $field, $data) => $value % 2 === 0,
message: 'The code must be an even number'
),
],
]);Create reusable rule classes:
use Infocyph\ReqShield\Contracts\Rule;
class StrongPassword implements Rule
{
public function passes(mixed $value, string $field, array $data): bool
{
return strlen($value) >= 12
&& preg_match('/[A-Z]/', $value)
&& preg_match('/[a-z]/', $value)
&& preg_match('/[0-9]/', $value)
&& preg_match('/[^A-Za-z0-9]/', $value);
}
public function message(string $field): string
{
return "The {$field} must be at least 12 characters with uppercase, lowercase, number, and special character.";
}
public function cost(): int { return 20; }
}
// Usage
$validator = Validator::make([
'password' => ['required', new StrongPassword()],
]);Validate against your database:
use Infocyph\ReqShield\Validator;
use Infocyph\ReqShield\Contracts\DatabaseProvider;
// Implement your database provider
class MyDatabaseProvider implements DatabaseProvider
{
// Implement required methods...
}
$db = new MyDatabaseProvider();
$validator = Validator::make([
'email' => 'required|email|unique:users,email',
'category_id' => 'required|exists:categories,id',
], $db);Database schemas require a provider and throw DatabaseProviderRequiredException
when it is absent. The contract contains only batchExists() and batchUnique();
ReqShield owns logical validation batching, while providers own query construction
and driver-safe physical chunking. ReqShield is database-library agnostic: a
provider may use PDO, DBLayer, Laravel, Doctrine, or another database layer.
DBLayer 5.1 is the development/reference integration and remains optional for normal consumers. When DBLayer is installed, ReqShield ships a native resolver-first bridge:
use Infocyph\ReqShield\Bridge\DBLayerDatabaseProvider;
use Infocyph\ReqShield\Validator;
$provider = new DBLayerDatabaseProvider(
static fn() => $applicationDatabase->connection(),
);
$validator = Validator::make([
'email' => 'required|email|unique:users,email',
'category_id' => 'required|exists:categories,id',
], $provider);The resolver is invoked only when database rules execute. ReqShield owns logical validation batching; DBLayer 5.1 owns physical bind-limit sizing and query execution.
Benefits:
- Automatic batching - Multiple checks become bounded DB-native match queries; restricted raw-SQL policies use query-builder-only lookups
- Update support -
Rule::unique('users', 'email')->ignore(5)ignores ID 5 - Explicit object syntax -
Rule::unique('users', 'email')->ignore($id)->withoutTrashed()
$jsonSchema = $validator->exportSchema('json_schema');
$openApiShape = $validator->exportSchema('openapi');
$introspection = $validator->exportSchema('introspection');For maximum performance, stop all validation on first error:
$validator = Validator::make($rules)
->setStopOnFirstError(true);
// Stops immediately when any field fails
$result = $validator->validate($data);ReqShield is built for speed:
Database rules are automatically batched:
// 3 separate rules...
'user_id' => 'exists:users,id',
'email' => 'unique:users,email',
'category_id' => 'exists:categories,id',
// ...become just 2 queries (50x faster!)
// - One batch for exists checks
// - One batch for unique checksStops validating a field on first rule failure:
'email' => 'required|email|max:255'
// If empty → fails on 'required', skips 'email' and 'max:255'Nested validation only activates if you use dot notation. No performance cost for simple flat arrays.
Do not disclose suspected vulnerabilities in a public issue, discussion or pull request. Follow SECURITY.md and use GitHub private vulnerability reporting.
ReqShield is protected by PHPForge, which provides automated tests, static and taint analysis, dependency auditing, architecture checks and release-readiness gates. Automated controls do not replace responsible disclosure or manual review.
MIT Licensed
Documentation • Security • Code of Conduct • Contributing
🗂️ Bug • Feature • Documentation • Question • CI failure
🔀 General • Bug fix • Feature • Refactor • Performance • Security & reliability • Documentation • Maintenance