Validation rules¶
Validation rules implement ValidationRuleInterface and throw ValidationFailException when a value is rejected. A field executes its validation rules sequentially in collection order.
PHP attribute support¶
Small Forms 2.3.0 contains 75 validator classes. All 75 are declared as PHP attributes and support property targets, so they can be attached directly to properties inspected by AnnotationAdapter when their constructor arguments can be represented by a PHP attribute expression.
Common examples:
#[StringType]
#[ValidateNotEmpty]
#[ValidateEmail]
private string $email = '';
#[IntType]
#[ValidateRange(1, 100)]
private int $priority = 1;
#[StringType]
#[ValidateBeginWith('USR-')]
#[ValidateNumberCharsAtLeast(8)]
private string $externalId = '';
The built-in validators are:
ValidateAll, ValidateArrayItem, ValidateArrayKeys, ValidateArrayShape, ValidateAtLeastOneOf, ValidateBase64, ValidateBeginWith, ValidateBoolean, ValidateCallback, ValidateChoice, ValidateCidr, ValidateCountGreaterOrEqualThan, ValidateCountGreaterThan, ValidateCountLessOrEqualThan, ValidateCountLessThan, ValidateDateAfter, ValidateDateBefore, ValidateDateBetween, ValidateDateTime, ValidateDecimal, ValidateDifferentFromField, ValidateDivisibleBy, ValidateEach, ValidateEmail, ValidateEmpty, ValidateEndWith, ValidateEnum, ValidateEqual, ValidateFileExists, ValidateFloat, ValidateFloatArray, ValidateGreater, ValidateGreaterOrEqual, ValidateHexadecimal, ValidateHostname, ValidateInstanceOf, ValidateInt, ValidateIntArray, ValidateIp, ValidateIsFalse, ValidateIsNull, ValidateIsTrue, ValidateJson, ValidateLess, ValidateLessOrEqual, ValidateMatchRegex, ValidateMixedArray, ValidateNegativeNumber, ValidateNoneOf, ValidateNotChoice, ValidateNotEmpty, ValidateNotEqual, ValidateNotMatchRegex, ValidateNotNull, ValidateNumberCharsAtLeast, ValidateNumberCharsBetween, ValidateNumberCharsLessThan, ValidatePathIsDirectory, ValidatePathReadable, ValidatePathWritable, ValidatePosifiveNumber, ValidateRange, ValidateRequired, ValidateRequiredIf, ValidateRequiredWith, ValidateRequiredWithout, ValidateSameAsField, ValidateSequancialy, ValidateSlug, ValidateString, ValidateStringArray, ValidateUlid, ValidateUnique, ValidateUrl, ValidateUuid.
ValidateCallback is still normally created programmatically because its constructor requires a Closure, which is not a practical PHP attribute argument. Declaring a class as #[Attribute] and being convenient to instantiate in attribute syntax are separate concerns.
All validators remain available programmatically through addField() or addRule().
Presence and nullability¶
Useful rules include:
| Rule | Purpose |
|---|---|
ValidateRequired |
Requires a supplied value according to the rule implementation. |
ValidateNotEmpty |
Rejects empty values. |
ValidateEmpty |
Requires an empty value. |
ValidateNotNull |
Rejects null. |
ValidateIsNull |
Requires null. |
ValidateIsTrue |
Requires true. |
ValidateIsFalse |
Requires false. |
Many validators accept canBeNull: true by default. Combine them with an explicit presence/null rule when null must be rejected.
Cross-field validation¶
Five rules are form-aware. Before a form validates a field, it injects the current form and field name into these rules:
ValidateRequiredIf(string $otherField, mixed $expectedValue, bool $strict = true)ValidateRequiredWith(string $otherField)ValidateRequiredWithout(string $otherField)ValidateSameAsField(string $otherField, bool $strict = true)ValidateDifferentFromField(string $otherField, bool $strict = true)
Example:
Use these for input consistency. Authorization and permission checks still belong in the application/domain layer.
Strings and identifiers¶
Available validators include email, URL, host, IP/CIDR, UUID, ULID, slug, JSON, Base64, hexadecimal, regex and string-length rules.
Typical constructors:
new ValidateNumberCharsBetween(3, 80);
new ValidateMatchRegex('/^[A-Z0-9_-]+$/');
new ValidateEmail(canBeNull: false);
new ValidateUrl(canBeNull: true);
new ValidateUuid(canBeNull: false);
Numeric rules¶
new ValidateGreater(0);
new ValidateGreaterOrEqual(0);
new ValidateLess(100);
new ValidateLessOrEqual(100);
new ValidateRange(1, 10);
new ValidateDivisibleBy(5);
new ValidateDecimal(integerSize: 8, decimalSize: 2);
The public class name ValidatePosifiveNumber retains its current historical spelling.
Arrays and collections¶
Small Forms includes validators for array contents and structure:
new ValidateEach(new ValidateUuid());
new ValidateAll(new StringType());
new ValidateArrayItem('email', new ValidateEmail());
new ValidateArrayKeys(
requiredKeys: ['name'],
allowedKeys: ['name', 'email'],
);
new ValidateArrayShape([
'name' => new ValidateNotEmpty(),
'email' => new ValidateEmail(),
]);
Count validators can be used either as property attributes or programmatically.
Composition¶
new ValidateAtLeastOneOf([
new ValidateEmail(),
new ValidateUuid(),
]);
new ValidateNoneOf([
new ValidateEqual('root'),
new ValidateEqual('admin'),
]);
new ValidateSequancialy([
new ValidateNotEmpty(),
new ValidateEmail(),
]);
ValidateSequancialy is the current public class name, including its spelling.
Dates¶
new ValidateDateAfter('2026-01-01 00:00:00');
new ValidateDateBefore(new DateTimeImmutable('+1 year'));
new ValidateDateBetween(
'2026-01-01 00:00:00',
'2026-12-31 23:59:59',
);
All three comparison rules support a format, inclusive/exclusive boundaries and nullable values.
Custom validation¶
Implement ValidationRuleInterface for reusable domain-independent input checks. For a callback-based rule:
new ValidateCallback(function (array $formData, mixed $value): void {
// Throw ValidationFailException when invalid.
});
ValidateUnique checks duplicate values inside an array/Collection; it is not a database uniqueness query.
Keep database-dependent uniqueness, authorization and race-sensitive invariants backed by authoritative database/domain checks even when a form provides an early validation layer.