验证类
1.提供两种调用方式
Validator::ValidatorInput 验证input数组
Validator::ValidatorInputRow 验证一行
2.验证规则两种写法
竖线分隔:ruler = array('type'=>'int','required'=>true,'min'=>10,);
3.扩展
自定义 {$type}Validator 方法 扩展验证类
使用示例
$arrInput = array(
'orderId' => '3',
'status' => '3',
);
$arrValidationRule = array(
'orderId' => 'type=int|required|min=3',// int 必须 最小值 3
'status' => 'type=int|status|enum=1,2,3,4|cnName=状态',// string 非必需 枚举[1,2,3,4] 中文名= 状态
);
Validator::ValidatorInput($arrInput, $arrValidationRule);
---
// int 必须 最小值 3
Validator::ValidatorInputRow($orderId,'type=int|required|min=3','orderId');
// string 非必需 枚举[1,2,3,4] 中文名= 状态
Validator::ValidatorInputRow($status,'type=string|status|enum=1,2,3,4|cnName=状态','status');
验证类
<?php
/**
* Created by PhpStorm.
* Date: 2018/1/11 16:54
*/
class Validator {
const ERR_CODE_INPUT_ERROR = 4000001;
const ERR_CODE_INPUT_REQUIRED = 4000002;
const ERR_CODE_INPUT_TYPE = 4000003;
const ERR_CODE_CHECK_REFER = 4000100;
public static $MSG = array(
self::ERR_CODE_INPUT_ERROR => '参数错误',
self::ERR_CODE_INPUT_REQUIRED => '必须的',
self::ERR_CODE_INPUT_TYPE => '类型错误',
self::ERR_CODE_CHECK_REFER => 'Refer验证',
);
/**
* Validator @desc 成批验证输入
* @param $arrInput
* array(
* 'k1'=>'v1',
* 'k2'=>'v2',
* 'k3'=>'v3',
* );
* @param $arrValidationRule
* 支持两种验证规则输入格式 字符串传参 和 输出传参
*
* 字符串传参
* array(
* 'k1'=>'required|type=string|format=date|pattern=#^1[3456789]\d{9}$#|min=1|max=1|enum=1,2,3|nameCn=中文名称',
* 'k2'=>'type=string',
* 'k3'=>'enum=1,2,3',
* );
* || || ||
* 输出传参
* array(
* 'k1'=>array(
* 'required',
* 'type'=>'string',
* 'format'=>'date',
* 'pattern'=>'#^1[3456789]\d{9}$#',
* 'min'=>'1',
* 'max'=>'2',
* 'enum'=>array('1','2','3'),
* ),
* 'k2'=>array(
* 'type'=>'string'
* ),
* 'k3'=>array(
* 'enum'=>array('1','2','3'),
* ),
* );
* @param bool $checkRequired 是否进行必要验证
* @param bool $isGetMessage 是否返回message false 不返回
* @return array|bool
*/
static public function ValidatorInput($arrInput, $arrValidationRule, $checkRequired = true, $GetMessage = false) {
$errMessages = array();
foreach ($arrValidationRule as $key => $val) {
try {
$arrRules = self::ruleTransform($val);
$name = (isset($arrRules['nameCn']) && (!empty($arrRules['nameCn']))) ? $arrRules['nameCn'] : $key;
//是否需要进行必要的参数校验
if (($checkRequired === true) && isset($arrRules['required'])) {
$value = isset($arrInput[$key])?$arrInput[$key]:'';
$checkRequired = self::requiredValidator($value,true);
if (!$checkRequired) {
throw new Exception(self::$MSG[self::ERR_CODE_INPUT_REQUIRED] . "[$name]", self::ERR_CODE_INPUT_REQUIRED);
}
}
//如果$arrInput数组存在该参数 对该参数进行格式校验
if (isset($arrInput[$key]) && ($arrInput[$key] !== '')) {
self::ValidatorInputRow( $arrInput[$key],$arrRules, $key);
}
} catch (Exception $e) {
$code = $e->getCode();
$message = $e->getMessage();
if ($GetMessage === false) {
throw new Exception($message, $code);
}
$errMessages[$key] = array(
'code' => $code,
'message' => $message,
);
}
}
return $errMessages;
}
/**
* ValidatorInputRow @desc 单条验证
* @param $rules
* array(
* 'required',
* 'type'=>'string', enum[integer|number,string]
* 'format'=>'date',
* 'pattern'=>'#^1[3456789]\d{9}$#',
* 'min'=>'1',
* 'max'=>'2',
* 'enum'=>array('1','2','3'),
* 'nameCn'=>'中文名称',
* ),
* date-time 2016-09-18 12:12:12
* date 2016-09-18
* time 12:12:12
* utc-millisec [>=0]
* ‘color’ ‘maroon’, ‘red’, ‘orange’, ‘yellow’, ‘olive’, ‘green’, ‘purple’, ‘fuchsia’, ‘lime’, ‘teal’, ‘aqua’, ‘blue’, ‘navy’, ‘black’, ‘gray’, ‘silver’, ‘white’ 颜色值
* style
* phone
* @param $value 验证的值
* @param $name 字段名称
* @throws Exception
* @return bool
*/
static public function ValidatorInputRow($value,$rules, $name) {
$rules = self::ruleTransform($rules);
$name = isset($rules['nameCn']) ? $rules['nameCn'] : $name;
$typeRow = isset($rules['type']) ? $rules['type'] : 'string';
if(isset($rules['required'])){
self::checkFormat($value, 'required', $name);
unset($rules['required']);
}
if(isset($rules['type'])){
self::checkFormat($value, $typeRow, $name);
unset($rules['type']);
}
foreach ($rules as $key => $val) {
switch ($key) {
case 'format':
self::checkFormat($value, $val, $name);
break;
case 'pattern':
if (!preg_match($val, $value)) {
$msg = sprintf('参数 [%s] 与指定的规则不匹配', $name);
throw new Exception($msg, self::ERR_CODE_INPUT_ERROR);
}
break;
case 'min':
if (($typeRow === 'integer') || ($typeRow === 'int') || ($typeRow === 'number')) {
if (intval($value) < (intval($val))) {
$msg = sprintf('参数 [%s] 最小值为[%s]', $name, $val);
throw new Exception($msg, self::ERR_CODE_INPUT_ERROR);
}
} else {
if (mb_strlen($value, 'UTF8') < ((int)$val)) {
$msg = sprintf('参数 [%s] 最小长度为[%s]', $name, $val);
throw new Exception($msg, self::ERR_CODE_INPUT_ERROR);
}
}
break;
case 'max':
if (($typeRow === 'integer') || ($typeRow === 'int') || ($typeRow === 'number')) {
if (intval($value) > (intval($val))) {
$msg = sprintf('参数 [%s] 最大值为[%s]', $name, $val);
throw new Exception($msg, self::ERR_CODE_INPUT_ERROR);
}
} else {
if (mb_strlen($value, 'UTF8') > ((int)$val)) {
$msg = sprintf('参数 [%s] 最大长度为[%s]', $name, $val);
throw new Exception($msg, self::ERR_CODE_INPUT_ERROR);
}
}
break;
case 'enum':
if (!in_array($value, $val)) {
$msg = sprintf('参数 [%s] 不是有效的输入', $name);
throw new Exception($msg, self::ERR_CODE_INPUT_ERROR);
}
break;
default:
}
}
return true;
}
/**
* checkFormat @desc 检测数值的数据类型
* @param $value
* @param $type
* @param $valueName
* @return bool
* @throws Exception
*/
public static function checkFormat($value, $type, $valueName = '') {
$valid = true;
switch ($type) {
case 'string':
if (is_string($value)) {
$xssEntity = self::xssValidator($value);
if ($xssEntity != $value) {
$msg = sprintf('[%s]对应的数值非法(存在跨站脚本攻击)', $valueName);
throw new Exception($msg, self::ERR_CODE_INPUT_ERROR);
}
} else {
$valid = false;
}
break;
case 'int':
case 'integer':
if ((!is_numeric($value)) || ($value != (int)$value)) {
$valid = false;
}
break;
case 'number':
if (!is_numeric($value)) {
$valid = false;
}
break;
case 'phone':
if (!preg_match('#^1[3456789]\d{9}$#', $value)) {
$valid = false;
}
break;
case 'uri':
if (!preg_match('#^[A-Za-z0-9:/;,\-_\?&\.%\+\|\#=]*$#', $value)) {
$valid = false;
}
break;
case 'date-time':
if (!preg_match('#^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$#', $value)) {
$valid = false;
}
break;
case 'date':
if (!preg_match('#^\d{4}-\d{2}-\d{2}$#', $value)) {
$valid = false;
}
$type = 'xxxx-xx-xx';
break;
case 'time':
if (!preg_match('#^\d{2}:\d{2}:\d{2}$#', $value)) {
$valid = false;
}
break;
case 'utc-millisec':
if ($value < 0) {
$valid = false;
}
break;
case 'color':
$arrColor = array(
'maroon',
'red',
'orange',
'yellow',
'olive',
'green',
'purple',
'fuchsia',
'lime',
'teal',
'aqua',
'blue',
'navy',
'black',
'gray',
'silver',
'white',
);
if (!in_array($value, $arrColor)) {
if (!preg_match('#^\#[0-9A-F]{6}$#', $value) && !preg_match('#^\#[0-9A-F]{3}$#', $value)) {
$valid = false;
}
}
break;
case 'style':
if (!preg_match('#(\.*?)[ ]?:[ ]?(.*?)#', $value)) {
$valid = false;
}
break;
default:
//调用自定义 Validator 类
$functionName = "{$type}Validator";
if(method_exists(__CLASS__,$functionName)){
$valid = self::$functionName($value);
}else{
$valid = false;
}
}
if (!$valid) {
$msg = sprintf(' 无效数值 [%s] ,数值格式必须是: [%s]', $valueName, $type);
throw new Exception($msg, self::ERR_CODE_INPUT_ERROR);
}
return $valid;
}
/**
* ruleTransform @desc 验证规则转换
* 如果是字符串规则则转成数组规则
* @param $rule
* @return array
*/
static public function ruleTransform($rule) {
//字符串验证规则转成数组验证规则
if (is_array($rule)) {
$arrRules = $rule;
} else {
$arrRule = explode('|', $rule);
$arrRules = array();
foreach ($arrRule as $rul) {
$arrRul = explode('=', $rul);
if (count($arrRul) === 1) {
$arrRules[$arrRul[0]] = $arrRul[0];
} else if (count($arrRul) === 2) {
if ($arrRul[0] === 'enum') {
$arrRules[$arrRul[0]] = explode(',', $arrRul[1]);
} else {
$arrRules[$arrRul[0]] = $arrRul[1];
}
}
}
}
return $arrRules;
}
/**
* requiredValidator @desc 不能为空验证
* @param $attribute
* @param bool $trim
* @return bool
*/
public static function requiredValidator($attribute, $trim = true) {
if(is_array($attribute)){
$res = empty($attribute)?false:true;
}elseif(is_string($attribute)){
$attribute = $trim?trim($attribute):$attribute;
$res = ($attribute==='')?false:true;
}else{
$res = empty($attribute)?false:true;
}
return $res;
}
/**
* stringValidator @desc 字符串验证
* @param $attribute
* @param $is
* @return bool
*/
public static function alphanumericValidator($attribute, $is) {
$pattern = "/^[a-zA-Z0-9]{" . $is . "}$/";
return preg_match($pattern, $attribute) === 1;
}
/**
* emailValidator @desc 邮件验证
* @param $attribute
* @param bool $allowName
* @return bool
*/
public static function emailValidator($attribute, $allowName = false) {
$pattern = "/^[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/";
$fullPattern = "/^[^@]*<[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?>$/";
$valid = preg_match($pattern, $attribute) || $allowName && preg_match($fullPattern, $attribute);
return $valid;
}
/**
* speCharValidator @desc 特殊字符验证
* @param $attribute
* @return bool
*/
public static function speCharValidator($attribute) {
$pattern = "/[`~!@#\$%\^&\*\(\)\+=\{\}':;\[\],\.<>|\x{FF01}\x{FFE5}\x{2026}\x{FF08}\x{FF09}\x{2014}\x{3010}\x{3011}\x{2018}\x{2019}\x{201C}\x{201D}\x{FF1A}\x{FF1B}\x{FF0C}\x{3002}\x{3001}\x{FF1F}\x{300A}\x{300B}]+/u";
return preg_match($pattern, $attribute) === 1;
}
/**
* companyPhoneValidator @desc 验证固话
* @param $companyPhone
* @return bool
* @throws Exception
*/
public static function companyPhoneValidator($companyPhone) {
$match_res = preg_match("/^\d{3,4}[|-]\d+$/", $companyPhone);
if (!$match_res) {
throw new Exception('固定电话格式错误', self::ERR_CODE_INPUT_ERROR);
} else {
return true;
}
}
/**
* mobilenumberValidator @desc 验证手机号
* @param $mobilenumber
* @return bool
*/
public static function mobileNumberValidator($mobilenumber) {
$match_res = preg_match("/^1[34578]\d{9}$/", $mobilenumber);
if (!$match_res) {
return false;
} else {
return true;
}
}
/**
* cardValidator @desc 银行卡号
* @param $cardNo
* @return bool
* @throws Exception
*/
public static function cardValidator($cardNo) {
$match_res = preg_match("/^\d{16,19}$/", $cardNo);
if (!$match_res) {
throw new Exception('银行卡号格式错误', self::ERR_CODE_INPUT_ERROR);
} else {
return true;
}
}
/**
* passwordValidator @desc 密码复杂度验证
* @param $passwd
* @param null $prcid
* @throws Exception
*/
public static function passwordValidator($passwd, $prcid = null) {
$match_res = preg_match("/^[0-9]{6}$/", $passwd);
if (!$match_res) {
throw new Exception('密码必须是6为数字', self::ERR_CODE_INPUT_ERROR);
}
$match_res = preg_match("/(\d)(\\1){2}/ie", $passwd);
if ($match_res) {
throw new Exception('密码不可以包含3个或以上相同的数字', self::ERR_CODE_INPUT_ERROR);
}
$arrVarPassWd = array('012', '123', '234', '345', '456', '567', '678', '789', '890');
for ($i = 0; $i < 4; $i++) {
$subpasswd = substr($passwd, $i, 3);
if (strstr('0123456789', $subpasswd) !== false) {
throw new Exception('密码不可以包含3个或以上连续的数字(例如123)', self::ERR_CODE_INPUT_ERROR);
}
if (strstr('9876543210', $subpasswd) !== false) {
throw new Exception('密码不可以包含3个或以上连续的数字(例如123)', self::ERR_CODE_INPUT_ERROR);
}
}
if ($prcid != null && strstr($prcid, $passwd) !== false) {
throw new Exception('密码不可以是身份证号的一部分', self::ERR_CODE_INPUT_ERROR);
}
}
/**
* 图片上传验证
* @param string $base64
* @return boolean
*/
public static function base64ImageValidator($base64Image) {
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64Image)) {
return true;
} else {
return false;
}
}
/**
* xssValidator @desc xss 过滤
* @param $data
* @param int $htmlentities
* @return mixed|string
*/
public static function xssValidator($data, $htmlentities = 0) {
$htmlentities && $data = htmlentities($data, ENT_QUOTES, 'utf-8');
// Fix &entity\n;
$data = str_replace(array('&', '<', '>'), array('&', '<', '>'), $data);
$data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
$data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
// Remove any attribute starting with "on" or xmlns
$data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
// Remove javascript: and vbscript: protocols
$data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"\\\\]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"\\\\]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"\\\\]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"\\\\]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"\\\\]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"\\\\]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
// Remove namespaced elements (we do not need them)
$data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
do {
// Remove really unwanted tags
$old_data = $data;
$data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
} while ($old_data !== $data);
// we are done...
//$data = self::filter_remote_img_type($data, false);
return $data;
}
/**
* @param $strParam 过滤特殊字符
* @return mixed
*/
public static function replaceSpecialChar($strParam) {
$regex = "/\/|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\_|\+|\{|\}|\:|\<|\>|\?|\[|\]|\,|\.|\/|\;|\'|\`|\-|\=|\\\|\|/";
return preg_replace($regex, "", $strParam);
}
/**
* sql 注入 字符串过滤
* @param $strData
* @return mixed
*/
public static function injectCheckMysql($strData) {
return preg_replace('(select|inert|update|delete|from|\'|\/\*|\*|\.\.\/|\.\/|UNION|into|load_file|outfile)', '', $strData);
}
/**
* 检查Refer
* @param string $method //方法名
* @return boolean
* @assert ( 'ajaxcomment' ) == true
*/
public static function checkRefer($method = '') {
$refConfig = Bd_Conf::getAppConf('referer');
//判断checkref 是否开启状态
if ($refConfig['checkRefererSwitch'] != 1) {
return true;
}
//ref 为空 抛出异常
if (empty($_SERVER['HTTP_REFERER'])) {
throw new Exception(self::getMsg(self::ERR_CODE_CHECK_REFER), self::ERR_CODE_CHECK_REFER);
}
//获取conf 信息
if (isset($refConfig['conf'][$method]) && count($refConfig['conf'][$method]) > 0) {
$refList = $refConfig['conf'][$method];
} elseif (isset($refConfig['conf']['default']) && count($refConfig['conf']['default']) > 0) {
$refList = $refConfig['conf']['default'];
} else {
$refList = array();
}
//循环获取list
if (!empty($refList)) {
$ref = &$_SERVER['HTTP_REFERER'];
if (strpos($ref, 'http://') !== 0 && strpos($ref, 'https://') !== 0) {
$ref = 'http://' . $ref;
}
foreach ($refList as $item) {
preg_match($item, $ref, $arrPregMatch);
if (count($arrPregMatch) > 0) {
return true;
}
}
//未匹配上正则 直接抛出异常
throw new Exception(self::getMsg(self::ERR_CODE_CHECK_REFER), self::ERR_CODE_CHECK_REFER);
}
}
}