bitrix-validation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Validation in Bitrix

Bitrix中的验证

The
Bitrix\Main\Validation\ValidationService
service validates objects using PHP 8 attributes. Any object with typed properties can be checked to obtain a
ValidationResult
with a list of errors.
Service id in
ServiceLocator
:
main.validation.service
(kernel registration). There is no
validation
section in
.settings.php
for registering rules.
Bitrix\Main\Validation\ValidationService
服务使用PHP 8属性验证对象。任何带有类型化属性的对象都可以被检查,以获取包含错误列表的
ValidationResult
ServiceLocator
中的服务ID:
main.validation.service
(内核注册)。
.settings.php
中没有用于注册规则的**
validation
**配置段。

First-level Attributes

一级属性

AttributeWhat it checks
#[NotEmpty]
Not empty (
!empty
); options
allowZero
,
allowSpaces
#[Length(min, max)]
String length
#[Min(n)]
/
#[Max(n)]
/
#[Range(min, max)]
Numeric constraints
#[PositiveNumber]
Numeric value >= 1 (internally
MinValidator(1)
, so
0.5
fails)
#[Email]
/
#[Phone]
/
#[PhoneOrEmail]
Format;
Email
options:
strict
,
domainCheck
(passed to
check_email()
)
#[Url]
URL (no options besides
errorMessage
)
#[RegExp('/pattern/')]
Regular expression (attribute name is
RegExp
, not
Regex
); options
flags
,
offset
are passed to
preg_match()
#[InArray($validValues)]
Value is one of the allowed list items; options
strict
(strict
in_array
),
showValues
(list allowed values in the error)
#[Json]
String is valid JSON
#[Validatable]
Recursively validate nested object;
iterable: true
validates each element of an array of objects
#[ElementsType(...)]
Type of array elements:
className: Dto::class
or a
Type
enum case (
Bitrix\Main\Validation\Rule\Enum\Type::Integer
/
String
/
Float
/
Numeric
;
Numeric
= anything
is_numeric()
, incl. numeric strings)
#[AtLeastOnePropertyNotEmpty(['name', 'email'])]
At least one of the fields is filled (on class)
#[OnlyOneOfPropertyRequired(['name', 'email'])]
Exactly one of the listed fields is filled (on class)
Each attribute accepts an optional
errorMessage
for a custom error text (not
message
). For localized texts pass a
Bitrix\Main\Localization\LocalizableMessage('PHRASE_CODE', phraseSrcFile: __FILE__)
instead of a string — the phrase is defined in the matching
lang/<code>/
file (
phraseSrcFile
is optional: when omitted it is guessed from the backtrace). Every rule also accepts
groups: [...]
;
ValidationService::validate($object, $group)
then runs rules of that group plus all ungrouped rules; without a group everything runs.
Nullable handling: an uninitialized nullable property is skipped by validation; an uninitialized non-nullable property produces an error (
MAIN_VALIDATION_EMPTY_PROPERTY
); a property explicitly assigned
null
counts as initialized and
null
is passed to its validators.
属性验证内容
#[NotEmpty]
非空(
!empty
);可选参数
allowZero
allowSpaces
#[Length(min, max)]
字符串长度
#[Min(n)]
/
#[Max(n)]
/
#[Range(min, max)]
数值约束
#[PositiveNumber]
数值≥1(内部基于
MinValidator(1)
,因此
0.5
会验证失败)
#[Email]
/
#[Phone]
/
#[PhoneOrEmail]
格式;
Email
可选参数:
strict
domainCheck
(会传入
check_email()
#[Url]
URL格式(除
errorMessage
外无其他参数)
#[RegExp('/pattern/')]
正则表达式(属性名称为**
RegExp
**,而非
Regex
);可选参数
flags
offset
会传入
preg_match()
#[InArray($validValues)]
值属于允许列表中的一项;可选参数
strict
(严格模式
in_array
)、
showValues
(在错误信息中列出允许值)
#[Json]
字符串为有效的JSON格式
#[Validatable]
递归验证嵌套对象;
iterable: true
会验证对象数组中的每个元素
#[ElementsType(...)]
数组元素类型:
className: Dto::class
Type
枚举值(
Bitrix\Main\Validation\Rule\Enum\Type::Integer
/
String
/
Float
/
Numeric
Numeric
表示所有
is_numeric()
判定为真的值,包括数值型字符串)
#[AtLeastOnePropertyNotEmpty(['name', 'email'])]
至少有一个字段已填写(作用于类)
#[OnlyOneOfPropertyRequired(['name', 'email'])]
恰好有一个指定字段已填写(作用于类)
每个属性都接受可选的**
errorMessage
**参数用于自定义错误文本(不是
message
)。如需本地化文本,可传入
Bitrix\Main\Localization\LocalizableMessage('PHRASE_CODE', phraseSrcFile: __FILE__)
而非字符串——短语定义在对应的
lang/<code>/
文件中(
phraseSrcFile
为可选参数:若省略,会通过回溯自动推断)。每个规则还支持
groups: [...]
参数;调用
ValidationService::validate($object, $group)
时,会运行该分组的规则以及所有未分组的规则;若未指定分组,则运行所有规则。
可空类型处理:未初始化的可空属性会被跳过验证;未初始化的非可空属性会产生错误(
MAIN_VALIDATION_EMPTY_PROPERTY
);显式赋值为
null
的属性视为已初始化,
null
会被传入其验证器。

DTO with Attributes

带属性的DTO

php
<?php declare(strict_types=1);

namespace Vendor\Module\Application\Dto;

use Bitrix\Main\Validation\Rule\NotEmpty;
use Bitrix\Main\Validation\Rule\Length;
use Bitrix\Main\Validation\Rule\Email;
use Bitrix\Main\Validation\Rule\Range;
use Bitrix\Main\Validation\Rule\InArray;

final class CreateUserDto
{
    public function __construct(
        #[NotEmpty, Length(min: 2, max: 64)]
        public readonly string $name,

        #[NotEmpty, Email]
        public readonly string $email,

        #[Range(min: 18, max: 120)]
        public readonly int $age,

        #[InArray(['user', 'admin'])]
        public readonly string $role,
    ) {}
}
php
<?php declare(strict_types=1);

namespace Vendor\Module\Application\Dto;

use Bitrix\Main\Validation\Rule\NotEmpty;
use Bitrix\Main\Validation\Rule\Length;
use Bitrix\Main\Validation\Rule\Email;
use Bitrix\Main\Validation\Rule\Range;
use Bitrix\Main\Validation\Rule\InArray;

final class CreateUserDto
{
    public function __construct(
        #[NotEmpty, Length(min: 2, max: 64)]
        public readonly string $name,

        #[NotEmpty, Email]
        public readonly string $email,

        #[Range(min: 18, max: 120)]
        public readonly int $age,

        #[InArray(['user', 'admin'])]
        public readonly string $role,
    ) {}
}

Direct Validation in Service

服务中的直接验证

php
use Bitrix\Main\DI\ServiceLocator;
use Bitrix\Main\Validation\ValidationService;

final class UserService
{
    private readonly ValidationService $validator;

    public function __construct()
    {
        $this->validator = ServiceLocator::getInstance()->get('main.validation.service');
    }

    public function register(CreateUserDto $dto): \Bitrix\Main\Result
    {
        $result = new \Bitrix\Main\Result();
        $validation = $this->validator->validate($dto);

        if (!$validation->isSuccess())
        {
            foreach ($validation->getErrors() as $error)
            {
                // getCode() holds the property path: 'email', 'items.0.name'
                $result->addError(new \Bitrix\Main\Error(
                    $error->getMessage(),
                    $error->getCode(),
                ));
            }
            return $result;
        }

        // ...
        return $result;
    }
}
Or inject via
constructorParams
/ factory when registering the service, still resolving
'main.validation.service'
.
php
use Bitrix\Main\DI\ServiceLocator;
use Bitrix\Main\Validation\ValidationService;

final class UserService
{
    private readonly ValidationService $validator;

    public function __construct()
    {
        $this->validator = ServiceLocator::getInstance()->get('main.validation.service');
    }

    public function register(CreateUserDto $dto): \Bitrix\Main\Result
    {
        $result = new \Bitrix\Main\Result();
        $validation = $this->validator->validate($dto);

        if (!$validation->isSuccess())
        {
            foreach ($validation->getErrors() as $error)
            {
                // getCode()返回属性路径:'email', 'items.0.name'
                $result->addError(new \Bitrix\Main\Error(
                    $error->getMessage(),
                    $error->getCode(),
                ));
            }
            return $result;
        }

        // ...
        return $result;
    }
}
也可在注册服务时通过
constructorParams
/工厂注入,仍需解析
'main.validation.service'

Scalar Action Parameters

标量动作参数

Rule attributes can be placed directly on controller action parameters — argument binding validates the value before the action is called:
php
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Validation\Rule\PositiveNumber;

final class User extends Controller
{
    public function getAction(#[PositiveNumber] int $id): array
    {
        return ['id' => $id];
    }
}
规则属性可直接放置在控制器动作参数上——参数绑定会在调用动作之前验证值:
php
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Validation\Rule\PositiveNumber;

final class User extends Controller
{
    public function getAction(#[PositiveNumber] int $id): array
    {
        return ['id' => $id];
    }
}

Request DTO in Controller (
ValidationParameter
autowire)

控制器中的Request DTO(
ValidationParameter
自动注入)

For a set of related values create a DTO and register it via
getAutoWiredParameters()
with
Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter
(an AutoWire rule, not a parameter attribute). It builds the DTO through the given factory and validates it before it reaches the action; on validation errors the action is not called and the controller returns the errors.
php
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter;
use Vendor\Module\Application\Service\PostService;

final class Post extends Controller
{
    public function getAutoWiredParameters(): array
    {
        return [
            new ValidationParameter(
                CreatePostRequest::class,
                fn () => CreatePostRequest::createFromRequest($this->getRequest()),
            ),
        ];
    }

    public function createAction(CreatePostRequest $request, PostService $postService): array
    {
        // We only get here if validation was successful.
        // Otherwise, the controller will return errors automatically.
        $result = $postService->create($request);

        if (!$result->isSuccess())
        {
            $this->addErrors($result->getErrors());
            return [];
        }

        return ['id' => $result->getId()];
    }
}
php
namespace Vendor\Blog\Application\Request;

use Bitrix\Main\Validation\Rule\NotEmpty;
use Bitrix\Main\Validation\Rule\Length;

final class CreatePostRequest
{
    public function __construct(
        #[NotEmpty, Length(min: 1, max: 255)]
        public readonly ?string $title = null,

        public readonly ?string $body = null,
    ) {}

    public static function createFromRequest(\Bitrix\Main\Request $request): self
    {
        return new self(
            $request->get('title'),
            $request->get('body'),
        );
    }
}
Keep DTO properties nullable with
null
defaults so construction from a raw request never fails — the rules (
NotEmpty
, etc.) report missing values instead.
Generation:
php bitrix/bitrix.php make:request CreatePost -m vendor.blog --fields=title,body
(Since main 25.900).
对于一组相关值,可创建DTO并通过
getAutoWiredParameters()
注册,使用
Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter
(这是一个AutoWire规则,不是参数属性)。它会通过指定的工厂构建DTO,并在其到达动作之前进行验证;若验证失败,动作不会被调用,控制器会直接返回错误信息。
php
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter;
use Vendor\Module\Application\Service\PostService;

final class Post extends Controller
{
    public function getAutoWiredParameters(): array
    {
        return [
            new ValidationParameter(
                CreatePostRequest::class,
                fn () => CreatePostRequest::createFromRequest($this->getRequest()),
            ),
        ];
    }

    public function createAction(CreatePostRequest $request, PostService $postService): array
    {
        // 只有验证成功时才会执行到这里
        // 否则控制器会自动返回错误信息
        $result = $postService->create($request);

        if (!$result->isSuccess())
        {
            $this->addErrors($result->getErrors());
            return [];
        }

        return ['id' => $result->getId()];
    }
}
php
namespace Vendor\Blog\Application\Request;

use Bitrix\Main\Validation\Rule\NotEmpty;
use Bitrix\Main\Validation\Rule\Length;

final class CreatePostRequest
{
    public function __construct(
        #[NotEmpty, Length(min: 1, max: 255)]
        public readonly ?string $title = null,

        public readonly ?string $body = null,
    ) {}

    public static function createFromRequest(\Bitrix\Main\Request $request): self
    {
        return new self(
            $request->get('title'),
            $request->get('body'),
        );
    }
}
保持DTO属性为可空类型并设置
null
默认值,这样从原始请求构建DTO时永远不会失败——缺失值会由规则(如
NotEmpty
)报告。
生成命令:
php bitrix/bitrix.php make:request CreatePost -m vendor.blog --fields=title,body
自main 25.900版本起支持)。

Class-Level Attributes

类级属性

php
use Bitrix\Main\Validation\Rule\AtLeastOnePropertyNotEmpty;

#[AtLeastOnePropertyNotEmpty(['email', 'phone'])]
final readonly class ContactRequest
{
    public function __construct(
        public ?string $email = null,
        public ?string $phone = null,
    ) {}
}
php
use Bitrix\Main\Validation\Rule\AtLeastOnePropertyNotEmpty;

#[AtLeastOnePropertyNotEmpty(['email', 'phone'])]
final readonly class ContactRequest
{
    public function __construct(
        public ?string $email = null,
        public ?string $phone = null,
    ) {}
}

Collections

集合验证

php
use Bitrix\Main\Validation\Rule\Recursive\Validatable;
use Bitrix\Main\Validation\Rule\ElementsType;

final class OrderDto
{
    /**
     * @var OrderItemDto[]
     */
    #[ElementsType(className: OrderItemDto::class)]
    #[Validatable(iterable: true)]
    public array $items = [];
}
php
use Bitrix\Main\Validation\Rule\Recursive\Validatable;
use Bitrix\Main\Validation\Rule\ElementsType;

final class OrderDto
{
    /**
     * @var OrderItemDto[]
     */
    #[ElementsType(className: OrderItemDto::class)]
    #[Validatable(iterable: true)]
    public array $items = [];
}

Custom Validator

自定义验证器

There is no
.settings.php
validation
section. Custom rules are PHP attributes that extend
AbstractPropertyValidationAttribute
and return validators from
getValidators()
.
  1. Attribute +
    getValidators()
    :
    php
    <?php declare(strict_types=1);
    
    namespace Vendor\Module\Validation\Rule;
    
    use Attribute;
    use Bitrix\Main\Localization\LocalizableMessageInterface;
    use Bitrix\Main\Validation\Rule\AbstractPropertyValidationAttribute;
    use Bitrix\Main\Validation\Validator\ValidatorInterface;
    use Bitrix\Main\Validation\ValidationResult;
    use Bitrix\Main\Validation\ValidationError;
    
    #[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
    final class EvenNumber extends AbstractPropertyValidationAttribute
    {
        public function __construct(
            // type must match the inherited trait property exactly
            protected string|LocalizableMessageInterface|null $errorMessage = null,
        ) {}
    
        protected function getValidators(): array
        {
            // $this->errorMessage is applied automatically by the base class
            // (replaceWithCustomError from ValidationErrorTrait)
            return [
                new EvenNumberValidator(),
            ];
        }
    }
    
    final class EvenNumberValidator implements ValidatorInterface
    {
        public function validate(mixed $value): ValidationResult
        {
            $result = new ValidationResult();
            if (!is_int($value) || $value % 2 !== 0)
            {
                $result->addError(new ValidationError(
                    'Number must be even',
                    'EVEN_NUMBER', // the property path is prepended later: 'age.EVEN_NUMBER'
                    failedValidator: $this,
                ));
            }
    
            return $result;
        }
    }
  2. Use the attribute on DTO properties — no kernel registration step:
    php
    #[EvenNumber(errorMessage: 'Age must be even')]
    public readonly int $age;
ValidatorInterface::validate(mixed $value): ValidationResult
no
Rule
parameter.
In attributes extending the abstract classes,
$errorMessage
must be typed
string|LocalizableMessageInterface|null
— the base
ValidationErrorTrait
declares the property with exactly this type, and PHP property types are invariant (a narrower
?string
is a fatal error). Class attributes (checks across several properties) extend
AbstractClassValidationAttribute
/ implement
ClassValidationAttributeInterface::validateObject(object $object)
.
.settings.php
没有
validation
配置段。自定义规则是继承自
AbstractPropertyValidationAttribute
的PHP属性,并通过
getValidators()
返回验证器。
  1. 属性 +
    getValidators()
    :
    php
    <?php declare(strict_types=1);
    
    namespace Vendor\Module\Validation\Rule;
    
    use Attribute;
    use Bitrix\Main\Localization\LocalizableMessageInterface;
    use Bitrix\Main\Validation\Rule\AbstractPropertyValidationAttribute;
    use Bitrix\Main\Validation\Validator\ValidatorInterface;
    use Bitrix\Main\Validation\ValidationResult;
    use Bitrix\Main\Validation\ValidationError;
    
    #[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
    final class EvenNumber extends AbstractPropertyValidationAttribute
    {
        public function __construct(
            // 类型必须与继承的trait属性完全匹配
            protected string|LocalizableMessageInterface|null $errorMessage = null,
        ) {}
    
        protected function getValidators(): array
        {
            // $this->errorMessage会被基类自动应用
            // (来自ValidationErrorTrait的replaceWithCustomError)
            return [
                new EvenNumberValidator(),
            ];
        }
    }
    
    final class EvenNumberValidator implements ValidatorInterface
    {
        public function validate(mixed $value): ValidationResult
        {
            $result = new ValidationResult();
            if (!is_int($value) || $value % 2 !== 0)
            {
                $result->addError(new ValidationError(
                    'Number must be even',
                    'EVEN_NUMBER', // 后续会追加属性路径:'age.EVEN_NUMBER'
                    failedValidator: $this,
                ));
            }
    
            return $result;
        }
    }
  2. 在DTO属性上使用该属性——无需内核注册步骤:
    php
    #[EvenNumber(errorMessage: 'Age must be even')]
    public readonly int $age;
ValidatorInterface::validate(mixed $value): ValidationResult
——没有
Rule
参数。
继承抽象类的属性中,
$errorMessage
的类型必须
string|LocalizableMessageInterface|null
——基类
ValidationErrorTrait
声明的属性正是此类型,且PHP属性类型是不变的(更窄的
?string
会导致致命错误)。类级属性(跨多个属性的检查)需继承
AbstractClassValidationAttribute
/实现
ClassValidationAttributeInterface::validateObject(object $object)

Retrieving Validation Result

获取验证结果

ValidationResult
extends
Bitrix\Main\Result
and contains
ValidationError
objects (extend
Bitrix\Main\Error
). Each error has:
  • getMessage()
    : localized message.
  • getCode()
    : property path that failed — the service prefixes the property name (and array index for iterables), e.g.
    email
    ,
    items.0.name
    ; a code set inside a validator is appended after a dot (
    age.EVEN_NUMBER
    ).
  • getFailedValidator()
    : the
    ValidatorInterface
    instance that produced the error (or
    null
    ).
There is no
getField()
method — the field name lives in the code.
ValidationResult
继承自
Bitrix\Main\Result
,包含
ValidationError
对象(继承自
Bitrix\Main\Error
)。每个错误包含:
  • getMessage()
    :本地化消息。
  • getCode()
    :验证失败的属性路径——服务会添加属性名称(以及数组的索引),例如
    email
    items.0.name
    ;验证器内部设置的代码会以点号追加在后面(如
    age.EVEN_NUMBER
    )。
  • getFailedValidator()
    :产生错误的
    ValidatorInterface
    实例(或
    null
    )。
没有
getField()
方法——字段名称包含在错误代码中。

Checklist

检查清单

  • Validation is handled via PHP 8 attributes.
  • DTOs are used for complex input structures.
  • Request DTOs are wired via
    ValidationParameter
    in
    getAutoWiredParameters()
    ; scalar action params carry rule attributes directly.
  • DTO properties are nullable with
    null
    defaults; remember: uninitialized nullable props are skipped, explicit
    null
    is validated.
  • Custom rules extend
    AbstractPropertyValidationAttribute
    and implement
    getValidators()
    — no
    .settings.php
    validation
    section.
  • Attribute names use
    RegExp
    /
    errorMessage
    (not
    Regex
    /
    message
    ).
  • ValidationService
    is retrieved as
    main.validation.service
    .
  • Error messages are localized or descriptive.
  • Collections of DTOs are validated with
    #[ElementsType(className: ...)]
    +
    #[Validatable(iterable: true)]
    .
  • 验证通过PHP 8属性实现。
  • 使用DTO处理复杂输入结构。
  • Request DTO通过
    getAutoWiredParameters()
    中的
    ValidationParameter
    注入;标量动作参数直接携带规则属性。
  • DTO属性为可空类型并设置
    null
    默认值;注意:未初始化的可空属性会被跳过验证,显式赋值的
    null
    会被验证。
  • 自定义规则继承
    AbstractPropertyValidationAttribute
    并实现
    getValidators()
    ——无需
    .settings.php
    中的
    validation
    配置段。
  • 属性名称使用
    RegExp
    /
    errorMessage
    (而非
    Regex
    /
    message
    )。
  • 通过
    main.validation.service
    获取
    ValidationService
  • 错误消息已本地化或描述清晰。
  • DTO集合通过
    #[ElementsType(className: ...)]
    +
    #[Validatable(iterable: true)]
    进行验证。