Bez popisu
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.

Validate.php 42KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385
  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->ey_getFilename()), [1, 2, 3, 6, 15, 18, 8080]); // by 小虎哥
  615. // $result = $value instanceof File && in_array($this->getImageType($value->getRealPath()), [1, 2, 3, 6, 15]);
  616. break;
  617. case 'token':
  618. $result = $this->token($value, '__token__', $data);
  619. break;
  620. default:
  621. if (isset(self::$type[$rule])) {
  622. // 注册的验证规则
  623. $result = call_user_func_array(self::$type[$rule], [$value]);
  624. } else {
  625. // 正则验证
  626. $result = $this->regex($value, $rule);
  627. }
  628. }
  629. return $result;
  630. }
  631. // 判断图像类型
  632. protected function getImageType($image)
  633. {
  634. if (function_exists('exif_imagetype')) {
  635. return exif_imagetype($image);
  636. } else {
  637. try {
  638. $info = @getimagesize($image);
  639. if ($info) {
  640. return $info[2];
  641. } else {
  642. if (function_exists('simplexml_load_file')) {
  643. $svgXML = simplexml_load_file($image);
  644. $xmlattributes = $svgXML->attributes();
  645. if (is_object($xmlattributes)) {
  646. return 8080;
  647. }
  648. }
  649. return false;
  650. }
  651. } catch (\Exception $e) {
  652. return false;
  653. }
  654. }
  655. }
  656. /**
  657. * 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型
  658. * @access protected
  659. * @param mixed $value 字段值
  660. * @param mixed $rule 验证规则
  661. * @return bool
  662. */
  663. protected function activeUrl($value, $rule)
  664. {
  665. if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) {
  666. $rule = 'MX';
  667. }
  668. return checkdnsrr($value, $rule);
  669. }
  670. /**
  671. * 验证是否有效IP
  672. * @access protected
  673. * @param mixed $value 字段值
  674. * @param mixed $rule 验证规则 ipv4 ipv6
  675. * @return bool
  676. */
  677. protected function ip($value, $rule)
  678. {
  679. if (!in_array($rule, ['ipv4', 'ipv6'])) {
  680. $rule = 'ipv4';
  681. }
  682. return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]);
  683. }
  684. /**
  685. * 验证上传文件后缀
  686. * @access protected
  687. * @param mixed $file 上传文件
  688. * @param mixed $rule 验证规则
  689. * @return bool
  690. */
  691. protected function fileExt($file, $rule)
  692. {
  693. if (is_array($file)) {
  694. foreach ($file as $item) {
  695. if (!($item instanceof File) || !$item->checkExt($rule)) {
  696. return false;
  697. }
  698. }
  699. return true;
  700. } elseif ($file instanceof File) {
  701. return $file->checkExt($rule);
  702. } else {
  703. return false;
  704. }
  705. }
  706. /**
  707. * 验证上传文件类型
  708. * @access protected
  709. * @param mixed $file 上传文件
  710. * @param mixed $rule 验证规则
  711. * @return bool
  712. */
  713. protected function fileMime($file, $rule)
  714. {
  715. if (is_array($file)) {
  716. foreach ($file as $item) {
  717. if (!($item instanceof File) || !$item->checkMime($rule)) {
  718. return false;
  719. }
  720. }
  721. return true;
  722. } elseif ($file instanceof File) {
  723. return $file->checkMime($rule);
  724. } else {
  725. return false;
  726. }
  727. }
  728. /**
  729. * 验证上传文件大小
  730. * @access protected
  731. * @param mixed $file 上传文件
  732. * @param mixed $rule 验证规则
  733. * @return bool
  734. */
  735. protected function fileSize($file, $rule)
  736. {
  737. if (is_array($file)) {
  738. foreach ($file as $item) {
  739. if (!($item instanceof File) || !$item->checkSize($rule)) {
  740. return false;
  741. }
  742. }
  743. return true;
  744. } elseif ($file instanceof File) {
  745. return $file->checkSize($rule);
  746. } else {
  747. return false;
  748. }
  749. }
  750. /**
  751. * 验证图片的宽高及类型
  752. * @access protected
  753. * @param mixed $file 上传文件
  754. * @param mixed $rule 验证规则
  755. * @return bool
  756. */
  757. protected function image($file, $rule)
  758. {
  759. if (!($file instanceof File)) {
  760. return false;
  761. }
  762. if ($rule) {
  763. $rule = explode(',', $rule);
  764. list($width, $height, $type) = getimagesize($file->ey_getFilename()); // by 小虎哥
  765. // list($width, $height, $type) = getimagesize($file->getRealPath());
  766. if (isset($rule[2])) {
  767. $imageType = strtolower($rule[2]);
  768. if ('jpeg' == $imageType) {
  769. $imageType = 'jpg';
  770. }
  771. if (image_type_to_extension($type, false) != $imageType) {
  772. return false;
  773. }
  774. }
  775. list($w, $h) = $rule;
  776. return $w == $width && $h == $height;
  777. } else {
  778. return in_array($this->getImageType($file->ey_getFilename()), [1, 2, 3, 6, 15, 18, 8080]); // by 小虎哥
  779. // return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6, 15]);
  780. }
  781. }
  782. /**
  783. * 验证请求类型
  784. * @access protected
  785. * @param mixed $value 字段值
  786. * @param mixed $rule 验证规则
  787. * @return bool
  788. */
  789. protected function method($value, $rule)
  790. {
  791. $method = Request::instance()->method();
  792. return strtoupper($rule) == $method;
  793. }
  794. /**
  795. * 验证时间和日期是否符合指定格式
  796. * @access protected
  797. * @param mixed $value 字段值
  798. * @param mixed $rule 验证规则
  799. * @return bool
  800. */
  801. protected function dateFormat($value, $rule)
  802. {
  803. $info = date_parse_from_format($rule, $value);
  804. return 0 == $info['warning_count'] && 0 == $info['error_count'];
  805. }
  806. /**
  807. * 验证是否唯一
  808. * @access protected
  809. * @param mixed $value 字段值
  810. * @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名
  811. * @param array $data 数据
  812. * @param string $field 验证字段名
  813. * @return bool
  814. */
  815. protected function unique($value, $rule, $data, $field)
  816. {
  817. if (is_string($rule)) {
  818. $rule = explode(',', $rule);
  819. }
  820. if (false !== strpos($rule[0], '\\')) {
  821. // 指定模型类
  822. $db = new $rule[0];
  823. } else {
  824. try {
  825. $db = Loader::model($rule[0]);
  826. } catch (ClassNotFoundException $e) {
  827. $db = Db::name($rule[0]);
  828. }
  829. }
  830. $key = isset($rule[1]) ? $rule[1] : $field;
  831. if (strpos($key, '^')) {
  832. // 支持多个字段验证
  833. $fields = explode('^', $key);
  834. foreach ($fields as $key) {
  835. if (isset($data[$key])) {
  836. $map[$key] = $data[$key];
  837. }
  838. }
  839. } elseif (strpos($key, '=')) {
  840. parse_str($key, $map);
  841. } elseif (isset($data[$field])) {
  842. $map[$key] = $data[$field];
  843. } else {
  844. $map = [];
  845. }
  846. $pk = isset($rule[3]) ? $rule[3] : $db->getPk();
  847. if (is_string($pk)) {
  848. if (isset($rule[2])) {
  849. $map[$pk] = ['neq', $rule[2]];
  850. } elseif (isset($data[$pk])) {
  851. $map[$pk] = ['neq', $data[$pk]];
  852. }
  853. }
  854. if ($db->where($map)->field($pk)->find()) {
  855. return false;
  856. }
  857. return true;
  858. }
  859. /**
  860. * 使用行为类验证
  861. * @access protected
  862. * @param mixed $value 字段值
  863. * @param mixed $rule 验证规则
  864. * @param array $data 数据
  865. * @return mixed
  866. */
  867. protected function behavior($value, $rule, $data)
  868. {
  869. return Hook::exec($rule, '', $data);
  870. }
  871. /**
  872. * 使用filter_var方式验证
  873. * @access protected
  874. * @param mixed $value 字段值
  875. * @param mixed $rule 验证规则
  876. * @return bool
  877. */
  878. protected function filter($value, $rule)
  879. {
  880. if (is_string($rule) && strpos($rule, ',')) {
  881. list($rule, $param) = explode(',', $rule);
  882. } elseif (is_array($rule)) {
  883. $param = isset($rule[1]) ? $rule[1] : null;
  884. $rule = $rule[0];
  885. } else {
  886. $param = null;
  887. }
  888. return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
  889. }
  890. /**
  891. * 验证某个字段等于某个值的时候必须
  892. * @access protected
  893. * @param mixed $value 字段值
  894. * @param mixed $rule 验证规则
  895. * @param array $data 数据
  896. * @return bool
  897. */
  898. protected function requireIf($value, $rule, $data)
  899. {
  900. list($field, $val) = explode(',', $rule);
  901. if ($this->getDataValue($data, $field) == $val) {
  902. return !empty($value) || '0' == $value;
  903. } else {
  904. return true;
  905. }
  906. }
  907. /**
  908. * 通过回调方法验证某个字段是否必须
  909. * @access protected
  910. * @param mixed $value 字段值
  911. * @param mixed $rule 验证规则
  912. * @param array $data 数据
  913. * @return bool
  914. */
  915. protected function requireCallback($value, $rule, $data)
  916. {
  917. $result = call_user_func_array($rule, [$value, $data]);
  918. if ($result) {
  919. return !empty($value) || '0' == $value;
  920. } else {
  921. return true;
  922. }
  923. }
  924. /**
  925. * 验证某个字段有值的情况下必须
  926. * @access protected
  927. * @param mixed $value 字段值
  928. * @param mixed $rule 验证规则
  929. * @param array $data 数据
  930. * @return bool
  931. */
  932. protected function requireWith($value, $rule, $data)
  933. {
  934. $val = $this->getDataValue($data, $rule);
  935. if (!empty($val)) {
  936. return !empty($value) || '0' == $value;
  937. } else {
  938. return true;
  939. }
  940. }
  941. /**
  942. * 验证是否在范围内
  943. * @access protected
  944. * @param mixed $value 字段值
  945. * @param mixed $rule 验证规则
  946. * @return bool
  947. */
  948. protected function in($value, $rule)
  949. {
  950. return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
  951. }
  952. /**
  953. * 验证是否不在某个范围
  954. * @access protected
  955. * @param mixed $value 字段值
  956. * @param mixed $rule 验证规则
  957. * @return bool
  958. */
  959. protected function notIn($value, $rule)
  960. {
  961. return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
  962. }
  963. /**
  964. * between验证数据
  965. * @access protected
  966. * @param mixed $value 字段值
  967. * @param mixed $rule 验证规则
  968. * @return bool
  969. */
  970. protected function between($value, $rule)
  971. {
  972. if (is_string($rule)) {
  973. $rule = explode(',', $rule);
  974. }
  975. list($min, $max) = $rule;
  976. return $value >= $min && $value <= $max;
  977. }
  978. /**
  979. * 使用notbetween验证数据
  980. * @access protected
  981. * @param mixed $value 字段值
  982. * @param mixed $rule 验证规则
  983. * @return bool
  984. */
  985. protected function notBetween($value, $rule)
  986. {
  987. if (is_string($rule)) {
  988. $rule = explode(',', $rule);
  989. }
  990. list($min, $max) = $rule;
  991. return $value < $min || $value > $max;
  992. }
  993. /**
  994. * 验证数据长度
  995. * @access protected
  996. * @param mixed $value 字段值
  997. * @param mixed $rule 验证规则
  998. * @return bool
  999. */
  1000. protected function length($value, $rule)
  1001. {
  1002. if (is_array($value)) {
  1003. $length = count($value);
  1004. } elseif ($value instanceof File) {
  1005. $length = $value->getSize();
  1006. } else {
  1007. $length = mb_strlen((string) $value);
  1008. }
  1009. if (strpos($rule, ',')) {
  1010. // 长度区间
  1011. list($min, $max) = explode(',', $rule);
  1012. return $length >= $min && $length <= $max;
  1013. } else {
  1014. // 指定长度
  1015. return $length == $rule;
  1016. }
  1017. }
  1018. /**
  1019. * 验证数据最大长度
  1020. * @access protected
  1021. * @param mixed $value 字段值
  1022. * @param mixed $rule 验证规则
  1023. * @return bool
  1024. */
  1025. protected function max($value, $rule)
  1026. {
  1027. if (is_array($value)) {
  1028. $length = count($value);
  1029. } elseif ($value instanceof File) {
  1030. $length = $value->getSize();
  1031. } else {
  1032. $length = mb_strlen((string) $value);
  1033. }
  1034. return $length <= $rule;
  1035. }
  1036. /**
  1037. * 验证数据最小长度
  1038. * @access protected
  1039. * @param mixed $value 字段值
  1040. * @param mixed $rule 验证规则
  1041. * @return bool
  1042. */
  1043. protected function min($value, $rule)
  1044. {
  1045. if (is_array($value)) {
  1046. $length = count($value);
  1047. } elseif ($value instanceof File) {
  1048. $length = $value->getSize();
  1049. } else {
  1050. $length = mb_strlen((string) $value);
  1051. }
  1052. return $length >= $rule;
  1053. }
  1054. /**
  1055. * 验证日期
  1056. * @access protected
  1057. * @param mixed $value 字段值
  1058. * @param mixed $rule 验证规则
  1059. * @param array $data 数据
  1060. * @return bool
  1061. */
  1062. protected function after($value, $rule, $data)
  1063. {
  1064. return strtotime($value) >= strtotime($rule);
  1065. }
  1066. /**
  1067. * 验证日期
  1068. * @access protected
  1069. * @param mixed $value 字段值
  1070. * @param mixed $rule 验证规则
  1071. * @param array $data 数据
  1072. * @return bool
  1073. */
  1074. protected function before($value, $rule, $data)
  1075. {
  1076. return strtotime($value) <= strtotime($rule);
  1077. }
  1078. /**
  1079. * 验证日期字段
  1080. * @access protected
  1081. * @param mixed $value 字段值
  1082. * @param mixed $rule 验证规则
  1083. * @param array $data 数据
  1084. * @return bool
  1085. */
  1086. protected function afterWith($value, $rule, $data)
  1087. {
  1088. $rule = $this->getDataValue($data, $rule);
  1089. return !is_null($rule) && strtotime($value) >= strtotime($rule);
  1090. }
  1091. /**
  1092. * 验证日期字段
  1093. * @access protected
  1094. * @param mixed $value 字段值
  1095. * @param mixed $rule 验证规则
  1096. * @param array $data 数据
  1097. * @return bool
  1098. */
  1099. protected function beforeWith($value, $rule, $data)
  1100. {
  1101. $rule = $this->getDataValue($data, $rule);
  1102. return !is_null($rule) && strtotime($value) <= strtotime($rule);
  1103. }
  1104. /**
  1105. * 验证有效期
  1106. * @access protected
  1107. * @param mixed $value 字段值
  1108. * @param mixed $rule 验证规则
  1109. * @return bool
  1110. */
  1111. protected function expire($value, $rule)
  1112. {
  1113. if (is_string($rule)) {
  1114. $rule = explode(',', $rule);
  1115. }
  1116. list($start, $end) = $rule;
  1117. if (!is_numeric($start)) {
  1118. $start = strtotime($start);
  1119. }
  1120. if (!is_numeric($end)) {
  1121. $end = strtotime($end);
  1122. }
  1123. return $_SERVER['REQUEST_TIME'] >= $start && $_SERVER['REQUEST_TIME'] <= $end;
  1124. }
  1125. /**
  1126. * 验证IP许可
  1127. * @access protected
  1128. * @param string $value 字段值
  1129. * @param mixed $rule 验证规则
  1130. * @return mixed
  1131. */
  1132. protected function allowIp($value, $rule)
  1133. {
  1134. return in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule));
  1135. }
  1136. /**
  1137. * 验证IP禁用
  1138. * @access protected
  1139. * @param string $value 字段值
  1140. * @param mixed $rule 验证规则
  1141. * @return mixed
  1142. */
  1143. protected function denyIp($value, $rule)
  1144. {
  1145. return !in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule));
  1146. }
  1147. /**
  1148. * 使用正则验证数据
  1149. * @access protected
  1150. * @param mixed $value 字段值
  1151. * @param mixed $rule 验证规则 正则规则或者预定义正则名
  1152. * @return mixed
  1153. */
  1154. protected function regex($value, $rule)
  1155. {
  1156. if (isset($this->regex[$rule])) {
  1157. $rule = $this->regex[$rule];
  1158. }
  1159. if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
  1160. // 不是正则表达式则两端补上/
  1161. $rule = '/^' . $rule . '$/';
  1162. }
  1163. return is_scalar($value) && 1 === preg_match($rule, (string) $value);
  1164. }
  1165. /**
  1166. * 验证表单令牌
  1167. * @access protected
  1168. * @param mixed $value 字段值
  1169. * @param mixed $rule 验证规则
  1170. * @param array $data 数据
  1171. * @return bool
  1172. */
  1173. protected function token($value, $rule, $data)
  1174. {
  1175. $rule = !empty($rule) ? $rule : '__token__';
  1176. if (!isset($data[$rule]) || !Session::has($rule)) {
  1177. // 令牌数据无效
  1178. return false;
  1179. }
  1180. // 令牌验证
  1181. if (isset($data[$rule]) && Session::get($rule) === $data[$rule]) {
  1182. // 防止重复提交
  1183. Session::delete($rule); // 验证完成销毁session
  1184. return true;
  1185. }
  1186. // 开启TOKEN重置
  1187. Session::delete($rule);
  1188. return false;
  1189. }
  1190. // 获取错误信息
  1191. public function getError()
  1192. {
  1193. return $this->error;
  1194. }
  1195. /**
  1196. * 获取数据值
  1197. * @access protected
  1198. * @param array $data 数据
  1199. * @param string $key 数据标识 支持二维
  1200. * @return mixed
  1201. */
  1202. protected function getDataValue($data, $key)
  1203. {
  1204. if (is_numeric($key)) {
  1205. $value = $key;
  1206. } elseif (strpos($key, '.')) {
  1207. // 支持二维数组验证
  1208. list($name1, $name2) = explode('.', $key);
  1209. $value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null;
  1210. } else {
  1211. $value = isset($data[$key]) ? $data[$key] : null;
  1212. }
  1213. return $value;
  1214. }
  1215. /**
  1216. * 获取验证规则的错误提示信息
  1217. * @access protected
  1218. * @param string $attribute 字段英文名
  1219. * @param string $title 字段描述名
  1220. * @param string $type 验证规则名称
  1221. * @param mixed $rule 验证规则数据
  1222. * @return string
  1223. */
  1224. protected function getRuleMsg($attribute, $title, $type, $rule)
  1225. {
  1226. if (isset($this->message[$attribute . '.' . $type])) {
  1227. $msg = $this->message[$attribute . '.' . $type];
  1228. } elseif (isset($this->message[$attribute][$type])) {
  1229. $msg = $this->message[$attribute][$type];
  1230. } elseif (isset($this->message[$attribute])) {
  1231. $msg = $this->message[$attribute];
  1232. } elseif (isset(self::$typeMsg[$type])) {
  1233. $msg = self::$typeMsg[$type];
  1234. } elseif (0 === strpos($type, 'require')) {
  1235. $msg = self::$typeMsg['require'];
  1236. } else {
  1237. $msg = $title . Lang::get('not conform to the rules');
  1238. }
  1239. if (is_string($msg) && 0 === strpos($msg, '{%')) {
  1240. $msg = Lang::get(substr($msg, 2, -1));
  1241. } elseif (Lang::has($msg)) {
  1242. $msg = Lang::get($msg);
  1243. }
  1244. if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) {
  1245. // 变量替换
  1246. if (is_string($rule) && strpos($rule, ',')) {
  1247. $array = array_pad(explode(',', $rule), 3, '');
  1248. } else {
  1249. $array = array_pad([], 3, '');
  1250. }
  1251. $msg = str_replace(
  1252. [':attribute', ':rule', ':1', ':2', ':3'],
  1253. [$title, (string) $rule, $array[0], $array[1], $array[2]],
  1254. $msg);
  1255. }
  1256. return $msg;
  1257. }
  1258. /**
  1259. * 获取数据验证的场景
  1260. * @access protected
  1261. * @param string $scene 验证场景
  1262. * @return array
  1263. */
  1264. protected function getScene($scene = '')
  1265. {
  1266. if (empty($scene)) {
  1267. // 读取指定场景
  1268. $scene = $this->currentScene;
  1269. }
  1270. if (!empty($scene) && isset($this->scene[$scene])) {
  1271. // 如果设置了验证适用场景
  1272. $scene = $this->scene[$scene];
  1273. if (is_string($scene)) {
  1274. $scene = explode(',', $scene);
  1275. }
  1276. } else {
  1277. $scene = [];
  1278. }
  1279. return $scene;
  1280. }
  1281. public static function __callStatic($method, $params)
  1282. {
  1283. $class = self::make();
  1284. if (method_exists($class, $method)) {
  1285. return call_user_func_array([$class, $method], $params);
  1286. } else {
  1287. throw new \BadMethodCallException('method not exists:' . __CLASS__ . '->' . $method);
  1288. }
  1289. }
  1290. }