diff --git a/README.md b/README.md index a1c6b51..010320d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - 支持基本的数组检查,数组的子级值检查 - 方便的获取错误信息,验证后的安全数据获取 - 已经内置了40多个常用的验证器[内置验证器](#built-in-validators) -- 规则设置参考自 yii 的。部分规则参考自 laravel +- 规则设置参考 yii. 部分规则参考自 laravel, Respect/Validation - 新增了独立的过滤器 `Inhere\Validate\Filter\Filtration` 用于数据过滤 支持两种规则配置方式: @@ -56,9 +56,9 @@ e.g - 使用 composer 命令 -```bash -composer require inhere/php-validate -// composer require inhere/php-validate ^version // 指定版本 +```php +composer require inhere/php-validate +// composer require inhere/php-validate ^2.2 ``` - 使用 composer.json @@ -97,14 +97,14 @@ class PageRequest extends Validation { return [ ['tagId,title,userId,freeTime', 'required'], - ['tagId', 'size', 'min'=>4, 'max'=>567], // 4<= tagId <=567 - ['title', 'min', 40], + ['tagId', 'size', 'min'=>4, 'max'=>567, 'filter' => 'int'], // 4<= tagId <=567 + ['title', 'min', 40, 'filter' => 'trim'], ['freeTime', 'number'], ['tagId', 'number', 'when' => function($data) { return isset($data['status']) && $data['status'] > 2; }], - ['userId', 'number', 'on' => 'scene1' ], - ['username', 'string', 'on' => 'scene2' ], + ['userId', 'number', 'on' => 'scene1', 'filter' => 'int'], + ['username', 'string', 'on' => 'scene2', 'filter' => 'trim'], ['username', 'regexp' ,'/^[a-z]\w{2,12}$/'], ['title', 'customValidator', 'msg' => '{attr} error msg!' ], // 指定当前规则的消息 ['status', function($status) { // 直接使用闭包验证 @@ -376,8 +376,7 @@ $v = Validation::make($_POST,[ { return [ ['title', 'required' ], - ['tagId', 'number', 'when' => function($data) - { + ['tagId', 'number', 'when' => function($data) { return isset($data['status']) && $data['status'] > 2; }], ]; @@ -514,6 +513,7 @@ public function isFail() // hasError() 的别名方法 public function fail() // hasError() 的别名方法 // 成功通过验证 +public function ok() public function passed() public function isPassed() // passed() 的别名方法 ``` @@ -597,17 +597,19 @@ public function get(string $key, $default = null) 过滤器 | 说明 | 示例 -------|-------------|------------ -`int/integer` | 过滤非法字符并转换为`int`类型 | `['userId', 'number', 'filter' => 'int'],` +`abs` | 返回绝对值 | `['field', 'int', 'filter' => 'abs'],` +`int/integer` | 过滤非法字符并转换为`int`类型 **支持数组** | `['userId', 'number', 'filter' => 'int'],` `float` | 过滤非法字符,保留`float`格式的数据 | `['price', 'float', 'filter' => 'float'],` `string` | 过滤非法字符并转换为`string`类型 | `['userId', 'number', 'filter' => 'string'],` `trim` | 去除首尾空白字符,支持数组。 | `['username', 'min', 4, 'filter' => 'trim'],` +`nl2br` | 转换 `\n` `\r\n` `\r` 为 `
` | `['content', 'string', 'filter' => 'nl2br'],` `lower/lowercase` | 字符串转换为小写 | `['description', 'string', 'filter' => 'lowercase'],` `upper/uppercase` | 字符串转换为大写 | `['title', 'string', 'filter' => 'uppercase'],` `snake/snakeCase` | 字符串转换为蛇形风格 | `['title', 'string', 'filter' => 'snakeCase'],` `camel/camelCase` | 字符串转换为驼峰风格 | `['title', 'string', 'filter' => 'camelCase'],` `timestamp/strToTime` | 字符串日期转换时间戳 | `['pulishedAt', 'number', 'filter' => 'strToTime'],` -`abs` | 返回绝对值 | `['field', 'int', 'filter' => 'abs'],` `url` | URL 过滤,移除所有不符合 URL 的字符 | `['field', 'url', 'filter' => 'url'],` +`str2list/str2array` | 字符串转数组 `'tag0,tag1' -> ['tag0', 'tag1']` | `['tags', 'strList', 'filter' => 'str2array'],` `email` | email 过滤,移除所有不符合 email 的字符 | `['field', 'email', 'filter' => 'email'],` `encoded` | 去除 URL 编码不需要的字符,与 `urlencode()` 函数很类似 | `['imgUrl', 'url', 'filter' => 'encoded'],` `clearTags/stripTags` | 相当于使用 `strip_tags()` | `['content', 'string', 'filter' => 'clearTags'],` @@ -623,11 +625,11 @@ public function get(string $key, $default = null) 验证器 | 说明 | 规则示例 ----------|-------------|------------ -`int/integer` | 验证是否是 int | `['userId', 'int']` -`num/number` | 验证是否是 number | `['userId', 'number']` +`int/integer` | 验证是否是 int **支持范围检查** | `['userId', 'int']` `['userId', 'int', 'min'=>4, 'max'=>16]` +`num/number` | 验证是否是 number **支持范围检查** | `['userId', 'number']` `['userId', 'number', 'min'=>4, 'max'=>16]` `bool/boolean` | 验证是否是 bool | `['open', 'bool']` `float` | 验证是否是 float | `['price', 'float']` -`string` | 验证是否是 string. 支持长度检查 | `['name', 'string']`, `['name', 'string', 'min'=>4, 'max'=>16]` +`string` | 验证是否是 string. **支持长度检查** | `['name', 'string']`, `['name', 'string', 'min'=>4, 'max'=>16]` `url` | 验证是否是 url | `['myUrl', 'url']` `email` | 验证是否是 email | `['userEmail', 'email']` `alpha` | 验证值是否仅包含字母字符 | `['name', 'alpha']` @@ -636,6 +638,7 @@ public function get(string $key, $default = null) `isMap` | 验证值是否是一个非自然数组 map (key - value 形式的) | `['goods', 'isMap']` `isList` | 验证值是否是一个自然数组 list (key是从0自然增长的) | `['tags', 'isList']` `isArray` | 验证是否是数组 | `['goods', 'isArray']` +`hasKey` | 验证数组存在给定的key(s) | `['goods', 'hasKey', 'pear']` `['goods', 'hasKey', ['pear', 'banana']]` `intList` | 验证字段值是否是一个 int list | `['tagIds', 'intList']` `numList` | 验证字段值是否是一个 number list | `['tagIds', 'numList']` `strList` | 验证字段值是否是一个 string list | `['tags', 'strList']` @@ -643,7 +646,10 @@ public function get(string $key, $default = null) `max` | 最大边界值验证 | `['title', 'max', 40]` `size/range/between` | 验证大小范围, 可以支持验证 `int`, `string`, `array` 数据类型 | `['tagId', 'size', 'min'=>4, 'max'=>567]` `length` | 长度验证( 跟 `size`差不多, 但只能验证 `string`, `array` 的长度 | `['username', 'length', 'min' => 5, 'max' => 20]` -`in/enum` | 枚举验证 | `['status', 'in', [1,2,3]` +`fixedSize/fixedLength` | 固定的长度/大小 | `['field', 'fixedSize', 12]` +`startWith` | 值(`string/array`)是以给定的字符串开始 | `['field', 'startWith', 'hell']` +`endWith` | 值(`string/array`)是以给定的字符串结尾 | `['field', 'endWith', 'world']` +`in/enum` | 枚举验证 | `['status', 'in', [1,2,3]]` `notIn` | 枚举验证 | `['status', 'notIn', [4,5,6]]` `mustBe` | 必须是等于给定值 | `['status', 'mustBe', 1]` `notBe` | 不能等于给定值 | `['status', 'notBe', 0]` @@ -663,12 +669,13 @@ public function get(string $key, $default = null) `beforeOrEqualDate` | 字段值必须是小于或等于给定日期的值 | `['publishedAt', 'beforeOrEqualDate', '2017-05-12']` `afterOrEqualDate` | 字段值必须是大于或等于给定日期的值 | `['publishedAt', 'afterOrEqualDate', '2017-05-12']` `afterDate` | 验证字段值必须是给定日期之前的值 | `['publishedAt', 'afterDate', '2017-05-12']` -`json` | 验证是否是json字符串 | `['goods', 'json']` +`json` | 验证是否是json字符串(默认严格验证,必须以`{` `[` 开始) | `['goods', 'json']` `['somedata', 'json', false]` - 非严格,普通字符串`eg 'test'`也会通过 `file` | 验证是否是上传的文件 | `['upFile', 'file']` `image` | 验证是否是上传的图片文件 | `['avatar', 'image']`, 限定后缀名 `['avatar', 'image', 'jpg,png']` `ip` | 验证是否是 IP | `['ipAddr', 'ip']` `ipv4` | 验证是否是 IPv4 | `['ipAddr', 'ipv4']` `ipv6` | 验证是否是 IPv6 | `['ipAddr', 'ipv6']` +`macAddress` | 验证是否是 mac Address | `['field', 'macAddress']` `md5` | 验证是否是 md5 格式的字符串 | `['passwd', 'md5']` `sha1` | 验证是否是 sha1 格式的字符串 | `['passwd', 'sha1']` `color` | 验证是否是html color | `['backgroundColor', 'color']` diff --git a/examples/DataModel.php b/examples/DataModel.php index 7446126..8e8ac16 100644 --- a/examples/DataModel.php +++ b/examples/DataModel.php @@ -11,6 +11,8 @@ class DataModel protected $data = []; + protected $db; + /** * @param array $data * @return $this @@ -21,4 +23,13 @@ public function setData($data) return $this; } + + public function create() + { + if ($this->validate()->fail()) { + return false; + } + + return $this->db->insert($this->getSafeData()); + } } diff --git a/api.md b/examples/api.md similarity index 100% rename from api.md rename to examples/api.md diff --git a/examples/filter.php b/examples/filter.php index cdd2c5c..30fa17d 100644 --- a/examples/filter.php +++ b/examples/filter.php @@ -1,7 +1,31 @@ ' tom ', + 'status' => ' 23 ', + 'word' => 'word', + 'toLower' => 'WORD', + 'title' => 'helloWorld', +]; + +$rules = [ + ['name', 'string|trim'], + ['status', 'trim|int'], + ['word', 'string|trim|upper'], + ['toLower', 'lower'], + ['title', [ + 'string', + 'snake' => ['-'], + 'ucfirst', + ]], +]; + +echo "------------- raw data: -------------\n"; +var_dump($data); + +$cleaned = \Inhere\Validate\Filter\Filtration::make($data, $rules)->filtering(); + +echo "------------- cleaned data: -------------\n"; +var_dump($cleaned); diff --git a/examples/help.md b/examples/help.md new file mode 100644 index 0000000..0beebb0 --- /dev/null +++ b/examples/help.md @@ -0,0 +1,60 @@ +# help + + +```php + + /** + * @link http://php.net/manual/zh/function.filter-input.php + * @param int $type INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV + * @param $varName + * @param array $filter 过滤/验证器 {@link http://php.net/manual/zh/filter.filters.php} + * @param array $options 一个选项的关联数组,或者按位区分的标示。 + * 如果过滤器接受选项,可以通过数组的 "flags" 位去提供这些标示。 + * 如果成功的话返回所请求的变量。 + * 如果成功的话返回所请求的变量。 + * 如果过滤失败则返回 FALSE , + * 如果 varName 不存在的话则返回 NULL 。 + * 如果标示 FILTER_NULL_ON_FAILURE 被使用了,那么当变量不存在时返回 FALSE ,当过滤失败时返回 NULL 。 + */ + public static function input($type, $varName, $filter, array $options = []) + { + } + + public static function multi(array $data, array $filters = []) + { + } + + /** + * @link http://php.net/manual/zh/function.filter-input-array.php + * 检查(验证/过滤)输入数据中的多个变量名 like filter_input_array() + * 当需要获取很多变量却不想重复调用 filter_input()时很有用。 + * @param int $type One of INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV. 要检查的输入数据 + * @param mixed $definition 一个定义参数的数组。 + * 一个有效的键必须是一个包含变量名的string, + * 一个有效的值要么是一个filter type,或者是一个array 指明了过滤器、标示和选项。 + * 如果值是一个数组,那么它的有效的键可以是 : + * filter, 用于指明 filter type, + * flags 用于指明任何想要用于过滤器的标示, + * options 用于指明任何想要用于过滤器的选项。 + * 参考下面的例子来更好的理解这段说明。 + * @param bool $addEmpty 在返回值中添加 NULL 作为不存在的键。 + * 如果成功的话返回一个所请求的变量的数组, + * 如果失败的话返回 FALSE 。 + * 对于数组的值, + * 如果过滤失败则返回 FALSE , + * 如果 variable_name 不存在的话则返回 NULL 。 + * 如果标示 FILTER_NULL_ON_FAILURE 被使用了,那么当变量不存在时返回 FALSE ,当过滤失败时返回 NULL 。 + */ + public static function inputMulti($type, $definition, $addEmpty = true) + { + } + + /** + * 检查变量名是否存在 + * @param int $type One of INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV. 要检查的输入数据 + * @param string $varName Name of a variable to check. 要检查的变量名 + */ + public static function inputHasVar($type, $varName) + { + } +``` \ No newline at end of file diff --git a/src/AbstractValidation.php b/src/AbstractValidation.php index 3d7f24d..0682b16 100644 --- a/src/AbstractValidation.php +++ b/src/AbstractValidation.php @@ -40,8 +40,13 @@ abstract class AbstractValidation implements ValidationInterface * @throws \InvalidArgumentException * @throws \RuntimeException */ - public function __construct(array $data = [], array $rules = [], array $translates = [], $scene = '', $startValidate = false) - { + public function __construct( + array $data = [], + array $rules = [], + array $translates = [], + $scene = '', + $startValidate = false + ) { $this->data = $data; $this->setRules($rules)->setScene($scene)->setTranslates($translates); if ($startValidate) { @@ -59,8 +64,28 @@ public function __construct(array $data = [], array $rules = [], array $translat * @throws \InvalidArgumentException * @throws \RuntimeException */ - public static function make(array $data, array $rules = [], array $translates = [], $scene = '', $startValidate = false) - { + public static function make( + array $data, + array $rules = [], + array $translates = [], + $scene = '', + $startValidate = false + ) { return new static($data, $rules, $translates, $scene, $startValidate); } + + /** + * 创建并且立即开始验证 + * @param array $data + * @param array $rules + * @param array $translates + * @param string $scene + * @return static + * @throws \InvalidArgumentException + * @throws \RuntimeException + */ + public static function makeAndValidate(array $data, array $rules = [], array $translates = [], $scene = '') + { + return new static($data, $rules, $translates, $scene, true); + } } \ No newline at end of file diff --git a/src/FieldValidation.php b/src/FieldValidation.php index 4d0b45d..5574366 100644 --- a/src/FieldValidation.php +++ b/src/FieldValidation.php @@ -9,6 +9,8 @@ namespace Inhere\Validate; +use Inhere\Validate\Utils\Helper; + /** * Class FieldValidation * - one field to many rules. like Laravel framework @@ -50,13 +52,12 @@ protected function collectRules() if (!$scene) { continue; } - $sceneList = \is_string($rule['on']) ? array_map('trim', explode(',', $rule['on'])) : (array)$rule['on']; + $sceneList = \is_string($rule['on']) ? Helper::explode($rule['on']) : (array)$rule['on']; if (!\in_array($scene, $sceneList, true)) { continue; } unset($rule['on']); } - $this->_usedRules[] = $rule; $field = array_shift($rule); if (\is_object($rule[0])) { @@ -70,8 +71,6 @@ protected function collectRules() } } } - - yield []; } /** diff --git a/src/Filter/FilterList.php b/src/Filter/FilterList.php index 5bdf94b..981a156 100644 --- a/src/Filter/FilterList.php +++ b/src/Filter/FilterList.php @@ -1,4 +1,5 @@ + * @param string $str String to transform + * @return string New string + */ + public static function nl2br($str) + { + return str_replace(["\r\n", "\r", "\n"], '
', $str); + } + + /** + * @param string $str + * @param string $sep + * @return array + */ + public static function str2list($str, $sep = ',') + { + return self::str2array($str, $sep); + } + + /** + * var_dump(str2array('34,56,678, 678, 89, ')); + * @param string $str + * @param string $sep + * @return array + */ + public static function str2array($str, $sep = ',') + { + $str = trim($str, "{$sep} "); + if (!$str) { + return []; + } + + return preg_split("/\\s*{$sep}\\s*/", $str, -1, PREG_SPLIT_NO_EMPTY); + } + /** * @see FilterList::string() * {@inheritdoc} @@ -106,8 +149,18 @@ public static function stripped($val, $flags = 0) public static function trim($val) { return \is_array($val) ? array_map(function ($val) { - return \is_string($val) ? trim($val) : $val; - }, $val) : trim((string)$val); + return \is_string($val) ? \trim($val) : $val; + }, $val) : \trim((string)$val); + } + + /** + * clear space + * @param string $val + * @return mixed + */ + public static function clearSpace($val) + { + return str_replace(' ', '', \trim($val)); } /** @@ -272,7 +325,6 @@ public static function stripTags($val, $allowedTags = null) public static function encoded($val, $flags = 0) { $settings = []; - if ((int)$flags !== 0) { $settings['flags'] = (int)$flags; } @@ -302,7 +354,6 @@ public static function quotes($val) public static function specialChars($val, $flags = 0) { $settings = []; - if ((int)$flags !== 0) { $settings['flags'] = (int)$flags; } @@ -329,7 +380,6 @@ public static function escape($val, $flags = 0) public static function fullSpecialChars($val, $flags = 0) { $settings = []; - if ((int)$flags !== 0) { $settings['flags'] = (int)$flags; } @@ -391,7 +441,6 @@ public static function email($val) public static function unsafeRaw($string, $flags = 0) { $settings = []; - if ((int)$flags !== 0) { $settings['flags'] = (int)$flags; } @@ -409,4 +458,4 @@ public static function callback($val, $callback) { return filter_var($val, FILTER_CALLBACK, ['options' => $callback]); } -} +} \ No newline at end of file diff --git a/src/Utils/DataFiltersTrait.php b/src/Utils/DataFiltersTrait.php index baf6188..50072ea 100644 --- a/src/Utils/DataFiltersTrait.php +++ b/src/Utils/DataFiltersTrait.php @@ -23,7 +23,6 @@ trait DataFiltersTrait */ private static $_filters = []; - /** * value sanitize 直接对给的值进行过滤 * @param mixed $value @@ -44,20 +43,17 @@ trait DataFiltersTrait protected function valueFiltering($value, $filters) { $filters = \is_string($filters) ? Helper::explode($filters, '|') : $filters; - foreach ($filters as $key => $filter) { // key is a filter. ['myFilter' => ['arg1', 'arg2']] if (\is_string($key)) { $args = (array)$filter; $value = $this->callStringCallback($key, $value, ...$args); - // closure } elseif (\is_object($filter) && method_exists($filter, '__invoke')) { $value = $filter($value); // string, trim, .... } elseif (\is_string($filter)) { $value = $this->callStringCallback($filter, $value); - // e.g ['Class', 'method'], } else { $value = Helper::call($filter, $value); @@ -78,26 +74,22 @@ protected function callStringCallback($filter, ...$args) if (isset(self::$_filters[$filter])) { $callback = self::$_filters[$filter]; $value = $callback(...$args); - // if $filter is a custom method of the subclass. } elseif (method_exists($this, $filter . 'Filter')) { $filter .= 'Filter'; - $value = $this->$filter(...$args); - + $value = $this->{$filter}(...$args); // $filter is a method of the class 'FilterList' } elseif (method_exists(FilterList::class, $filter)) { $value = FilterList::$filter(...$args); - // it is function name } elseif (\function_exists($filter)) { $value = $filter(...$args); } else { - throw new \InvalidArgumentException("The filter [$filter] don't exists!"); + throw new \InvalidArgumentException("The filter [{$filter}] don't exists!"); } return $value; } - /******************************************************************************* * custom filters ******************************************************************************/ diff --git a/src/Utils/ErrorMessageTrait.php b/src/Utils/ErrorMessageTrait.php index a78964e..8c65075 100644 --- a/src/Utils/ErrorMessageTrait.php +++ b/src/Utils/ErrorMessageTrait.php @@ -22,39 +22,71 @@ trait ErrorMessageTrait public static $messages = [ 'int' => '{attr} must be an integer!', 'integer' => '{attr} must be an integer!', - 'num' => '{attr} must be an integer greater than 0!', 'number' => '{attr} must be an integer greater than 0!', 'bool' => '{attr} must be is boolean!', 'boolean' => '{attr} must be is boolean!', 'float' => '{attr} must be is float!', 'url' => '{attr} is not a url address!', 'email' => '{attr} is not a email address!', 'date' => '{attr} is not a date format!', 'dateFormat' => '{attr} is not in a {value0} date format!', 'ip' => '{attr} is not IP address!', 'ipv4' => '{attr} is not a IPv4 address!', 'ipv6' => '{attr} is not a IPv6 address!', 'required' => 'parameter {attr} is required!', 'length' => ['{attr} length validation is not through!', '{attr} must be an string/array and minimum length is {min}', '{attr} must be an string/array and length range {min} ~ {max}'], + 'num' => '{attr} must be an integer greater than 0!', + 'number' => '{attr} must be an integer greater than 0!', + 'bool' => '{attr} must be is boolean!', + 'boolean' => '{attr} must be is boolean!', + 'float' => '{attr} must be is float!', + 'url' => '{attr} is not a url address!', + 'email' => '{attr} is not a email address!', + 'date' => '{attr} is not a date format!', + 'dateFormat' => '{attr} is not in a {value0} date format!', + 'ip' => '{attr} is not IP address!', + 'ipv4' => '{attr} is not a IPv4 address!', + 'ipv6' => '{attr} is not a IPv6 address!', + 'required' => 'parameter {attr} is required!', + 'length' => [ + '{attr} length validation is not through!', + '{attr} must be an string/array and minimum length is {min}', + '{attr} must be an string/array and length range {min} ~ {max}' + ], 'size' => [ - '{attr} size validation is not through!', - '{attr} must be an integer/string/array and minimum value/length is {min}', - // '{attr} must be an integer/string/array and value/length range {min} ~ {max}', - '{attr} must be in the range {min} ~ {max}', - ], + '{attr} size validation is not through!', + '{attr} must be an integer/string/array and minimum value/length is {min}', + // '{attr} must be an integer/string/array and value/length range {min} ~ {max}', + '{attr} must be in the range {min} ~ {max}', + ], 'range' => [ '{attr} range validation is not through!', '{attr} must be an integer/string/array and minimum value/length is {min}', '{attr} must be an integer/string/array and value/length range {min} ~ {max}' ], - 'between' => ['{attr} between validation is not through!', '{attr} must be an integer/string/array and minimum value/length is {min}', '{attr} must be an integer/string/array and value/length between {min} ~ {max}'], + 'between' => [ + '{attr} between validation is not through!', + '{attr} must be an integer/string/array and minimum value/length is {min}', + '{attr} must be an integer/string/array and value/length between {min} ~ {max}' + ], + 'fixedSize' => '{attr} length must is {value0}', 'min' => '{attr} minimum boundary is {value0}', 'max' => '{attr} maximum boundary is {value0}', 'in' => '{attr} must in ({value0})', 'enum' => '{attr} must in ({value0})', 'notIn' => '{attr} cannot in ({value0})', - 'string' => ['{attr} must be a string', - '{attr} must be a string and minimum length be {min}', '{attr} must be a string and length range must be {min} ~ {max}'], + 'string' => [ + '{attr} must be a string', + '{attr} must be a string and minimum length be {min}', + '{attr} must be a string and length range must be {min} ~ {max}' + ], 'regex' => '{attr} does not match the {value0} conditions', 'regexp' => '{attr} does not match the {value0} conditions', - 'mustBe' => '{attr} must be equals to {value0}', 'notBe' => '{attr} can not be equals to {value0}', - - 'compare' => '{attr} must be equals to {value0}', 'same' => '{attr} must be equals to {value0}', 'equal' => '{attr} must be equals to {value0}', + 'compare' => '{attr} must be equals to {value0}', + 'same' => '{attr} must be equals to {value0}', + 'equal' => '{attr} must be equals to {value0}', 'notEqual' => '{attr} can not be equals to {value0}', - - 'isArray' => '{attr} must be an array', 'isMap' => '{attr} must be an array and is key-value format', 'isList' => '{attr} must be an array of nature', + 'isArray' => '{attr} must be an array', + 'isMap' => '{attr} must be an array and is key-value format', + 'isList' => '{attr} must be an array of nature', 'intList' => '{attr} must be an array and value is all integers', 'numList' => '{attr} must be an array and value is all numbers', - 'strList' => '{attr} must be an array and value is all strings', 'json' => '{attr} must be an json string', 'file' => '{attr} must be an uploaded file', 'image' => '{attr} must be an uploaded image file', 'callback' => '{attr} don\'t pass the test and verify!', '_' => '{attr} validation is not through!']; + 'strList' => '{attr} must be an array and value is all strings', + 'json' => '{attr} must be an json string', + 'file' => '{attr} must be an uploaded file', + 'image' => '{attr} must be an uploaded image file', + 'callback' => '{attr} don\'t pass the test and verify!', + '_' => '{attr} validation is not through!' + ]; /** * attribute field translate list * @var array @@ -66,7 +98,7 @@ trait ErrorMessageTrait * [ * [ field => errorMessage1 ], * [ field => errorMessage2 ], - * [ field2 => errorMessage3 ] + * [ field2 => errorMessage3 ], * ] */ private $_errors = []; @@ -80,16 +112,6 @@ trait ErrorMessageTrait /******************************************************************************* * Errors Information ******************************************************************************/ - /** - * @return $this - */ - public function clearErrors() - { - $this->_errors = []; - - return $this; - } - /** * 是否有错误 * @return boolean @@ -123,6 +145,14 @@ public function failed() return $this->isFail(); } + /** + * @return bool + */ + public function ok() + { + return !$this->isFail(); + } + /** * @return bool */ @@ -156,6 +186,16 @@ public function getErrors() return $this->_errors; } + /** + * @return $this + */ + public function clearErrors() + { + $this->_errors = []; + + return $this; + } + /** * 得到第一个错误信息 * @author inhere @@ -324,19 +364,6 @@ public function getTranslate($attr) { $trans = $this->getTranslates(); - return isset($trans[$attr]) ? $trans[$attr] : Helper::beautifyFieldName($attr, ' '); - } - - /** - * set the attrs translation data - * @deprecated please use setTranslates() instead. - * @param array $attrTrans - * @return $this - */ - public function setAttrTrans(array $attrTrans) - { - $this->_translates = $attrTrans; - - return $this; + return isset($trans[$attr]) ? $trans[$attr] : Helper::beautifyFieldName($attr); } -} +} \ No newline at end of file diff --git a/src/Utils/Helper.php b/src/Utils/Helper.php index 76c5c8d..e5abf67 100644 --- a/src/Utils/Helper.php +++ b/src/Utils/Helper.php @@ -1,4 +1,5 @@ 'image/bmp', 'gif' => 'image/gif', 'ief' => 'image/ief', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpe' => 'image/jpeg', 'png' => 'image/png', 'svg' => 'image/svg+xml', 'ico' => 'image/x-icon']; - public static $imgMimeConstants = [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_WBMP, IMAGETYPE_ICO]; + public static $imgMimeTypes = [ + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'ico' => 'image/x-icon' + ]; + /** + * @var array + */ + public static $imgMimeConstants = [ + IMAGETYPE_GIF, + IMAGETYPE_JPEG, + IMAGETYPE_PNG, + IMAGETYPE_BMP, + IMAGETYPE_WBMP, + IMAGETYPE_ICO + ]; /** * @param string $ext @@ -282,4 +303,4 @@ public static function call($cb, ...$args) } throw new \InvalidArgumentException('The parameter is not a callable'); } -} +} \ No newline at end of file diff --git a/src/Utils/UserAndContextValidatorsTrait.php b/src/Utils/UserAndContextValidatorsTrait.php index 00927cf..f014eaa 100644 --- a/src/Utils/UserAndContextValidatorsTrait.php +++ b/src/Utils/UserAndContextValidatorsTrait.php @@ -376,7 +376,7 @@ public function sameValidator($val, $compareField) return $this->compareValidator($val, $compareField); } - public function equal($val, $compareField) + public function equalValidator($val, $compareField) { return $this->compareValidator($val, $compareField); } @@ -389,9 +389,8 @@ public function equal($val, $compareField) */ public function notEqualValidator($val, $compareField) { - return $compareField && ($val !== $this->get($compareField)); + return $compareField && $val !== $this->get($compareField); } - /******************************************************************************* * getter/setter ******************************************************************************/ diff --git a/src/ValidationTrait.php b/src/ValidationTrait.php index e183e9f..eede161 100644 --- a/src/ValidationTrait.php +++ b/src/ValidationTrait.php @@ -33,21 +33,18 @@ trait ValidationTrait * @var string */ protected $scene = ''; - /** - * Through the validation of the data - * @var array - */ - private $_safeData = []; + /** @var array used rules at current scene */ + protected $_usedRules = []; /** * the rules is by setRules() * @var array */ private $_rules = []; /** - * used rules at current scene + * Through the validation of the data * @var array */ - protected $_usedRules = []; + private $_safeData = []; /** @var bool */ private $_validated = false; /** @var \Closure before validate handler */ @@ -144,24 +141,21 @@ public function validate(array $onlyChecked = null, $stopOnError = null) } $data = $this->data; foreach ($this->collectRules() as $fields => $rule) { - $fields = \is_string($fields) ? array_map('trim', explode(',', $fields)) : (array)$fields; + $fields = \is_string($fields) ? Helper::explode($fields) : (array)$fields; $validator = array_shift($rule); // 为空时是否跳过(非 required 时). 参考自 yii2 $skipOnEmpty = isset($rule['skipOnEmpty']) ? $rule['skipOnEmpty'] : true; - $filters = isset($rule['filter']) ? $rule['filter'] : null; // 使用过滤器 $defMsg = isset($rule['msg']) ? $rule['msg'] : null; // 自定义错误消息 $defValue = isset($rule['default']) ? $rule['default'] : null; // 允许默认值 - // 如何判断属性为空 默认使用 ValidatorList::isEmpty(). 也可自定义 $isEmpty = [ValidatorList::class, 'isEmpty']; if (!empty($rule['isEmpty']) && (\is_string($rule['isEmpty']) || $rule['isEmpty'] instanceof \Closure)) { $isEmpty = $rule['isEmpty']; } - // 验证的前置条件 -- 不满足条件,跳过此条规则 $when = isset($rule['when']) ? $rule['when'] : null; if ($when && $when instanceof \Closure && $when($data, $this) !== true) { @@ -169,9 +163,8 @@ public function validate(array $onlyChecked = null, $stopOnError = null) } // clear all keywords options unset($rule['msg'], $rule['default'], $rule['skipOnEmpty'], $rule['isEmpty'], $rule['when'], $rule['filter']); - // 验证设置, 有一些验证器需要参数。 e.g. size() + // 验证器参数, 有一些验证器需要参数。 e.g. size() $args = $rule; - // 循环检查属性 foreach ($fields as $field) { if (!$field || ($onlyChecked && !\in_array($field, $onlyChecked, true))) { continue; @@ -371,14 +364,12 @@ protected function collectRules() } unset($rule['on']); } - $this->_usedRules[] = $rule; $fields = array_shift($rule); - (yield $fields => $this->prepareRule($rule)); } - yield []; + // } /** @@ -388,26 +379,33 @@ protected function collectRules() protected function prepareRule(array $rule) { $validator = $rule[0]; - switch ($validator) { + case 'num': + case 'number': + case 'string': + case 'length': + // fixed: 当只有 max 时,自动补充一个 min. 字符串最小长度就是 0 + if (isset($rule['max']) && !isset($rule['min'])) { + $rule['min'] = 0; + } + break; + case 'int': case 'size': case 'range': - case 'string': + case 'integer': case 'between': - // fixed: 当只有 max 时,自动补充一个 min. PHP_INT_MIN 在 php 5 不可用 + // fixed: 当只有 max 时,自动补充一个 min if (isset($rule['max']) && !isset($rule['min'])) { - $rule['min'] = - PHP_INT_MAX; + $rule['min'] = PHP_INT_MIN; } break; } return $rule; } - /******************************************************************************* * getter/setter ******************************************************************************/ - /** * @return bool */ @@ -465,7 +463,7 @@ public function getScene() */ public function setScene($scene) { - $this->scene = $scene; + $this->scene = trim($scene); return $this; } @@ -480,6 +478,16 @@ public function atScene($scene) return $this->setScene($scene); } + /** + * alias of the `setScene()` + * @param string $scene + * @return static + */ + public function onScene($scene) + { + return $this->setScene($scene); + } + /** * Get all items in collection * @return array The collection's source data @@ -523,6 +531,16 @@ public function get($key, $default = null) return $this->has($key) ? $this->data[$key] : $default; } + /** + * @param string $key + * @param null $default + * @return mixed + */ + public function getRaw($key, $default = null) + { + return $this->has($key) ? $this->data[$key] : $default; + } + /** * Get data item by key * 支持以 '.' 分割进行子级值获取 eg: $this->get('goods.apple') @@ -557,6 +575,15 @@ public function getValid($key, $default = null) return array_key_exists($key, $this->_safeData) ? $this->_safeData[$key] : $default; } + /** + * @param string $key + * @param mixed $value + */ + public function setSafe($key, $value) + { + $this->_safeData[$key] = $value; + } + /** * @return array */ @@ -565,6 +592,18 @@ public function getSafeData() return $this->_safeData; } + /** + * @param array $safeData + * @param bool $clearOld + */ + public function setSafeData(array $safeData, $clearOld = false) + { + if ($clearOld) { + $this->_safeData = []; + } + $this->_safeData = array_merge($this->_safeData, $safeData); + } + /** * @return array */ @@ -572,4 +611,4 @@ public function getSafeFields() { return array_keys($this->_safeData); } -} +} \ No newline at end of file diff --git a/src/ValidatorList.php b/src/ValidatorList.php index d22e62b..968963c 100644 --- a/src/ValidatorList.php +++ b/src/ValidatorList.php @@ -34,7 +34,6 @@ public static function isEmpty($val) return $val === '' || $val === null || $val === false || $val === []; } - /******************************************************************************* * bool/int/float/string validators ******************************************************************************/ @@ -58,7 +57,7 @@ public static function boolean($val, $default = null, $flags = 0) $settings['flags'] = $flags; } - return filter_var($val, FILTER_VALIDATE_BOOLEAN, $settings); + return (bool)filter_var($val, FILTER_VALIDATE_BOOLEAN, $settings); } /** @@ -72,31 +71,52 @@ public static function bool($val, $default = null, $flags = 0) /** * @param mixed $val 要验证的变量 - * @param array $options 可选的选项设置 + * @param null|integer|float $min 最小值 + * @param null|int|float $max 最大值 * $options = [ * 'default' => 'default value', * 'decimal' => 2 * ] * @param int $flags FILTER_FLAG_ALLOW_THOUSAND - * @return bool + * @return mixed */ - public static function float($val, array $options = [], $flags = 0) + public static function float($val, $min = null, $max = null, $flags = 0) { $settings = []; - if ($options) { - $settings['options'] = $options; - } if ($flags !== 0) { $settings['flags'] = $flags; } + if (filter_var($val, FILTER_VALIDATE_FLOAT, $settings) === false) { + return false; + } + $minIsNum = is_numeric($min); + $maxIsNum = is_numeric($max); + if ($minIsNum && $maxIsNum) { + if ($max > $min) { + $minV = $min; + $maxV = $max; + } else { + $minV = $max; + $maxV = $min; + } + + return $val >= $minV && $val <= $maxV; + } + if ($minIsNum) { + return $val >= $min; + } + if ($maxIsNum) { + return $val <= $max; + } - return filter_var($val, FILTER_VALIDATE_FLOAT, $settings); + return true; } /** - * int 验证 + * int 验证 (所有的最小、最大都是包含边界值的) * @param mixed $val 要验证的变量 - * @param array $options 可选的选项设置 + * @param null|integer $min 最小值 + * @param null|int $max 最大值 * @param int $flags 标志 * FILTER_FLAG_ALLOW_OCTAL - 允许八进制数值 * FILTER_FLAG_ALLOW_HEX - 允许十六进制数值 @@ -108,12 +128,27 @@ public static function float($val, array $options = [], $flags = 0) * // 'default' => 3, // value to return if the filter fails * ] */ - public static function integer($val, array $options = [], $flags = 0) + public static function integer($val, $min = null, $max = null, $flags = 0) { if (!is_numeric($val)) { return false; } - $settings = []; + $options = $settings = []; + $minIsNum = is_numeric($min); + $maxIsNum = is_numeric($max); + if ($minIsNum && $maxIsNum) { + if ($max > $min) { + $options['min_range'] = (int)$min; + $options['max_range'] = (int)$max; + } else { + $options['min_range'] = (int)$max; + $options['max_range'] = (int)$min; + } + } elseif ($minIsNum) { + $options['min_range'] = (int)$min; + } elseif ($maxIsNum) { + $options['max_range'] = (int)$max; + } if ($options) { $settings['options'] = $options; } @@ -128,42 +163,58 @@ public static function integer($val, array $options = [], $flags = 0) * @see ValidatorList::integer() * {@inheritdoc} */ - public static function int($val, array $options = [], $flags = 0) + public static function int($val, $min = null, $max = null, $flags = 0) { - return self::integer($val, $options, $flags); + return self::integer($val, $min, $max, $flags); } /** * check var is a integer and greater than 0 - * @param $val - * @param array $options + * @param mixed $val + * @param null|integer $min 最小值 + * @param null|int $max 最大值 * @param int $flags * @return bool */ - public static function number($val, array $options = [], $flags = 0) + public static function number($val, $min = null, $max = null, $flags = 0) { - return self::integer($val, $options, $flags) && $val > 0; + if (!is_numeric($val)) { + return false; + } + if ($val <= 0) { + return false; + } + + return self::integer($val, $min, $max, $flags); } /** * @see ValidatorList::number() * {@inheritdoc} */ - public static function num($val, array $options = [], $flags = 0) + public static function num($val, $min = null, $max = null, $flags = 0) { - return self::number($val, $options, $flags); + return self::number($val, $min, $max, $flags); } /** * check val is a string * @param mixed $val - * @param int $minLength - * @param null|int $maxLength + * @param int $minLen + * @param null|int $maxLen * @return bool */ - public static function string($val, $minLength = 0, $maxLength = null) + public static function string($val, $minLen = 0, $maxLen = null) { - return !\is_string($val) ? false : self::length($val, $minLength, $maxLength); + if (!\is_string($val)) { + return false; + } + // only type check. + if ($minLen === 0 && $maxLen === null) { + return true; + } + + return self::integer(Helper::strlen($val), $minLen, $maxLen); } /** @@ -212,7 +263,7 @@ public static function alphaDash($val) * @param int|string|array $val 待检测的值。 数字检查数字范围; 字符串、数组则检查长度 * @param null|integer $min 最小值 * @param null|int $max 最大值 - * @return bool + * @return mixed */ public static function size($val, $min = null, $max = null) { @@ -226,26 +277,7 @@ public static function size($val, $min = null, $max = null) } } - $options = []; - $minIsNum = is_numeric($min); - $maxIsNum = is_numeric($max); - if ($minIsNum && $maxIsNum) { - if ($max > $min) { - $options['min_range'] = (int)$min; - $options['max_range'] = (int)$max; - } else { - $options['min_range'] = (int)$max; - $options['max_range'] = (int)$min; - } - } elseif ($minIsNum) { - $options['min_range'] = (int)$min; - } elseif ($maxIsNum) { - $options['max_range'] = (int)$max; - } else { - return false; - } - - return self::integer($val, $options); + return self::integer($val, $min, $max); } /** @@ -266,17 +298,6 @@ public static function range($val, $min = null, $max = null) return self::size($val, $min, $max); } - /** - * 必须是等于给定值 - * @param mixed $val - * @param mixed $excepted - * @return bool - */ - public static function mustBe($val, $excepted) - { - return $val === $excepted; - } - /** * 最小值检查 * @param int|string|array $val @@ -302,21 +323,77 @@ public static function max($val, $maxRange) /** * 字符串/数组长度检查 * @param string|array $val 字符串/数组 - * @param integer $minLength 最小长度 - * @param int $maxLength 最大长度 + * @param integer $minLen 最小长度 + * @param int $maxLen 最大长度 * @return bool */ - public static function length($val, $minLength = 0, $maxLength = null) + public static function length($val, $minLen = 0, $maxLen = null) { if (!\is_string($val) && !\is_array($val)) { return false; } - return self::size($val, $minLength, $maxLength); + return self::size($val, $minLen, $maxLen); + } + + /** + * 固定的长度 + * @param mixed $val + * @param int $size + * @return bool + */ + public static function fixedLength($val, $size) + { + return self::fixedSize($val, $size); + } + + /** + * @param mixed $val + * @param int $size + * @return bool + */ + public static function fixedSize($val, $size) + { + if (!\is_int($val)) { + if (\is_string($val)) { + $val = Helper::strlen(trim($val)); + } elseif (\is_array($val)) { + $val = \count($val); + } else { + return false; + } + } + + return $val === (int)$size; } /******************************************************************************* - * custom validators + * extra string validators ******************************************************************************/ + /** + * 值是否包含给的数据 + * @param string|mixed $val + * @param string|array $needle + * @return bool + */ + public static function contains($val, $needle) + { + if (!$val || !\is_string($val)) { + return false; + } + if (\is_string($needle)) { + return stripos($val, $needle) !== false; + } + if (\is_array($needle)) { + foreach ((array)$needle as $item) { + if (stripos($val, $item) !== false) { + return true; + } + } + } + + return false; + } + /** * 用正则验证数据 * @param string $val 要验证的数据 @@ -422,8 +499,53 @@ public static function ipv6($val) { return self::ip($val, false, FILTER_FLAG_IPV6); } + + /** + * mac Address + * @param string $input + * @return bool + */ + public static function macAddress($input) + { + return !empty($input) && preg_match('/^(([0-9a-fA-F]{2}-){5}|([0-9a-fA-F]{2}:){5})[0-9a-fA-F]{2}$/', $input); + } + + /** + * english chars string + * @param string $val + * @return bool + */ + public static function english($val) + { + if (!$val || !\is_string($val)) { + return false; + } + + return preg_match('/^[A-Za-z]+$/', $val) === 1; + } + + /** + * 验证字段值是否是一个有效的 JSON 字符串。 + * @param mixed $val + * @param bool $strict + * @return bool + */ + public static function json($val, $strict = true) + { + if (!$val || (!\is_string($val) && !method_exists($val, '__toString'))) { + return false; + } + $val = (string)$val; + // must start with: { OR [ + if ($strict && '[' !== $val[0] && '{' !== $val[0]) { + return false; + } + json_decode($val); + + return json_last_error() === JSON_ERROR_NONE; + } /******************************************************************************* - * list/map/enum validators + * array(list/map/enum) validators ******************************************************************************/ /** * 验证值是否是一个数组 @@ -436,7 +558,7 @@ public static function isArray($val) } /** - * 验证值是否是一个非自然数组 map (key - value 形式的) + * 验证值是否是一个非自然数组 map (key不是自然增长的 OR key - value 形式的) * @param mixed $val * @return bool */ @@ -445,20 +567,15 @@ public static function isMap($val) if (!\is_array($val)) { return false; } - /** @var array $val */ - foreach ($val as $k => $v) { - if (\is_string($k)) { - return true; - } - } + $keys = array_keys($val); - return false; + return array_keys($keys) !== $keys; } /** * 验证值是否是一个自然数组 list (key是从0自然增长的) - * @param array|mixed $val + * @param array|mixed $val * @return bool */ public static function isList($val) @@ -466,78 +583,65 @@ public static function isList($val) if (!\is_array($val) || !isset($val[0])) { return false; } - - $prevKey = 0; - /** @var array $val */ - foreach ($val as $k => $v) { - if (!\is_int($k)) { - return false; - } - - if ($k !== $prevKey) { - return false; - } - - $prevKey++; - } + $keys = array_keys($val); - return true; + return array_keys($keys) === $keys; } /** - * 验证字段值是否是一个 int list - * @param array|mixed $val + * 验证字段值是否是一个 int list(key是从0自然增长的, val是数字) + * @param array|mixed $val * @return bool */ public static function intList($val) { - if (!$val || !\is_array($val)) { + if (!\is_array($val) || !isset($val[0])) { return false; } - + $lastK = -1; /** @var array $val */ foreach ($val as $k => $v) { - if (!\is_int($k)) { + if (!\is_int($k) || $k !== $lastK + 1) { return false; } - if (!is_numeric($v)) { return false; } + $lastK = $k; } return true; } /** - * 验证字段值是否是一个 number list - * @param array|mixed $val + * 验证字段值是否是一个 number list(key是从0自然增长的, val是大于0的数字) + * @param array|mixed $val * @return bool */ public static function numList($val) { - if (!$val || !\is_array($val)) { + if (!\is_array($val) || !isset($val[0])) { return false; } - + $lastK = -1; /** @var array $val */ - foreach ($val as $k => $v) { - if (!\is_int($k)) { + foreach ($val as $k => $v) { + if (!\is_int($k) || $k !== $lastK + 1) { return false; } - if (!is_numeric($v) || $v <= 0) { return false; } + $lastK = $k; } return true; } /** - * 验证字段值是否是一个 string list - * @param array|mixed $val + * 验证字段值是否是一个 string list(key是从0自然增长的, val是 string) + * @param array|mixed $val * @return bool */ public static function strList($val) @@ -545,47 +649,45 @@ public static function strList($val) if (!$val || !\is_array($val)) { return false; } - + $lastK = -1; /** @var array $val */ foreach ($val as $k => $v) { - if (!\is_int($k)) { + if (!\is_int($k) || $k !== $lastK + 1) { return false; } - - if (\is_string($v)) { - return true; + if (!\is_string($v)) { + return false; } + $lastK = $k; } - return false; + return true; } /** - * 验证字段值是否是一个有效的 JSON 字符串。 - * @param mixed $val - * @param bool $strict + * @param array|mixed $val + * @param string|int|array $key * @return bool */ - public static function json($val, $strict = true) + public static function hasKey($val, $key) { - if (!$val || (!\is_string($val) && !method_exists($val, '__toString'))) { + if (!$val || !\is_array($val)) { return false; } - - $val = (string)$val; - - // must start with: { OR [ - if ($strict && '[' !== $val[0] && '{' !== $val[0]) { - return false; + if (\is_string($key) || \is_int($key)) { + return array_key_exists($key, $val); } + if (\is_array($key)) { + $keys = array_keys($val); - json_decode($val); + return !array_diff($key, $keys); + } - return json_last_error() === JSON_ERROR_NONE; + return false; } /** - * @param mixed $val + * @param mixed $val * @param array|string $dict * @return bool */ @@ -623,15 +725,70 @@ public static function notIn($val, $dict) return !\in_array($val, (array)$dict, true); } + /******************************************************************************* + * mixed data validators + ******************************************************************************/ + /** + * @param mixed $val + * @param string $start + * @param bool $identical + * @return bool + */ + public static function startWith($val, $start, $identical = true) + { + $start = (string)$start; + if (\is_string($val)) { + return ($identical ? strpos($val, $start) : stripos($val, $start)) === 0; + } + if (\is_array($val)) { + $first = array_shift($val); + + return $identical ? $first === $start : $first == $start; + } + + return false; + } /** * @param mixed $val - * @param mixed $compareVal + * @param string $end + * @param bool $identical * @return bool */ - public static function compare($val, $compareVal) + public static function endWith($val, $end, $identical = true) { - return $val === $compareVal; + $last = null; + $end = (string)$end; + if (\is_string($val)) { + $last = substr($val, -Helper::strlen($end)); + } + if (\is_array($val)) { + $last = array_pop($val); + } + + return $identical ? $last === $end : $last == $end; + } + + /** + * 必须是等于给定值 + * @param mixed $val + * @param mixed $excepted + * @return bool + */ + public static function mustBe($val, $excepted) + { + return $val === $excepted; + } + + /** + * 不能等于给定值 + * @param mixed $val + * @param mixed $excepted + * @return bool + */ + public static function notBe($val, $excepted) + { + return $val !== $excepted; } /******************************************************************************* * date validators @@ -759,7 +916,8 @@ public static function afterOrEqualDate($val, $afterDate) */ public static function isDateFormat($date) { - return (bool)preg_match('/^([\\d]{4})-((0?[\\d])|(1[0-2]))-((0?[\\d])|([1-2][\\d])|(3[01]))( [\\d]{2}:[\\d]{2}:[\\d]{2})?$/', $date); + return (bool)preg_match('/^([\\d]{4})-((0?[\\d])|(1[0-2]))-((0?[\\d])|([1-2][\\d])|(3[01]))( [\\d]{2}:[\\d]{2}:[\\d]{2})?$/', + $date); } /** @@ -769,7 +927,8 @@ public static function isDateFormat($date) */ public static function isDate($date) { - if (!preg_match('/^([\\d]{4})-((?:0?[\\d])|(?:1[0-2]))-((?:0?[\\d])|(?:[1-2][\\d])|(?:3[01]))( [\\d]{2}:[\\d]{2}:[\\d]{2})?$/', $date, $matches)) { + if (!preg_match('/^([\\d]{4})-((?:0?[\\d])|(?:1[0-2]))-((?:0?[\\d])|(?:[1-2][\\d])|(?:3[01]))( [\\d]{2}:[\\d]{2}:[\\d]{2})?$/', + $date, $matches)) { return false; } @@ -928,60 +1087,4 @@ public static function dirName($dir) { return (bool)preg_match('/^[a-zA-Z0-9_.-]*$/', $dir); } - /////////////////////////////////////////// - - /** - * @link http://php.net/manual/zh/function.filter-input.php - * @param int $type INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV - * @param $varName - * @param array $filter 过滤/验证器 {@link http://php.net/manual/zh/filter.filters.php} - * @param array $options 一个选项的关联数组,或者按位区分的标示。 - * 如果过滤器接受选项,可以通过数组的 "flags" 位去提供这些标示。 - * 如果成功的话返回所请求的变量。 - * 如果成功的话返回所请求的变量。 - * 如果过滤失败则返回 FALSE , - * 如果 varName 不存在的话则返回 NULL 。 - * 如果标示 FILTER_NULL_ON_FAILURE 被使用了,那么当变量不存在时返回 FALSE ,当过滤失败时返回 NULL 。 - */ - public static function input($type, $varName, $filter, array $options = []) - { - } - - public static function multi(array $data, array $filters = []) - { - } - - /** - * @link http://php.net/manual/zh/function.filter-input-array.php - * 检查(验证/过滤)输入数据中的多个变量名 like filter_input_array() - * 当需要获取很多变量却不想重复调用 filter_input()时很有用。 - * @param int $type One of INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV. 要检查的输入数据 - * @param mixed $definition 一个定义参数的数组。 - * 一个有效的键必须是一个包含变量名的string, - * 一个有效的值要么是一个filter type,或者是一个array 指明了过滤器、标示和选项。 - * 如果值是一个数组,那么它的有效的键可以是 : - * filter, 用于指明 filter type, - * flags 用于指明任何想要用于过滤器的标示, - * options 用于指明任何想要用于过滤器的选项。 - * 参考下面的例子来更好的理解这段说明。 - * @param bool $addEmpty 在返回值中添加 NULL 作为不存在的键。 - * 如果成功的话返回一个所请求的变量的数组, - * 如果失败的话返回 FALSE 。 - * 对于数组的值, - * 如果过滤失败则返回 FALSE , - * 如果 variable_name 不存在的话则返回 NULL 。 - * 如果标示 FILTER_NULL_ON_FAILURE 被使用了,那么当变量不存在时返回 FALSE ,当过滤失败时返回 NULL 。 - */ - public static function inputMulti($type, $definition, $addEmpty = true) - { - } - - /** - * 检查变量名是否存在 - * @param int $type One of INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV. 要检查的输入数据 - * @param string $varName Name of a variable to check. 要检查的变量名 - */ - public static function inputHasVar($type, $varName) - { - } -} +} \ No newline at end of file diff --git a/tests/FilterListTest.php b/tests/FilterListTest.php index 548da0a..3035712 100644 --- a/tests/FilterListTest.php +++ b/tests/FilterListTest.php @@ -12,6 +12,8 @@ public function testInteger() { $this->assertSame(FilterList::integer('456'), 456); $this->assertSame(FilterList::integer('4df5dg6'), 456); + + $this->assertSame(FilterList::integer(['34', '67gh']), [34, 67]); } public function testAbs() @@ -25,6 +27,9 @@ public function testFloat() // $this->assertSame(FilterList::float('4.45'), 4.45); $this->assertSame(FilterList::float(45.78), 45.78); $this->assertSame(FilterList::float(-45.78), -45.78); + + $this->assertSame(FilterList::float(45.78678, 2), 45.79); + $this->assertSame(FilterList::float(457, 2), 457.00); } public function testTrim() @@ -43,4 +48,12 @@ public function testUppercase() { $this->assertSame(FilterList::uppercase('Test'), 'TEST'); } + + public function testStr2list() + { + $this->assertSame(FilterList::str2array('a,b,c,'), ['a', 'b', 'c']); + $this->assertSame(FilterList::str2array('a, b ,c,'), ['a', 'b', 'c']); + $this->assertSame(FilterList::str2array(' a, b , c'), ['a', 'b', 'c']); + $this->assertSame(FilterList::str2array(' a,, b ,, c'), ['a', 'b', 'c']); + } } diff --git a/tests/RuleValidationTest.php b/tests/RuleValidationTest.php index df5d79d..d4e5aec 100644 --- a/tests/RuleValidationTest.php +++ b/tests/RuleValidationTest.php @@ -55,6 +55,45 @@ public function testValidatePassed() $this->assertSame($v->getSafe('tagId'), 35); } + public function testCollectRules() + { + $data = [ + 'userId' => 234, + 'tagId' => 35, + 'freeTime' => '1456767657', + 'status' => 2, + 'name' => '1234a2', + 'goods' => [ + 'apple' => 34, + 'pear' => 50, + ], + + ]; + + $rules = [ + ['tagId,userId,freeTime', 'required'], + ['tagId,userId,freeTime', 'number', 'on' => 's1'], + ['tagId', 'size', 'max'=> 567, 'min'=> 4, 'on' => 's2'], + ['name', 'string', 'on' => 's2'], + ['goods.pear', 'max', 60], + ]; + + $v = RuleValidation::make($data, $rules)->validate(); + + $this->assertTrue($v->passed()); + $this->assertCount(2, $v->getUsedRules()); + + $v = RuleValidation::make($data, $rules)->atScene('s1')->validate(); + + $this->assertTrue($v->passed()); + $this->assertCount(3, $v->getUsedRules()); + + $v = RuleValidation::make($data, $rules)->atScene('s2')->validate(); + + $this->assertTrue($v->passed()); + $this->assertCount(4, $v->getUsedRules()); + } + public function testValidateFailed() { $rules = $this->someRules(); @@ -95,6 +134,28 @@ public function testValidateString() $this->assertEquals($v->getSafe('user_name'), $val); } + public function testValidateJson() + { + $v = Validation::make([ + 'log_level' => 'debug', + 'log_data' => '[23]', + 'log_data1' => '234', + ], [ + ['log_level, log_data', 'required'], + ['log_level, log_data', 'string'], + ['log_data', 'json'], + ['log_data1', 'json', false], + ])->validate(); + + // var_dump($v->getErrors()); + $this->assertTrue($v->passed()); + $this->assertFalse($v->failed()); + + $errors = $v->getErrors(); + $this->assertEmpty($errors); + $this->assertCount(0, $errors); + } + protected function someRules() { return [ diff --git a/tests/ValidatorListTest.php b/tests/ValidatorListTest.php index 7681ab0..6fdccb6 100644 --- a/tests/ValidatorListTest.php +++ b/tests/ValidatorListTest.php @@ -8,7 +8,6 @@ */ class ValidatorListTest extends TestCase { - public function testIsEmpty() { $this->assertFalse(ValidatorList::isEmpty(1)); @@ -21,16 +20,44 @@ public function testIsEmpty() $this->assertTrue(ValidatorList::isEmpty(' ')); } + public function testBool() + { + $this->assertFalse(ValidatorList::bool(null)); + $this->assertFalse(ValidatorList::bool([])); + + $this->assertTrue(ValidatorList::bool('1')); + $this->assertTrue(ValidatorList::bool(1)); + } + + public function testFloat() + { + $this->assertFalse(ValidatorList::float(null)); + $this->assertFalse(ValidatorList::float(false)); + $this->assertFalse(ValidatorList::float('')); + + $this->assertTrue(ValidatorList::float('1')); + $this->assertTrue(ValidatorList::float('1.0')); + $this->assertTrue(ValidatorList::float(3.4)); + $this->assertTrue(ValidatorList::float(-3.4)); + $this->assertTrue(ValidatorList::float(3.4, 3.1)); + $this->assertTrue(ValidatorList::float(3.4, 3.1, 5.4)); + $this->assertTrue(ValidatorList::float(3.4, null, 5.4)); + } + public function testInteger() { $this->assertFalse(ValidatorList::integer('')); $this->assertFalse(ValidatorList::integer(null)); $this->assertFalse(ValidatorList::integer(false)); + $this->assertFalse(ValidatorList::integer(2, 5)); $this->assertTrue(ValidatorList::integer(0)); $this->assertTrue(ValidatorList::integer(1)); $this->assertTrue(ValidatorList::integer(-1)); $this->assertTrue(ValidatorList::integer('1')); + $this->assertTrue(ValidatorList::integer(-2, -3, 1)); + $this->assertTrue(ValidatorList::integer(2, 2, 5)); + $this->assertTrue(ValidatorList::integer(2, null, 5)); } public function testNumber() @@ -40,6 +67,9 @@ public function testNumber() $this->assertFalse(ValidatorList::number(0)); $this->assertTrue(ValidatorList::number(1)); + $this->assertTrue(ValidatorList::number(10, 5, 12)); + $this->assertTrue(ValidatorList::number(10, 12, 5)); + $this->assertTrue(ValidatorList::number(10, null, 12)); } public function testString() @@ -177,6 +207,7 @@ public function testIsList() $this->assertFalse(ValidatorList::isList([])); $this->assertFalse(ValidatorList::isList(['a' => 'v'])); $this->assertFalse(ValidatorList::isList(['value', 'a' => 'v'])); + $this->assertFalse(ValidatorList::isList([3 => 'abc'])); $this->assertTrue(ValidatorList::isList(['abc'])); $this->assertTrue(ValidatorList::isList(['abc', 565, null])); @@ -187,8 +218,10 @@ public function testIntList() $this->assertFalse(ValidatorList::intList('test')); $this->assertFalse(ValidatorList::intList([])); $this->assertFalse(ValidatorList::intList(['a', 'v'])); + $this->assertFalse(ValidatorList::intList(['a', 456])); $this->assertFalse(ValidatorList::intList(['a' => 'v'])); $this->assertFalse(ValidatorList::intList(['value', 'a' => 'v'])); + $this->assertFalse(ValidatorList::intList([2 => '343', 45])); $this->assertTrue(ValidatorList::intList(['343', 45])); $this->assertTrue(ValidatorList::intList([565, 3234, -56])); @@ -202,6 +235,7 @@ public function testNumList() $this->assertFalse(ValidatorList::numList(['a' => 'v'])); $this->assertFalse(ValidatorList::numList(['value', 'a' => 'v'])); $this->assertFalse(ValidatorList::numList([565, 3234, -56])); + $this->assertFalse(ValidatorList::numList([2 => 56, 45])); $this->assertTrue(ValidatorList::numList(['343', 45])); $this->assertTrue(ValidatorList::numList([56, 45])); @@ -212,9 +246,68 @@ public function testStrList() $this->assertFalse(ValidatorList::strList('test')); $this->assertFalse(ValidatorList::strList([])); $this->assertFalse(ValidatorList::strList(['a' => 'v'])); + $this->assertFalse(ValidatorList::strList(['value', 'a' => 'v'])); + $this->assertFalse(ValidatorList::strList(['abc', 565])); + $this->assertFalse(ValidatorList::strList(['abc', 565, null])); + + $this->assertTrue(ValidatorList::strList(['abc', 'efg'])); + } + + public function testJson() + { + $this->assertFalse(ValidatorList::json('test')); + $this->assertFalse(ValidatorList::json([])); + + $this->assertFalse(ValidatorList::json(123)); + $this->assertFalse(ValidatorList::json('123')); + $this->assertTrue(ValidatorList::json('123', false)); + + $this->assertFalse(ValidatorList::json('{aa: 34}')); + + $this->assertTrue(ValidatorList::json('{}')); + $this->assertTrue(ValidatorList::json('[]')); + $this->assertTrue(ValidatorList::json('{"aa": 34}')); + } + + public function testHasKey() + { + $this->assertFalse(ValidatorList::hasKey('hello, world', 'all')); + $this->assertFalse(ValidatorList::hasKey(['a' => 'v0', 'b' => 'v1', 'c' => 'v2'], 'd')); + $this->assertFalse(ValidatorList::hasKey(['a' => 'v0', 'b' => 'v1', 'c' => 'v2'], ['c', 'd'])); + + $this->assertTrue(ValidatorList::hasKey(['a' => 'v0', 'b' => 'v1', 'c' => 'v2'], 'b')); + $this->assertTrue(ValidatorList::hasKey(['a' => 'v0', 'b' => 'v1', 'c' => 'v2'], ['b', 'c'])); + } + + public function testContains() + { + $this->assertFalse(ValidatorList::contains('hello, world', 'all')); + + $this->assertTrue(ValidatorList::contains('hello, world', 'llo')); + $this->assertTrue(ValidatorList::contains('hello, world', ['llo', 'wor'])); + } + + public function testStartWith() + { + $this->assertFalse(ValidatorList::startWith('hello, world', 'ell')); + + $this->assertTrue(ValidatorList::startWith('hello, world', 'hell')); + $this->assertTrue(ValidatorList::startWith(['hello', 'world'], 'hello')); + } + + public function testEndWith() + { + $this->assertFalse(ValidatorList::endWith('hello, world', 'ell')); + + $this->assertTrue(ValidatorList::endWith('hello, world', 'world')); + $this->assertTrue(ValidatorList::endWith(['hello', 'world'], 'world')); + } + + public function testDate() + { + $this->assertFalse(ValidatorList::date('hello')); - $this->assertTrue(ValidatorList::strList(['value', 'a' => 'v'])); - $this->assertTrue(ValidatorList::strList(['abc'])); - $this->assertTrue(ValidatorList::strList(['abc', 565, null])); + $this->assertTrue(ValidatorList::date(170526)); + $this->assertTrue(ValidatorList::date('20170526')); } } diff --git a/tests/boot.php b/tests/boot.php index fe9000a..b8dfdcb 100644 --- a/tests/boot.php +++ b/tests/boot.php @@ -1,6 +1,6 @@