Validation 类

CodeIgniter 提供了全面的 Validation 类,帮助减少需要编写的代码量。

概述

在介绍 CodeIgniter 的数据验证方法之前,先描述理想场景:

  1. 显示表单。

  2. 填写并提交表单。

  3. 如果提交的内容无效,或者遗漏了必填项,则重新显示表单,其中包含已填写的数据以及描述问题的错误消息。

  4. 此过程持续进行,直到提交有效的表单。

在处理端,脚本必须:

  1. 检查必填数据。

  2. 验证数据类型是否正确,是否符合条件。例如,提交的用户名必须仅包含允许的字符,必须达到最小长度,不能超过最大长度。用户名不能是已有的用户名,甚至不能是保留字。等等。

  3. 对数据进行安全过滤。

  4. 按需预格式化数据。

  5. 为数据库插入准备数据。

虽然上述过程并不复杂,但通常需要大量代码,而且为了显示错误消息,往往需要在表单 HTML 中放置各种控制结构。表单验证虽然逻辑简单,但实现起来通常非常繁琐和枯燥。

表单验证教程

以下是一个"动手实践"式教程,演示如何实现 CodeIgniter 的表单验证。

要实现表单验证,需要三样东西:

  1. 包含表单的 视图 文件。

  2. 包含 "success" 消息的视图文件,在提交成功后显示。

  3. 用于接收和处理提交数据的 控制器 方法。

以会员注册表单为例,来创建这三样东西。

表单

使用文本编辑器创建名为 signup.php 的表单。将以下代码放入其中,并保存到 app/Views/ 目录:

<html>
<head>
    <title>My Form</title>
</head>
<body>

    <?= validation_list_errors() ?>

    <?= form_open('form') ?>

        <h5>Username</h5>
        <input type="text" name="username" value="<?= set_value('username') ?>" size="50">

        <h5>Password</h5>
        <input type="text" name="password" value="<?= set_value('password') ?>" size="50">

        <h5>Password Confirm</h5>
        <input type="text" name="passconf" value="<?= set_value('passconf') ?>" size="50">

        <h5>Email Address</h5>
        <input type="text" name="email" value="<?= set_value('email') ?>" size="50">

        <div><input type="submit" value="Submit"></div>

    <?= form_close() ?>

</body>
</html>

成功页面

使用文本编辑器创建名为 success.php 的表单。将以下代码放入其中,并保存到 app/Views/ 目录:

<html>
<head>
    <title>My Form</title>
</head>
<body>

    <h3>Your form was successfully submitted!</h3>

    <p><?= anchor('form', 'Try it again!') ?></p>

</body>
</html>

控制器

使用文本编辑器创建名为 Form.php 的控制器。将以下代码放入其中,并保存到 app/Controllers/ 目录:

<?php

namespace App\Controllers;

class Form extends BaseController
{
    protected $helpers = ['form'];

    public function index()
    {
        if (! $this->request->is('post')) {
            return view('signup');
        }

        $rules = [
            // @TODO
        ];

        $data = $this->request->getPost(array_keys($rules));

        if (! $this->validateData($data, $rules)) {
            return view('signup');
        }

        // If you want to get the validated data.
        $validData = $this->validator->getValidated();

        return view('success');
    }
}

备注

方法 $this->request->is() 自 v4.3.0 起可用。 在之前版本中,需要使用 if (strtolower($this->request->getMethod()) !== 'post')

备注

方法 $this->validator->getValidated() 自 v4.4.0 起可用。

路由

然后在 app/Config/Routes.php 中为控制器添加路由:

// ...

$routes->get('form', 'Form::index');
$routes->post('form', 'Form::index');

// ...

试试看!

使用类似下面的 URL 访问站点,来测试表单:

example.com/index.php/form/

提交表单后,应该会看到表单重新加载。这是因为还没有在 $this->validateData() 中设置任何验证规则。

validateData() 是控制器提供的方法,内部使用 Validation 类。详见 $this->validateData()

备注

由于还没有告诉 validateData() 方法需要验证什么, 它默认 返回 false (布尔值 false)。只有当成功应用了 所有规则且没有任何规则失败时,validateData() 方法才会返回 true。

说明

关于上述页面,有几点需要注意。

signup.php

表单(signup.php)是一个标准网页表单,但有两处特别的地方:

  1. 使用 表单辅助函数 创建表单的起始和结束标签。严格来说,这并非必需,可以使用标准 HTML 创建表单。但使用辅助函数的好处是,它会根据配置文件中的 URL 自动生成 action URL。这使得应用在 URL 变更时更具可移植性。

  2. 在表单顶部,你会注意到以下函数调用:

    <?= validation_list_errors() ?>
    

    此函数将返回验证器发送的任何错误消息。如果没有消息,则返回空字符串。

Form.php

控制器(Form.php)有一个属性:$helpers。它加载视图文件中使用的表单辅助函数。

控制器有一个方法:index()。当收到非 POST 请求时,此方法返回 signup 视图以显示表单。否则,它使用控制器提供的 $this->validateData() 方法运行验证流程。根据验证是否成功,它要么重新显示表单,要么显示成功页面。

添加验证规则

然后在控制器(Form.php)中添加验证规则:

// ...

$rules = [
    'username' => 'required|max_length[30]',
    'password' => 'required|max_length[255]|min_length[10]',
    'passconf' => 'required|max_length[255]|matches[password]',
    'email'    => 'required|max_length[254]|valid_email',
];

// ...

提交表单后,应该会看到成功页面或带有错误消息的表单。

验证配置

传统规则与严格规则

CodeIgniter 4 有两种验证规则类。

默认规则类(严格规则)的命名空间为 CodeIgniter\Validation\StrictRules,提供严格的验证。

传统规则类(传统规则)的命名空间为 CodeIgniter\Validation。它们仅为了向后兼容而提供,可能无法正确验证非字符串值,新项目不应使用。

备注

自 v4.3.0 起,默认使用 严格规则 以提高安全性。

严格规则

Added in version 4.2.0.

严格规则 不使用隐式类型转换。

传统规则

重要

传统规则仅为了向后兼容而存在。不要在新项目中使用它们。即使已在使用,也建议切换到严格规则。

警告

验证包含非字符串值(如 JSON 数据)的数据时,应使用 严格规则

传统规则 隐式假定验证的是字符串值,输入值可能被隐式转换为字符串值。它适用于大多数基本场景,如验证 POST 数据。

但是,例如使用 JSON 输入数据时,数据可能是 bool/null/array 类型。使用传统规则类验证布尔值 true 时,它会被转换为字符串 '1'。如果使用 integer 规则验证它,'1' 会通过验证。

使用传统规则

警告

传统规则 仅为了向后兼容而提供。它们可能无法正确验证非字符串值,新项目不应使用。

如果需要使用传统规则,需要在 app/Config/Validation.php 中更改规则类:

<?php

namespace Config;

// ...

class Validation extends BaseConfig
{
    // ...

    public array $ruleSets = [
        \CodeIgniter\Validation\CreditCardRules::class,
        \CodeIgniter\Validation\FileRules::class,
        \CodeIgniter\Validation\FormatRules::class,
        \CodeIgniter\Validation\Rules::class,
    ];

    // ...
}

加载库

该库以 validation 服务的形式加载:

$validation = service('validation');

这段代码会自动加载 Config\Validation 文件,其中包含用于引入多个规则集以及可复用规则集合的设置。

备注

可能永远不需要使用此方法,因为 控制器模型 都提供了使验证更加简便的方法。

验证的工作原理

  • 验证永远不会更改要验证的数据。

  • 验证按照设置的验证规则逐个检查每个字段。如果任何规则返回 false,则该字段的检查在此结束。

  • 格式规则不允许空字符串。如需允许空字符串,请添加 permit_empty 规则。

  • 如果要验证的数据中不存在某个字段,则值被解释为 null。如需检查字段是否存在,请添加 field_exists 规则。

备注

field_exists 规则自 v4.5.0 起可用。

设置验证规则

CodeIgniter 允许为给定字段设置任意数量的验证规则,按顺序级联。要设置验证规则,可使用 setRule()setRules()withRequest() 方法。

设置单个规则

setRule()

此方法设置单个规则。方法签名为:

setRule(string $field, ?string $label, array|string $rules[, array $errors = []])

$rules 接受管道符分隔的规则列表或规则数组:

$validation->setRule('username', 'Username', 'required|max_length[30]|min_length[3]');
$validation->setRule('password', 'Password', ['required', 'max_length[255]', 'min_length[8]', 'alpha_numeric_punct']);

传递给 $field 的值必须与发送的任何数据数组中的键匹配。如果数据直接取自 $_POST,则必须与表单输入名称完全匹配。

警告

v4.2.0 之前,此方法的第三个参数 $rules 的类型声明为 string。v4.2.0 及之后版本,类型声明被移除以允许数组。为避免破坏继承类中重写此方法的 LSP 一致性,子类的此方法也应移除类型声明。

设置多个规则

setRules()

setRule() 类似,但接受字段名称及其规则的数组:

$validation->setRules([
    'username' => 'required|max_length[30]',
    'password' => 'required|max_length[255]|min_length[10]',
]);
// or
$validation->setRules([
    'username' => ['required', 'max_length[30]'],
    'password' => ['required', 'max_length[255]', 'min_length[10]'],
]);

要设置带标签的错误消息,可以这样设置:

$validation->setRules([
    'username' => ['label' => 'Username', 'rules' => 'required|max_length[30]'],
    'password' => ['label' => 'Password', 'rules' => 'required|max_length[255]|min_length[10]'],
]);
// or
$validation->setRules([
    'username' => ['label' => 'Username', 'rules' => 'required|max_length[30]'],
    'password' => ['label' => 'Password', 'rules' => ['required', 'max_length[255]', 'min_length[10]']],
]);

备注

setRules() 会覆盖之前设置的任何规则。要向现有规则集添加多条规则,请多次调用 setRule()

为数组数据设置规则

如果数据嵌套在关联数组中,可以使用"点数组语法"来轻松验证数据:

/*
 * The data to test:
 * [
 *     'contacts' => [
 *         'name' => 'Joe Smith',
 *         'friends' => [
 *             [
 *                 'name' => 'Fred Flinstone',
 *             ],
 *             [
 *                 'name' => 'Wilma',
 *             ],
 *         ]
 *     ]
 * ]
 */

// Joe Smith
$validation->setRules([
    'contacts.name' => 'required|max_length[60]',
]);

可以使用 * 通配符匹配数组的任意一层:

// Fred Flintsone & Wilma
$validation->setRules([
    'contacts.friends.*.name' => 'required|max_length[60]',
]);

备注

v4.4.4 之前,由于一个 bug,通配符 * 在错误的维度验证数据。详见 升级说明

"点数组语法"在处理单维数组数据时也很有用。例如,多选下拉框返回的数据:

/*
 * The data to test:
 * [
 *     'user_ids' => [
 *         1,
 *         2,
 *         3,
 *     ]
 * ]
 */

// Rule
$validation->setRules([
    'user_ids.*' => 'required|max_length[19]',
]);

withRequest()

重要

此方法仅为了向后兼容而存在。不要在新项目中使用它。即使已在使用,也建议使用其他更合适的方法。

警告

如需验证 POST 数据,请勿使用 withRequest()。此方法使用 $request->getVar(),按顺序返回 $_GET$_POST$_COOKIE 数据(取决于 php.ini 中的 request-order)。后面的值会覆盖前面的值。如果 Cookie 与 POST 参数同名,POST 值可能会被 Cookie 覆盖。

使用 Validation 类最常见的场景是验证来自 HTTP 请求的输入数据。如果需要,可以传入当前请求实例,它会自动将所有输入数据设置为要验证的数据:

$validation = service('validation');
$request    = service('request');

if ($validation->withRequest($request)->run()) {
    // If you use the input data, you should get it from the getValidated() method.
    // Otherwise you may create a vulnerability.
    $validData = $validation->getValidated();

    // ...
}

警告

使用此方法时,应使用 getValidated() 方法获取已验证的数据。因为当请求为 JSON 请求(Content-Type: application/json)时,此方法会从 $request->getJSON() 获取 JSON 数据;当请求为 PUT、PATCH、DELETE 请求且不是 HTML 表单 POST(Content-Type: multipart/form-data)时,会从 $request->getRawInput() 获取原始数据;或者从 $request->getVar() 获取数据。攻击者可以更改被验证的数据。

备注

getValidated() 方法自 v4.4.0 起可用。

使用验证

运行验证

run() 方法运行验证。方法签名为:

run(?array $data = null, ?string $group = null, ?string $dbGroup = null): bool

$data 是要验证的数据数组。可选的第二个参数 $group 是要应用的 预定义规则组。可选的第三个参数 $dbGroup 是要使用的数据库组。

验证成功时此方法返回 true。

if (! $validation->run($data)) {
    // handle validation errors
}
// or
if (! $validation->run($data, 'signup')) {
    // handle validation errors
}

运行多个验证

备注

run() 方法不会重置错误状态。如果之前的运行失败,run() 将始终返回 false,getErrors() 会返回之前所有错误,直到显式重置。

如果打算运行多个验证(例如针对不同数据集或连续应用不同规则),可能需要在每次运行前调用 $validation->reset() 以清除之前的错误。注意 reset() 会使之前设置的任何数据、规则或自定义错误失效,因此需要重复调用 setRules()setRuleGroup() 等:

foreach ($userAccounts as $user) {
    $validation->reset();
    $validation->setRules($userAccountRules);

    if (! $validation->run($user)) {
        // handle validation errors
    }
}

验证单个值

check() 方法根据规则验证单个值。第一个参数 $value 是要验证的值,第二个参数 $rule 是验证规则。可选的第三个参数 $errors 是自定义错误消息。

if ($validation->check($value, 'required')) {
    // $value is valid.
}

备注

v4.4.0 之前,此方法的第二个参数 $rule 的类型声明为 string。v4.4.0 及之后版本,类型声明被移除以允许数组。

备注

此方法在内部调用 setRule() 方法来设置规则。

获取已验证的数据

Added in version 4.4.0.

可通过 getValidated() 方法获取实际已验证的数据。此方法仅返回通过验证规则的元素数组。

$validation = service('validation');
$validation->setRules([
    'username' => 'required',
    'password' => 'required|min_length[10]',
]);

$data = [
    'username'   => 'john',
    'password'   => 'BPi-$Swu7U5lm$dX',
    'csrf_token' => '8b9218a55906f9dcc1dc263dce7f005a',
];

if ($validation->run($data)) {
    $validatedData = $validation->getValidated();
    // $validatedData = [
    //     'username' => 'john',
    //     'password' => 'BPi-$Swu7U5lm$dX',
    // ];
}
// In Controller.

if (! $this->validateData($data, [
    'username' => 'required',
    'password' => 'required|min_length[10]',
])) {
    // The validation failed.
    return view('login', [
        'errors' => $this->validator->getErrors(),
    ]);
}

// The validation was successful.

// Get the validated data.
$validData = $this->validator->getValidated();

将验证规则集保存到配置文件

Validation 类的一项实用功能是允许将整个应用的验证规则存储在配置文件中。可以将规则组织成"组",每次运行验证时可以指定不同的组。

如何保存规则

要保存验证规则,只需在 Config\Validation 类中创建以组名命名的新公共属性。该属性包含一个验证规则数组。如前所示,验证数组的原型如下:

<?php

namespace Config;

// ...

class Validation extends BaseConfig
{
    // ...

    public array $signup = [
        'username'     => 'required|max_length[30]',
        'password'     => 'required|max_length[255]',
        'pass_confirm' => 'required|max_length[255]|matches[password]',
        'email'        => 'required|max_length[254]|valid_email',
    ];

    // ...
}

如何指定规则组

调用 run() 方法时可以指定要使用的组:

$validation->run($data, 'signup');

如何保存错误消息

也可以将这些自定义错误消息存储在配置文件中,属性名与组名相同,并追加 _errors。使用该组时,这些消息会自动用于任何错误:

<?php

namespace Config;

// ...

class Validation extends BaseConfig
{
    // ...

    public array $signup = [
        'username'     => 'required|max_length[30]',
        'password'     => 'required|max_length[255]',
        'pass_confirm' => 'required|max_length[255]|matches[password]',
        'email'        => 'required|max_length[254]|valid_email',
    ];

    public array $signup_errors = [
        'username' => [
            'required' => 'You must choose a username.',
        ],
        'email' => [
            'valid_email' => 'Please check the Email field. It does not appear to be valid.',
        ],
    ];

    // ...
}

或者在数组中传递所有设置:

<?php

namespace Config;

// ...

class Validation extends BaseConfig
{
    // ...

    public array $signup = [
        'username' => [
            'rules'  => 'required|max_length[30]',
            'errors' => [
                'required' => 'You must choose a Username.',
            ],
        ],
        'email' => [
            'rules'  => 'required|max_length[254]|valid_email',
            'errors' => [
                'valid_email' => 'Please check the Email field. It does not appear to be valid.',
            ],
        ],
    ];

    // ...
}

详见 设置自定义错误消息 了解数组格式详情。

获取和设置规则组

获取规则组

此方法从验证配置中获取规则组:

$validation->getRuleGroup('signup');
设置规则组

此方法从验证配置中设置规则组到验证服务:

$validation->setRuleGroup('signup');

验证占位符

Validation 类提供了一种简单的方法,可以根据传入的数据替换规则中的部分内容。这听起来可能比较抽象,但在使用 is_unique 验证规则时尤其方便。

占位符是传入的 $data 中的字段名(或数组键),用花括号包裹。它将被匹配的传入字段的 替换。下面是一个示例:

$validation->setRules([
    'id'    => 'max_length[19]|is_natural_no_zero',
    'email' => 'required|max_length[254]|valid_email|is_unique[users.email,id,{id}]',
]);

警告

自 v4.3.5 起,出于安全原因,必须为占位符字段(上面示例代码中的 id 字段)设置验证规则。因为攻击者可以向应用发送任何数据。

在这组规则中,它声明 Email 地址在数据库中应该是唯一的,但 id 与占位符值匹配的行除外。假设表单 POST 数据如下:

$_POST = [
    'id'    => 4,
    'email' => 'foo@example.com',
];

{id} 占位符将被替换为数字 4,得到以下修订后的规则:

$validation->setRules([
    'id'    => 'max_length[19]|is_natural_no_zero',
    'email' => 'required|max_length[254]|valid_email|is_unique[users.email,id,4]',
]);

因此在验证电子邮件唯一性时,会忽略 id=4 的行。

备注

自 v4.3.5 起,如果占位符(id)的值未通过验证,占位符将不会被替换。

这也可用于在运行时创建更动态的规则,只要确保传入的任何动态键不会与表单数据冲突即可。

处理错误

Validation 类提供了多种方法来帮助设置错误消息、提供自定义错误消息,以及检索一个或多个错误以进行显示。

默认情况下,错误消息派生于 system/Language/en/Validation.php 中的语言字符串,每个规则都有一个对应条目。如需更改默认消息,请创建文件 app/Language/en/Validation.php (和/或在相应语言目录中创建),并放置要更改默认值的键值对。

设置自定义错误消息

setRule()setRules() 方法都可以接受自定义消息数组作为最后一个参数,这些消息将用作每个字段的特定错误。这样可以为用户提供更友好的体验,因为错误消息是针对每个实例定制的。如果未提供自定义错误消息,将使用默认值。

有两种方式提供自定义错误消息。

作为最后一个参数:

$validation->setRules(
    [
        'username' => 'required|max_length[30]|is_unique[users.username]',
        'password' => 'required|max_length[254]|min_length[10]',
    ],
    [   // Errors
        'username' => [
            'required' => 'All accounts must have usernames provided',
        ],
        'password' => [
            'min_length' => 'Your password is too short. You want to get hacked?',
        ],
    ],
);

或者标签样式:

$validation->setRules([
    'username' => [
        'label'  => 'Username',
        'rules'  => 'required|max_length[30]|is_unique[users.username]',
        'errors' => [
            'required' => 'All accounts must have {field} provided',
        ],
    ],
    'password' => [
        'label'  => 'Password',
        'rules'  => 'required|max_length[255]|min_length[10]',
        'errors' => [
            'min_length' => 'Your {field} is too short. You want to get hacked?',
        ],
    ],
]);

如果需要在消息中包含字段的"人性化"名称、某些规则允许的可选参数(如 max_length),或者被验证的值,可以分别在消息中添加 {field}{param}{value} 标签:

'min_length' => 'Supplied value ({value}) for {field} must have at least {param} characters.'

对于人性化名称为 Username、规则为 min_length[6]、值为 "Pizza" 的字段,将显示错误消息:"Supplied value (Pizza) for Username must have at least 6 characters."

警告

使用 getErrors()getError() 获取错误消息时,消息未进行 HTML 转义。如果使用用户输入数据(如 ({value}))构建错误消息,其中可能包含 HTML 标签。如果在显示前不转义消息,可能存在 XSS 攻击风险。

备注

使用标签样式错误消息时,如果将第二个参数传递给 setRules(),它会被第一个参数的值覆盖。

消息和验证标签的翻译

要使用语言文件中的翻译字符串,只需使用点语法。假设在 app/Languages/en/Rules.php 有一个翻译文件。可以直接使用该文件中定义的语言行,如下所示:

$validation->setRules([
    'username' => [
        'label'  => 'Rules.username',
        'rules'  => 'required|max_length[30]|is_unique[users.username]',
        'errors' => [
            'required' => 'Rules.username.required',
        ],
    ],
    'password' => [
        'label'  => 'Rules.password',
        'rules'  => 'required|max_length[255]|min_length[10]',
        'errors' => [
            'min_length' => 'Rules.password.min_length',
        ],
    ],
]);

获取所有错误

如需获取所有失败字段的错误消息,可使用 getErrors() 方法:

$errors = $validation->getErrors();
/*
 * Produces:
 * [
 *     'field1' => 'error message',
 *     'field2' => 'error message',
 * ]
 */

如果没有错误,将返回空数组。

使用通配符(*)时,错误会指向特定字段,将星号替换为相应的键:

// 对于数据
'contacts' => [
    'friends' => [
        [
            'name' => 'Fred Flinstone',
        ],
        [
            'name' => '',
        ],
    ]
]

// 规则
'contacts.friends.*.name' => 'required'

// 错误将是
'contacts.friends.1.name' => 'The contacts.friends.*.name field is required.'

获取单个错误

可使用 getError() 方法获取单个字段的错误。唯一参数是字段名称:

$error = $validation->getError('username');

如果没有错误,将返回空字符串。

备注

使用通配符时,所有匹配通配符的错误都会合并为单行,并以 EOL 字符分隔。

检查是否存在错误

可使用 hasError() 方法检查是否存在错误。唯一参数是字段名称:

if ($validation->hasError('username')) {
    echo $validation->getError('username');
}

使用通配符指定字段时,将检查所有匹配通配符字段的错误:

/*
 * For errors:
 * [
 *     'foo.0.bar'   => 'Error',
 *     'foo.baz.bar' => 'Error',
 * ]
 */

// returns true
$validation->hasError('foo.*.bar');

重定向与验证错误

PHP 在请求之间不共享任何内容。因此,当验证失败并重定向时,重定向后的请求中不会有验证错误,因为验证是在之前的请求中运行的。

在这种情况下,需要使用辅助函数 validation_errors()validation_list_errors()validation_show_error()。这些函数检查存储在 Session 中的验证错误。

要将验证错误存储到 Session 中,需要在使用 redirect() 时配合 withInput()

// In Controller.
if (! $this->validateData($data, $rules)) {
    return redirect()->back()->withInput();
}

自定义错误显示

调用 $validation->listErrors()$validation->showError() 时,它会在后台加载一个视图文件来确定错误如何显示。默认情况下,用 errors class 包裹 div。可以轻松创建新视图并在整个应用中使用。

创建视图

第一步是创建自定义视图。这些视图可以放在 view() 方法能找到的任何位置,即标准视图目录或任何命名空间视图文件夹。例如,可以在 app/Views/_errors_list.php 创建新视图:

<?php if (! empty($errors)): ?>
    <div class="alert alert-danger" role="alert">
        <ul>
        <?php foreach ($errors as $error): ?>
            <li><?= esc($error) ?></li>
        <?php endforeach ?>
        </ul>
    </div>
<?php endif ?>

视图中有一个名为 $errors 的数组可用,其中包含错误列表,键是有错误的字段名,值是错误消息,如下所示:

$errors = [
    'username' => 'The username field must be unique.',
    'email'    => 'You must provide a valid email address.',
];

实际上可以创建两种类型的视图。第一种包含所有错误的数组,就是刚才看到的那种。另一种类型更简单,只包含一个变量 $error,其中包含错误消息。这与 showError() 方法配合使用,必须指定字段:

<span class="help-block"><?= esc($error) ?></span>

配置

创建视图后,需要让 Validation 类知道它们。打开 app/Config/Validation.php。在其中会找到 $templates 属性,可以列出任意数量的自定义视图,并提供一个简短的别名供引用。如果添加上面的示例文件,看起来类似这样:

<?php

namespace Config;

// ...

class Validation extends BaseConfig
{
    // ...

    public array $templates = [
        'list'    => 'CodeIgniter\Validation\Views\list',
        'single'  => 'CodeIgniter\Validation\Views\single',
        'my_list' => '_errors_list',
    ];

    // ...
}

指定模板

可以通过在 listErrors() 中传递别名作为第一个参数来指定要使用的模板:

<?= $validation->listErrors('my_list') ?>

显示字段特定错误时,可以将别名作为第二个参数传递给 showError() 方法,紧跟在应显示错误的字段名之后:

<?= $validation->showError('username', 'my_single') ?>

创建自定义规则

使用规则类

规则存储在简单的命名空间类中。可以存储在任何位置,只要自动加载器能找到。这些文件称为规则集(RuleSet)。

添加规则集

要添加新规则集,编辑 app/Config/Validation.php 并将新文件添加到 $ruleSets 数组:

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Validation\CreditCardRules;
use CodeIgniter\Validation\FileRules;
use CodeIgniter\Validation\FormatRules;
use CodeIgniter\Validation\Rules;

class Validation extends BaseConfig
{
    // ...

    public array $ruleSets = [
        Rules::class,
        FormatRules::class,
        FileRules::class,
        CreditCardRules::class,
    ];

    // ...
}

可以作为完全限定类名的简单字符串添加,也可以如上所示使用 ::class 后缀。使用 ::class 后缀的主要好处是,它在更高级的 IDE 中提供额外的导航功能。

创建规则类

在文件中,每个方法都是一条规则,必须接受要验证的值作为第一个参数,并且必须返回布尔值 true 或 false,表示是否通过测试:

<?php

class MyRules
{
    public function even($value): bool
    {
        return (int) $value % 2 === 0;
    }
}

默认情况下,系统会在 system/Language/en/Validation.php 中查找用于错误的语言字符串。如需为自定义规则提供默认错误消息,可以将它们放在 app/Language/en/Validation.php (和/或在相应语言目录中替换 en)。此外,如果需要使用其他语言字符串文件代替默认的 Validation.php,可以通过在第二个参数(或者,如果规则需要使用参数,如下所述——第四个参数)中接受 &$error 变量来提供错误消息:

<?php

class MyRules
{
    public function even($value, ?string &$error = null): bool
    {
        if ((int) $value % 2 !== 0) {
            $error = lang('myerrors.evenError');

            return false;
        }

        return true;
    }
}

使用自定义规则

新的自定义规则现在可以像任何其他规则一样使用:

$validation->setRules([
    'foo' => 'required|max_length[19]|even',
]);

允许参数

如果方法需要使用参数,函数至少需要三个参数:

  1. 要验证的值($value

  2. 参数字符串($params

  3. 表单提交的所有数据的数组($data

  4. (可选)自定义错误字符串(&$error),如上所述。

警告

$data 中的字段值未经验证(或可能无效)。使用未经验证的输入数据是漏洞的来源。在使用 $data 中的数据之前,必须在自定义规则中执行必要的验证。

$data 数组在需要检查其他提交字段值的规则中特别有用,例如 required_with

<?php

class MyRules
{
    public function required_with($value, string $params, array $data): bool
    {
        $params = explode(',', $params);

        // If the field is present we can safely assume that
        // the field is here, no matter whether the corresponding
        // search field is present or not.
        $present = $this->required($value ?? '');

        if ($present) {
            return true;
        }

        // Still here? Then we fail this test if
        // any of the fields are present in $data
        // as $fields in the list
        $requiredFields = [];

        foreach ($params as $field) {
            if (array_key_exists($field, $data)) {
                $requiredFields[] = $field;
            }
        }

        // Remove any keys with empty values since, that means they
        // weren't truly there, as far as this is concerned.
        $requiredFields = array_filter($requiredFields, static fn ($item) => ! empty($data[$item]));

        return empty($requiredFields);
    }
}

使用闭包规则

Added in version 4.3.0.

若自定义规则仅需在应用中使用一次,可以使用闭包代替规则类。

需要使用数组作为验证规则:

$validation->setRules(
    [
        'foo' => [
            'required',
            static fn ($value) => (int) $value % 2 === 0,
        ],
    ],
    [
        // Errors
        'foo' => [
            // Specify the array key for the closure rule.
            1 => 'The value is not even.',
        ],
    ],
);

if (! $validation->run($data)) {
    // handle validation errors
}

必须为闭包规则设置错误消息。指定错误消息时,为闭包规则设置数组键。在上面的代码中,required 规则的键为 0,闭包的键为 1

或者可以使用以下参数:

$validation->setRules([
    'foo' => [
        'required',
        static function ($value, $data, &$error, $field) {
            if ((int) $value % 2 === 0) {
                return true;
            }

            $error = 'The value is not even.';

            return false;
        },
    ],
]);

使用可调用规则

Added in version 4.5.0.

如需使用数组回调作为规则,可以使用它代替闭包规则。

需要使用数组作为验证规则:

namespace App\Controllers;

class Form extends BaseController
{
    // Define a custom validation rule.
    public function _ruleEven($value): bool
    {
        return (int) $value % 2 === 0;
    }

    public function process()
    {
        // ...

        $validation = service('validation');
        $validation->setRules(
            [
                'foo' => [
                    'required',
                    // Specify the method in this controller as a rule.
                    [$this, '_ruleEven'],
                ],
            ],
            [
                // Errors
                'foo' => [
                    // Specify the array key for the callable rule.
                    1 => 'The value is not even.',
                ],
            ],
        );

        if (! $validation->run($data)) {
            // handle validation errors
        }

        // ...
    }
}

必须为可调用规则设置错误消息。指定错误消息时,为可调用规则设置数组键。在上面的代码中,required 规则的键为 0,可调用的键为 1

或者可以使用以下参数:

namespace App\Controllers;

use Config\Services;

class Form extends BaseController
{
    // Define a custom validation rule.
    public function _ruleEven($value, $data, &$error, $field): bool
    {
        if ((int) $value % 2 === 0) {
            return true;
        }

        $error = 'The value is not even.';

        return false;
    }

    // ...
}

可用规则

备注

规则是字符串;参数之间 不能有空格,尤其是 is_unique 规则。ignore_value 前后不能有空格。

// is_unique[table.field,ignore_field,ignore_value]

$validation->setRules([
    'name' => "max_length[36]|is_unique[supplier.name,uuid, {$uuid}]", // is not ok
    'name' => "max_length[36]|is_unique[supplier.name,uuid,{$uuid} ]", // is not ok
    'name' => "max_length[36]|is_unique[supplier.name,uuid,{$uuid}]",  // is ok
    'name' => 'max_length[36]|is_unique[supplier.name,uuid,{uuid}]',   // is ok - see "Validation Placeholders"
]);
// Warning: If `$uuid` is a user input, be sure to validate the format of the value before using it.
// Otherwise, it is vulnerable.

通用规则

以下是所有可用的原生规则列表:

规则

参数

描述

示例

alpha

若字段为空或包含 ASCII 字母以外的内容, 则校验失败。

alpha_dash

若字段为空或包含 ASCII 字母数字、 下划线或减号以外的内容,则校验失败。

alpha_numeric

若字段为空或包含 ASCII 字母数字以外的内容, 则校验失败。

alpha_numeric_punct

若字段为空或包含字母数字、 空格及以下指定标点符号以外的内容, 则校验失败: ~ (波浪号)、! (感叹号)、 # (井号)、$ (美元符号)、 % (百分号)、& (与符号)、 * (星号)、- (减号)、 _ (下划线)、+ (加号)、 = (等号)、| (竖线)、 : (冒号)、. (点号)。

alpha_numeric_space

若字段为空或包含 ASCII 字母数字或空格以外的内容,则校验失败。

alpha_space

若字段为空或包含 ASCII 字母或空格以外的内容,则校验失败。

decimal

若字段不是十进制数字,则校验失败。 允许带有正负号(+-)。

differs

若字段值与参数指定的字段值相同,则校验失败。

differs[field_name]

exact_length

若字段长度不等于参数指定的值,则校验失败。 支持以逗号分隔的多个值。

exact_length[5]exact_length[5,8,12]

field_exists

若字段不存在,则校验失败。 (此规则自 v4.5.0 起新增)

greater_than

若字段值小于等于参数值,或不是数字, 则校验失败。

greater_than[8]

greater_than_equal_to

若字段值小于参数值,或不是数字,则校验失败。

greater_than_equal_to[5]

hex

若字段包含十六进制字符以外的内容, 则校验失败。

if_exist

若包含此规则,则仅在待验证数据中 存在该字段键名时进行校验。

in_list

若字段值不在预定义列表中,则校验失败。

in_list[red,blue,green]

integer

若字段包含整数以外的内容,则校验失败。

is_natural

若字段包含自然数 (0, 1, 2, 3 等) 以外的内容,则校验失败。

is_natural_no_zero

若字段包含非 0 自然数 (1, 2, 3 等)以外的内容, 则校验失败。

is_not_unique

检查数据库中是否存在给定值。 支持通过字段/值过滤掉部分记录 (目前仅支持一个过滤器)。 (自 v4.6.0 起,可选择性地将 dbGroup 作为参数传递)

is_not_unique[table.field,where_field,where_value]is_not_unique[dbGroup.table.field,where_field,where_value]

is_unique

检查字段值是否已存在于数据库中。 可选择性地设置要忽略的列和值, 在更新记录时用于排除自身。 (自 v4.6.0 起,可选择性地将 dbGroup 作为参数传递)

is_unique[table.field,ignore_field,ignore_value]is_unique[dbGroup.table.field,ignore_field,ignore_value]

less_than

若字段值大于等于参数值,或不是数字, 则校验失败。

less_than[8]

less_than_equal_to

若字段值大于参数值,或不是数字, 则校验失败。

less_than_equal_to[8]

matches

字段值必须与参数指定的字段值一致。

matches[field]

max_length

若字段长度超过参数值,则校验失败。

max_length[8]

min_length

若字段长度短于参数值,则校验失败。

min_length[3]

not_in_list

若字段值在预定义列表中,则校验失败。

not_in_list[red,blue,green]

numeric

若字段包含数字字符以外的内容,则校验失败。

permit_empty

允许字段接收空数组、空字符串、null 或 false。验证通过后,将跳过除 required_withrequired_without 以外的所有其他验证规则。

regex_match

若字段不匹配正则表达式,则校验失败。 注意: 自 v4.7.0 起, 若在此规则中使用占位符, 必须使用双大括号 {{...}} 而非单大括号 {...}

regex_match[/regex/]

required

若字段是空数组、空字符串、null 或 false, 则校验失败。

required_with

当数据中指定的任一其他字段不为 empty() 时,此字段必填。

required_with[field1,field2]

required_without

当数据中指定的任一其他字段为 empty() 时,此字段必填。

required_without[field1,field2]

string

alpha* 系列规则的通用替代方案, 用于确认元素是否为字符串。

timezone

若字段不符合 timezone_identifiers_list() 定义的时区,则校验失败。

valid_base64

若字段包含有效的 Base64 字符以外的内容, 则校验失败。

valid_cc_number

验证信用卡号是否符合指定发卡机构的格式。 支持的机构包括: American Express (amex), China Unionpay (unionpay), Diners Club CarteBlance (carteblanche), Diners Club (dinersclub), Discover Card (discover), Interpayment (interpayment), JCB (jcb), Maestro (maestro), Dankort (dankort), NSPK MIR (mir), Troy (troy), MasterCard (mastercard), Visa (visa), UATP (uatp), Verve (verve), CIBC Convenience Card (cibc), Royal Bank of Canada Client Card (rbc), TD Canada Trust Access Card (tdtrust), Scotiabank Scotia Card (scotia), BMO ABM Card (bmoabm), HSBC Canada Card (hsbc)

valid_cc_number[amex]

valid_date

若字段不包含有效日期,则校验失败。 若未指定可选的日期格式参数, 则任何 strtotime() 接受的字符串 均视为有效。因此通常需要指定参数。

valid_date[d/m/Y]

valid_email

若字段不包含有效的 Email 地址,则校验失败。

valid_emails

若逗号分隔列表中存在任何无效的 Email 地址, 则校验失败。

valid_ip

若提供的 IP 无效,则校验失败。可指定可选参数 ipv4ipv6 以限定 IP 格式。

valid_ip[ipv6]

valid_json

若字段不包含有效的 JSON 字符串,则校验失败。

valid_url

若字段不包含(宽泛意义上的)URL, 则校验失败。包含可能为主机名的简单字符串 (如 "codeigniter")。 通常应使用 valid_url_strict

valid_url_strict

若字段不包含有效的 URL,则校验失败。 可指定允许的协议列表。若未指定, 则默认为 http,https。此规则 使用 PHP 的 FILTER_VALIDATE_URL

valid_url_strict[https]

备注

还可以使用任何返回布尔值且至少允许一个参数(要验证的字段数据)的原生 PHP 函数。

重要

Validation 类 永远不会更改 要验证的数据。

文件上传规则

验证上传的文件时,必须使用专门为文件验证创建的规则。

重要

只能使用下表中列出的规则来验证文件。因此,如果在文件验证规则数组或字符串中添加任何通用规则(如 permit_empty),文件验证将无法正常工作。

由于 HTML 文件上传的内容不存在于常规数据中,而是存储在 $_FILES 全局变量中,因此需要两次指定输入字段名:一次按常规规则指定字段名,另一次则作为所有文件上传相关规则的第一个参数:

// 在 HTML 中
<input type="file" name="avatar">

// 在控制器中
$this->validateData([], [
    'avatar' => 'uploaded[avatar]|max_size[avatar,1024]',
]);

另见 文件上传教程

规则

参数

描述

示例

uploaded

若参数名与上传文件名不匹配,则校验失败。 如需设为可选上传(非必填), 则无需定义此规则。

uploaded[field_name]

max_size

若上传文件超过第二个参数指定的大小 (单位为 KB),或超过 php.ini 配置文件中 upload_max_filesize 指令允许的最大值,则校验失败。

max_size[field_name,2048]

max_dims

若上传图像的最大宽度或高度超过指定值, 则校验失败。参数依次为:字段名、宽度、高度。 若文件无法识别为图像,同样校验失败。

max_dims[field_name,300,150]

min_dims

若上传图像的最小宽度或高度未达到指定值, 则校验失败。参数依次为:字段名、宽度、高度。 若文件无法识别为图像,同样校验失败。(此规则 自 v4.6.0 起新增)

min_dims[field_name,300,150]

mime_in

若文件的 MIME 类型不在参数列出的范围内, 则校验失败。

mime_in[field_name,image/png,image/jpeg]

ext_in

若文件的扩展名不在参数列出的范围内, 则校验失败。

ext_in[field_name,png,jpg,gif]

is_image

若基于 MIME 类型无法将文件识别为图像, 则校验失败。

is_image[field_name]

文件上传规则适用于单文件和多文件上传。