Validate.php 46 KB

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