grithin/php-conform 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

grithin/php-conform

Composer 安装命令:

composer require grithin/php-conform

包简介

PHP Input Conform Handler

README 文档

README

Ordered filtering and validation with standard input logic.

About

Back around 2009, I wanted to build something that would resolve having to code the normal input logic in situations like

  • if x field failed, don't check y field
  • if x field failed one step of validation, don't validate it further
  • if x field failed validation, stop validating all input and return with error

There are a lot of combinations of this input field dependent logic which I thought could be simplified in expression. So, I created this class, which uses logic flags.

Use

Smallest Example

# 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
}

Setting The Input

# 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.

Using Default Conformers

There are three conformers that come auto-attached to a Conform instance: \Grithin\Conform\Filter, \Grithin\Conform\Validate, and \Grithin\Conform\GlobalFunction. These are represented in rules by the prefixes f, v, and g.

To filter an input to an int, you can use f.int, but to validate the input is an int, you would use v.int. Filter and validate can be used in conjuction because the value passed to a proceeding rule is the output from the preceeding rule. For instance, we can filter to digits, and then check if the result is a valid 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 is used to reference a global function.

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

(The $Conform parameter will not be passed to the global function)

Adding Conformers

Use the conformer_add function.

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

The pathed callable must be callable at the path provided by the rule (according to lodash style pathing for _.get)

The callable receives the $Conform instance as the final parameter.

You can add various types of conformers

# 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)

Using Flags

Form validation logic mostly follows patterns.

For example, if the id input is an int, check it in the database:

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

The ! says, ensure id input exists as an int before proceeding to the following rules on the field. In this way, we don't attempt to check the database with input that might be some arbitrary string.

What if we had multiple fields that relied on resolving a user id? You can exit out of validation entirely:

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

Here, the !! says, if id is not an int, exit with fail and parse no more fields. And if db.check fails, also exit with fail.

Sometimes there are optional fields that we still want to filter if they are present. To do this, you can combine two prefixes.

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

This says, if the field is not filled, stop applying the rule, but don't show as an error. This way, if the user left the field empty, there will be no email validation and no error, but if the user had filled out the email input, there will be email validation. (The order of ! and ? doesn't matter.)

Special Cases

There are prefixes for some rarer cases.

What if we wanted to collect multiple validation errors for a field, but then not process some ending rules b/c of the errors?

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

If the email field were not filled, this field rule set would result in an error for not being filled and an error for not validating as an email. The & prefix indicates, if there were any errors in the previous part of the rule chain, stop execution of rules for the field.

This can similarly be done for the entire form. What if we wanted to collect errors across multiple fields, but wanted the presence of those errors to prevent some ending rule exectuion?

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

Here, by use of the &&, if there were any errors in any of the previous chain or previous fields, the proceeding rule will not execute.

Finally, some times the reverse of a rule is desired. For instance, what if I wanted an email that was unique in the database but I just has a email_exists conformer function?

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

Here, the ~ is like a "not", and indicates a lack of error indicates an error.

Rule Format And Parameters

Parameters may be passed to a conformer in addition to the value of the input.

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

Here we see the | separates the parameters from the function path, and the ; separates the parameters from themselves. This form is the short form. Sometimes the use of ! or ; can be problematic, so there are long forms

# 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]]];

Note, with the array separated callable, the function itself can be a callable instead of a path.

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

Retrieving Partial Output

Although get returns false if there were any errors, the Conform output for the fields that did not have errors is available with $Conform->output

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

Using The Errors

Errors are available from Conform and in the form of \Grithin\Conform\Error

$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',
)
*/

The error objects can be used as arrays. There are three properties

  • type
  • message
  • fields

It is left up to the implementor to change the message of the built in validation errors.

You can select the field or fields you want the errors for

//...
# 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']);

Making Validation

Within a conformer, you can signal an error

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

A few things to note here

  • the $Conform instance is passed in as the last parameter to the conformer function
  • you can still return the value, even when there is an error, so that further validation can be done

By default, the error type will be whatever the path to the validator function was. But, you set your own type, and you can even specify the fields if the error is linked to multiple.

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

The current field is added into the fields array.

Context Data

Sometimes it is useful to know context data within a conform function. To access context data, you can use the $Conform instance that is passed in as the last parameter to conformer functions.

The context data that is useful:

  • ->field the current field that is being conformed
  • ->output the current output of the conform process (ie, fields that have been conformed)
  • ->input the current input

Let's use a conformer that relies on the output of a previous field (field must come prior), and the input of another field

$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;
};

Validate And Filter Alone

You can use Validate and Filter pseudo statically, b/c they are traited with SingletonDefault.

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

Rule Item Prefixes

The prefix can be some combination of the following

  • "!" to break on error with no more rules for that field should be applied
  • "!!" to break on error with no more rules for any field should be applied
  • "?" to indicate the validation is optional, and not to throw an error (useful when combined with '!' => '?!v.filled,email')
  • "~" to indicate if the validation does not fail, then there was an error. Note, the original value (passed in to the function) will be pushed forward
  • "&" to indicate code should break if there were any previous errors on that field
  • "&&" to indicate code should break if there were any previous errors on any field in the validate run

grithin/php-conform 适用场景与选型建议

grithin/php-conform 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 94 次下载、GitHub Stars 达 0, 最近一次更新时间为 2016 年 08 月 03 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「php input get post filter validate」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 grithin/php-conform 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 grithin/php-conform 我们能提供哪些服务?
定制开发 / 二次开发

基于 grithin/php-conform 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 94
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: Apache-2.0
  • 更新时间: 2016-08-03