grithin/php-conform

PHP 输入合规处理器

v3 2021-06-27 03:18 UTC

This package is auto-updated.

Last update: 2024-09-09 02:01:25 UTC


README

使用标准输入逻辑进行排序过滤和验证。

关于

大约在2009年,我想构建一个东西,用于解决在以下情况中需要编写正常输入逻辑的问题:

  • 如果x字段失败,则不检查y字段
  • 如果x字段验证失败了一步,则不再进一步验证
  • 如果x字段验证失败,则停止验证所有输入并返回错误

这种输入字段依赖逻辑有很多组合,我认为可以简化表达式。因此,我创建了这个类,它使用逻辑标志

使用

最小示例

# Simplest example
$Conform = new Conform(['age'=>' 10 ']);
if($conformed = $Conform->get(['age'=>'f.int'])){
	# in converting age to in, Conform has stripped the spaces
	echo $conformed['age']; # => 10
}

设置输入

# You can set the input either at construction or after
$Conform = new Conform(['name'=>'bob']);
$Conform->input(['name'=>'sue']);

# if you do not provide an input on the construction (input value is false), Conform will use $_GET and $_POST as defaults, with $_POST overwriting colliding $_GET keys
$_POST = ['name'=>'bob'];
$Conform = new Conform();
$Conform->input(); # > ['name'=>'bob']

# if you want to set the input to the default input after construction, you can use the `request_input` method
$Conform->input(Conform::request_input());

# the request_input has some additional logic about pulling input from the request that include both the use of a _json key and the handling of request type application/json.  To see more, read the function doc.

使用默认合规器

有三个conformers会自动附加到Conform实例:\Grithin\Conform\Filter\Grithin\Conform\Validate和\Grithin\Conform\GlobalFunction。这些在规则中分别用前缀fvg表示。

要将输入过滤为整数,可以使用f.int,但为了验证输入是整数,则应使用v.int。过滤和验证可以一起使用,因为传递给后续规则的值是前一个规则的输出。例如,我们可以过滤到数字,然后检查结果是否是有效的整数

$Conform = new Conform(['age'=>'bob']);
$rules = ['age'=>'f.digits, v.int'];

if($conformed = $Conform->get($rules)){
	# ...
}else{
	echo 'did not validate';
}
# > did not validate

g用于引用全局函数。

$rules = ['age'=>'g.strtoupper'];

($Conform参数不会被传递给全局函数)

添加合规器

使用conformer_add函数。

$Conform->conformer_add($string_identity, $pathed_callable);

路径调用必须在规则提供的路径上是可调用的(根据lodash风格的路径对_.get进行路径解析)

调用接收$Conform实例作为最后一个参数。

您可以添加各种类型的合规器

# a closure
$Conform->conformer_add('upper', function($x){ return strtoupper($x);});
$rules = ['name'=>'upper'];

# an object with methods
$Conform->conformer_add('f', new \Grithin\Conform\Filter);
$rules = ['id'=>'f.int'];

# a function reference
$Conform->conformer_add('upper', 'strtoupper');
$rules = ['name'=>'upper']; # note, this will error b/c strtoupper does not expect 2 parameters ($Conform instance is passed as the last parameter)

使用标志

表单验证逻辑主要遵循模式。

例如,如果id输入是整数,则检查数据库

$rules = ['id' => `!v.int, db.check`];

其中的!表示,在继续执行该字段的后续规则之前,确保id输入存在且为整数。这样,我们就不会尝试使用可能是任意字符串的输入检查数据库。

如果我们有多个字段依赖于解析用户id,则可以完全退出验证

$rules = [
	'id' => `!!v.int, !!db.check`,
	'name' => 'db.matches_id'
];

在这里,!!表示,如果id不是整数,则退出失败并解析更多字段。如果db.check失败,也退出失败。

有时有一些可选字段,我们仍然希望过滤如果它们存在。为此,可以组合两个前缀。

$rules = [
	'email' => `?!v.filled, v.email`,
];

这表示,如果字段为空,则停止应用规则,但不会显示为错误。这样,如果用户留空该字段,则不会进行电子邮件验证并显示错误,但如果用户填写了电子邮件输入,则将进行电子邮件验证。(!?的顺序不重要。)

特殊情况

有一些前缀用于一些较少见的情况。

如果我们想收集一个字段的多个验证错误,但又因为错误而不处理某些结束规则怎么办?

$rules = [
	'email' => `v.filled, v.email, &db.check`,
];

如果电子邮件字段为空,则此字段规则集将导致因未填写而出现错误,并因未验证为电子邮件而出现错误。&前缀表示,如果规则链的前一部分有错误,则停止执行该字段的规则。

这可以类似地用于整个表单。如果我们想收集多个字段的错误,但又想阻止这些错误的产生阻止某些结束规则执行怎么办?

$rules = [
	'id' => `!v.int, db.check`,
	'name' => 'v.name, &&db.matches_id'
];

在这里,通过使用&&,如果前一个链或前一个字段中存在任何错误,后续规则将不会执行。

最后,有时可能希望得到一个规则的逆。例如,如果我想要一个在数据库中唯一的电子邮件,但我只有一个email_exists验证函数呢?

$rules = [
	'email' => `~db.email_exists`,
];

在这里,~就像一个“非”,表示没有错误表示存在错误。

规则格式和参数

除了输入值外,还可以将参数传递给验证函数。

$rules = ['age'=>'!v.range|18;60'];

在这里我们看到|将参数与函数路径分开,而;将参数彼此分开。这种形式是简短形式。有时使用!;可能会有问题,因此存在长形式

# array seperated rules
$rules = ['age'=>['!v.int', '!v.range|18;60']];
# array separated parameters
$rules = ['age'=>[['!v.range', 18, 60]]];
# array separated callable
$rules = ['age'=>[[['!', 'v.range'], 18, 60]]];

注意,使用数组分隔的可调用对象时,函数本身可以是可调用的,而不仅仅是路径。

$rules = [
	'age'=>[
		[['!', function(){}], 18, 60]]
	];

检索部分输出

虽然get在存在错误时返回false,但通过$Conform->output可以获取没有错误的字段的Conform输出。

$input = ['name' => 'bob', 'age' => 2];
$Conform = new Conform($input);
$conformed = $Conform->get(['name'=>'v.int', 'age'=>'v.int']);
# > false
$Conform->output;
# > ['age'=>2]

使用错误

错误以\Grithin\Conform\Error的形式从Conform中可用。

$input = ['year' => 'bob', 'age' => 2, 'number'=>'bill'];
$Conform = new Conform($input);
$conformed = $Conform->get([
	'year'=>'v.int',
	'age'=>'v.int',
	'number'=>'v.int'
]);

foreach($Conform->errors() as $error){
	var_export((array)$error);
}

/*
array (
  'message' => 'v.int',
  'fields' =>
  array (
    0 => 'year',
  ),
  'type' => 'v.int',
)array (
  'message' => 'v.int',
  'fields' =>
  array (
    0 => 'number',
  ),
  'type' => 'v.int',
)
*/

错误对象可以用作数组。有三个属性

  • 类型
  • 消息
  • 字段

内置验证错误的消息更改由实现者决定。

您可以选择您想要的错误字段或字段。

//...
# get all the errors for the field "year"
$Conform->field_errors('year');
# get all the errors for the fields "year" and "number"
$Conform->fields_errors(['year', 'number']);

制作验证

在验证函数内部,您可以发出错误信号

$conformer = function($v, $Conform){
	$Conform->error('Error Message');
	return $v;
};
$Conform->conformer_add('conformer', $conformer);

以下是一些注意事项

  • $Conform实例作为验证函数的最后一个参数传入
  • 即使存在错误,您仍然可以返回值,以便进行进一步的验证

默认情况下,错误类型将是验证函数的路径。但是,您可以设置自己的类型,并且如果错误与多个字段相关,您甚至可以指定字段。

$conformer = function($v, $Conform){
	$error = [
		'message' => 'Error Message',
		'type' => 'custom_type',
		'fields' => ['name', 'age']
	];
	$Conform->error($error);
	return $v;
};
$Conform->conformer_add('conformer', $conformer);

当前字段被添加到字段数组中。

上下文数据

有时在验证函数中了解上下文数据很有用。要访问上下文数据,您可以使用作为验证函数最后一个参数传入的$Conform实例。

有用的上下文数据

  • ->field正在验证的当前字段
  • ->output当前验证过程的输出(即已验证的字段)
  • ->input当前输入

让我们使用一个依赖于前一个字段输出(字段必须先于)和另一个字段输入的验证函数

$auth = function($v, $Conform){
	$query = ['id'=>$Conform->output['id'], 'password'=>$conform->input['password']]
	if(!Db::check($query)){
		$Conform->error('Password and id did not match');
	}
	return $v;
};

独立验证和过滤

由于它们具有SingletonDefault特性,因此可以使用Validate和Filter伪静态。

Filter::init()->url($url)
Validate::init()->test('url', $url);

规则项目前缀

前缀可以是以下组合之一

  • "!"在错误时断开,不应应用该字段的更多规则
  • "!!"在错误时断开,不应应用任何字段的更多规则
  • "?"表示验证是可选的,不要抛出错误(与'!'结合使用时很有用,例如'?!v.filled,email')
  • "~"表示如果验证没有失败,则存在错误。注意,原始值(传递给函数的)将被推向前
  • "&"表示如果该字段之前有错误,则应断开代码
  • "&&"表示如果验证运行中任何字段有错误,则应断开代码