大多数时候提交表单都是手写业务代码,大部分都是字段名不一样,但是校验逻辑是一样的,因此 仿校验器抽离出规则进行校验
/**
* 校验表单字段 是否符合既定规则
* @param $data 表单数组
* @param array $rules 请求规则 格式为 '表单字段' => '规则信息1|规则信息2'
* 规则信息约定 多个规则用 |号隔开 每一个规则如有 详细约束 参数可以补充在 : 后面
* 必填项 + 限定数字 'name' => 'require|number'
* 必填项 + 限定数字 (10-25) 之间 'name' => 'require|number|between:10,25'
* 必填项 + 限定数字 + 最大不超过50 'age' => 'require|number|max:50'
* 必填项 + 限定数字 + 不小于18 'age' => 'require|number|min:18'
* 必填项 + 限定坐标 'pos' => 'require|position'
* 邮箱 'qqemail' => 'email'
* tel 座机 'call' => 'tel'
* mobile 手机 'call' => 'mobile'
* phone 手机+座机 'call' => 'phone'
* !!! depend 依赖项检测 'name' => '其他表单名1,其他表单名2' 不支持检测 数组变量+非数组变量的情况
* @param array $messages 错误提示内容 不传 则用 表单字段名.规则 报错
*/
function validateParams(&$data, $rules = [], $messages = [])
{
foreach ($data as $k => $v) {
if (!isset($rules[$k])) {
continue;
}
// 校验必须
$require = function ($param) use ($k, $messages) {
$mk = "{$k}.require";
if (is_array($param)) {
foreach ($param as $p) {
if ($p == null || $p == '') {
fail($messages[$mk] ?? $mk);
}
}
} else {
if ($param == null || $param == '') {
fail($messages[$mk] ?? $mk);
}
}
};
// 校验长度最值
$maxOrMin = function ($param, $length, $kind = 'max') use ($k, $messages) {
$mk = "{$k}.{$kind}";
if ($kind == 'max' && strlen($param) > $length) {
fail($messages[$mk] ?? $mk . ' length > ' . $length);
} else if ($kind == 'min' && strlen($param) < $length) {
fail($messages[$mk] ?? $mk . ' length < ' . $length);
}
};
// 校验数值判断
$checkNumber = function ($param, $length) use ($k, $messages) {
$mk = "{$k}.number";
if (is_array($param)) {
foreach ($param as $p) {
if (is_numeric($p)) {
fail($messages[$mk] ?? $mk);
}
}
} else {
if (is_numeric($param)) {
fail($messages[$mk] ?? $mk);
}
}
};
// 校验区间
$checkBetween = function ($param, $range) use ($k, $messages) {
$mk = "{$k}.between";
sort($range, SORT_NUMERIC);
if (is_array($param)) {
foreach ($param as $p) {
if ($p < $range[0] || $p > $range[1]) {
fail($messages[$mk] ?? $mk);
}
}
} else {
if ($param < $range[0] || $param > $range[1]) {
fail($messages[$mk] ?? $mk);
}
}
};
// 邮箱验证
$checkEmail = function ($param) use ($k, $messages) {
$mk = "{$k}.email";
if (!is_string($param) || !preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/", $param, $matches)) {
fail($messages[$mk] ?? $mk);
}
};
// 号码验证 支持手机号码和座机
$checkPhone = function ($param) use ($k, $messages) {
$mk = "{$k}.phone";
if (!preg_match("/^1(\d{10})||([0-9]{3,4}-)?[0-9]{7,8}$/", $param, $matches)) {
fail($messages[$mk] ?? $mk);
}
};
// 只检查手机号码
$checkMobile = function ($param) use ($k, $messages) {
$mk = "{$k}.mobile";
if (!preg_match("/^1(\d{10})$/", $param, $matches)) {
fail($messages[$mk] ?? $mk);
}
};
// 只检查座机
$checkTel = function ($param) use ($k, $messages) {
$mk = "{$k}.tel";
if (!preg_match("/^([0-9]{3,4}-)?[0-9]{7,8}$/", $param, $matches)) {
fail($messages[$mk] ?? $mk);
}
};
// 检查是否是经纬度坐标
$checkPosition = function ($param) use ($k, $messages) {
$mk = "{$k}.position";
if (is_array($param)) {
foreach ($param as $p) {
if (!verifyPosition($p)) {
fail($messages[$mk] ?? $mk);
}
}
} else {
if (!verifyPosition($param)) {
fail($messages[$mk] ?? $mk);
}
}
};
//检测依赖项 用于判断某个变量存在的前提是另外一个变量存在 如果同时都不存在都 unset 掉 如果其中一个存在而有一个不存在 就返回报错
$checkDepend = function ($depends) use ($k, $messages, &$data) {
$mk = "{$k}.depend";
$depends[] = $k;
if (is_array($data[$k])) {
// 如果是数组 检测数组每一个相同序号的元素是否对的上
// 存储每一个二维数组的元素长度
$lengths = [];
foreach ($depends as $depend) {
$lengths[] = count($data[$depend]);
}
if (count(array_unique($lengths)) > 1) {
// 存在不同长度的二维元素 报错
fail("{$k} 依赖项列表元素无法一一对应");
}
foreach ($data[$k] as $kk => $vv) {
$all_exists = true;
$all_empty = true;
foreach ($depends as $depend) {
if (!isset($data[$depend][$kk]) || !$data[$depend][$kk]) {
$all_exists = false;
continue;
}
if ($data[$depend][$kk]) {
$all_empty = false;
}
}
if (!$all_exists && !$all_empty) {
// 部分存在 报错
fail($messages[$mk] ?? $mk . ' has error');
}
if ($all_empty) {
// 所有数字都为空 unset
foreach ($depends as $depend) {
unset($data[$depend][$kk]);
}
foreach ($depends as $depend) {
$data[$depend] = array_values($data[$depend]);
}
}
}
} else {
$all_exists = true;
$all_empty = true;
foreach ($depends as $depend) {
if (!isset($data[$depend]) || !$data[$depend]) {
$all_exists = false;
continue;
}
if ($data[$depend]) {
$all_empty = false;
}
}
if (!$all_exists && !$all_empty) {
// 部分存在 报错
fail($messages[$mk] ?? $mk . ' has error');
}
if ($all_empty) {
// 所有数字都为空 unset
foreach ($depends as $depend) {
unset($data[$depend]);
}
}
}
};
$limits = is_array($rules[$k]['limit']) ?: explode("|", $rules[$k]['limit']);
// 变量限定检测
foreach ($limits as $kk => $limit) {
list($limit, $param) = explode(":", $limit);
switch ($limit) {
case 'require':
$require($v);
break;
case 'max':
case 'min':
$maxOrMin($v, $param, $limit);
break;
case 'number':
$checkNumber($v);
break;
case 'between':
if (strpos($param, ",") === false) {
fail("校验 between 规则 限定参数有误");
}
$checkBetween($v, explode(",", $param));
break;
case 'email':
$checkEmail($v);
break;
case 'phone':
$checkPhone($v);
break;
case 'mobile':
$checkMobile($v);
break;
case 'tel':
$checkTel($v);
break;
case 'postion':
$checkPosition($v);
break;
case 'depend':
$checkDepend(explode(",", $param));
}
}
}
}
比较特殊的是 depend 的情况,有些情况下我需要保证另外几个变量同时存在 才可以提交 ,这种情况就存在依赖关系
$rules = [
'run_default_address_pos' => ['limit' => 'require|postion|depend:run_default_address_name']
];
$messages = [
'run_default_address_pos.depend' => '地址或坐标不能为空',
'run_default_address_pos.require' => '坐标必填'
];
validateParams($_POST, $rules, $messages);