Prerequisite Setup
Before using custom validation rules you must configure the namespace prefix for custom rule classes:
ValidateConfig::instance()->setRulesPath('Itwmw\\App\\Model\\Validate\\Rules\\');
It is recommended to place validation related configuration inside a Provider.
Priority
The validator's built‑in rules take precedence over extensions, so do not use duplicate names.
Extending Global Rules
Global rules are rules that can be used in every validator instance.
Using Rule Objects
The validator ships with many useful rules; you can also add your own. One way to register a custom rule is to create a new class that extends Itwmw\Validate\Support\Rule\BaseRule
. Place new rule classes under the directory you configured.
After creating the rule class you define its behavior. The passes
method receives the attribute name and value and must return true
or false
depending on whether the value satisfies the rule. The $message
property is the default error message when validation fails; it can be overridden by the validator's own message
definition.
Implicit Extensions
If you want a rule object to run even when the attribute is empty, implement the Itwmw\Validation\Support\Interfaces\ImplicitRule
interface. This is a marker interface only; it does not require you to implement any methods.
namespace Itwmw\App\Model\Validate\Rules;
class Chs extends BaseRule
{
/**
* Default error message
* @var string
*/
protected $message = 'The value of :attribute may contain only Chinese characters';
/**
* Determine if the validation rule passes.
*
* @param mixed $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value): bool
{
return is_scalar($value) && 1 === preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', (string)$value);
}
}
Once the rule object is defined, include its name with other rules:
protected $rule = [
'title' => 'required|chs',
];
If you did not configure the custom rule class path, you may also pass the fully‑qualified class name:
use Itwmw\App\Model\Validate\Rules\Chs;
protected $rule = [
'title' => [
'required', Chs::class
],
];
Naming
Custom extension rule names should be written in snake_case and lowercase.
Rule object error message formatting support: Formatting error messages in rule classes
You can also invoke a rule object independently:
Chs::make()->check('名字');
Returns true
on pass, false
on failure.
extend Method
Inside a validator you can register a rule via extend
:
class UserValidator extends Validate
{
}
$v = UserValidator::make();
$v->extend('chs', function ($attribute, $value, $parameters, $validator) {
return is_scalar($value) && 1 === preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', (string)$value);
}, 'The value of :attribute may contain only Chinese characters');
The custom validation closure receives four parameters: $attribute
(name), $value
(value), $parameters
(array of parameters supplied to the rule), and the validator instance $validator
. Besides closures you can supply a class and method to extend
:
class Rules
{
public function chs($attribute, $value, $parameters, $validator) {
return is_scalar($value) && 1 === preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', (string)$value);
}
}
$v->extend('chs', 'Rules@chs', 'The value of :attribute may contain only Chinese characters');
If the method is static you may pass an array: [ClassName::class, 'method']
:
class Rules
{
public static function chs($attribute, $value, $parameters, $validator)
{
return is_scalar($value) && 1 === preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', (string)$value);
}
}
$v->extend('chs', [Rules::class, 'chs'], 'The value of :attribute may contain only Chinese characters');
Note
When passing a class & method, the method must be public
, the class must be fully‑qualified. If no method name is given the default is validate
.
replacer Error Message Replacer
To define the error message you can provide it as the third argument to extend
, or define a replacer via extendReplacer
. extendReplacer
accepts the rule name and a closure. The closure receives $message
(current message), $attribute
, $rule
(rule name), $parameters
(parameter array):
$v->extendReplacer('chs', function ($message, $attribute, $rule, $parameters) {
return 'Custom error message';
});
You may also pass a class & method:
$v->extendReplacer('chs', 'Rules@chsMessage');
// Static method
$v->extendReplacer('chs', [Rules::class, 'chsMessage']);
Note
When passing a class & method, the method must be public
, the class must be fully‑qualified. If no method is supplied the default is replace
. This can override a custom message defined for a built‑in rule.
Usage is the same as with a rule object:
protected $rule = [
'title' => 'required|chs',
];
extendImplicit (Implicit Rules)
By default, when the attribute is missing or an empty string, normal rules that rely on custom extensions are not executed. For example, the unique
rule will not validate an empty string:
$rules = ['name' => 'unique:users,name'];
$input = ['name' => ''];
// Passes
If you need the rule to execute even for empty values, you must mark it as implicit. Use extendImplicit()
:
$v->extendImplicit('foo', function ($attribute, $value, $parameters, $validator) {
return $value == 'foo';
});
Note
An "implicit" extension only implies the attribute is required. Determining whether it is missing or merely empty is up to you.
extendDependent (Dependent Rules)
When validating arrays with custom rules you'll notice extend
and extendImplicit
do not expand *
. Use extendDependent
instead:
$v = new Validate();
$v->extendDependent('contains', function ($attribute, $value, $parameters, $validator) {
// $parameters is ['*.provider'] when declared.
// The validator will replace the asterisk with the current index (e.g. *.provider -> 0.provider).
// Now we can fetch another field using Arr::get().
// This custom rule asserts the attribute's value contains the value of another attribute.
return str_contains($value, Arr::get($validator->getData(), $parameters[0]));
});
$v->setRules([
'*.email' => 'contains:*.provider'
])->check([
[
'email' => '995645888@qq.com', 'provider' => 'qq.com'
]
]);
Custom Rules Inside a Validator Class
Rule Naming
Use snake_case lowercase names. For a method rule ruleCheckLogin
the rule name is check_login
; for class Chs
the rule name is chs
.
Using Class Methods
You can declare a method on the current validator class. Method naming pattern: rule
+ StudlyCase rule name, e.g. ruleCheckLogin
.
Note
The resulting rule name (check_login
) must not collide with other registered rules or it will be overridden.
A custom rule method receives $attribute
, $value
, $parameters
, $validator
:
class Test extends Validate
{
protected $rule = [
'user' => 'required|alphaNum|check_login'
];
protected $message = [
'user.check_login' => 'Login failed'
];
public function ruleCheckLogin($attribute, $value, $parameters, $validator): bool
{
return 'admin' === $value;
}
}
You can also define a default message for method rules via $ruleMessage
:
class Test extends Validate
{
protected $ruleMessage = [
'chs' => 'The value of :attribute may contain only Chinese characters'
];
protected function ruleChs($attribute, $value, $parameters, $validator): bool
{
return is_scalar($value) && 1 === preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', (string)$value);
}
}
Inline messages in $message are also supported:
class Test extends Validate
{
protected $message = [
'chs.scalar' => 'The value of :attribute must be scalar',
'chs' => 'The value of :attribute may contain only Chinese characters'
];
protected function ruleChs($attribute, $value, $parameters, $validator): true|string
{
if (!is_scalar($value)) {
return 'chs.scalar';
}
if (!1 === preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', (string) $value)) {
return 'chs';
}
return true;
}
}
Tips
Error messages for custom rules can also be defined via the Rule attribute.
Regular Expression Rules
You can validate with a regex directly:
'id' => 'regex:/^\d+$/',
If the regex itself contains |
, use array syntax:
'id' => ['regex:/^\d+$/']
You can predefine named patterns via a $regex
property and then reference them with regex:
+ name:
class Test extends \Itwmw\Validate\Validate
{
protected $regex = [
'number' => '/^\d+$/'
];
protected $rule = [
'id' => 'regex:number'
];
}
Passing Parameters to Custom Rules
Custom rules accept parameters like built‑in rules (max:100
, in:0,1,2
). Parameters are passed in order to the rule class constructor:
class Length extends BaseRule
{
protected $message = 'The length of :attribute is invalid';
protected $size;
public function __construct(int $size)
{
$this->size = $size;
}
public function passes($attribute, $value): bool
{
return strlen($value) === $this->size;
}
}
Or Rule
Use or
when any one of several rules should allow the value to pass:
class User extends Validate {
protected $rule = [
'username' => 'required|or:email,mobile'
];
}
If username
satisfies either email or mobile the validation passes:
User::make()->check([
'username' => '995645888@qq.com'
]);
User::make()->check([
'username' => '18899996666'
]);
Both 995645888@qq.com
and 18899996666
pass.
Using Rule Groups
or
also supports rule groups:
class User extends Validate {
protected $group = [
'username' => 'email|mobile'
];
protected $rule = [
'username' => 'required|or:username'
];
}
Multiple rule groups may be used:
class User extends Validate
{
protected $group = [
'username' => 'email|mobile',
'username_nl' => 'alpha|chs'
];
protected $rule = [
'username' => 'required|or:username,username_nl'
];
}
Here username
passes if it matches any rule in either group username
or username_nl
.
You can mix groups and individual rules:
class User extends Validate
{
protected $group = [
'username_nl' => 'alpha|chs',
'username_specific' => 'in:admin123,root520'
];
protected $rule = [
'username' => 'required|or:username_specific,username_nl,email,mobile'
];
}
In this example username
passes if any rule in username_specific
or username_nl
passes, or if it satisfies either email
or mobile
:
User::make()->check([
'username' => '995645888@qq.com'
]);
User::make()->check([
'username' => '18899996666'
]);
User::make()->check([
'username' => '虞灪'
]);
User::make()->check([
'username' => 'yapyuyu'
]);
User::make()->check([
'username' => 'admin123'
]);
User::make()->check([
'username' => 'root520'
]);
All of the above pass.
And Rule
When using groups inside an or rule, only one rule inside a group needs to pass for that group to be considered satisfied.
If you need all rules in a group to pass, use the and
rule:
class User extends Validate
{
public function __construct()
{
$this->group = [
'chinese_name' => new AndRule(['chs', 'min:2', 'max:4']),
'english_name' => new AndRule(['alpha', 'min:4', 'max:20']),
];
}
protected $rule = [
'name' => 'required|string|or:chinese_name,english_name',
];
}
User::make()->check([
'name' => '虞灪',
]);
User::make()->check([
'name' => 'Yepyuyu',
]);
Here validation passes if either group chinese_name
or english_name
has all of its rules satisfied.
Rule Attribute
If you want to add parameter checks etc. for a rule you can use the Itwmw\Validate\Support\Attribute\Rule
attribute. It can be applied to a class or a method allowing you to define the rule's parameters, error message and description.
Rule
can be applied like this:
#[Rule(['max','[min]'], ':attribute length must be between %{min} and %{max}.', 'Validate length')]
class CheckLength extends BaseRule {
protected $message = 'Built‑in error message'; // Will be overridden by the attribute's message
public function __construct(
protected int $max,
protected int $min = 0
) {
}
public function passes($attribute, $value): bool
{
$length = 0;
if (is_array($value)) {
$length = count($value);
}
if (is_scalar($value)) {
$length = mb_strlen($value);
}
return $length >= $this->min && $length <= $this->max;
}
}
After defining the attribute the validator will verify that the correct parameters were supplied; if not it throws Itwmw\Validate\Exception\ValidateRuntimeException
.
IDE plugins can leverage the attribute to show rule descriptions and parameter hints: