dimtrovich/validation

PHP 独立验证库。扩展 rakit/validation

1.5.1 2024-02-22 18:48 UTC

This package is auto-updated.

Last update: 2024-09-12 12:38:47 UTC


README

Kahlan Coding Standards PHPStan Software License contributions welcome PHPStan level Build Status Code Intelligence Status Quality Score Code Coverage Total Downloads Latest Version GitHub stars


PHP 独立数据验证库。灵感来源于 Laravel 的 Illuminate\Validation,并扩展了 Rakit Validation

特性

  • Laravel 验证类似的 API。
  • 数组验证。
  • 支持多个文件的 $_FILES 验证。
  • 自定义属性别名。
  • 自定义验证消息。
  • 自定义规则。

要求

  • PHP 8.0 或更高版本
  • Composer 用于安装

快速入门

安装

composer require "dimtrovich/validation"

使用

使用此库验证数据有两种方式。使用 make 创建验证对象,然后使用 validate 验证。或者直接使用 validate。示例

使用 make

<?php

require('vendor/autoload.php');

use Dimtrovich\Validation\Validator;

// make it
$validation = Validator::make($_POST + $_FILES, [
    'name'                  => 'required',
    'email'                 => 'required|email',
    'password'              => 'required|min:6|confirmed',
    'avatar'                => 'required|uploaded_file:0,500K,png,jpeg',
    'skills'                => 'array',
    'skills.*.id'           => 'required|numeric',
    'skills.*.percentage'   => 'required|numeric'
]);

if ($validation->fails()) {
    // handling errors
    $errors = $validation->errors();
    echo "<pre>";
    print_r($errors->firstOfAll());
    echo "</pre>";
    exit;
} else {
    // validation passes
    echo "Success!";
}

属性别名

默认情况下,我们将您的属性转换为更易读的文本。例如,confirm_password 将显示为 Confirm password。但您可以使用 aliasaliases 方法将其设置为任何您想要的内容。

示例

$validation = Validator::make([
	'province_id' => $_POST['province_id'],
	'district_id' => $_POST['district_id']
], [
	'province_id' => 'required|numeric',
	'district_id' => 'required|numeric'
]);

// now you can set aliases using this way:
$validation->alias('province_id', 'Province');
$validation->alias('district_id', 'District');

// or this way:
$validation->alias([
	'province_id' => 'Province',
	'district_id' => 'District'
]);

// then validate it
$validation->validate();

如果 province_id 的值为空,错误消息将是 'Province is required'。

自定义验证消息

在注册/设置自定义消息之前,这里有一些您可以在自定义消息中使用变量

  • :attribute: 将替换为属性别名。
  • :value: 将替换为属性的字符串化值。对于数组和对象将替换为 JSON。

还有根据其规则的一些消息变量。

以下是一些注册/设置您的自定义消息的方式

Validator 的自定义消息

使用这种方式,每次您使用 makevalidate 进行验证时,都会设置您的自定义消息。这对于本地化非常有用。

为此,您可以像这样将自定义消息作为构造函数的第一个参数设置

$validator = new Validator([
	'required' => ':attribute harus diisi',
	'email' => ':email tidak valid',
	// etc
]);

// then validation belows will use those custom messages
$validation_a = $validator->validate($dataset_a, $rules_for_a);
$validation_b = $validator->validate($dataset_b, $rules_for_b);

或使用 messages 方法像这样

$validation = Validator::make(...);
$validator->messages([
	'required' => ':attribute harus diisi',
	'email' => ':email tidak valid',
	// etc
]);

// now validation belows will use those custom messages
$validation_a = $validator->validate($dataset_a, $rules_for_dataset_a);
$validation_b = $validator->validate($dataset_b, $rules_for_dataset_b);

验证的自定义消息

有时您可能想为特定的验证设置自定义消息。为此,您可以将自定义消息设置为 Validator::make 的第三个参数,如下所示

$validator = Validator::make($dataset_a, $rules_for_dataset_a, [
	'required' => ':attribute harus diisi',
	'email' => ':email tidak valid',
	// etc
]);

或者您可以使用 $validation->messages 如此

$validation_a = Validator::make($dataset_a, $rules_for_dataset_a);
$validation_a->messages([
	'required' => ':attribute harus diisi',
	'email' => ':email tidak valid',
	// etc
]);

...

$validation_a->validate();

特定属性规则的自定义消息

有时您可能想为特定规则的属性设置自定义消息。为此,您可以使用冒号 : 作为消息分隔符或使用链式方法。

示例

$validation_a = Validator::make($dataset_a, [
	'age' => 'required|min:18'
]);

$validation_a->messages([
	'age:min' => '18+ only',
]);

$validation_a->validate();

或者使用链式方法

$validation_a = Validator::make($dataset_a, [
	'photo' => [
		'required',
		Validator::rule('uploaded_file')->fileTypes('jpeg|png')->message('Photo must be jpeg/png image')
	]
]);

$validation_a->validate();

翻译

翻译与自定义消息不同。当您为规则 innot_inmimesuploaded_file 使用自定义消息时可能需要翻译。

例如,如果您使用规则 in:1,2,3,我们将设置无效消息 "The Attribute only allows '1', '2', or '3'",其中部分 "'1', '2', or '3'" 来自 ":allowed_values" 标签。因此,如果您有自定义印度尼西亚语消息 ":attribute hanya memperbolehkan :allowed_values",我们将设置无效消息 "Attribute hanya memperbolehkan '1', '2', or '3'",其中 "or" 一词不是印度尼西亚语的一部分。

因此,为了解决这个问题,我们可以使用翻译如下

// Set translation for words 'and' and 'or'.
$validation->translations([
    'and' => 'dan',
    'or' => 'atau'
]);

// Set custom message for 'in' rule
$validation->message('in', ":attribute hanya memperbolehkan :allowed_values");

// Validate
$validation = Validator::validate($inputs, [
    'nomor' => 'in:1,2,3'
]);

$message = $validation->errors()->first('nomor'); // "Nomor hanya memperbolehkan '1', '2', atau '3'"

实际上,我们内置的规则只使用单词 "and" 和 "or",您可能需要翻译。

处理错误消息

错误消息收集在 Rakit\Validation\ErrorBag 对象中,您可以使用 errors() 方法获取它。

$validation = Validator::validate($inputs, $rules);

$errors = $validation->errors(); // << ErrorBag

现在您可以使用以下方法检索错误消息

all(string $format = ':message')

获取所有消息作为扁平数组。

示例

$messages = $errors->all();
// [
//     'Email is not valid email',
//     'Password minimum 6 character',
//     'Password must contains capital letters'
// ]

$messages = $errors->all('<li>:message</li>');
// [
//     '<li>Email is not valid email</li>',
//     '<li>Password minimum 6 character</li>',
//     '<li>Password must contains capital letters</li>'
// ]

首先(string $format = ':message', bool $dotNotation = false)

从所有现有键中获取仅第一条消息。

示例

$messages = $errors->firstOfAll();
// [
//     'email' => Email is not valid email',
//     'password' => 'Password minimum 6 character',
// ]

$messages = $errors->firstOfAll('<li>:message</li>');
// [
//     'email' => '<li>Email is not valid email</li>',
//     'password' => '<li>Password minimum 6 character</li>',
// ]

参数 $dotNotation 用于数组验证。如果它是 false,则将返回原始数组结构;如果它是 true,则将返回具有点符号键的扁平化数组。

例如

$messages = $errors->firstOfAll(':message', false);
// [
//     'contacts' => [
//          1 => [
//              'email' => 'Email is not valid email',
//              'phone' => 'Phone is not valid phone number'
//          ],
//     ],
// ]

$messages = $errors->firstOfAll(':message', true);
// [
//     'contacts.1.email' => 'Email is not valid email',
//     'contacts.1.phone' => 'Email is not valid phone number',
// ]

first(string $key)

从给定键获取第一条消息。如果键有任何错误消息,则返回 string;如果没有错误,则返回 null

例如

if ($emailError = $errors->first('email')) {
    echo $emailError;
}

toArray()

按其键分组获取所有消息。

例如

$messages = $errors->toArray();
// [
//     'email' => [
//         'Email is not valid email'
//     ],
//     'password' => [
//         'Password minimum 6 character',
//         'Password must contains capital letters'
//     ]
// ]

count()

获取消息计数。

has(string $key)

检查给定键是否有错误。如果键有错误,则返回 bool,否则返回。

获取已验证、有效和无效数据

例如,你有如下验证

$validation = $validator->validate([
    'title' => 'Lorem Ipsum',
    'body' => 'Lorem ipsum dolor sit amet ...',
    'published' => null,
    'something' => '-invalid-'
], [
    'title' => 'required',
    'body' => 'required',
    'published' => 'default:1|required|in:0,1',
    'something' => 'required|numeric'
]);

您可以使用以下示例中的方法获取已验证数据、有效数据或无效数据

$validatedData = $validation->getValidatedData();
// [
//     'title' => 'Lorem Ipsum',
//     'body' => 'Lorem ipsum dolor sit amet ...',
//     'published' => '1' // notice this
//     'something' => '-invalid-'
// ]

$validData = $validation->getValidData();
// [
//     'title' => 'Lorem Ipsum',
//     'body' => 'Lorem ipsum dolor sit amet ...',
//     'published' => '1'
// ]

$invalidData = $validation->getInvalidData();
// [
//     'something' => '-invalid-'
// ]

可用规则

点击查看详细信息。

required

此验证字段下必须存在且不为 'empty'。

以下是一些示例

对于上传的文件,$_FILES['key']['error'] 必须不是 UPLOAD_ERR_NO_FILE

required_if:another_field,value_1,value_2,...

此规则下的字段必须存在且不为空,如果 anotherfield 字段等于任何值。

例如,required_if:something,1,yes,onsomething 的值为 1'1''yes''on' 之一时,将强制要求。

required_unless:another_field,value_1,value_2,...

验证字段必须存在且不为空,除非 anotherfield 字段等于任何值。

required_with:field_1,field_2,...

验证字段必须存在且不为空,只有当其他指定的字段之一存在时。

required_without:field_1,field_2,...

验证字段必须存在且不为空,只有当其他指定的字段之一不存在时。

required_with_all:field_1,field_2,...

验证字段必须存在且不为空,只有当所有其他指定的字段都存在时。

required_without_all:field_1,field_2,...

验证字段必须存在且不为空,只有当所有其他指定的字段都不存在时。

uploaded_file:min_size,max_size,extension_a,extension_b,...

此规则将验证来自 $_FILES 的数据。此规则下的字段必须遵循以下规则才能有效:

  • $_FILES['key']['error'] 必须是 UPLOAD_ERR_OKUPLOAD_ERR_NO_FILE。对于 UPLOAD_ERR_NO_FILE,您可以使用 required 规则进行验证。
  • 如果指定了最小大小,上传的文件大小 不得 低于最小大小。
  • 如果指定了最大大小,上传的文件大小 不得 超过最大大小。
  • 如果指定了文件类型,MIME 类型必须是给定类型之一。

以下是一些示例定义和说明

  • uploaded_file:上传文件是可选的。当它不为空时,它必须是 ERR_UPLOAD_OK
  • required|uploaded_file:上传文件是必需的,并且必须是 ERR_UPLOAD_OK
  • uploaded_file:0,1M:上传文件大小必须在 0 - 1 MB 之间,但上传文件是可选的。
  • required|uploaded_file:0,1M,png,jpeg:上传文件大小必须在 0 - 1MB 之间,并且 MIME 类型必须是 image/jpegimage/png

可选地,如果您想在不同的大小和类型验证之间有单独的错误消息,您可以使用 mimes 规则来验证文件类型,并使用 minmaxbetween 来验证其大小。

对于多个文件上传,PHP会提供不理想的数组$_FILES结构(详细信息请参阅此处)。因此,我们创建了uploaded_file规则来自动将$_FILES值解析为有组织的数组结构。这意味着,您不仅可以使用minmaxbetweenmimes规则来验证多个文件上传。您只需将uploaded_file规则应用于解析其值,并确保该值是正确的已上传文件值。

例如,如果您有如下输入文件

<input type="file" name="photos[]"/>
<input type="file" name="photos[]"/>
<input type="file" name="photos[]"/>

您可以简单地这样验证

$validation = $validator->validate($_FILES, [
    'photos.*' => 'uploaded_file:0,2M,jpeg,png'
]);

// or

$validation = $validator->validate($_FILES, [
    'photos.*' => 'uploaded_file|max:2M|mimes:jpeg,png'
]);

或者如果您有如下输入文件

<input type="file" name="images[profile]"/>
<input type="file" name="images[cover]"/>

您可以这样验证

$validation = $validator->validate($_FILES, [
    'images.*' => 'uploaded_file|max:2M|mimes:jpeg,png',
]);

// or

$validation = $validator->validate($_FILES, [
    'images.profile' => 'uploaded_file|max:2M|mimes:jpeg,png',
    'images.cover' => 'uploaded_file|max:5M|mimes:jpeg,png',
]);

现在,当您使用getValidData()getInvalidData()时,您将得到像单文件上传一样的良好数组结构。

mimes:extension_a,extension_b,...

在验证过程中,$_FILES项必须具有与列表中某个扩展名对应的MIME类型。

default/defaults

这是一个特殊的规则,它不会进行验证。它只是在属性为空或不存在时为您的属性设置默认值。

例如,如果您有如下验证

$validation = $validator->validate([
    'enabled' => null
], [
    'enabled' => 'default:1|required|in:0,1'
    'published' => 'default:0|required|in:0,1'
]);

$validation->passes(); // true

// Get the valid/default data
$valid_data = $validation->getValidData();

$enabled = $valid_data['enabled'];
$published = $valid_data['published'];

验证通过,因为我们为enabledpublished设置了默认值10,它们是有效的。然后我们可以获取有效/默认的数据。

email

此验证下的字段必须是有效的电子邮件地址。

uppercase

此验证下的字段必须是有效的大写。

lowercase

此验证下的字段必须是有效的小写。

json

此验证下的字段必须是有效的JSON字符串。

alpha

此规则下的字段必须完全由字母字符组成。

numeric

此规则下的字段必须是数字。

alpha_num

此规则下的字段必须完全由字母数字字符组成。

alpha_dash

此规则下的字段可以有字母数字字符,以及破折号和下划线。

alpha_spaces

此规则下的字段可以有字母字符,以及空格。

in:value_1,value_2,...

此规则下的字段必须在给定的值列表中。

此规则使用in_array来检查值。默认情况下,in_array禁用严格检查。因此,它不检查数据类型。如果您想启用严格检查,可以像以下示例中调用验证器一样

$validation = $validator->validate($data, [
    'enabled' => [
        'required',
        $validator('in', [true, 1])->strict()
    ]
]);

然后'enabled'值应该是布尔值true或整数值1

not_in:value_1,value_2,...

此规则下的字段不得包含在给定的值列表中。

此规则也使用in_array。您可以通过调用验证器并调用strict()来启用严格检查,如上述in规则中的示例所示。

min:number

此规则下的字段的大小必须大于或等于给定的数字。

对于字符串值,大小对应于字符数。对于整数或浮点值,大小对应于其数值。对于数组,大小对应于数组中的元素数量。如果您的值是数值字符串,您可以添加numeric规则来按数值值而不是字符数来处理其大小。

您还可以使用此规则来验证上传的文件,以验证上传文件的最小大小。例如

$validation = $validator->validate([
    'photo' => $_FILES['photo']
], [
    'photo' => 'required|min:1M'
]);
max:number

此规则下的字段的大小必须小于或等于给定的数字。值大小计算方式与min规则相同。

您还可以使用此规则来验证上传的文件,以验证上传文件的最大大小。例如

$validation = $validator->validate([
    'photo' => $_FILES['photo']
], [
    'photo' => 'required|max:2M'
]);
between:min,max

根据此规则的字段大小必须在min和max参数之间。值大小以与minmax规则相同的方式进行计算。

您还可以使用此规则验证上传的文件,以验证上传文件的大小。例如

$validation = $validator->validate([
    'photo' => $_FILES['photo']
], [
    'photo' => 'required|between:1M,2M'
]);
digits:value

正在验证的字段必须是数值型,并且必须具有value的确切长度。

digits_between:min,max

正在验证的字段长度必须在给定的minmax之间。

url

根据此规则的字段必须是有效的URL格式。默认情况下,它检查常见的URL方案格式,如any_scheme://...。但您可以根据需要指定URL方案。

例如

$validation = $validator->validate($inputs, [
    'random_url' => 'url',          // value can be `any_scheme://...`
    'https_url' => 'url:http',      // value must be started with `https://`
    'http_url' => 'url:http,https', // value must be started with `http://` or `https://`
    'ftp_url' => 'url:ftp',         // value must be started with `ftp://`
    'custom_url' => 'url:custom',   // value must be started with `custom://`
    'mailto_url' => 'url:mailto',   // value must conatin valid mailto URL scheme like `mailto:a@mail.com,b@mail.com`
    'jdbc_url' => 'url:jdbc',       // value must contain valid jdbc URL scheme like `jdbc:mysql://localhost/dbname`
]);

对于常见的URL方案和mailto,我们结合FILTER_VALIDATE_URL来验证URL格式,以及使用preg_match来验证它的方案。除了JDBC URL之外,目前它仅检查有效的JDBC方案。

integer根据t规则的字段必须是整数。
boolean

根据此规则的字段必须是布尔型。接受的输入是truefalse10"1""0"

ip

根据此规则的字段必须是有效的IPv4或IPv6。

ipv4

根据此规则的字段必须是有效的IPv4。

ipv6

根据此规则的字段必须是有效的IPv6。

extension:extension_a,extension_b,...

根据此规则的字段必须以列表中指定的扩展名之一结尾。

这对于验证给定路径或URL的文件类型非常有用。应使用mimes规则来验证上传。

array

根据此规则的字段必须是数组。

same:another_field

根据此规则的字段值必须与another_field的值相同。

regex:/your-regex/

根据此规则的字段必须与给定的正则表达式匹配。

date:format

根据此规则的字段必须是有效的日期格式。参数format是可选的,默认格式是Y-m-d

accepted

根据此规则的字段必须是'on''yes''1''true'true之一。

present

根据此规则的字段必须存在,无论其值如何。

different:another_field

same的对立面。根据此规则的字段值必须与another_field的值不同。

after:tomorrow

可以将任何可以被strtotime解析的参数传递给此规则。有效的示例包括

  • after:next week
  • after:2016-12-31
  • after:2016
  • after:2016-12-31 09:56:02
before:yesterday

这同样可以像after规则一样工作。传递任何可以被strtotime解析的内容。

callback

您可以使用此规则来定义自己的验证规则。此规则不能使用字符串管道进行注册。要使用此规则,您应该在规则数组中放置闭包。

例如

$validation = $validator->validate($_POST, [
    'even_number' => [
        'required',
        function ($value) {
            // false = invalid
            return (is_numeric($value) AND $value % 2 === 0);
        }
    ]
]);

您可以通过返回一个字符串来设置无效消息。例如,上面的示例将是

$validation = $validator->validate($_POST, [
    'even_number' => [
        'required',
        function ($value) {
            if (!is_numeric($value)) {
                return ":attribute must be numeric.";
            }
            if ($value % 2 !== 0) {
                return ":attribute is not even number.";
            }
            // you can return true or don't return anything if value is valid
        }
    ]
]);

注意:dimtrovich\Validation\Rules\Callback实例已绑定到您的闭包中。因此,您可以使用$this访问规则属性和方法。

nullable

根据此规则的字段可能是空的。

注册/覆盖规则

使用自定义验证规则的另一种方法是创建一个扩展dimtrovich\Validation\Rule的类。然后使用setValidatoraddValidator进行注册。

例如,您想要创建一个检查字段在数据库中可用性的unique验证器。

首先,让我们创建UniqueRule

<?php

use Dimtrovich\Validation\Rules\AbstractRule;

class UniqueRule extends AbstractRule
{
    protected $message = ":attribute :value has been used";

    protected $fillableParams = ['table', 'column', 'except'];

    protected $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function check($value): bool
    {
        // make sure required parameters exists
        $this->requireParameters(['table', 'column']);

        // getting parameters
        $column = $this->parameter('column');
        $table = $this->parameter('table');
        $except = $this->parameter('except');

        if ($except AND $except == $value) {
            return true;
        }

        // do query
        $stmt = $this->pdo->prepare("select count(*) as count from `{$table}` where `{$column}` = :value");
        $stmt->bindParam(':value', $value);
        $stmt->execute();
        $data = $stmt->fetch(PDO::FETCH_ASSOC);

        // true for valid, false for invalid
        return intval($data['count']) === 0;
    }
}

然后您需要将UniqueRule实例注册到验证器中,如下所示

use Dimtrovich\Validation\Validator;

$validator = new Validator;

$validator->addValidator('unique', new UniqueRule($pdo));

现在您可以使用它像这样

$validation = $validator->validate($_POST, [
    'email' => 'email|unique:users,email,exception@mail.com'
]);

在上面的 UniqueRule 中,属性 $message 用于默认无效信息。而属性 $fillable_params 用于 fillParameters 方法(在 dimtrovich\Validation\Rule 类中定义)。默认情况下,fillParameters 将填充 $fillable_params 中列出的参数。例如,上面的示例中的 unique:users,email,exception@mail.com 将设置

$params['table'] = 'users';
$params['column'] = 'email';
$params['except'] = 'exception@mail.com';

如果您希望自定义规则接受如 innot_inuploaded_file 规则的参数列表,您只需在自定义规则类中覆盖 fillParameters(array $params) 方法。

注意,我们上面创建的 unique 规则也可以这样使用

$validation = $validator->validate($_POST, [
    'email' => [
    	'required', 'email',
    	$validator('unique', 'users', 'email')->message('Custom message')
    ]
]);

因此,您可以通过添加一些返回自身实例的方法来改进上面的 UniqueRule

<?php

use dimtrovich\Validation\Rule;

class UniqueRule extends Rule
{
    ...

    public function table($table)
    {
        $this->params['table'] = $table;
        return $this;
    }

    public function column($column)
    {
        $this->params['column'] = $column;
        return $this;
    }

    public function except($value)
    {
        $this->params['except'] = $value;
        return $this;
    }

    ...
}

然后您可以用这种方式使用它

$validation = $validator->validate($_POST, [
    'email' => [
    	'required', 'email',
    	$validator('unique')->table('users')->column('email')->except('exception@mail.com')->message('Custom message')
    ]
]);

隐式规则

隐式规则是一种规则,如果它无效,则忽略后续规则。例如,如果属性未通过 required* 规则,则大多数情况下,后续规则也将无效。因此,为了防止我们的后续规则消息被收集,我们将 required* 规则设置为隐式。

要使您的自定义规则成为隐式的,您可以设置 $implicit 属性值为 true。例如

<?php

use dimtrovich\Validation\Rule;

class YourCustomRule extends Rule
{

    protected $implicit = true;

}

修改值

在某些情况下,您可能希望自定义规则能够修改其属性值,例如我们的 default/defaults 规则。因此,在当前和后续规则检查中,将使用您修改的值。

为此,您应该实现 dimtrovich\Validation\Rules\Interfaces\ModifyValue 并在自定义规则类中创建 modifyValue($value) 方法。

例如

<?php

use dimtrovich\Validation\Rule;
use dimtrovich\Validation\Rules\Interfaces\ModifyValue;

class YourCustomRule extends Rule implements ModifyValue
{
    ...

    public function modifyValue($value)
    {
        // Do something with $value

        return $value;
    }

    ...
}

在验证之前

您可能希望在验证运行之前做一些准备工作。例如,我们的 uploaded_file 规则将把来自 $_FILES(不理想的)数组的属性值解析为有组织的数组结构,这样我们就可以像验证其他数据一样验证多个文件上传。

为此,您应该实现 dimtrovich\Validation\Rules\Interfaces\BeforeValidate 并在自定义规则类中创建 beforeValidate() 方法。

例如

<?php

use dimtrovich\Validation\Rule;
use dimtrovich\Validation\Rules\Interfaces\BeforeValidate;

class YourCustomRule extends Rule implements BeforeValidate
{
    ...

    public function beforeValidate()
    {
        $attribute = $this->getAttribute(); // dimtrovich\Validation\Attribute instance
        $validation = $this->validation; // dimtrovich\Validation\Validation instance

        // Do something with $attribute and $validation
        // For example change attribute value
        $validation->setValue($attribute->getKey(), "your custom value");
    }

    ...
}