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 @@