Validate.php 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2019 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}]+$/u',
  120. 'chsAlpha' => '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u',
  121. 'chsAlphaNum' => '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+$/u',
  122. 'chsDash' => '/^[\x{4e00}-\x{9fa5}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{2}$)/',
  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 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 (empty($rules)) {
  423. // 读取验证规则
  424. $rules = $this->rule;
  425. }
  426. if ($this->currentScene) {
  427. $this->getScene($this->currentScene);
  428. }
  429. foreach ($this->append as $key => $rule) {
  430. if (!isset($rules[$key])) {
  431. $rules[$key] = $rule;
  432. }
  433. }
  434. foreach ($rules as $key => $rule) {
  435. // field => 'rule1|rule2...' field => ['rule1','rule2',...]
  436. if (strpos($key, '|')) {
  437. // 字段|描述 用于指定属性名称
  438. list($key, $title) = explode('|', $key);
  439. } else {
  440. $title = $this->field[$key] ?? $key;
  441. }
  442. // 场景检测
  443. if (!empty($this->only) && !in_array($key, $this->only)) {
  444. continue;
  445. }
  446. // 获取数据 支持二维数组
  447. $value = $this->getDataValue($data, $key);
  448. // 字段验证
  449. if ($rule instanceof Closure) {
  450. $result = call_user_func_array($rule, [$value, $data]);
  451. } elseif ($rule instanceof ValidateRule) {
  452. // 验证因子
  453. $result = $this->checkItem($key, $value, $rule->getRule(), $data, $rule->getTitle() ?: $title, $rule->getMsg());
  454. } else {
  455. $result = $this->checkItem($key, $value, $rule, $data, $title);
  456. }
  457. if (true !== $result) {
  458. // 没有返回true 则表示验证失败
  459. if (!empty($this->batch)) {
  460. // 批量验证
  461. $this->error[$key] = $result;
  462. } elseif ($this->failException) {
  463. throw new ValidateException($result);
  464. } else {
  465. $this->error = $result;
  466. return false;
  467. }
  468. }
  469. }
  470. if (!empty($this->error)) {
  471. if ($this->failException) {
  472. throw new ValidateException($this->error);
  473. }
  474. return false;
  475. }
  476. return true;
  477. }
  478. /**
  479. * 根据验证规则验证数据
  480. * @access public
  481. * @param mixed $value 字段值
  482. * @param mixed $rules 验证规则
  483. * @return bool
  484. */
  485. public function checkRule($value, $rules): bool
  486. {
  487. if ($rules instanceof Closure) {
  488. return call_user_func_array($rules, [$value]);
  489. } elseif ($rules instanceof ValidateRule) {
  490. $rules = $rules->getRule();
  491. } elseif (is_string($rules)) {
  492. $rules = explode('|', $rules);
  493. }
  494. foreach ($rules as $key => $rule) {
  495. if ($rule instanceof Closure) {
  496. $result = call_user_func_array($rule, [$value]);
  497. } else {
  498. // 判断验证类型
  499. list($type, $rule) = $this->getValidateType($key, $rule);
  500. $callback = $this->type[$type] ?? [$this, $type];
  501. $result = call_user_func_array($callback, [$value, $rule]);
  502. }
  503. if (true !== $result) {
  504. if ($this->failException) {
  505. throw new ValidateException($result);
  506. }
  507. return $result;
  508. }
  509. }
  510. return true;
  511. }
  512. /**
  513. * 验证单个字段规则
  514. * @access protected
  515. * @param string $field 字段名
  516. * @param mixed $value 字段值
  517. * @param mixed $rules 验证规则
  518. * @param array $data 数据
  519. * @param string $title 字段描述
  520. * @param array $msg 提示信息
  521. * @return mixed
  522. */
  523. protected function checkItem(string $field, $value, $rules, $data, string $title = '', array $msg = [])
  524. {
  525. if (isset($this->remove[$field]) && true === $this->remove[$field] && empty($this->append[$field])) {
  526. // 字段已经移除 无需验证
  527. return true;
  528. }
  529. // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...]
  530. if (is_string($rules)) {
  531. $rules = explode('|', $rules);
  532. }
  533. if (isset($this->append[$field])) {
  534. // 追加额外的验证规则
  535. $rules = array_unique(array_merge($rules, $this->append[$field]), SORT_REGULAR);
  536. }
  537. $i = 0;
  538. foreach ($rules as $key => $rule) {
  539. if ($rule instanceof Closure) {
  540. $result = call_user_func_array($rule, [$value, $data]);
  541. $info = is_numeric($key) ? '' : $key;
  542. } else {
  543. // 判断验证类型
  544. list($type, $rule, $info) = $this->getValidateType($key, $rule);
  545. if (isset($this->append[$field]) && in_array($info, $this->append[$field])) {
  546. } elseif (isset($this->remove[$field]) && in_array($info, $this->remove[$field])) {
  547. // 规则已经移除
  548. $i++;
  549. continue;
  550. }
  551. if (isset($this->type[$type])) {
  552. $result = call_user_func_array($this->type[$type], [$value, $rule, $data, $field, $title]);
  553. } elseif ('must' == $info || 0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
  554. $result = call_user_func_array([$this, $type], [$value, $rule, $data, $field, $title]);
  555. } else {
  556. $result = true;
  557. }
  558. }
  559. if (false === $result) {
  560. // 验证失败 返回错误信息
  561. if (!empty($msg[$i])) {
  562. $message = $msg[$i];
  563. if (is_string($message) && strpos($message, '{%') === 0) {
  564. $message = $this->lang->get(substr($message, 2, -1));
  565. }
  566. } else {
  567. $message = $this->getRuleMsg($field, $title, $info, $rule);
  568. }
  569. return $message;
  570. } elseif (true !== $result) {
  571. // 返回自定义错误信息
  572. if (is_string($result) && false !== strpos($result, ':')) {
  573. $result = str_replace(':attribute', $title, $result);
  574. if (strpos($result, ':rule') && is_scalar($rule)) {
  575. $result = str_replace(':rule', (string) $rule, $result);
  576. }
  577. }
  578. return $result;
  579. }
  580. $i++;
  581. }
  582. return $result;
  583. }
  584. /**
  585. * 获取当前验证类型及规则
  586. * @access public
  587. * @param mixed $key
  588. * @param mixed $rule
  589. * @return array
  590. */
  591. protected function getValidateType($key, $rule): array
  592. {
  593. // 判断验证类型
  594. if (!is_numeric($key)) {
  595. if (isset($this->alias[$key])) {
  596. // 判断别名
  597. $key = $this->alias[$key];
  598. }
  599. return [$key, $rule, $key];
  600. }
  601. if (strpos($rule, ':')) {
  602. list($type, $rule) = explode(':', $rule, 2);
  603. if (isset($this->alias[$type])) {
  604. // 判断别名
  605. $type = $this->alias[$type];
  606. }
  607. $info = $type;
  608. } elseif (method_exists($this, $rule)) {
  609. $type = $rule;
  610. $info = $rule;
  611. $rule = '';
  612. } else {
  613. $type = 'is';
  614. $info = $rule;
  615. }
  616. return [$type, $rule, $info];
  617. }
  618. /**
  619. * 验证是否和某个字段的值一致
  620. * @access public
  621. * @param mixed $value 字段值
  622. * @param mixed $rule 验证规则
  623. * @param array $data 数据
  624. * @param string $field 字段名
  625. * @return bool
  626. */
  627. public function confirm($value, $rule, array $data = [], string $field = ''): bool
  628. {
  629. if ('' == $rule) {
  630. if (strpos($field, '_confirm')) {
  631. $rule = strstr($field, '_confirm', true);
  632. } else {
  633. $rule = $field . '_confirm';
  634. }
  635. }
  636. return $this->getDataValue($data, $rule) === $value;
  637. }
  638. /**
  639. * 验证是否和某个字段的值是否不同
  640. * @access public
  641. * @param mixed $value 字段值
  642. * @param mixed $rule 验证规则
  643. * @param array $data 数据
  644. * @return bool
  645. */
  646. public function different($value, $rule, array $data = []): bool
  647. {
  648. return $this->getDataValue($data, $rule) != $value;
  649. }
  650. /**
  651. * 验证是否大于等于某个值
  652. * @access public
  653. * @param mixed $value 字段值
  654. * @param mixed $rule 验证规则
  655. * @param array $data 数据
  656. * @return bool
  657. */
  658. public function egt($value, $rule, array $data = []): bool
  659. {
  660. return $value >= $this->getDataValue($data, $rule);
  661. }
  662. /**
  663. * 验证是否大于某个值
  664. * @access public
  665. * @param mixed $value 字段值
  666. * @param mixed $rule 验证规则
  667. * @param array $data 数据
  668. * @return bool
  669. */
  670. public function gt($value, $rule, array $data = []): bool
  671. {
  672. return $value > $this->getDataValue($data, $rule);
  673. }
  674. /**
  675. * 验证是否小于等于某个值
  676. * @access public
  677. * @param mixed $value 字段值
  678. * @param mixed $rule 验证规则
  679. * @param array $data 数据
  680. * @return bool
  681. */
  682. public function elt($value, $rule, array $data = []): bool
  683. {
  684. return $value <= $this->getDataValue($data, $rule);
  685. }
  686. /**
  687. * 验证是否小于某个值
  688. * @access public
  689. * @param mixed $value 字段值
  690. * @param mixed $rule 验证规则
  691. * @param array $data 数据
  692. * @return bool
  693. */
  694. public function lt($value, $rule, array $data = []): bool
  695. {
  696. return $value < $this->getDataValue($data, $rule);
  697. }
  698. /**
  699. * 验证是否等于某个值
  700. * @access public
  701. * @param mixed $value 字段值
  702. * @param mixed $rule 验证规则
  703. * @return bool
  704. */
  705. public function eq($value, $rule): bool
  706. {
  707. return $value == $rule;
  708. }
  709. /**
  710. * 必须验证
  711. * @access public
  712. * @param mixed $value 字段值
  713. * @param mixed $rule 验证规则
  714. * @return bool
  715. */
  716. public function must($value, $rule = null): bool
  717. {
  718. return !empty($value) || '0' == $value;
  719. }
  720. /**
  721. * 验证字段值是否为有效格式
  722. * @access public
  723. * @param mixed $value 字段值
  724. * @param string $rule 验证规则
  725. * @param array $data 数据
  726. * @return bool
  727. */
  728. public function is($value, string $rule, array $data = []): bool
  729. {
  730. switch (Str::camel($rule)) {
  731. case 'require':
  732. // 必须
  733. $result = !empty($value) || '0' == $value;
  734. break;
  735. case 'accepted':
  736. // 接受
  737. $result = in_array($value, ['1', 'on', 'yes']);
  738. break;
  739. case 'date':
  740. // 是否是一个有效日期
  741. $result = false !== strtotime($value);
  742. break;
  743. case 'activeUrl':
  744. // 是否为有效的网址
  745. $result = checkdnsrr($value);
  746. break;
  747. case 'boolean':
  748. case 'bool':
  749. // 是否为布尔值
  750. $result = in_array($value, [true, false, 0, 1, '0', '1'], true);
  751. break;
  752. case 'number':
  753. $result = ctype_digit((string) $value);
  754. break;
  755. case 'alphaNum':
  756. $result = ctype_alnum($value);
  757. break;
  758. case 'array':
  759. // 是否为数组
  760. $result = is_array($value);
  761. break;
  762. case 'file':
  763. $result = $value instanceof File;
  764. break;
  765. case 'image':
  766. $result = $value instanceof File && in_array($this->getImageType($value->getRealPath()), [1, 2, 3, 6]);
  767. break;
  768. case 'token':
  769. $result = $this->token($value, '__token__', $data);
  770. break;
  771. default:
  772. if (isset($this->type[$rule])) {
  773. // 注册的验证规则
  774. $result = call_user_func_array($this->type[$rule], [$value]);
  775. } elseif (function_exists('ctype_' . $rule)) {
  776. // ctype验证规则
  777. $ctypeFun = 'ctype_' . $rule;
  778. $result = $ctypeFun($value);
  779. } elseif (isset($this->filter[$rule])) {
  780. // Filter_var验证规则
  781. $result = $this->filter($value, $this->filter[$rule]);
  782. } else {
  783. // 正则验证
  784. $result = $this->regex($value, $rule);
  785. }
  786. }
  787. return $result;
  788. }
  789. // 判断图像类型
  790. protected function getImageType($image)
  791. {
  792. if (function_exists('exif_imagetype')) {
  793. return exif_imagetype($image);
  794. }
  795. try {
  796. $info = getimagesize($image);
  797. return $info ? $info[2] : false;
  798. } catch (\Exception $e) {
  799. return false;
  800. }
  801. }
  802. /**
  803. * 验证表单令牌
  804. * @access public
  805. * @param mixed $value 字段值
  806. * @param mixed $rule 验证规则
  807. * @param array $data 数据
  808. * @return bool
  809. */
  810. public function token($value, string $rule, array $data): bool
  811. {
  812. $rule = !empty($rule) ? $rule : '__token__';
  813. return $this->request->checkToken($rule, $data);
  814. }
  815. /**
  816. * 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型
  817. * @access public
  818. * @param mixed $value 字段值
  819. * @param mixed $rule 验证规则
  820. * @return bool
  821. */
  822. public function activeUrl(string $value, string $rule = 'MX'): bool
  823. {
  824. if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) {
  825. $rule = 'MX';
  826. }
  827. return checkdnsrr($value, $rule);
  828. }
  829. /**
  830. * 验证是否有效IP
  831. * @access public
  832. * @param mixed $value 字段值
  833. * @param mixed $rule 验证规则 ipv4 ipv6
  834. * @return bool
  835. */
  836. public function ip($value, string $rule = 'ipv4'): bool
  837. {
  838. if (!in_array($rule, ['ipv4', 'ipv6'])) {
  839. $rule = 'ipv4';
  840. }
  841. return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]);
  842. }
  843. /**
  844. * 检测上传文件后缀
  845. * @access public
  846. * @param File $file
  847. * @param array|string $ext 允许后缀
  848. * @return bool
  849. */
  850. protected function checkExt(File $file, $ext): bool
  851. {
  852. if (is_string($ext)) {
  853. $ext = explode(',', $ext);
  854. }
  855. return in_array(strtolower($file->extension()), $ext);
  856. }
  857. /**
  858. * 检测上传文件大小
  859. * @access public
  860. * @param File $file
  861. * @param integer $size 最大大小
  862. * @return bool
  863. */
  864. protected function checkSize(File $file, $size): bool
  865. {
  866. return $file->getSize() <= (int) $size;
  867. }
  868. /**
  869. * 检测上传文件类型
  870. * @access public
  871. * @param File $file
  872. * @param array|string $mime 允许类型
  873. * @return bool
  874. */
  875. protected function checkMime(File $file, $mime): bool
  876. {
  877. if (is_string($mime)) {
  878. $mime = explode(',', $mime);
  879. }
  880. return in_array(strtolower($file->getMime()), $mime);
  881. }
  882. /**
  883. * 验证上传文件后缀
  884. * @access public
  885. * @param mixed $file 上传文件
  886. * @param mixed $rule 验证规则
  887. * @return bool
  888. */
  889. public function fileExt($file, $rule): bool
  890. {
  891. if (is_array($file)) {
  892. foreach ($file as $item) {
  893. if (!($item instanceof File) || !$this->checkExt($item, $rule)) {
  894. return false;
  895. }
  896. }
  897. return true;
  898. } elseif ($file instanceof File) {
  899. return $this->checkExt($file, $rule);
  900. }
  901. return false;
  902. }
  903. /**
  904. * 验证上传文件类型
  905. * @access public
  906. * @param mixed $file 上传文件
  907. * @param mixed $rule 验证规则
  908. * @return bool
  909. */
  910. public function fileMime($file, $rule): bool
  911. {
  912. if (is_array($file)) {
  913. foreach ($file as $item) {
  914. if (!($item instanceof File) || !$this->checkMime($item, $rule)) {
  915. return false;
  916. }
  917. }
  918. return true;
  919. } elseif ($file instanceof File) {
  920. return $this->checkMime($file, $rule);
  921. }
  922. return false;
  923. }
  924. /**
  925. * 验证上传文件大小
  926. * @access public
  927. * @param mixed $file 上传文件
  928. * @param mixed $rule 验证规则
  929. * @return bool
  930. */
  931. public function fileSize($file, $rule): bool
  932. {
  933. if (is_array($file)) {
  934. foreach ($file as $item) {
  935. if (!($item instanceof File) || !$this->checkSize($item, $rule)) {
  936. return false;
  937. }
  938. }
  939. return true;
  940. } elseif ($file instanceof File) {
  941. return $this->checkSize($file, $rule);
  942. }
  943. return false;
  944. }
  945. /**
  946. * 验证图片的宽高及类型
  947. * @access public
  948. * @param mixed $file 上传文件
  949. * @param mixed $rule 验证规则
  950. * @return bool
  951. */
  952. public function image($file, $rule): bool
  953. {
  954. if (!($file instanceof File)) {
  955. return false;
  956. }
  957. if ($rule) {
  958. $rule = explode(',', $rule);
  959. list($width, $height, $type) = getimagesize($file->getRealPath());
  960. if (isset($rule[2])) {
  961. $imageType = strtolower($rule[2]);
  962. if ('jpeg' == $imageType) {
  963. $imageType = 'jpg';
  964. }
  965. if (image_type_to_extension($type, false) != $imageType) {
  966. return false;
  967. }
  968. }
  969. list($w, $h) = $rule;
  970. return $w == $width && $h == $height;
  971. }
  972. return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]);
  973. }
  974. /**
  975. * 验证时间和日期是否符合指定格式
  976. * @access public
  977. * @param mixed $value 字段值
  978. * @param mixed $rule 验证规则
  979. * @return bool
  980. */
  981. public function dateFormat($value, $rule): bool
  982. {
  983. $info = date_parse_from_format($rule, $value);
  984. return 0 == $info['warning_count'] && 0 == $info['error_count'];
  985. }
  986. /**
  987. * 验证是否唯一
  988. * @access public
  989. * @param mixed $value 字段值
  990. * @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名
  991. * @param array $data 数据
  992. * @param string $field 验证字段名
  993. * @return bool
  994. */
  995. public function unique($value, $rule, array $data = [], string $field = ''): bool
  996. {
  997. if (is_string($rule)) {
  998. $rule = explode(',', $rule);
  999. }
  1000. if (false !== strpos($rule[0], '\\')) {
  1001. // 指定模型类
  1002. $db = new $rule[0];
  1003. } else {
  1004. $db = $this->db->name($rule[0]);
  1005. }
  1006. $key = $rule[1] ?? $field;
  1007. $map = [];
  1008. if (strpos($key, '^')) {
  1009. // 支持多个字段验证
  1010. $fields = explode('^', $key);
  1011. foreach ($fields as $key) {
  1012. if (isset($data[$key])) {
  1013. $map[] = [$key, '=', $data[$key]];
  1014. }
  1015. }
  1016. } elseif (isset($data[$field])) {
  1017. $map[] = [$key, '=', $data[$field]];
  1018. } else {
  1019. $map = [];
  1020. }
  1021. $pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
  1022. if (is_string($pk)) {
  1023. if (isset($rule[2])) {
  1024. $map[] = [$pk, '<>', $rule[2]];
  1025. } elseif (isset($data[$pk])) {
  1026. $map[] = [$pk, '<>', $data[$pk]];
  1027. }
  1028. }
  1029. if ($db->where($map)->field($pk)->find()) {
  1030. return false;
  1031. }
  1032. return true;
  1033. }
  1034. /**
  1035. * 使用filter_var方式验证
  1036. * @access public
  1037. * @param mixed $value 字段值
  1038. * @param mixed $rule 验证规则
  1039. * @return bool
  1040. */
  1041. public function filter($value, $rule): bool
  1042. {
  1043. if (is_string($rule) && strpos($rule, ',')) {
  1044. list($rule, $param) = explode(',', $rule);
  1045. } elseif (is_array($rule)) {
  1046. $param = $rule[1] ?? null;
  1047. $rule = $rule[0];
  1048. } else {
  1049. $param = null;
  1050. }
  1051. return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
  1052. }
  1053. /**
  1054. * 验证某个字段等于某个值的时候必须
  1055. * @access public
  1056. * @param mixed $value 字段值
  1057. * @param mixed $rule 验证规则
  1058. * @param array $data 数据
  1059. * @return bool
  1060. */
  1061. public function requireIf($value, $rule, array $data = []): bool
  1062. {
  1063. list($field, $val) = explode(',', $rule);
  1064. if ($this->getDataValue($data, $field) == $val) {
  1065. return !empty($value) || '0' == $value;
  1066. }
  1067. return true;
  1068. }
  1069. /**
  1070. * 通过回调方法验证某个字段是否必须
  1071. * @access public
  1072. * @param mixed $value 字段值
  1073. * @param mixed $rule 验证规则
  1074. * @param array $data 数据
  1075. * @return bool
  1076. */
  1077. public function requireCallback($value, $rule, array $data = []): bool
  1078. {
  1079. $result = call_user_func_array([$this, $rule], [$value, $data]);
  1080. if ($result) {
  1081. return !empty($value) || '0' == $value;
  1082. }
  1083. return true;
  1084. }
  1085. /**
  1086. * 验证某个字段有值的情况下必须
  1087. * @access public
  1088. * @param mixed $value 字段值
  1089. * @param mixed $rule 验证规则
  1090. * @param array $data 数据
  1091. * @return bool
  1092. */
  1093. public function requireWith($value, $rule, array $data = []): bool
  1094. {
  1095. $val = $this->getDataValue($data, $rule);
  1096. if (!empty($val)) {
  1097. return !empty($value) || '0' == $value;
  1098. }
  1099. return true;
  1100. }
  1101. /**
  1102. * 验证某个字段没有值的情况下必须
  1103. * @access public
  1104. * @param mixed $value 字段值
  1105. * @param mixed $rule 验证规则
  1106. * @param array $data 数据
  1107. * @return bool
  1108. */
  1109. public function requireWithout($value, $rule, array $data = []): bool
  1110. {
  1111. $val = $this->getDataValue($data, $rule);
  1112. if (empty($val)) {
  1113. return !empty($value) || '0' == $value;
  1114. }
  1115. return true;
  1116. }
  1117. /**
  1118. * 验证是否在范围内
  1119. * @access public
  1120. * @param mixed $value 字段值
  1121. * @param mixed $rule 验证规则
  1122. * @return bool
  1123. */
  1124. public function in($value, $rule): bool
  1125. {
  1126. return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
  1127. }
  1128. /**
  1129. * 验证是否不在某个范围
  1130. * @access public
  1131. * @param mixed $value 字段值
  1132. * @param mixed $rule 验证规则
  1133. * @return bool
  1134. */
  1135. public function notIn($value, $rule): bool
  1136. {
  1137. return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
  1138. }
  1139. /**
  1140. * between验证数据
  1141. * @access public
  1142. * @param mixed $value 字段值
  1143. * @param mixed $rule 验证规则
  1144. * @return bool
  1145. */
  1146. public function between($value, $rule): bool
  1147. {
  1148. if (is_string($rule)) {
  1149. $rule = explode(',', $rule);
  1150. }
  1151. list($min, $max) = $rule;
  1152. return $value >= $min && $value <= $max;
  1153. }
  1154. /**
  1155. * 使用notbetween验证数据
  1156. * @access public
  1157. * @param mixed $value 字段值
  1158. * @param mixed $rule 验证规则
  1159. * @return bool
  1160. */
  1161. public function notBetween($value, $rule): bool
  1162. {
  1163. if (is_string($rule)) {
  1164. $rule = explode(',', $rule);
  1165. }
  1166. list($min, $max) = $rule;
  1167. return $value < $min || $value > $max;
  1168. }
  1169. /**
  1170. * 验证数据长度
  1171. * @access public
  1172. * @param mixed $value 字段值
  1173. * @param mixed $rule 验证规则
  1174. * @return bool
  1175. */
  1176. public function length($value, $rule): bool
  1177. {
  1178. if (is_array($value)) {
  1179. $length = count($value);
  1180. } elseif ($value instanceof File) {
  1181. $length = $value->getSize();
  1182. } else {
  1183. $length = mb_strlen((string) $value);
  1184. }
  1185. if (is_string($rule) && strpos($rule, ',')) {
  1186. // 长度区间
  1187. list($min, $max) = explode(',', $rule);
  1188. return $length >= $min && $length <= $max;
  1189. }
  1190. // 指定长度
  1191. return $length == $rule;
  1192. }
  1193. /**
  1194. * 验证数据最大长度
  1195. * @access public
  1196. * @param mixed $value 字段值
  1197. * @param mixed $rule 验证规则
  1198. * @return bool
  1199. */
  1200. public function max($value, $rule): bool
  1201. {
  1202. if (is_array($value)) {
  1203. $length = count($value);
  1204. } elseif ($value instanceof File) {
  1205. $length = $value->getSize();
  1206. } else {
  1207. $length = mb_strlen((string) $value);
  1208. }
  1209. return $length <= $rule;
  1210. }
  1211. /**
  1212. * 验证数据最小长度
  1213. * @access public
  1214. * @param mixed $value 字段值
  1215. * @param mixed $rule 验证规则
  1216. * @return bool
  1217. */
  1218. public function min($value, $rule): bool
  1219. {
  1220. if (is_array($value)) {
  1221. $length = count($value);
  1222. } elseif ($value instanceof File) {
  1223. $length = $value->getSize();
  1224. } else {
  1225. $length = mb_strlen((string) $value);
  1226. }
  1227. return $length >= $rule;
  1228. }
  1229. /**
  1230. * 验证日期
  1231. * @access public
  1232. * @param mixed $value 字段值
  1233. * @param mixed $rule 验证规则
  1234. * @param array $data 数据
  1235. * @return bool
  1236. */
  1237. public function after($value, $rule, array $data = []): bool
  1238. {
  1239. return strtotime($value) >= strtotime($rule);
  1240. }
  1241. /**
  1242. * 验证日期
  1243. * @access public
  1244. * @param mixed $value 字段值
  1245. * @param mixed $rule 验证规则
  1246. * @param array $data 数据
  1247. * @return bool
  1248. */
  1249. public function before($value, $rule, array $data = []): bool
  1250. {
  1251. return strtotime($value) <= strtotime($rule);
  1252. }
  1253. /**
  1254. * 验证日期
  1255. * @access public
  1256. * @param mixed $value 字段值
  1257. * @param mixed $rule 验证规则
  1258. * @param array $data 数据
  1259. * @return bool
  1260. */
  1261. public function afterWith($value, $rule, array $data = []): bool
  1262. {
  1263. $rule = $this->getDataValue($data, $rule);
  1264. return !is_null($rule) && strtotime($value) >= strtotime($rule);
  1265. }
  1266. /**
  1267. * 验证日期
  1268. * @access public
  1269. * @param mixed $value 字段值
  1270. * @param mixed $rule 验证规则
  1271. * @param array $data 数据
  1272. * @return bool
  1273. */
  1274. public function beforeWith($value, $rule, array $data = []): bool
  1275. {
  1276. $rule = $this->getDataValue($data, $rule);
  1277. return !is_null($rule) && strtotime($value) <= strtotime($rule);
  1278. }
  1279. /**
  1280. * 验证有效期
  1281. * @access public
  1282. * @param mixed $value 字段值
  1283. * @param mixed $rule 验证规则
  1284. * @return bool
  1285. */
  1286. public function expire($value, $rule): bool
  1287. {
  1288. if (is_string($rule)) {
  1289. $rule = explode(',', $rule);
  1290. }
  1291. list($start, $end) = $rule;
  1292. if (!is_numeric($start)) {
  1293. $start = strtotime($start);
  1294. }
  1295. if (!is_numeric($end)) {
  1296. $end = strtotime($end);
  1297. }
  1298. return time() >= $start && time() <= $end;
  1299. }
  1300. /**
  1301. * 验证IP许可
  1302. * @access public
  1303. * @param mixed $value 字段值
  1304. * @param mixed $rule 验证规则
  1305. * @return bool
  1306. */
  1307. public function allowIp($value, $rule): bool
  1308. {
  1309. return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
  1310. }
  1311. /**
  1312. * 验证IP禁用
  1313. * @access public
  1314. * @param mixed $value 字段值
  1315. * @param mixed $rule 验证规则
  1316. * @return bool
  1317. */
  1318. public function denyIp($value, $rule): bool
  1319. {
  1320. return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
  1321. }
  1322. /**
  1323. * 使用正则验证数据
  1324. * @access public
  1325. * @param mixed $value 字段值
  1326. * @param mixed $rule 验证规则 正则规则或者预定义正则名
  1327. * @return bool
  1328. */
  1329. public function regex($value, $rule): bool
  1330. {
  1331. if (isset($this->regex[$rule])) {
  1332. $rule = $this->regex[$rule];
  1333. } elseif (isset($this->defaultRegex[$rule])) {
  1334. $rule = $this->defaultRegex[$rule];
  1335. }
  1336. if (is_string($rule) && 0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
  1337. // 不是正则表达式则两端补上/
  1338. $rule = '/^' . $rule . '$/';
  1339. }
  1340. return is_scalar($value) && 1 === preg_match($rule, (string) $value);
  1341. }
  1342. // 获取错误信息
  1343. public function getError()
  1344. {
  1345. return $this->error;
  1346. }
  1347. /**
  1348. * 获取数据值
  1349. * @access protected
  1350. * @param array $data 数据
  1351. * @param string $key 数据标识 支持二维
  1352. * @return mixed
  1353. */
  1354. protected function getDataValue(array $data, $key)
  1355. {
  1356. if (is_numeric($key)) {
  1357. $value = $key;
  1358. } elseif (is_string($key) && strpos($key, '.')) {
  1359. // 支持多维数组验证
  1360. foreach (explode('.', $key) as $key) {
  1361. if (!isset($data[$key])) {
  1362. $value = null;
  1363. break;
  1364. }
  1365. $value = $data = $data[$key];
  1366. }
  1367. } else {
  1368. $value = $data[$key] ?? null;
  1369. }
  1370. return $value;
  1371. }
  1372. /**
  1373. * 获取验证规则的错误提示信息
  1374. * @access protected
  1375. * @param string $attribute 字段英文名
  1376. * @param string $title 字段描述名
  1377. * @param string $type 验证规则名称
  1378. * @param mixed $rule 验证规则数据
  1379. * @return string|array
  1380. */
  1381. protected function getRuleMsg(string $attribute, string $title, string $type, $rule)
  1382. {
  1383. if (isset($this->message[$attribute . '.' . $type])) {
  1384. $msg = $this->message[$attribute . '.' . $type];
  1385. } elseif (isset($this->message[$attribute][$type])) {
  1386. $msg = $this->message[$attribute][$type];
  1387. } elseif (isset($this->message[$attribute])) {
  1388. $msg = $this->message[$attribute];
  1389. } elseif (isset($this->typeMsg[$type])) {
  1390. $msg = $this->typeMsg[$type];
  1391. } elseif (0 === strpos($type, 'require')) {
  1392. $msg = $this->typeMsg['require'];
  1393. } else {
  1394. $msg = $title . $this->lang->get('not conform to the rules');
  1395. }
  1396. if (is_array($msg)) {
  1397. return $this->errorMsgIsArray($msg, $rule, $title);
  1398. }
  1399. return $this->parseErrorMsg($msg, $rule, $title);
  1400. }
  1401. /**
  1402. * 获取验证规则的错误提示信息
  1403. * @access protected
  1404. * @param string $msg 错误信息
  1405. * @param mixed $rule 验证规则数据
  1406. * @param string $title 字段描述名
  1407. * @return string
  1408. */
  1409. protected function parseErrorMsg(string $msg, $rule, string $title)
  1410. {
  1411. if (0 === strpos($msg, '{%')) {
  1412. $msg = $this->lang->get(substr($msg, 2, -1));
  1413. } elseif ($this->lang->has($msg)) {
  1414. $msg = $this->lang->get($msg);
  1415. }
  1416. if (is_array($msg)) {
  1417. return $this->errorMsgIsArray($msg, $rule, $title);
  1418. }
  1419. if (is_scalar($rule) && false !== strpos($msg, ':')) {
  1420. // 变量替换
  1421. if (is_string($rule) && strpos($rule, ',')) {
  1422. $array = array_pad(explode(',', $rule), 3, '');
  1423. } else {
  1424. $array = array_pad([], 3, '');
  1425. }
  1426. $msg = str_replace(
  1427. [':attribute', ':1', ':2', ':3'],
  1428. [$title, $array[0], $array[1], $array[2]],
  1429. $msg);
  1430. if (strpos($msg, ':rule')) {
  1431. $msg = str_replace(':rule', (string) $rule, $msg);
  1432. }
  1433. }
  1434. return $msg;
  1435. }
  1436. /**
  1437. * 错误信息数组处理
  1438. * @access protected
  1439. * @param array $msg 错误信息
  1440. * @param mixed $rule 验证规则数据
  1441. * @param string $title 字段描述名
  1442. * @return array
  1443. */
  1444. protected function errorMsgIsArray(array $msg, $rule, string $title)
  1445. {
  1446. foreach ($msg as $key => $val) {
  1447. if (is_string($val)) {
  1448. $msg[$key] = $this->parseErrorMsg($val, $rule, $title);
  1449. }
  1450. }
  1451. return $msg;
  1452. }
  1453. /**
  1454. * 获取数据验证的场景
  1455. * @access protected
  1456. * @param string $scene 验证场景
  1457. * @return void
  1458. */
  1459. protected function getScene(string $scene): void
  1460. {
  1461. $this->only = $this->append = $this->remove = [];
  1462. if (method_exists($this, 'scene' . $scene)) {
  1463. call_user_func([$this, 'scene' . $scene]);
  1464. } elseif (isset($this->scene[$scene])) {
  1465. // 如果设置了验证适用场景
  1466. $this->only = $this->scene[$scene];
  1467. }
  1468. }
  1469. /**
  1470. * 动态方法 直接调用is方法进行验证
  1471. * @access public
  1472. * @param string $method 方法名
  1473. * @param array $args 调用参数
  1474. * @return bool
  1475. */
  1476. public function __call($method, $args)
  1477. {
  1478. if ('is' == strtolower(substr($method, 0, 2))) {
  1479. $method = substr($method, 2);
  1480. }
  1481. array_push($args, lcfirst($method));
  1482. return call_user_func_array([$this, 'is'], $args);
  1483. }
  1484. }