High-performance PHP C extension for input validation. Provides the same API as the pure PHP library but with significantly better performance for CPU-bound validation tasks.
- PHP 8.2+ (NTS and ZTS)
- PCRE2 library (libpcre2-8)
- Linux or macOS
Use the included build script which handles multiple PHP versions:
# Install dependencies (Ubuntu/Debian)
sudo apt-get install php8.4-dev libpcre2-dev # For PHP 8.4
sudo apt-get install php8.5-dev libpcre2-dev # For PHP 8.5
# Build for a specific PHP version
./build.sh 8.4 # Build for PHP 8.4
./build.sh 8.5 # Build for PHP 8.5
./build.sh # Build for default PHP version
# Run tests
./build.sh test 8.4 # Test with PHP 8.4
./build.sh test 8.5 # Test with PHP 8.5
# Clean build artifacts
./build.sh clean# Install dependencies (Ubuntu/Debian)
sudo apt-get install php-dev libpcre2-dev
# Install dependencies (macOS)
brew install pcre2
# Build the extension
phpize
./configure --enable-signalforge-validation
make
make test
# Install
sudo make install
# Enable the extension
echo "extension=signalforge_validation.so" | sudo tee /etc/php/8.4/cli/conf.d/signalforge_validation.iniPHP extensions are version-specific. To support multiple PHP versions, build separately for each:
# Build for PHP 8.4
./build.sh clean
./build.sh 8.4
sudo cp modules/signalforge_validation.so /usr/lib/php/20240924/
# Build for PHP 8.5
./build.sh clean
./build.sh 8.5
sudo cp modules/signalforge_validation.so /usr/lib/php/20250925/<?php
use Signalforge\Validation\Validator;
$validator = new Validator([
'email' => ['required', 'email'],
'name' => ['required', 'string', ['min', 2], ['max', 100]],
'age' => ['nullable', 'integer', ['between', 18, 120]],
]);
$result = $validator->validate([
'email' => 'user@example.com',
'name' => 'John',
'age' => 25,
]);
if ($result->valid()) {
$data = $result->validated();
// Process validated data
} else {
$errors = $result->errors();
// Handle validation errors
}$validator = Validator::make($data, $rules);
$result = $validator->validate($data);$result->valid(); // bool - true if validation passed
$result->failed(); // bool - true if validation failed
$result->errors(); // array - all errors grouped by field
$result->validated(); // array - validated data
$result->errorsFor('email'); // array - errors for specific field
$result->hasError('email'); // bool - check if field has errorsrequired- Field must be present and not emptynullable- Allow null/empty valuesfilled- If present, must not be emptypresent- Field must exist (can be empty)
string- Must be a stringinteger- Must be an integernumeric- Must be numericboolean- Must be booleanarray- Must be an array
['min', n]- Minimum length['max', n]- Maximum length['between', min, max]- Length between min and max['regex', pattern]- Must match regex['not_regex', pattern]- Must not match regexalpha- Only alphabetic charactersalpha_num- Only alphanumeric charactersalpha_dash- Alphanumeric plus dashes and underscoreslowercase- Must be all lowercaseuppercase- Must be all uppercase['starts_with', str]- Must start with string['ends_with', str]- Must end with string['contains', str]- Must contain string
['gt', n]- Greater than['gte', n]- Greater than or equal['lt', n]- Less than['lte', n]- Less than or equal
distinct- All values must be unique
email- Valid email addressurl- Valid URLip- Valid IP address (v4 or v6)uuid- Valid UUIDjson- Valid JSON stringdate- Valid date string['date_format', format]- Date matches format
['in', [...]]- Value must be in list['not_in', [...]]- Value must not be in list['same', field]- Must match another field['different', field]- Must differ from another fieldconfirmed- Must have matching{field}_confirmation
['after', field]- Date after another field['before', field]- Date before another field['after_or_equal', field]- Date after or equal['before_or_equal', field]- Date before or equal
oib- Croatian personal ID (OIB)phone- Valid phone numberiban- Valid IBANvat_eu- EU VAT number
$validator = new Validator([
'type' => ['required', ['in', ['personal', 'business']]],
'company' => [
['when', ['type', '=', 'business'], [
'required', 'string', ['min', 2],
]],
],
]);Cross-field:
['field', '=', 'value']
['field', '!=', 'value']
['field', '>', 100]
['field', 'in', ['a', 'b', 'c']]
['field', 'filled']
['field', 'empty']Self-referential:
['@length', '>=', 256]
['@value', '=', 'special']
['@empty']
['@filled']Compound (multiple conditions):
// All conditions must be true (AND)
['and', ['type', '=', 'business'], ['country', '=', 'HR']]
// At least one condition must be true (OR)
['or', ['role', '=', 'admin'], ['role', '=', 'moderator']]
// Nested conditions
['and',
['type', '=', 'business'],
['or', ['country', '=', 'HR'], ['country', '=', 'SI']]
]
// Multiple conditions with self-referential checks
['and', ['@filled'], ['@length', '>=', 5], ['@length', '<=', 100]]Full example with compound conditions:
$validator = new Validator([
'type' => ['required', ['in', ['personal', 'business']]],
'country' => ['required', 'string'],
'vat_number' => [
// Required only for EU businesses
['when', ['and', ['type', '=', 'business'], ['country', 'in', ['HR', 'SI', 'AT', 'DE']]], [
'required', 'vat_eu',
]],
],
'company_name' => [
// Required for business OR if VAT number is provided
['when', ['or', ['type', '=', 'business'], ['vat_number', 'filled']], [
'required', 'string', ['min', 2],
]],
],
]);$validator = new Validator([
'items' => ['required', 'array'],
'items.*.name' => ['required', 'string'],
'items.*.price' => ['required', 'numeric', ['gt', 0]],
]);Errors are returned as keys for i18n:
[
'email' => [
[
'key' => 'validation.required',
'params' => ['field' => 'email'],
],
],
]| Operation | PHP Library | C Extension |
|---|---|---|
| 50 fields, 200 rules | 0.5-1ms | 0.05-0.1ms |
| Regex validation | Compile each time | Compile once, cache |
| Email validation | filter_var | Optimized parser |
| Memory per validation | ~50KB | ~5KB |
make test
# Memory leak testing with Valgrind
USE_ZEND_ALLOC=0 valgrind --leak-check=full php tests/memory_test.phpMIT