You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1372 lines
42 KiB

  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. namespace think;
  12. use think\exception\ClassNotFoundException;
  13. class Validate
  14. {
  15. // 实例
  16. protected static $instance;
  17. // 自定义的验证类型
  18. protected static $type = [];
  19. // 验证类型别名
  20. protected $alias = [
  21. '>' => 'gt', '>=' => 'egt', '<' => 'lt', '<=' => 'elt', '=' => 'eq', 'same' => 'eq',
  22. ];
  23. // 当前验证的规则
  24. protected $rule = [];
  25. // 验证提示信息
  26. protected $message = [];
  27. // 验证字段描述
  28. protected $field = [];
  29. // 验证规则默认提示信息
  30. protected static $typeMsg = [
  31. 'require' => ':attribute require',
  32. 'number' => ':attribute must be numeric',
  33. 'integer' => ':attribute must be integer',
  34. 'float' => ':attribute must be float',
  35. 'boolean' => ':attribute must be bool',
  36. 'email' => ':attribute not a valid email address',
  37. 'array' => ':attribute must be a array',
  38. 'accepted' => ':attribute must be yes,on or 1',
  39. 'date' => ':attribute not a valid datetime',
  40. 'file' => ':attribute not a valid file',
  41. 'image' => ':attribute not a valid image',
  42. 'alpha' => ':attribute must be alpha',
  43. 'alphaNum' => ':attribute must be alpha-numeric',
  44. 'alphaDash' => ':attribute must be alpha-numeric, dash, underscore',
  45. 'activeUrl' => ':attribute not a valid domain or ip',
  46. 'chs' => ':attribute must be chinese',
  47. 'chsAlpha' => ':attribute must be chinese or alpha',
  48. 'chsAlphaNum' => ':attribute must be chinese,alpha-numeric',
  49. 'chsDash' => ':attribute must be chinese,alpha-numeric,underscore, dash',
  50. 'url' => ':attribute not a valid url',
  51. 'ip' => ':attribute not a valid ip',
  52. 'dateFormat' => ':attribute must be dateFormat of :rule',
  53. 'in' => ':attribute must be in :rule',
  54. 'notIn' => ':attribute be notin :rule',
  55. 'between' => ':attribute must between :1 - :2',
  56. 'notBetween' => ':attribute not between :1 - :2',
  57. 'length' => 'size of :attribute must be :rule',
  58. 'max' => 'max size of :attribute must be :rule',
  59. 'min' => 'min size of :attribute must be :rule',
  60. 'after' => ':attribute cannot be less than :rule',
  61. 'before' => ':attribute cannot exceed :rule',
  62. 'afterWith' => ':attribute cannot be less than :rule',
  63. 'beforeWith' => ':attribute cannot exceed :rule',
  64. 'expire' => ':attribute not within :rule',
  65. 'allowIp' => 'access IP is not allowed',
  66. 'denyIp' => 'access IP denied',
  67. 'confirm' => ':attribute out of accord with :2',
  68. 'different' => ':attribute cannot be same with :2',
  69. 'egt' => ':attribute must greater than or equal :rule',
  70. 'gt' => ':attribute must greater than :rule',
  71. 'elt' => ':attribute must less than or equal :rule',
  72. 'lt' => ':attribute must less than :rule',
  73. 'eq' => ':attribute must equal :rule',
  74. 'unique' => ':attribute has exists',
  75. 'regex' => ':attribute not conform to the rules',
  76. 'method' => 'invalid Request method',
  77. 'token' => 'invalid token',
  78. 'fileSize' => 'filesize not match',
  79. 'fileExt' => 'extensions to upload is not allowed',
  80. 'fileMime' => 'mimetype to upload is not allowed',
  81. ];
  82. // 当前验证场景
  83. protected $currentScene = null;
  84. // 正则表达式 regex = ['zip'=>'\d{6}',...]
  85. protected $regex = [];
  86. // 验证场景 scene = ['edit'=>'name1,name2,...']
  87. protected $scene = [];
  88. // 验证失败错误信息
  89. protected $error = [];
  90. // 批量验证
  91. protected $batch = false;
  92. /**
  93. * 构造函数
  94. * @access public
  95. * @param array $rules 验证规则
  96. * @param array $message 验证提示信息
  97. * @param array $field 验证字段描述信息
  98. */
  99. public function __construct(array $rules = [], $message = [], $field = [])
  100. {
  101. $this->rule = array_merge($this->rule, $rules);
  102. $this->message = array_merge($this->message, $message);
  103. $this->field = array_merge($this->field, $field);
  104. }
  105. /**
  106. * 实例化验证
  107. * @access public
  108. * @param array $rules 验证规则
  109. * @param array $message 验证提示信息
  110. * @param array $field 验证字段描述信息
  111. * @return Validate
  112. */
  113. public static function make($rules = [], $message = [], $field = [])
  114. {
  115. if (is_null(self::$instance)) {
  116. self::$instance = new self($rules, $message, $field);
  117. }
  118. return self::$instance;
  119. }
  120. /**
  121. * 添加字段验证规则
  122. * @access protected
  123. * @param string|array $name 字段名称或者规则数组
  124. * @param mixed $rule 验证规则
  125. * @return Validate
  126. */
  127. public function rule($name, $rule = '')
  128. {
  129. if (is_array($name)) {
  130. $this->rule = array_merge($this->rule, $name);
  131. } else {
  132. $this->rule[$name] = $rule;
  133. }
  134. return $this;
  135. }
  136. /**
  137. * 注册验证(类型)规则
  138. * @access public
  139. * @param string $type 验证规则类型
  140. * @param mixed $callback callback方法(或闭包)
  141. * @return void
  142. */
  143. public static function extend($type, $callback = null)
  144. {
  145. if (is_array($type)) {
  146. self::$type = array_merge(self::$type, $type);
  147. } else {
  148. self::$type[$type] = $callback;
  149. }
  150. }
  151. /**
  152. * 设置验证规则的默认提示信息
  153. * @access protected
  154. * @param string|array $type 验证规则类型名称或者数组
  155. * @param string $msg 验证提示信息
  156. * @return void
  157. */
  158. public static function setTypeMsg($type, $msg = null)
  159. {
  160. if (is_array($type)) {
  161. self::$typeMsg = array_merge(self::$typeMsg, $type);
  162. } else {
  163. self::$typeMsg[$type] = $msg;
  164. }
  165. }
  166. /**
  167. * 设置提示信息
  168. * @access public
  169. * @param string|array $name 字段名称
  170. * @param string $message 提示信息
  171. * @return Validate
  172. */
  173. public function message($name, $message = '')
  174. {
  175. if (is_array($name)) {
  176. $this->message = array_merge($this->message, $name);
  177. } else {
  178. $this->message[$name] = $message;
  179. }
  180. return $this;
  181. }
  182. /**
  183. * 设置验证场景
  184. * @access public
  185. * @param string|array $name 场景名或者场景设置数组
  186. * @param mixed $fields 要验证的字段
  187. * @return Validate
  188. */
  189. public function scene($name, $fields = null)
  190. {
  191. if (is_array($name)) {
  192. $this->scene = array_merge($this->scene, $name);
  193. }if (is_null($fields)) {
  194. // 设置当前场景
  195. $this->currentScene = $name;
  196. } else {
  197. // 设置验证场景
  198. $this->scene[$name] = $fields;
  199. }
  200. return $this;
  201. }
  202. /**
  203. * 判断是否存在某个验证场景
  204. * @access public
  205. * @param string $name 场景名
  206. * @return bool
  207. */
  208. public function hasScene($name)
  209. {
  210. return isset($this->scene[$name]);
  211. }
  212. /**
  213. * 设置批量验证
  214. * @access public
  215. * @param bool $batch 是否批量验证
  216. * @return Validate
  217. */
  218. public function batch($batch = true)
  219. {
  220. $this->batch = $batch;
  221. return $this;
  222. }
  223. /**
  224. * 数据自动验证
  225. * @access public
  226. * @param array $data 数据
  227. * @param mixed $rules 验证规则
  228. * @param string $scene 验证场景
  229. * @return bool
  230. */
  231. public function check($data, $rules = [], $scene = '')
  232. {
  233. $this->error = [];
  234. if (empty($rules)) {
  235. // 读取验证规则
  236. $rules = $this->rule;
  237. }
  238. // 分析验证规则
  239. $scene = $this->getScene($scene);
  240. if (is_array($scene)) {
  241. // 处理场景验证字段
  242. $change = [];
  243. $array = [];
  244. foreach ($scene as $k => $val) {
  245. if (is_numeric($k)) {
  246. $array[] = $val;
  247. } else {
  248. $array[] = $k;
  249. $change[$k] = $val;
  250. }
  251. }
  252. }
  253. foreach ($rules as $key => $item) {
  254. // field => rule1|rule2... field=>['rule1','rule2',...]
  255. if (is_numeric($key)) {
  256. // [field,rule1|rule2,msg1|msg2]
  257. $key = $item[0];
  258. $rule = $item[1];
  259. if (isset($item[2])) {
  260. $msg = is_string($item[2]) ? explode('|', $item[2]) : $item[2];
  261. } else {
  262. $msg = [];
  263. }
  264. } else {
  265. $rule = $item;
  266. $msg = [];
  267. }
  268. if (strpos($key, '|')) {
  269. // 字段|描述 用于指定属性名称
  270. list($key, $title) = explode('|', $key);
  271. } else {
  272. $title = isset($this->field[$key]) ? $this->field[$key] : $key;
  273. }
  274. // 场景检测
  275. if (!empty($scene)) {
  276. if ($scene instanceof \Closure && !call_user_func_array($scene, [$key, $data])) {
  277. continue;
  278. } elseif (is_array($scene)) {
  279. if (!in_array($key, $array)) {
  280. continue;
  281. } elseif (isset($change[$key])) {
  282. // 重载某个验证规则
  283. $rule = $change[$key];
  284. }
  285. }
  286. }
  287. // 获取数据 支持二维数组
  288. $value = $this->getDataValue($data, $key);
  289. // 字段验证
  290. if ($rule instanceof \Closure) {
  291. // 匿名函数验证 支持传入当前字段和所有字段两个数据
  292. $result = call_user_func_array($rule, [$value, $data]);
  293. } else {
  294. $result = $this->checkItem($key, $value, $rule, $data, $title, $msg);
  295. }
  296. if (true !== $result) {
  297. // 没有返回true 则表示验证失败
  298. if (!empty($this->batch)) {
  299. // 批量验证
  300. if (is_array($result)) {
  301. $this->error = array_merge($this->error, $result);
  302. } else {
  303. $this->error[$key] = $result;
  304. }
  305. } else {
  306. $this->error = $result;
  307. return false;
  308. }
  309. }
  310. }
  311. return !empty($this->error) ? false : true;
  312. }
  313. /**
  314. * 根据验证规则验证数据
  315. * @access protected
  316. * @param mixed $value 字段值
  317. * @param mixed $rules 验证规则
  318. * @return bool
  319. */
  320. protected function checkRule($value, $rules)
  321. {
  322. if ($rules instanceof \Closure) {
  323. return call_user_func_array($rules, [$value]);
  324. } elseif (is_string($rules)) {
  325. $rules = explode('|', $rules);
  326. }
  327. foreach ($rules as $key => $rule) {
  328. if ($rule instanceof \Closure) {
  329. $result = call_user_func_array($rule, [$value]);
  330. } else {
  331. // 判断验证类型
  332. list($type, $rule) = $this->getValidateType($key, $rule);
  333. $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type];
  334. $result = call_user_func_array($callback, [$value, $rule]);
  335. }
  336. if (true !== $result) {
  337. return $result;
  338. }
  339. }
  340. return true;
  341. }
  342. /**
  343. * 验证单个字段规则
  344. * @access protected
  345. * @param string $field 字段名
  346. * @param mixed $value 字段值
  347. * @param mixed $rules 验证规则
  348. * @param array $data 数据
  349. * @param string $title 字段描述
  350. * @param array $msg 提示信息
  351. * @return mixed
  352. */
  353. protected function checkItem($field, $value, $rules, $data, $title = '', $msg = [])
  354. {
  355. // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...]
  356. if (is_string($rules)) {
  357. $rules = explode('|', $rules);
  358. }
  359. $i = 0;
  360. foreach ($rules as $key => $rule) {
  361. if ($rule instanceof \Closure) {
  362. $result = call_user_func_array($rule, [$value, $data]);
  363. $info = is_numeric($key) ? '' : $key;
  364. } else {
  365. // 判断验证类型
  366. list($type, $rule, $info) = $this->getValidateType($key, $rule);
  367. // 如果不是require 有数据才会行验证
  368. if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
  369. // 验证类型
  370. $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type];
  371. // 验证数据
  372. $result = call_user_func_array($callback, [$value, $rule, $data, $field, $title]);
  373. } else {
  374. $result = true;
  375. }
  376. }
  377. if (false === $result) {
  378. // 验证失败 返回错误信息
  379. if (isset($msg[$i])) {
  380. $message = $msg[$i];
  381. if (is_string($message) && strpos($message, '{%') === 0) {
  382. $message = Lang::get(substr($message, 2, -1));
  383. }
  384. } else {
  385. $message = $this->getRuleMsg($field, $title, $info, $rule);
  386. }
  387. return $message;
  388. } elseif (true !== $result) {
  389. // 返回自定义错误信息
  390. if (is_string($result) && false !== strpos($result, ':')) {
  391. $result = str_replace([':attribute', ':rule'], [$title, (string) $rule], $result);
  392. }
  393. return $result;
  394. }
  395. $i++;
  396. }
  397. return $result;
  398. }
  399. /**
  400. * 获取当前验证类型及规则
  401. * @access public
  402. * @param mixed $key
  403. * @param mixed $rule
  404. * @return array
  405. */
  406. protected function getValidateType($key, $rule)
  407. {
  408. // 判断验证类型
  409. if (!is_numeric($key)) {
  410. return [$key, $rule, $key];
  411. }
  412. if (strpos($rule, ':')) {
  413. list($type, $rule) = explode(':', $rule, 2);
  414. if (isset($this->alias[$type])) {
  415. // 判断别名
  416. $type = $this->alias[$type];
  417. }
  418. $info = $type;
  419. } elseif (method_exists($this, $rule)) {
  420. $type = $rule;
  421. $info = $rule;
  422. $rule = '';
  423. } else {
  424. $type = 'is';
  425. $info = $rule;
  426. }
  427. return [$type, $rule, $info];
  428. }
  429. /**
  430. * 验证是否和某个字段的值一致
  431. * @access protected
  432. * @param mixed $value 字段值
  433. * @param mixed $rule 验证规则
  434. * @param array $data 数据
  435. * @param string $field 字段名
  436. * @return bool
  437. */
  438. protected function confirm($value, $rule, $data, $field = '')
  439. {
  440. if ('' == $rule) {
  441. if (strpos($field, '_confirm')) {
  442. $rule = strstr($field, '_confirm', true);
  443. } else {
  444. $rule = $field . '_confirm';
  445. }
  446. }
  447. return $this->getDataValue($data, $rule) === $value;
  448. }
  449. /**
  450. * 验证是否和某个字段的值是否不同
  451. * @access protected
  452. * @param mixed $value 字段值
  453. * @param mixed $rule 验证规则
  454. * @param array $data 数据
  455. * @return bool
  456. */
  457. protected function different($value, $rule, $data)
  458. {
  459. return $this->getDataValue($data, $rule) != $value;
  460. }
  461. /**
  462. * 验证是否大于等于某个值
  463. * @access protected
  464. * @param mixed $value 字段值
  465. * @param mixed $rule 验证规则
  466. * @param array $data 数据
  467. * @return bool
  468. */
  469. protected function egt($value, $rule, $data)
  470. {
  471. $val = $this->getDataValue($data, $rule);
  472. return !is_null($val) && $value >= $val;
  473. }
  474. /**
  475. * 验证是否大于某个值
  476. * @access protected
  477. * @param mixed $value 字段值
  478. * @param mixed $rule 验证规则
  479. * @param array $data 数据
  480. * @return bool
  481. */
  482. protected function gt($value, $rule, $data)
  483. {
  484. $val = $this->getDataValue($data, $rule);
  485. return !is_null($val) && $value > $val;
  486. }
  487. /**
  488. * 验证是否小于等于某个值
  489. * @access protected
  490. * @param mixed $value 字段值
  491. * @param mixed $rule 验证规则
  492. * @param array $data 数据
  493. * @return bool
  494. */
  495. protected function elt($value, $rule, $data)
  496. {
  497. $val = $this->getDataValue($data, $rule);
  498. return !is_null($val) && $value <= $val;
  499. }
  500. /**
  501. * 验证是否小于某个值
  502. * @access protected
  503. * @param mixed $value 字段值
  504. * @param mixed $rule 验证规则
  505. * @param array $data 数据
  506. * @return bool
  507. */
  508. protected function lt($value, $rule, $data)
  509. {
  510. $val = $this->getDataValue($data, $rule);
  511. return !is_null($val) && $value < $val;
  512. }
  513. /**
  514. * 验证是否等于某个值
  515. * @access protected
  516. * @param mixed $value 字段值
  517. * @param mixed $rule 验证规则
  518. * @return bool
  519. */
  520. protected function eq($value, $rule)
  521. {
  522. return $value == $rule;
  523. }
  524. /**
  525. * 验证字段值是否为有效格式
  526. * @access protected
  527. * @param mixed $value 字段值
  528. * @param string $rule 验证规则
  529. * @param array $data 验证数据
  530. * @return bool
  531. */
  532. protected function is($value, $rule, $data = [])
  533. {
  534. switch ($rule) {
  535. case 'require':
  536. // 必须
  537. $result = !empty($value) || '0' == $value;
  538. break;
  539. case 'accepted':
  540. // 接受
  541. $result = in_array($value, ['1', 'on', 'yes']);
  542. break;
  543. case 'date':
  544. // 是否是一个有效日期
  545. $result = false !== strtotime($value);
  546. break;
  547. case 'alpha':
  548. // 只允许字母
  549. $result = $this->regex($value, '/^[A-Za-z]+$/');
  550. break;
  551. case 'alphaNum':
  552. // 只允许字母和数字
  553. $result = $this->regex($value, '/^[A-Za-z0-9]+$/');
  554. break;
  555. case 'alphaDash':
  556. // 只允许字母、数字和下划线 破折号
  557. $result = $this->regex($value, '/^[A-Za-z0-9\-\_]+$/');
  558. break;
  559. case 'chs':
  560. // 只允许汉字
  561. $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}]+$/u');
  562. break;
  563. case 'chsAlpha':
  564. // 只允许汉字、字母
  565. $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u');
  566. break;
  567. case 'chsAlphaNum':
  568. // 只允许汉字、字母和数字
  569. $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+$/u');
  570. break;
  571. case 'chsDash':
  572. // 只允许汉字、字母、数字和下划线_及破折号-
  573. $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9\_\-]+$/u');
  574. break;
  575. case 'activeUrl':
  576. // 是否为有效的网址
  577. $result = checkdnsrr($value);
  578. break;
  579. case 'ip':
  580. // 是否为IP地址
  581. $result = $this->filter($value, [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6]);
  582. break;
  583. case 'url':
  584. // 是否为一个URL地址
  585. $result = $this->filter($value, FILTER_VALIDATE_URL);
  586. break;
  587. case 'float':
  588. // 是否为float
  589. $result = $this->filter($value, FILTER_VALIDATE_FLOAT);
  590. break;
  591. case 'number':
  592. $result = is_numeric($value);
  593. break;
  594. case 'integer':
  595. // 是否为整型
  596. $result = $this->filter($value, FILTER_VALIDATE_INT);
  597. break;
  598. case 'email':
  599. // 是否为邮箱地址
  600. $result = $this->filter($value, FILTER_VALIDATE_EMAIL);
  601. break;
  602. case 'boolean':
  603. // 是否为布尔值
  604. $result = in_array($value, [true, false, 0, 1, '0', '1'], true);
  605. break;
  606. case 'array':
  607. // 是否为数组
  608. $result = is_array($value);
  609. break;
  610. case 'file':
  611. $result = $value instanceof File;
  612. break;
  613. case 'image':
  614. $result = $value instanceof File && in_array($this->getImageType($value->getRealPath()), [1, 2, 3, 6]);
  615. break;
  616. case 'token':
  617. $result = $this->token($value, '__token__', $data);
  618. break;
  619. default:
  620. if (isset(self::$type[$rule])) {
  621. // 注册的验证规则
  622. $result = call_user_func_array(self::$type[$rule], [$value]);
  623. } else {
  624. // 正则验证
  625. $result = $this->regex($value, $rule);
  626. }
  627. }
  628. return $result;
  629. }
  630. // 判断图像类型
  631. protected function getImageType($image)
  632. {
  633. if (function_exists('exif_imagetype')) {
  634. return exif_imagetype($image);
  635. } else {
  636. try {
  637. $info = getimagesize($image);
  638. return $info ? $info[2] : false;
  639. } catch (\Exception $e) {
  640. return false;
  641. }
  642. }
  643. }
  644. /**
  645. * 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型
  646. * @access protected
  647. * @param mixed $value 字段值
  648. * @param mixed $rule 验证规则
  649. * @return bool
  650. */
  651. protected function activeUrl($value, $rule)
  652. {
  653. if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) {
  654. $rule = 'MX';
  655. }
  656. return checkdnsrr($value, $rule);
  657. }
  658. /**
  659. * 验证是否有效IP
  660. * @access protected
  661. * @param mixed $value 字段值
  662. * @param mixed $rule 验证规则 ipv4 ipv6
  663. * @return bool
  664. */
  665. protected function ip($value, $rule)
  666. {
  667. if (!in_array($rule, ['ipv4', 'ipv6'])) {
  668. $rule = 'ipv4';
  669. }
  670. return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]);
  671. }
  672. /**
  673. * 验证上传文件后缀
  674. * @access protected
  675. * @param mixed $file 上传文件
  676. * @param mixed $rule 验证规则
  677. * @return bool
  678. */
  679. protected function fileExt($file, $rule)
  680. {
  681. if (is_array($file)) {
  682. foreach ($file as $item) {
  683. if (!($item instanceof File) || !$item->checkExt($rule)) {
  684. return false;
  685. }
  686. }
  687. return true;
  688. } elseif ($file instanceof File) {
  689. return $file->checkExt($rule);
  690. } else {
  691. return false;
  692. }
  693. }
  694. /**
  695. * 验证上传文件类型
  696. * @access protected
  697. * @param mixed $file 上传文件
  698. * @param mixed $rule 验证规则
  699. * @return bool
  700. */
  701. protected function fileMime($file, $rule)
  702. {
  703. if (is_array($file)) {
  704. foreach ($file as $item) {
  705. if (!($item instanceof File) || !$item->checkMime($rule)) {
  706. return false;
  707. }
  708. }
  709. return true;
  710. } elseif ($file instanceof File) {
  711. return $file->checkMime($rule);
  712. } else {
  713. return false;
  714. }
  715. }
  716. /**
  717. * 验证上传文件大小
  718. * @access protected
  719. * @param mixed $file 上传文件
  720. * @param mixed $rule 验证规则
  721. * @return bool
  722. */
  723. protected function fileSize($file, $rule)
  724. {
  725. if (is_array($file)) {
  726. foreach ($file as $item) {
  727. if (!($item instanceof File) || !$item->checkSize($rule)) {
  728. return false;
  729. }
  730. }
  731. return true;
  732. } elseif ($file instanceof File) {
  733. return $file->checkSize($rule);
  734. } else {
  735. return false;
  736. }
  737. }
  738. /**
  739. * 验证图片的宽高及类型
  740. * @access protected
  741. * @param mixed $file 上传文件
  742. * @param mixed $rule 验证规则
  743. * @return bool
  744. */
  745. protected function image($file, $rule)
  746. {
  747. if (!($file instanceof File)) {
  748. return false;
  749. }
  750. if ($rule) {
  751. $rule = explode(',', $rule);
  752. list($width, $height, $type) = getimagesize($file->getRealPath());
  753. if (isset($rule[2])) {
  754. $imageType = strtolower($rule[2]);
  755. if ('jpeg' == $imageType) {
  756. $imageType = 'jpg';
  757. }
  758. if (image_type_to_extension($type, false) != $imageType) {
  759. return false;
  760. }
  761. }
  762. list($w, $h) = $rule;
  763. return $w == $width && $h == $height;
  764. } else {
  765. return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]);
  766. }
  767. }
  768. /**
  769. * 验证请求类型
  770. * @access protected
  771. * @param mixed $value 字段值
  772. * @param mixed $rule 验证规则
  773. * @return bool
  774. */
  775. protected function method($value, $rule)
  776. {
  777. $method = Request::instance()->method();
  778. return strtoupper($rule) == $method;
  779. }
  780. /**
  781. * 验证时间和日期是否符合指定格式
  782. * @access protected
  783. * @param mixed $value 字段值
  784. * @param mixed $rule 验证规则
  785. * @return bool
  786. */
  787. protected function dateFormat($value, $rule)
  788. {
  789. $info = date_parse_from_format($rule, $value);
  790. return 0 == $info['warning_count'] && 0 == $info['error_count'];
  791. }
  792. /**
  793. * 验证是否唯一
  794. * @access protected
  795. * @param mixed $value 字段值
  796. * @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名
  797. * @param array $data 数据
  798. * @param string $field 验证字段名
  799. * @return bool
  800. */
  801. protected function unique($value, $rule, $data, $field)
  802. {
  803. if (is_string($rule)) {
  804. $rule = explode(',', $rule);
  805. }
  806. if (false !== strpos($rule[0], '\\')) {
  807. // 指定模型类
  808. $db = new $rule[0];
  809. } else {
  810. try {
  811. $db = Loader::model($rule[0]);
  812. } catch (ClassNotFoundException $e) {
  813. $db = Db::name($rule[0]);
  814. }
  815. }
  816. $key = isset($rule[1]) ? $rule[1] : $field;
  817. if (strpos($key, '^')) {
  818. // 支持多个字段验证
  819. $fields = explode('^', $key);
  820. foreach ($fields as $key) {
  821. if (isset($data[$key])) {
  822. $map[$key] = $data[$key];
  823. }
  824. }
  825. } elseif (strpos($key, '=')) {
  826. parse_str($key, $map);
  827. } elseif (isset($data[$field])) {
  828. $map[$key] = $data[$field];
  829. } else {
  830. $map = [];
  831. }
  832. $pk = isset($rule[3]) ? $rule[3] : $db->getPk();
  833. if (is_string($pk)) {
  834. if (isset($rule[2])) {
  835. $map[$pk] = ['neq', $rule[2]];
  836. } elseif (isset($data[$pk])) {
  837. $map[$pk] = ['neq', $data[$pk]];
  838. }
  839. }
  840. if ($db->where($map)->field($pk)->find()) {
  841. return false;
  842. }
  843. return true;
  844. }
  845. /**
  846. * 使用行为类验证
  847. * @access protected
  848. * @param mixed $value 字段值
  849. * @param mixed $rule 验证规则
  850. * @param array $data 数据
  851. * @return mixed
  852. */
  853. protected function behavior($value, $rule, $data)
  854. {
  855. return Hook::exec($rule, '', $data);
  856. }
  857. /**
  858. * 使用filter_var方式验证
  859. * @access protected
  860. * @param mixed $value 字段值
  861. * @param mixed $rule 验证规则
  862. * @return bool
  863. */
  864. protected function filter($value, $rule)
  865. {
  866. if (is_string($rule) && strpos($rule, ',')) {
  867. list($rule, $param) = explode(',', $rule);
  868. } elseif (is_array($rule)) {
  869. $param = isset($rule[1]) ? $rule[1] : null;
  870. $rule = $rule[0];
  871. } else {
  872. $param = null;
  873. }
  874. return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
  875. }
  876. /**
  877. * 验证某个字段等于某个值的时候必须
  878. * @access protected
  879. * @param mixed $value 字段值
  880. * @param mixed $rule 验证规则
  881. * @param array $data 数据
  882. * @return bool
  883. */
  884. protected function requireIf($value, $rule, $data)
  885. {
  886. list($field, $val) = explode(',', $rule);
  887. if ($this->getDataValue($data, $field) == $val) {
  888. return !empty($value) || '0' == $value;
  889. } else {
  890. return true;
  891. }
  892. }
  893. /**
  894. * 通过回调方法验证某个字段是否必须
  895. * @access protected
  896. * @param mixed $value 字段值
  897. * @param mixed $rule 验证规则
  898. * @param array $data 数据
  899. * @return bool
  900. */
  901. protected function requireCallback($value, $rule, $data)
  902. {
  903. $result = call_user_func_array($rule, [$value, $data]);
  904. if ($result) {
  905. return !empty($value) || '0' == $value;
  906. } else {
  907. return true;
  908. }
  909. }
  910. /**
  911. * 验证某个字段有值的情况下必须
  912. * @access protected
  913. * @param mixed $value 字段值
  914. * @param mixed $rule 验证规则
  915. * @param array $data 数据
  916. * @return bool
  917. */
  918. protected function requireWith($value, $rule, $data)
  919. {
  920. $val = $this->getDataValue($data, $rule);
  921. if (!empty($val)) {
  922. return !empty($value) || '0' == $value;
  923. } else {
  924. return true;
  925. }
  926. }
  927. /**
  928. * 验证是否在范围内
  929. * @access protected
  930. * @param mixed $value 字段值
  931. * @param mixed $rule 验证规则
  932. * @return bool
  933. */
  934. protected function in($value, $rule)
  935. {
  936. return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
  937. }
  938. /**
  939. * 验证是否不在某个范围
  940. * @access protected
  941. * @param mixed $value 字段值
  942. * @param mixed $rule 验证规则
  943. * @return bool
  944. */
  945. protected function notIn($value, $rule)
  946. {
  947. return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
  948. }
  949. /**
  950. * between验证数据
  951. * @access protected
  952. * @param mixed $value 字段值
  953. * @param mixed $rule 验证规则
  954. * @return bool
  955. */
  956. protected function between($value, $rule)
  957. {
  958. if (is_string($rule)) {
  959. $rule = explode(',', $rule);
  960. }
  961. list($min, $max) = $rule;
  962. return $value >= $min && $value <= $max;
  963. }
  964. /**
  965. * 使用notbetween验证数据
  966. * @access protected
  967. * @param mixed $value 字段值
  968. * @param mixed $rule 验证规则
  969. * @return bool
  970. */
  971. protected function notBetween($value, $rule)
  972. {
  973. if (is_string($rule)) {
  974. $rule = explode(',', $rule);
  975. }
  976. list($min, $max) = $rule;
  977. return $value < $min || $value > $max;
  978. }
  979. /**
  980. * 验证数据长度
  981. * @access protected
  982. * @param mixed $value 字段值
  983. * @param mixed $rule 验证规则
  984. * @return bool
  985. */
  986. protected function length($value, $rule)
  987. {
  988. if (is_array($value)) {
  989. $length = count($value);
  990. } elseif ($value instanceof File) {
  991. $length = $value->getSize();
  992. } else {
  993. $length = mb_strlen((string) $value);
  994. }
  995. if (strpos($rule, ',')) {
  996. // 长度区间
  997. list($min, $max) = explode(',', $rule);
  998. return $length >= $min && $length <= $max;
  999. } else {
  1000. // 指定长度
  1001. return $length == $rule;
  1002. }
  1003. }
  1004. /**
  1005. * 验证数据最大长度
  1006. * @access protected
  1007. * @param mixed $value 字段值
  1008. * @param mixed $rule 验证规则
  1009. * @return bool
  1010. */
  1011. protected function max($value, $rule)
  1012. {
  1013. if (is_array($value)) {
  1014. $length = count($value);
  1015. } elseif ($value instanceof File) {
  1016. $length = $value->getSize();
  1017. } else {
  1018. $length = mb_strlen((string) $value);
  1019. }
  1020. return $length <= $rule;
  1021. }
  1022. /**
  1023. * 验证数据最小长度
  1024. * @access protected
  1025. * @param mixed $value 字段值
  1026. * @param mixed $rule 验证规则
  1027. * @return bool
  1028. */
  1029. protected function min($value, $rule)
  1030. {
  1031. if (is_array($value)) {
  1032. $length = count($value);
  1033. } elseif ($value instanceof File) {
  1034. $length = $value->getSize();
  1035. } else {
  1036. $length = mb_strlen((string) $value);
  1037. }
  1038. return $length >= $rule;
  1039. }
  1040. /**
  1041. * 验证日期
  1042. * @access protected
  1043. * @param mixed $value 字段值
  1044. * @param mixed $rule 验证规则
  1045. * @param array $data 数据
  1046. * @return bool
  1047. */
  1048. protected function after($value, $rule, $data)
  1049. {
  1050. return strtotime($value) >= strtotime($rule);
  1051. }
  1052. /**
  1053. * 验证日期
  1054. * @access protected
  1055. * @param mixed $value 字段值
  1056. * @param mixed $rule 验证规则
  1057. * @param array $data 数据
  1058. * @return bool
  1059. */
  1060. protected function before($value, $rule, $data)
  1061. {
  1062. return strtotime($value) <= strtotime($rule);
  1063. }
  1064. /**
  1065. * 验证日期字段
  1066. * @access protected
  1067. * @param mixed $value 字段值
  1068. * @param mixed $rule 验证规则
  1069. * @param array $data 数据
  1070. * @return bool
  1071. */
  1072. protected function afterWith($value, $rule, $data)
  1073. {
  1074. $rule = $this->getDataValue($data, $rule);
  1075. return !is_null($rule) && strtotime($value) >= strtotime($rule);
  1076. }
  1077. /**
  1078. * 验证日期字段
  1079. * @access protected
  1080. * @param mixed $value 字段值
  1081. * @param mixed $rule 验证规则
  1082. * @param array $data 数据
  1083. * @return bool
  1084. */
  1085. protected function beforeWith($value, $rule, $data)
  1086. {
  1087. $rule = $this->getDataValue($data, $rule);
  1088. return !is_null($rule) && strtotime($value) <= strtotime($rule);
  1089. }
  1090. /**
  1091. * 验证有效期
  1092. * @access protected
  1093. * @param mixed $value 字段值
  1094. * @param mixed $rule 验证规则
  1095. * @return bool
  1096. */
  1097. protected function expire($value, $rule)
  1098. {
  1099. if (is_string($rule)) {
  1100. $rule = explode(',', $rule);
  1101. }
  1102. list($start, $end) = $rule;
  1103. if (!is_numeric($start)) {
  1104. $start = strtotime($start);
  1105. }
  1106. if (!is_numeric($end)) {
  1107. $end = strtotime($end);
  1108. }
  1109. return $_SERVER['REQUEST_TIME'] >= $start && $_SERVER['REQUEST_TIME'] <= $end;
  1110. }
  1111. /**
  1112. * 验证IP许可
  1113. * @access protected
  1114. * @param string $value 字段值
  1115. * @param mixed $rule 验证规则
  1116. * @return mixed
  1117. */
  1118. protected function allowIp($value, $rule)
  1119. {
  1120. return in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule));
  1121. }
  1122. /**
  1123. * 验证IP禁用
  1124. * @access protected
  1125. * @param string $value 字段值
  1126. * @param mixed $rule 验证规则
  1127. * @return mixed
  1128. */
  1129. protected function denyIp($value, $rule)
  1130. {
  1131. return !in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule));
  1132. }
  1133. /**
  1134. * 使用正则验证数据
  1135. * @access protected
  1136. * @param mixed $value 字段值
  1137. * @param mixed $rule 验证规则 正则规则或者预定义正则名
  1138. * @return mixed
  1139. */
  1140. protected function regex($value, $rule)
  1141. {
  1142. if (isset($this->regex[$rule])) {
  1143. $rule = $this->regex[$rule];
  1144. }
  1145. if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
  1146. // 不是正则表达式则两端补上/
  1147. $rule = '/^' . $rule . '$/';
  1148. }
  1149. return is_scalar($value) && 1 === preg_match($rule, (string) $value);
  1150. }
  1151. /**
  1152. * 验证表单令牌
  1153. * @access protected
  1154. * @param mixed $value 字段值
  1155. * @param mixed $rule 验证规则
  1156. * @param array $data 数据
  1157. * @return bool
  1158. */
  1159. protected function token($value, $rule, $data)
  1160. {
  1161. $rule = !empty($rule) ? $rule : '__token__';
  1162. if (!isset($data[$rule]) || !Session::has($rule)) {
  1163. // 令牌数据无效
  1164. return false;
  1165. }
  1166. // 令牌验证
  1167. if (isset($data[$rule]) && Session::get($rule) === $data[$rule]) {
  1168. // 防止重复提交
  1169. Session::delete($rule); // 验证完成销毁session
  1170. return true;
  1171. }
  1172. // 开启TOKEN重置
  1173. Session::delete($rule);
  1174. return false;
  1175. }
  1176. // 获取错误信息
  1177. public function getError()
  1178. {
  1179. return $this->error;
  1180. }
  1181. /**
  1182. * 获取数据值
  1183. * @access protected
  1184. * @param array $data 数据
  1185. * @param string $key 数据标识 支持二维
  1186. * @return mixed
  1187. */
  1188. protected function getDataValue($data, $key)
  1189. {
  1190. if (is_numeric($key)) {
  1191. $value = $key;
  1192. } elseif (strpos($key, '.')) {
  1193. // 支持二维数组验证
  1194. list($name1, $name2) = explode('.', $key);
  1195. $value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null;
  1196. } else {
  1197. $value = isset($data[$key]) ? $data[$key] : null;
  1198. }
  1199. return $value;
  1200. }
  1201. /**
  1202. * 获取验证规则的错误提示信息
  1203. * @access protected
  1204. * @param string $attribute 字段英文名
  1205. * @param string $title 字段描述名
  1206. * @param string $type 验证规则名称
  1207. * @param mixed $rule 验证规则数据
  1208. * @return string
  1209. */
  1210. protected function getRuleMsg($attribute, $title, $type, $rule)
  1211. {
  1212. if (isset($this->message[$attribute . '.' . $type])) {
  1213. $msg = $this->message[$attribute . '.' . $type];
  1214. } elseif (isset($this->message[$attribute][$type])) {
  1215. $msg = $this->message[$attribute][$type];
  1216. } elseif (isset($this->message[$attribute])) {
  1217. $msg = $this->message[$attribute];
  1218. } elseif (isset(self::$typeMsg[$type])) {
  1219. $msg = self::$typeMsg[$type];
  1220. } elseif (0 === strpos($type, 'require')) {
  1221. $msg = self::$typeMsg['require'];
  1222. } else {
  1223. $msg = $title . Lang::get('not conform to the rules');
  1224. }
  1225. if (is_string($msg) && 0 === strpos($msg, '{%')) {
  1226. $msg = Lang::get(substr($msg, 2, -1));
  1227. } elseif (Lang::has($msg)) {
  1228. $msg = Lang::get($msg);
  1229. }
  1230. if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) {
  1231. // 变量替换
  1232. if (is_string($rule) && strpos($rule, ',')) {
  1233. $array = array_pad(explode(',', $rule), 3, '');
  1234. } else {
  1235. $array = array_pad([], 3, '');
  1236. }
  1237. $msg = str_replace(
  1238. [':attribute', ':rule', ':1', ':2', ':3'],
  1239. [$title, (string) $rule, $array[0], $array[1], $array[2]],
  1240. $msg);
  1241. }
  1242. return $msg;
  1243. }
  1244. /**
  1245. * 获取数据验证的场景
  1246. * @access protected
  1247. * @param string $scene 验证场景
  1248. * @return array
  1249. */
  1250. protected function getScene($scene = '')
  1251. {
  1252. if (empty($scene)) {
  1253. // 读取指定场景
  1254. $scene = $this->currentScene;
  1255. }
  1256. if (!empty($scene) && isset($this->scene[$scene])) {
  1257. // 如果设置了验证适用场景
  1258. $scene = $this->scene[$scene];
  1259. if (is_string($scene)) {
  1260. $scene = explode(',', $scene);
  1261. }
  1262. } else {
  1263. $scene = [];
  1264. }
  1265. return $scene;
  1266. }
  1267. public static function __callStatic($method, $params)
  1268. {
  1269. $class = self::make();
  1270. if (method_exists($class, $method)) {
  1271. return call_user_func_array([$class, $method], $params);
  1272. } else {
  1273. throw new \BadMethodCallException('method not exists:' . __CLASS__ . '->' . $method);
  1274. }
  1275. }
  1276. }