這個(gè)項(xiàng)目由來很久了,大概至少有6年的歷史,最早我工作的時(shí)候接觸的一個(gè)項(xiàng)目叫struct,他可以把數(shù)組映射到對(duì)象屬性上進(jìn)行數(shù)據(jù)類型內(nèi)容等判斷或者過濾,也可以進(jìn)行轉(zhuǎn)換,也可以自行注冊(cè)handle進(jìn)行過濾、判斷;后來我把這個(gè)項(xiàng)目重構(gòu)了一下,適用在常駐內(nèi)存的環(huán)境下,同時(shí),這個(gè)項(xiàng)目也是我用在生產(chǎn)環(huán)境中的一個(gè)項(xiàng)目;
這個(gè)項(xiàng)目有點(diǎn)像殘疾的注解,不過我覺得用起來還挺好的,所以推薦給大家
測(cè)試覆蓋率應(yīng)該超過了80%,常用的幾種方案都是100%覆蓋的;
項(xiàng)目地址:https://github.com/chaz6chez/structure
1.x已經(jīng)沒有再維護(hù)了,現(xiàn)在只維護(hù)2.x
composer require chaz6chez/structure
namespace YoursNamespace;
use Structure\Struct;
class User extends Struct {
// 當(dāng)id為非null值時(shí),必須滿足int 10 < id < 20
/**
* @rule int,min:10,max:20|id format error:1001
*/
public $id;
// name為必填,必須滿足string 1 < name長(zhǎng)度 < 20
/**
* @rule string,min:1,max:20|name format error:2001
* @required true|name cannot be empty:2002
*/
public $name;
// 當(dāng)sex為null時(shí)默認(rèn) string female
// 滿足string 0 < sex長(zhǎng)度 < 10
/**
* @rule string,min:0,max:10|sex format error:1001
* @default string:female
*/
public $sex;
}
$struct = \YoursNamespace\User::factory();
$struct->create([
'id' => 12,
'name' => 'John Smith'
]);
// or
$struct = \YoursNamespace\User::factory([
'id' => 12,
'name' => 'John Smith'
]);
// or
$struct = new \YoursNamespace\User();
$struct = \YoursNamespace\User::factory();
$struct->id = 12;
$struct->name = 'John Smith';
// or
$struct = new \YoursNamespace\User([
'id' => 12,
'name' => 'John Smith'
]);
if($struct->hasError()) {
throw new \RuntimeException(
$struct->getError()->getMessage() . '->' . $struct->getError()->getPosition(),
$struct->getError()->getCode()
);
}
return $struct->output(); // array
namespace Example;
use Structure\Struct;
class User extends Struct{
public $id;
public $name;
public $sex;
}
/**
* @rule string,min:10,max:20|name format error:1001
*/
public $name;
/**
* a區(qū) b區(qū) c區(qū) d區(qū)
* @標(biāo)簽 [場(chǎng)景] 驗(yàn)證方式 | 錯(cuò)誤信息 : 錯(cuò)誤碼
* ↓ ↓ ↓ ↓
* @rule[check] string,min:1|error message:error code
*/
轉(zhuǎn)換類的標(biāo)簽配合 filter()在output() 方法內(nèi)生效,
會(huì)對(duì)包含該標(biāo)簽的屬性執(zhí)行轉(zhuǎn)換或者過濾操作
驗(yàn)證類的標(biāo)簽在 validate() 中生效返回布爾值,
通過getError() 可以獲得錯(cuò)誤信息
標(biāo)簽名 | 方式 | 類型 | 說明 |
---|---|---|---|
@default | Structure\Handler、func、method | 轉(zhuǎn)換 | func與method是將返回值默認(rèn)賦予該標(biāo)簽 |
@required | true | 驗(yàn)證 | 判斷是否為必要值 |
@rule | Structure\Handler、func、method | 驗(yàn)證 | 以func與method的bool返回類型判斷驗(yàn)證 |
@skip | 無 | 驗(yàn)證 | 跳過驗(yàn)證 |
@ghost | 無 | 轉(zhuǎn)換 | 跳過輸出 |
@key | 無 | 轉(zhuǎn)換 | 標(biāo)記鑰匙屬性 |
@mapping | 映射鍵名 | 轉(zhuǎn)換 | 映射鍵轉(zhuǎn)換 |
@operator | true、func、method | 轉(zhuǎn)換 | 鍵值特殊轉(zhuǎn)換 |
/**
* @default string:abc
* @default int:123
* @default float:1.1
* @default object:Handler\Help
* @default map:{"a":"1"}
* @default array:["1"]
* @default bool:true
*/
public $name;
/**
* @default func:is_array 會(huì)找到is_array函數(shù)
* @default method:_set 會(huì)定位當(dāng)前類的_set方法
* @default method:Handler\Help,get 會(huì)定位Handler\Help類的get方法
*/
public $name;
public static function _set(){
return 'abc';
}
// 以name的@default標(biāo)簽為string:John舉例
/**
* @default string:John
*/
public $name;
$struct = new Struct();
// @default無法生效,值為null
$struct->name;
// @default可以生效,值為字符串John
$struct()->name;
/**
* @required true|name cannot empty
*/
public $name;
/**
* @rule string,min:10,max:20|name format error
* @rule int,min:10,max:20|name format error
* @rule float,min:1.0,max:2.1,scale:3|name format error
* @rule bool,true|name format error
* @rule object,class:|name format error
* @rule array,min:10,max:20,values:string|name format error
* @rule map,min:1,max:5,keys:string,values:int|name format error
* @rule url,path:true,query:true|name format error
* @rule ip,ipv4:false,ipv6:false,private:false,reserved:false|name format error
* @rule regex,min:10,max:20,regex:/.?/|name format error
*/
public $name;
/**
* @rule func:_set 會(huì)找到_set函數(shù)
* @rule method:_set 會(huì)定位當(dāng)前類的_set方法
* @rule method:Handler\Help,get 會(huì)定位Handler\Help類的get方法
*/
public $name;
public static function _set($value) : bool
{
return $value === '_method';
}
}
function _set($value) : bool
{
return $value === '_func';
}
// @ghost true
$user->id = 'id';
$user->name = 'name';
$user->output();
// 以上會(huì)輸出
[
'name' => 'name'
];
$user->output(true);
// 以上會(huì)輸出
[
'id' => 'id',
'name' => 'name'
];
// @key true
$user->id = 'id';
$user->name = 'name';
$user->filter(STRUCT_FILTER_KEY)->output();
// 以上會(huì)輸出
[
'name' => 'name'
];
$user->transfer(STRUCT_FILTER_KEY_REVERSE)->output();
// 以上會(huì)輸出
[
'id' => 'id',
];
/**
* @operator true
*/
public $name;
通過 transfer()->output() 可以做到轉(zhuǎn)換輸出
// @operator true
$user->id = 'abc[>]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出
[
'id[>]' => 'abc'
];
$user->id = '123,456[<>]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出
[
'id[<>]' => ['123','456'],
];
2.2以上版本完善了該標(biāo)簽下的類型轉(zhuǎn)換
2.2以下版本中不會(huì)對(duì)類型轉(zhuǎn)換
// @operator true
$user->id = '123[>]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出
[
'id[>]' => '123'
];
// 并非期待的
[
'id[>]' => 123
];
// 此種狀況會(huì)影響數(shù)據(jù)庫(kù)查詢的索引
// @operator true
$user->id = '123[>]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id[>]' => 123
];
// @operator true
$user->id = '123';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id' => 123
];
$user->id = '1.23[>]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 float
[
'id[>]' => 1.23
];
$user->id = '1.23';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出
[
'id' => 1.23
];
# String :
// @operator true
$user->id = '123[String][>]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id[>]' => '123'
];
// @operator true
$user->id = '123[String]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id' => '123'
];
# Int :
// @operator true
$user->id = '1[Int][>]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id[>]' => 1
];
// @operator true
$user->id = '1[Int]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id' => 1
];
# Float :
// @operator true
$user->id = '123[Float][>]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id[>]' => 123.0
];
// @operator true
$user->id = '123[Float]';
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id' => 123.0
];
# Bool :
// @operator true
$user->id = '1[Bool][>]'; // 0 false
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id[>]' => true
];
// @operator true
$user->id = '1[Bool]'; // 0 false
$user->transfer(STRUCT_TRANSFER_OPERATOR)->output();
// 以上會(huì)輸出 int
[
'id' => true
];
/**
* @operator func:_add 相當(dāng)于_add($name)
* @operator method:_add 相當(dāng)于$this->_add($name)
* @operator method:Handler\Help,_add 相當(dāng)于Handler\Help::_add($name)
*/
public $name;
// @mapping key
$user->id = 123;
$user->name = 'john';
$user->output();
// 輸出
[
'id' => 123,
'name' => 'john'
];
$user->transfer(STRUCT_TRANSFER_MAPPING)->output();
// 輸出
[
'key' => 123,
'name' => 'john'
];
$user = User::factory([
'id' => 1,
'name' => 'john'
],'check');
輸入
// 1.使用create輸入數(shù)據(jù)
$user->create([
'id' => 1,
'name' => 'john'
]); // return $this
// 2.使用屬性賦值輸入數(shù)據(jù)
$user->id = 1;
$user->name = 'john';
// 使用create可以保存原始數(shù)據(jù),建議使用create輸入數(shù)據(jù)
$user->getRaw(); // return array
$user->scene('check'); // return $this
轉(zhuǎn)換
$user->transfer(STRUCT_TRANSFER_MAPPING); // return $this
// STRUCT_TRANSFER_MAPPING
// STRUCT_TRANSFER_OPERATOR
// 接受可變長(zhǎng)參數(shù)
$user->transfer(
STRUCT_TRANSFER_MAPPING,
STRUCT_TRANSFER_OPERATOR
); // return $this
$user->filter(STRUCT_FILTER_NULL); // return $this
// STRUCT_FILTER_NULL
// STRUCT_FILTER_EMPTY
// STRUCT_FILTER_ZERO
// STRUCT_FILTER_KEY
// STRUCT_FILTER_KEY_REVERSE
// STRUCT_FILTER_OPERATOR
// STRUCT_FILTER_OPERATOR_REVERSE
// 接受可變長(zhǎng)參數(shù)
$user->filter(
STRUCT_FILTER_NULL,
STRUCT_FILTER_EMPTY
); // return $this
$user->validate(); // return bool
$user->hasError(); // return bool
// true 有錯(cuò)誤,驗(yàn)證未通過
// false 無錯(cuò)誤,驗(yàn)證通過
獲取錯(cuò)誤
$user->getError(); // return Structure\Error
$user->getError()->getMessage(); // 錯(cuò)誤信息 string
$user->getError()->getCode(); // 錯(cuò)誤碼 string
$user->getError()->getField(); // 字段名 string
$user->getError()->getPosition(); // 錯(cuò)誤定位 對(duì)應(yīng)Handler對(duì)應(yīng)的options字段
$user->getErrors(); // return Structure\Error[]
輸出
$user->output(); // return array
$user->output(true); // 全量輸出
$user->clean(); // 默認(rèn)不裝載raw數(shù)據(jù)
$user->clean(true); // 裝載raw數(shù)據(jù)
\Structure\Handler::register();
try {
// ...
}catch (\Structure\Exceptions\StructureException $exception){
$exception->getPosition(); // 錯(cuò)誤定位信息
$exception->getMessage(); // Structure Exception [{position info}]
$exception->getCode(); // 始終以-666返回
}
感謝分享,這個(gè)東西看上去不錯(cuò)
以下是提出的一些關(guān)于使用場(chǎng)景的問題
1.入?yún)⑹嵌嗑S數(shù)組的情況是是否有相應(yīng)的驗(yàn)證規(guī)則,如下
$a = [
['a' => [1,2,3,'child' => [666]]]
];
2.在很多業(yè)務(wù)場(chǎng)景中前端會(huì)傳過來很多亂七八糟的數(shù)據(jù),如果需要參數(shù)僅需要某些字段的時(shí)候是否有相關(guān)的校驗(yàn)方式
//現(xiàn)在的
$liat = [
'name' => 'xxx',
'mobile' => 'xxx',
'pwd' => 'xxx'
];
$model->name = $list['name'];
$model->mobile = $list['mobile'];
//增加驗(yàn)證后就可以驗(yàn)證存在非name和mobile字段,直接提示參數(shù)異常
$model->attributes = $list;
望指導(dǎo)
你是說不能確定哪些參數(shù)需要驗(yàn)證,整個(gè)字段的驗(yàn)證是隨機(jī)的?
如果是固定字段,但有可能客戶端傳來的參數(shù)并不一定傳入該參數(shù)的話,其實(shí)可以用skip標(biāo)簽
不好意思,上面的沒有表述清楚,我現(xiàn)在有修改個(gè)人資料接口,是以數(shù)組形式傳過來的鍵值對(duì)參數(shù)列表,比如姓名、手機(jī)號(hào),性別等,在賦值的時(shí)候我要判斷有什么賦值什么
理想情況是直接把給過來的東西進(jìn)行數(shù)據(jù)驗(yàn)證,然后塞到屬性里即可,但是對(duì)方可能把其它字段拿過來進(jìn)行試探,比如修改企業(yè)id,修改角色,修改密碼
我這個(gè)修改資料接口僅支持修改姓名、性別、手機(jī)號(hào)、郵箱、身份證,如接收到的請(qǐng)求數(shù)據(jù)存在這些以外的數(shù)據(jù)就進(jìn)行報(bào)錯(cuò)
不知道我表述的清不清晰
那就嵌套一下唄,就是這個(gè)數(shù)組再創(chuàng)建一個(gè)struct,這個(gè)struct對(duì)象是外層struct的某個(gè)屬性,然后外層用func的方式獲取內(nèi)層struct的message
感謝分享!