Completed
Pull Request — 6.0 (#2510)
by
unknown
03:50 queued 01:55
created

Validate::getErrorKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
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 <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think;
14
15
use Closure;
16
use think\exception\ValidateException;
17
use think\helper\Str;
18
use think\validate\ValidateRule;
19
20
/**
21
 * 数据验证类
22
 * @package think
23
 */
24
class Validate
25
{
26
    /**
27
     * 自定义验证类型
28
     * @var array
29
     */
30
    protected $type = [];
31
32
    /**
33
     * 验证类型别名
34
     * @var array
35
     */
36
    protected $alias = [
37
        '>' => 'gt', '>=' => 'egt', '<' => 'lt', '<=' => 'elt', '=' => 'eq', 'same' => 'eq',
38
    ];
39
40
    /**
41
     * 当前验证规则
42
     * @var array
43
     */
44
    protected $rule = [];
45
46
    /**
47
     * 验证提示信息
48
     * @var array
49
     */
50
    protected $message = [];
51
52
    /**
53
     * 验证字段描述
54
     * @var array
55
     */
56
    protected $field = [];
57
58
    /**
59
     * 默认规则提示
60
     * @var array
61
     */
62
    protected $typeMsg = [
63
        'require'     => ':attribute require',
64
        'must'        => ':attribute must',
65
        'number'      => ':attribute must be numeric',
66
        'integer'     => ':attribute must be integer',
67
        'float'       => ':attribute must be float',
68
        'boolean'     => ':attribute must be bool',
69
        'email'       => ':attribute not a valid email address',
70
        'mobile'      => ':attribute not a valid mobile',
71
        'array'       => ':attribute must be a array',
72
        'accepted'    => ':attribute must be yes,on or 1',
73
        'date'        => ':attribute not a valid datetime',
74
        'file'        => ':attribute not a valid file',
75
        'image'       => ':attribute not a valid image',
76
        'alpha'       => ':attribute must be alpha',
77
        'alphaNum'    => ':attribute must be alpha-numeric',
78
        'alphaDash'   => ':attribute must be alpha-numeric, dash, underscore',
79
        'activeUrl'   => ':attribute not a valid domain or ip',
80
        'chs'         => ':attribute must be chinese',
81
        'chsAlpha'    => ':attribute must be chinese or alpha',
82
        'chsAlphaNum' => ':attribute must be chinese,alpha-numeric',
83
        'chsDash'     => ':attribute must be chinese,alpha-numeric,underscore, dash',
84
        'url'         => ':attribute not a valid url',
85
        'ip'          => ':attribute not a valid ip',
86
        'dateFormat'  => ':attribute must be dateFormat of :rule',
87
        'in'          => ':attribute must be in :rule',
88
        'notIn'       => ':attribute be notin :rule',
89
        'between'     => ':attribute must between :1 - :2',
90
        'notBetween'  => ':attribute not between :1 - :2',
91
        'length'      => 'size of :attribute must be :rule',
92
        'max'         => 'max size of :attribute must be :rule',
93
        'min'         => 'min size of :attribute must be :rule',
94
        'after'       => ':attribute cannot be less than :rule',
95
        'before'      => ':attribute cannot exceed :rule',
96
        'expire'      => ':attribute not within :rule',
97
        'allowIp'     => 'access IP is not allowed',
98
        'denyIp'      => 'access IP denied',
99
        'confirm'     => ':attribute out of accord with :2',
100
        'different'   => ':attribute cannot be same with :2',
101
        'egt'         => ':attribute must greater than or equal :rule',
102
        'gt'          => ':attribute must greater than :rule',
103
        'elt'         => ':attribute must less than or equal :rule',
104
        'lt'          => ':attribute must less than :rule',
105
        'eq'          => ':attribute must equal :rule',
106
        'unique'      => ':attribute has exists',
107
        'regex'       => ':attribute not conform to the rules',
108
        'method'      => 'invalid Request method',
109
        'token'       => 'invalid token',
110
        'fileSize'    => 'filesize not match',
111
        'fileExt'     => 'extensions to upload is not allowed',
112
        'fileMime'    => 'mimetype to upload is not allowed',
113
    ];
114
115
    /**
116
     * 当前验证场景
117
     * @var string
118
     */
119
    protected $currentScene;
120
121
    /**
122
     * 内置正则验证规则
123
     * @var array
124
     */
125
    protected $defaultRegex = [
126
        'alpha'       => '/^[A-Za-z]+$/',
127
        'alphaNum'    => '/^[A-Za-z0-9]+$/',
128
        'alphaDash'   => '/^[A-Za-z0-9\-\_]+$/',
129
        'chs'         => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}]+$/u',
130
        'chsAlpha'    => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z]+$/u',
131
        'chsAlphaNum' => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z0-9]+$/u',
132
        'chsDash'     => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z0-9\_\-]+$/u',
133
        'mobile'      => '/^1[3-9]\d{9}$/',
134
        '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}$)/',
135
        'zip'         => '/\d{6}/',
136
    ];
137
138
    /**
139
     * Filter_var 规则
140
     * @var array
141
     */
142
    protected $filter = [
143
        'email'   => FILTER_VALIDATE_EMAIL,
144
        'ip'      => [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6],
145
        'integer' => FILTER_VALIDATE_INT,
146
        'url'     => FILTER_VALIDATE_URL,
147
        'macAddr' => FILTER_VALIDATE_MAC,
148
        'float'   => FILTER_VALIDATE_FLOAT,
149
    ];
150
151
    /**
152
     * 验证场景定义
153
     * @var array
154
     */
155
    protected $scene = [];
156
157
    /**
158
     * 验证失败错误键名
159
     * @var string
160
     */
161
    protected $errorKey = '';
162
163
    /**
164
     * 验证失败错误信息
165
     * @var string|array
166
     */
167
    protected $error = [];
168
169
    /**
170
     * 是否批量验证
171
     * @var bool
172
     */
173
    protected $batch = false;
174
175
    /**
176
     * 验证失败是否抛出异常
177
     * @var bool
178
     */
179
    protected $failException = false;
180
181
    /**
182
     * 场景需要验证的规则
183
     * @var array
184
     */
185
    protected $only = [];
186
187
    /**
188
     * 场景需要移除的验证规则
189
     * @var array
190
     */
191
    protected $remove = [];
192
193
    /**
194
     * 场景需要追加的验证规则
195
     * @var array
196
     */
197
    protected $append = [];
198
199
    /**
200
     * 验证正则定义
201
     * @var array
202
     */
203
    protected $regex = [];
204
205
    /**
206
     * Db对象
207
     * @var Db
208
     */
209
    protected $db;
210
211
    /**
212
     * 语言对象
213
     * @var Lang
214
     */
215
    protected $lang;
216
217
    /**
218
     * 请求对象
219
     * @var Request
220
     */
221
    protected $request;
222
223
    /**
224
     * @var Closure[]
225
     */
226
    protected static $maker = [];
227
228
    /**
229
     * 构造方法
230
     * @access public
231
     */
232
    public function __construct()
233
    {
234
        if (!empty(static::$maker)) {
235
            foreach (static::$maker as $maker) {
236
                call_user_func($maker, $this);
237
            }
238
        }
239
    }
240
241
    /**
242
     * 设置服务注入
243
     * @access public
244
     * @param Closure $maker
245
     * @return void
246
     */
247
    public static function maker(Closure $maker)
248
    {
249
        static::$maker[] = $maker;
250
    }
251
252
    /**
253
     * 设置Lang对象
254
     * @access public
255
     * @param Lang $lang Lang对象
256
     * @return void
257
     */
258
    public function setLang(Lang $lang)
259
    {
260
        $this->lang = $lang;
261
    }
262
263
    /**
264
     * 设置Db对象
265
     * @access public
266
     * @param Db $db Db对象
267
     * @return void
268
     */
269
    public function setDb(Db $db)
270
    {
271
        $this->db = $db;
272
    }
273
274
    /**
275
     * 设置Request对象
276
     * @access public
277
     * @param Request $request Request对象
278
     * @return void
279
     */
280
    public function setRequest(Request $request)
281
    {
282
        $this->request = $request;
283
    }
284
285
    /**
286
     * 添加字段验证规则
287
     * @access protected
288
     * @param string|array $name 字段名称或者规则数组
289
     * @param mixed        $rule 验证规则或者字段描述信息
290
     * @return $this
291
     */
292
    public function rule($name, $rule = '')
293
    {
294
        if (is_array($name)) {
295
            $this->rule = $name + $this->rule;
296
            if (is_array($rule)) {
297
                $this->field = array_merge($this->field, $rule);
298
            }
299
        } else {
300
            $this->rule[$name] = $rule;
301
        }
302
303
        return $this;
304
    }
305
306
    /**
307
     * 注册验证(类型)规则
308
     * @access public
309
     * @param string   $type     验证规则类型
310
     * @param callable $callback callback方法(或闭包)
311
     * @param string   $message  验证失败提示信息
312
     * @return $this
313
     */
314
    public function extend(string $type, callable $callback = null, string $message = null)
315
    {
316
        $this->type[$type] = $callback;
317
318
        if ($message) {
319
            $this->typeMsg[$type] = $message;
320
        }
321
322
        return $this;
323
    }
324
325
    /**
326
     * 设置验证规则的默认提示信息
327
     * @access public
328
     * @param string|array $type 验证规则类型名称或者数组
329
     * @param string       $msg  验证提示信息
330
     * @return void
331
     */
332
    public function setTypeMsg($type, string $msg = null): void
333
    {
334
        if (is_array($type)) {
335
            $this->typeMsg = array_merge($this->typeMsg, $type);
336
        } else {
337
            $this->typeMsg[$type] = $msg;
338
        }
339
    }
340
341
    /**
342
     * 设置提示信息
343
     * @access public
344
     * @param array $message 错误信息
345
     * @return Validate
346
     */
347
    public function message(array $message)
348
    {
349
        $this->message = array_merge($this->message, $message);
350
351
        return $this;
352
    }
353
354
    /**
355
     * 设置验证场景
356
     * @access public
357
     * @param string $name 场景名
358
     * @return $this
359
     */
360
    public function scene(string $name)
361
    {
362
        // 设置当前场景
363
        $this->currentScene = $name;
364
365
        return $this;
366
    }
367
368
    /**
369
     * 判断是否存在某个验证场景
370
     * @access public
371
     * @param string $name 场景名
372
     * @return bool
373
     */
374
    public function hasScene(string $name): bool
375
    {
376
        return isset($this->scene[$name]) || method_exists($this, 'scene' . $name);
377
    }
378
379
    /**
380
     * 设置批量验证
381
     * @access public
382
     * @param bool $batch 是否批量验证
383
     * @return $this
384
     */
385
    public function batch(bool $batch = true)
386
    {
387
        $this->batch = $batch;
388
389
        return $this;
390
    }
391
392
    /**
393
     * 设置验证失败后是否抛出异常
394
     * @access protected
395
     * @param bool $fail 是否抛出异常
396
     * @return $this
397
     */
398
    public function failException(bool $fail = true)
399
    {
400
        $this->failException = $fail;
401
402
        return $this;
403
    }
404
405
    /**
406
     * 指定需要验证的字段列表
407
     * @access public
408
     * @param array $fields 字段名
409
     * @return $this
410
     */
411
    public function only(array $fields)
412
    {
413
        $this->only = $fields;
414
415
        return $this;
416
    }
417
418
    /**
419
     * 移除某个字段的验证规则
420
     * @access public
421
     * @param string|array $field 字段名
422
     * @param mixed        $rule  验证规则 true 移除所有规则
423
     * @return $this
424
     */
425
    public function remove($field, $rule = null)
426
    {
427
        if (is_array($field)) {
428
            foreach ($field as $key => $rule) {
429
                if (is_int($key)) {
430
                    $this->remove($rule);
431
                } else {
432
                    $this->remove($key, $rule);
433
                }
434
            }
435
        } else {
436
            if (is_string($rule)) {
437
                $rule = explode('|', $rule);
438
            }
439
440
            $this->remove[$field] = $rule;
441
        }
442
443
        return $this;
444
    }
445
446
    /**
447
     * 追加某个字段的验证规则
448
     * @access public
449
     * @param string|array $field 字段名
450
     * @param mixed        $rule  验证规则
451
     * @return $this
452
     */
453
    public function append($field, $rule = null)
454
    {
455
        if (is_array($field)) {
456
            foreach ($field as $key => $rule) {
457
                $this->append($key, $rule);
458
            }
459
        } else {
460
            if (is_string($rule)) {
461
                $rule = explode('|', $rule);
462
            }
463
464
            $this->append[$field] = $rule;
465
        }
466
467
        return $this;
468
    }
469
470
    /**
471
     * 数据自动验证
472
     * @access public
473
     * @param array $data  数据
474
     * @param array $rules 验证规则
475
     * @return bool
476
     */
477
    public function check(array $data, array $rules = []): bool
478
    {
479
        $this->error = [];
480
481
        if ($this->currentScene) {
482
            $this->getScene($this->currentScene);
483
        }
484
485
        if (empty($rules)) {
486
            // 读取验证规则
487
            $rules = $this->rule;
488
        }
489
490
        foreach ($this->append as $key => $rule) {
491
            if (!isset($rules[$key])) {
492
                $rules[$key] = $rule;
493
                unset($this->append[$key]);
494
            }
495
        }
496
497
        foreach ($rules as $key => $rule) {
498
            // field => 'rule1|rule2...' field => ['rule1','rule2',...]
499
            if (strpos($key, '|')) {
500
                // 字段|描述 用于指定属性名称
501
                [$key, $title] = explode('|', $key);
502
            } else {
503
                $title = $this->field[$key] ?? $key;
504
            }
505
506
            // 场景检测
507
            if (!empty($this->only) && !in_array($key, $this->only)) {
508
                continue;
509
            }
510
511
            // 获取数据 支持二维数组
512
            $value = $this->getDataValue($data, $key);
513
514
            // 字段验证
515
            if ($rule instanceof Closure) {
516
                $result = call_user_func_array($rule, [$value, $data]);
517
            } elseif ($rule instanceof ValidateRule) {
518
                //  验证因子
519
                $result = $this->checkItem($key, $value, $rule->getRule(), $data, $rule->getTitle() ?: $title, $rule->getMsg());
520
            } else {
521
                $result = $this->checkItem($key, $value, $rule, $data, $title);
522
            }
523
524
            if (true !== $result) {
525
                // 没有返回true 则表示验证失败
526
                if (!empty($this->batch)) {
527
                    // 批量验证
528
                    $this->error[$key] = $result;
529
                } elseif ($this->failException) {
530
                    throw new ValidateException($result);
531
                } else {
532
                    $this->error = $result;
533
                    $this->errorKey = $key;
534
                    return false;
535
                }
536
            }
537
        }
538
539
        if (!empty($this->error)) {
540
            if ($this->failException) {
541
                throw new ValidateException($this->error);
542
            }
543
            return false;
544
        }
545
546
        return true;
547
    }
548
549
    /**
550
     * 根据验证规则验证数据
551
     * @access public
552
     * @param mixed $value 字段值
553
     * @param mixed $rules 验证规则
554
     * @return bool
555
     */
556
    public function checkRule($value, $rules): bool
557
    {
558
        if ($rules instanceof Closure) {
559
            return call_user_func_array($rules, [$value]);
560
        } elseif ($rules instanceof ValidateRule) {
561
            $rules = $rules->getRule();
562
        } elseif (is_string($rules)) {
563
            $rules = explode('|', $rules);
564
        }
565
566
        foreach ($rules as $key => $rule) {
567
            if ($rule instanceof Closure) {
568
                $result = call_user_func_array($rule, [$value]);
569
            } else {
570
                // 判断验证类型
571
                [$type, $rule] = $this->getValidateType($key, $rule);
572
573
                $callback = $this->type[$type] ?? [$this, $type];
574
575
                $result = call_user_func_array($callback, [$value, $rule]);
576
            }
577
578
            if (true !== $result) {
579
                if ($this->failException) {
580
                    throw new ValidateException($result);
581
                }
582
583
                return $result;
584
            }
585
        }
586
587
        return true;
588
    }
589
590
    /**
591
     * 验证单个字段规则
592
     * @access protected
593
     * @param string $field 字段名
594
     * @param mixed  $value 字段值
595
     * @param mixed  $rules 验证规则
596
     * @param array  $data  数据
597
     * @param string $title 字段描述
598
     * @param array  $msg   提示信息
599
     * @return mixed
600
     */
601
    protected function checkItem(string $field, $value, $rules, $data, string $title = '', array $msg = [])
602
    {
603
        if (isset($this->remove[$field]) && true === $this->remove[$field] && empty($this->append[$field])) {
604
            // 字段已经移除 无需验证
605
            return true;
606
        }
607
608
        // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...]
609
        if (is_string($rules)) {
610
            $rules = explode('|', $rules);
611
        }
612
613
        if (isset($this->append[$field])) {
614
            // 追加额外的验证规则
615
            $rules = array_unique(array_merge($rules, $this->append[$field]), SORT_REGULAR);
616
            unset($this->append[$field]);
617
        }
618
619
        if (empty($rules)) {
620
            return true;
621
        }
622
623
        $i = 0;
624
        foreach ($rules as $key => $rule) {
625
            if ($rule instanceof Closure) {
626
                $result = call_user_func_array($rule, [$value, $data]);
627
                $info   = is_numeric($key) ? '' : $key;
628
            } else {
629
                // 判断验证类型
630
                [$type, $rule, $info] = $this->getValidateType($key, $rule);
631
632
                if (isset($this->append[$field]) && in_array($info, $this->append[$field])) {
633
                } elseif (isset($this->remove[$field]) && in_array($info, $this->remove[$field])) {
634
                    // 规则已经移除
635
                    $i++;
636
                    continue;
637
                }
638
639
                if (isset($this->type[$type])) {
640
                    $result = call_user_func_array($this->type[$type], [$value, $rule, $data, $field, $title]);
641
                } elseif ('must' == $info || 0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
642
                    $result = call_user_func_array([$this, $type], [$value, $rule, $data, $field, $title]);
643
                } else {
644
                    $result = true;
645
                }
646
            }
647
648
            if (false === $result) {
649
                // 验证失败 返回错误信息
650
                if (!empty($msg[$i])) {
651
                    $message = $msg[$i];
652
                    if (is_string($message) && strpos($message, '{%') === 0) {
653
                        $message = $this->lang->get(substr($message, 2, -1));
654
                    }
655
                } else {
656
                    $message = $this->getRuleMsg($field, $title, $info, $rule);
657
                }
658
659
                return $message;
660
            } elseif (true !== $result) {
661
                // 返回自定义错误信息
662
                if (is_string($result) && false !== strpos($result, ':')) {
663
                    $result = str_replace(':attribute', $title, $result);
664
665
                    if (strpos($result, ':rule') && is_scalar($rule)) {
666
                        $result = str_replace(':rule', (string) $rule, $result);
667
                    }
668
                }
669
670
                return $result;
671
            }
672
            $i++;
673
        }
674
675
        return $result ?? true;
676
    }
677
678
    /**
679
     * 获取当前验证类型及规则
680
     * @access public
681
     * @param mixed $key
682
     * @param mixed $rule
683
     * @return array
684
     */
685
    protected function getValidateType($key, $rule): array
686
    {
687
        // 判断验证类型
688
        if (!is_numeric($key)) {
689
            if (isset($this->alias[$key])) {
690
                // 判断别名
691
                $key = $this->alias[$key];
692
            }
693
            return [$key, $rule, $key];
694
        }
695
696
        if (strpos($rule, ':')) {
697
            [$type, $rule] = explode(':', $rule, 2);
698
            if (isset($this->alias[$type])) {
699
                // 判断别名
700
                $type = $this->alias[$type];
701
            }
702
            $info = $type;
703
        } elseif (method_exists($this, $rule)) {
704
            $type = $rule;
705
            $info = $rule;
706
            $rule = '';
707
        } else {
708
            $type = 'is';
709
            $info = $rule;
710
        }
711
712
        return [$type, $rule, $info];
713
    }
714
715
    /**
716
     * 验证是否和某个字段的值一致
717
     * @access public
718
     * @param mixed  $value 字段值
719
     * @param mixed  $rule  验证规则
720
     * @param array  $data  数据
721
     * @param string $field 字段名
722
     * @return bool
723
     */
724
    public function confirm($value, $rule, array $data = [], string $field = ''): bool
725
    {
726
        if ('' == $rule) {
727
            if (strpos($field, '_confirm')) {
728
                $rule = strstr($field, '_confirm', true);
729
            } else {
730
                $rule = $field . '_confirm';
731
            }
732
        }
733
734
        return $this->getDataValue($data, $rule) === $value;
735
    }
736
737
    /**
738
     * 验证是否和某个字段的值是否不同
739
     * @access public
740
     * @param mixed $value 字段值
741
     * @param mixed $rule  验证规则
742
     * @param array $data  数据
743
     * @return bool
744
     */
745
    public function different($value, $rule, array $data = []): bool
746
    {
747
        return $this->getDataValue($data, $rule) != $value;
748
    }
749
750
    /**
751
     * 验证是否大于等于某个值
752
     * @access public
753
     * @param mixed $value 字段值
754
     * @param mixed $rule  验证规则
755
     * @param array $data  数据
756
     * @return bool
757
     */
758
    public function egt($value, $rule, array $data = []): bool
759
    {
760
        return $value >= $this->getDataValue($data, $rule);
761
    }
762
763
    /**
764
     * 验证是否大于某个值
765
     * @access public
766
     * @param mixed $value 字段值
767
     * @param mixed $rule  验证规则
768
     * @param array $data  数据
769
     * @return bool
770
     */
771
    public function gt($value, $rule, array $data = []): bool
772
    {
773
        return $value > $this->getDataValue($data, $rule);
774
    }
775
776
    /**
777
     * 验证是否小于等于某个值
778
     * @access public
779
     * @param mixed $value 字段值
780
     * @param mixed $rule  验证规则
781
     * @param array $data  数据
782
     * @return bool
783
     */
784
    public function elt($value, $rule, array $data = []): bool
785
    {
786
        return $value <= $this->getDataValue($data, $rule);
787
    }
788
789
    /**
790
     * 验证是否小于某个值
791
     * @access public
792
     * @param mixed $value 字段值
793
     * @param mixed $rule  验证规则
794
     * @param array $data  数据
795
     * @return bool
796
     */
797
    public function lt($value, $rule, array $data = []): bool
798
    {
799
        return $value < $this->getDataValue($data, $rule);
800
    }
801
802
    /**
803
     * 验证是否等于某个值
804
     * @access public
805
     * @param mixed $value 字段值
806
     * @param mixed $rule  验证规则
807
     * @return bool
808
     */
809
    public function eq($value, $rule): bool
810
    {
811
        return $value == $rule;
812
    }
813
814
    /**
815
     * 必须验证
816
     * @access public
817
     * @param mixed $value 字段值
818
     * @param mixed $rule  验证规则
819
     * @return bool
820
     */
821
    public function must($value, $rule = null): bool
822
    {
823
        return !empty($value) || '0' == $value;
824
    }
825
826
    /**
827
     * 验证字段值是否为有效格式
828
     * @access public
829
     * @param mixed  $value 字段值
830
     * @param string $rule  验证规则
831
     * @param array  $data  数据
832
     * @return bool
833
     */
834
    public function is($value, string $rule, array $data = []): bool
835
    {
836
        switch (Str::camel($rule)) {
837
            case 'require':
838
                // 必须
839
                $result = !empty($value) || '0' == $value;
840
                break;
841
            case 'accepted':
842
                // 接受
843
                $result = in_array($value, ['1', 'on', 'yes']);
844
                break;
845
            case 'date':
846
                // 是否是一个有效日期
847
                $result = false !== strtotime($value);
848
                break;
849
            case 'activeUrl':
850
                // 是否为有效的网址
851
                $result = checkdnsrr($value);
852
                break;
853
            case 'boolean':
854
            case 'bool':
855
                // 是否为布尔值
856
                $result = in_array($value, [true, false, 0, 1, '0', '1'], true);
857
                break;
858
            case 'number':
859
                $result = ctype_digit((string) $value);
860
                break;
861
            case 'alphaNum':
862
                $result = ctype_alnum($value);
863
                break;
864
            case 'array':
865
                // 是否为数组
866
                $result = is_array($value);
867
                break;
868
            case 'file':
869
                $result = $value instanceof File;
870
                break;
871
            case 'image':
872
                $result = $value instanceof File && in_array($this->getImageType($value->getRealPath()), [1, 2, 3, 6]);
873
                break;
874
            case 'token':
875
                $result = $this->token($value, '__token__', $data);
876
                break;
877
            default:
878
                if (isset($this->type[$rule])) {
879
                    // 注册的验证规则
880
                    $result = call_user_func_array($this->type[$rule], [$value]);
881
                } elseif (function_exists('ctype_' . $rule)) {
882
                    // ctype验证规则
883
                    $ctypeFun = 'ctype_' . $rule;
884
                    $result   = $ctypeFun($value);
885
                } elseif (isset($this->filter[$rule])) {
886
                    // Filter_var验证规则
887
                    $result = $this->filter($value, $this->filter[$rule]);
888
                } else {
889
                    // 正则验证
890
                    $result = $this->regex($value, $rule);
891
                }
892
        }
893
894
        return $result;
895
    }
896
897
    // 判断图像类型
898
    protected function getImageType($image)
899
    {
900
        if (function_exists('exif_imagetype')) {
901
            return exif_imagetype($image);
902
        }
903
904
        try {
905
            $info = getimagesize($image);
906
            return $info ? $info[2] : false;
907
        } catch (\Exception $e) {
908
            return false;
909
        }
910
    }
911
912
    /**
913
     * 验证表单令牌
914
     * @access public
915
     * @param mixed $value 字段值
916
     * @param mixed $rule  验证规则
917
     * @param array $data  数据
918
     * @return bool
919
     */
920
    public function token($value, string $rule, array $data): bool
921
    {
922
        $rule = !empty($rule) ? $rule : '__token__';
923
        return $this->request->checkToken($rule, $data);
924
    }
925
926
    /**
927
     * 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型
928
     * @access public
929
     * @param mixed $value 字段值
930
     * @param mixed $rule  验证规则
931
     * @return bool
932
     */
933
    public function activeUrl(string $value, string $rule = 'MX'): bool
934
    {
935
        if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) {
936
            $rule = 'MX';
937
        }
938
939
        return checkdnsrr($value, $rule);
940
    }
941
942
    /**
943
     * 验证是否有效IP
944
     * @access public
945
     * @param mixed $value 字段值
946
     * @param mixed $rule  验证规则 ipv4 ipv6
947
     * @return bool
948
     */
949
    public function ip($value, string $rule = 'ipv4'): bool
950
    {
951
        if (!in_array($rule, ['ipv4', 'ipv6'])) {
952
            $rule = 'ipv4';
953
        }
954
955
        return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]);
956
    }
957
958
    /**
959
     * 检测上传文件后缀
960
     * @access public
961
     * @param File         $file
962
     * @param array|string $ext 允许后缀
963
     * @return bool
964
     */
965
    protected function checkExt(File $file, $ext): bool
966
    {
967
        if (is_string($ext)) {
968
            $ext = explode(',', $ext);
969
        }
970
971
        return in_array(strtolower($file->extension()), $ext);
972
    }
973
974
    /**
975
     * 检测上传文件大小
976
     * @access public
977
     * @param File    $file
978
     * @param integer $size 最大大小
979
     * @return bool
980
     */
981
    protected function checkSize(File $file, $size): bool
982
    {
983
        return $file->getSize() <= (int) $size;
984
    }
985
986
    /**
987
     * 检测上传文件类型
988
     * @access public
989
     * @param File         $file
990
     * @param array|string $mime 允许类型
991
     * @return bool
992
     */
993
    protected function checkMime(File $file, $mime): bool
994
    {
995
        if (is_string($mime)) {
996
            $mime = explode(',', $mime);
997
        }
998
999
        return in_array(strtolower($file->getMime()), $mime);
1000
    }
1001
1002
    /**
1003
     * 验证上传文件后缀
1004
     * @access public
1005
     * @param mixed $file 上传文件
1006
     * @param mixed $rule 验证规则
1007
     * @return bool
1008
     */
1009
    public function fileExt($file, $rule): bool
1010
    {
1011
        if (is_array($file)) {
1012
            foreach ($file as $item) {
1013
                if (!($item instanceof File) || !$this->checkExt($item, $rule)) {
1014
                    return false;
1015
                }
1016
            }
1017
            return true;
1018
        } elseif ($file instanceof File) {
1019
            return $this->checkExt($file, $rule);
1020
        }
1021
1022
        return false;
1023
    }
1024
1025
    /**
1026
     * 验证上传文件类型
1027
     * @access public
1028
     * @param mixed $file 上传文件
1029
     * @param mixed $rule 验证规则
1030
     * @return bool
1031
     */
1032
    public function fileMime($file, $rule): bool
1033
    {
1034
        if (is_array($file)) {
1035
            foreach ($file as $item) {
1036
                if (!($item instanceof File) || !$this->checkMime($item, $rule)) {
1037
                    return false;
1038
                }
1039
            }
1040
            return true;
1041
        } elseif ($file instanceof File) {
1042
            return $this->checkMime($file, $rule);
1043
        }
1044
1045
        return false;
1046
    }
1047
1048
    /**
1049
     * 验证上传文件大小
1050
     * @access public
1051
     * @param mixed $file 上传文件
1052
     * @param mixed $rule 验证规则
1053
     * @return bool
1054
     */
1055
    public function fileSize($file, $rule): bool
1056
    {
1057
        if (is_array($file)) {
1058
            foreach ($file as $item) {
1059
                if (!($item instanceof File) || !$this->checkSize($item, $rule)) {
1060
                    return false;
1061
                }
1062
            }
1063
            return true;
1064
        } elseif ($file instanceof File) {
1065
            return $this->checkSize($file, $rule);
1066
        }
1067
1068
        return false;
1069
    }
1070
1071
    /**
1072
     * 验证图片的宽高及类型
1073
     * @access public
1074
     * @param mixed $file 上传文件
1075
     * @param mixed $rule 验证规则
1076
     * @return bool
1077
     */
1078
    public function image($file, $rule): bool
1079
    {
1080
        if (!($file instanceof File)) {
1081
            return false;
1082
        }
1083
1084
        if ($rule) {
1085
            $rule = explode(',', $rule);
1086
1087
            [$width, $height, $type] = getimagesize($file->getRealPath());
1088
1089
            if (isset($rule[2])) {
1090
                $imageType = strtolower($rule[2]);
1091
1092
                if ('jpg' == $imageType) {
1093
                    $imageType = 'jpeg';
1094
                }
1095
1096
                if (image_type_to_extension($type, false) != $imageType) {
1097
                    return false;
1098
                }
1099
            }
1100
1101
            [$w, $h] = $rule;
1102
1103
            return $w == $width && $h == $height;
1104
        }
1105
1106
        return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]);
1107
    }
1108
1109
    /**
1110
     * 验证时间和日期是否符合指定格式
1111
     * @access public
1112
     * @param mixed $value 字段值
1113
     * @param mixed $rule  验证规则
1114
     * @return bool
1115
     */
1116
    public function dateFormat($value, $rule): bool
1117
    {
1118
        $info = date_parse_from_format($rule, $value);
1119
        return 0 == $info['warning_count'] && 0 == $info['error_count'];
1120
    }
1121
1122
    /**
1123
     * 验证是否唯一
1124
     * @access public
1125
     * @param mixed  $value 字段值
1126
     * @param mixed  $rule  验证规则 格式:数据表,字段名,排除ID,主键名
1127
     * @param array  $data  数据
1128
     * @param string $field 验证字段名
1129
     * @return bool
1130
     */
1131
    public function unique($value, $rule, array $data = [], string $field = ''): bool
1132
    {
1133
        if (is_string($rule)) {
1134
            $rule = explode(',', $rule);
1135
        }
1136
1137
        if (false !== strpos($rule[0], '\\')) {
1138
            // 指定模型类
1139
            $db = new $rule[0];
1140
        } else {
1141
            $db = $this->db->name($rule[0]);
0 ignored issues
show
Bug introduced by
The method name() does not exist on think\Db. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1141
            /** @scrutinizer ignore-call */ 
1142
            $db = $this->db->name($rule[0]);
Loading history...
1142
        }
1143
1144
        $key = $rule[1] ?? $field;
1145
        $map = [];
1146
1147
        if (strpos($key, '^')) {
1148
            // 支持多个字段验证
1149
            $fields = explode('^', $key);
1150
            foreach ($fields as $key) {
1151
                if (isset($data[$key])) {
1152
                    $map[] = [$key, '=', $data[$key]];
1153
                }
1154
            }
1155
        } elseif (isset($data[$field])) {
1156
            $map[] = [$key, '=', $data[$field]];
1157
        } else {
1158
            $map = [];
1159
        }
1160
1161
        $pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
1162
1163
        if (is_string($pk)) {
1164
            if (isset($rule[2])) {
1165
                $map[] = [$pk, '<>', $rule[2]];
1166
            } elseif (isset($data[$pk])) {
1167
                $map[] = [$pk, '<>', $data[$pk]];
1168
            }
1169
        }
1170
1171
        if ($db->where($map)->field($pk)->find()) {
1172
            return false;
1173
        }
1174
1175
        return true;
1176
    }
1177
1178
    /**
1179
     * 使用filter_var方式验证
1180
     * @access public
1181
     * @param mixed $value 字段值
1182
     * @param mixed $rule  验证规则
1183
     * @return bool
1184
     */
1185
    public function filter($value, $rule): bool
1186
    {
1187
        if (is_string($rule) && strpos($rule, ',')) {
1188
            [$rule, $param] = explode(',', $rule);
1189
        } elseif (is_array($rule)) {
1190
            $param = $rule[1] ?? 0;
1191
            $rule  = $rule[0];
1192
        } else {
1193
            $param = 0;
1194
        }
1195
1196
        return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
1197
    }
1198
1199
    /**
1200
     * 验证某个字段等于某个值的时候必须
1201
     * @access public
1202
     * @param mixed $value 字段值
1203
     * @param mixed $rule  验证规则
1204
     * @param array $data  数据
1205
     * @return bool
1206
     */
1207
    public function requireIf($value, $rule, array $data = []): bool
1208
    {
1209
        [$field, $val] = explode(',', $rule);
1210
1211
        if ($this->getDataValue($data, $field) == $val) {
1212
            return !empty($value) || '0' == $value;
1213
        }
1214
1215
        return true;
1216
    }
1217
1218
    /**
1219
     * 通过回调方法验证某个字段是否必须
1220
     * @access public
1221
     * @param mixed $value 字段值
1222
     * @param mixed $rule  验证规则
1223
     * @param array $data  数据
1224
     * @return bool
1225
     */
1226
    public function requireCallback($value, $rule, array $data = []): bool
1227
    {
1228
        $result = call_user_func_array([$this, $rule], [$value, $data]);
1229
1230
        if ($result) {
1231
            return !empty($value) || '0' == $value;
1232
        }
1233
1234
        return true;
1235
    }
1236
1237
    /**
1238
     * 验证某个字段有值的情况下必须
1239
     * @access public
1240
     * @param mixed $value 字段值
1241
     * @param mixed $rule  验证规则
1242
     * @param array $data  数据
1243
     * @return bool
1244
     */
1245
    public function requireWith($value, $rule, array $data = []): bool
1246
    {
1247
        $val = $this->getDataValue($data, $rule);
1248
1249
        if (!empty($val)) {
1250
            return !empty($value) || '0' == $value;
1251
        }
1252
1253
        return true;
1254
    }
1255
1256
    /**
1257
     * 验证某个字段没有值的情况下必须
1258
     * @access public
1259
     * @param mixed $value 字段值
1260
     * @param mixed $rule  验证规则
1261
     * @param array $data  数据
1262
     * @return bool
1263
     */
1264
    public function requireWithout($value, $rule, array $data = []): bool
1265
    {
1266
        $val = $this->getDataValue($data, $rule);
1267
1268
        if (empty($val)) {
1269
            return !empty($value) || '0' == $value;
1270
        }
1271
1272
        return true;
1273
    }
1274
1275
    /**
1276
     * 验证是否在范围内
1277
     * @access public
1278
     * @param mixed $value 字段值
1279
     * @param mixed $rule  验证规则
1280
     * @return bool
1281
     */
1282
    public function in($value, $rule): bool
1283
    {
1284
        return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1285
    }
1286
1287
    /**
1288
     * 验证是否不在某个范围
1289
     * @access public
1290
     * @param mixed $value 字段值
1291
     * @param mixed $rule  验证规则
1292
     * @return bool
1293
     */
1294
    public function notIn($value, $rule): bool
1295
    {
1296
        return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1297
    }
1298
1299
    /**
1300
     * between验证数据
1301
     * @access public
1302
     * @param mixed $value 字段值
1303
     * @param mixed $rule  验证规则
1304
     * @return bool
1305
     */
1306
    public function between($value, $rule): bool
1307
    {
1308
        if (is_string($rule)) {
1309
            $rule = explode(',', $rule);
1310
        }
1311
        [$min, $max] = $rule;
1312
1313
        return $value >= $min && $value <= $max;
1314
    }
1315
1316
    /**
1317
     * 使用notbetween验证数据
1318
     * @access public
1319
     * @param mixed $value 字段值
1320
     * @param mixed $rule  验证规则
1321
     * @return bool
1322
     */
1323
    public function notBetween($value, $rule): bool
1324
    {
1325
        if (is_string($rule)) {
1326
            $rule = explode(',', $rule);
1327
        }
1328
        [$min, $max] = $rule;
1329
1330
        return $value < $min || $value > $max;
1331
    }
1332
1333
    /**
1334
     * 验证数据长度
1335
     * @access public
1336
     * @param mixed $value 字段值
1337
     * @param mixed $rule  验证规则
1338
     * @return bool
1339
     */
1340
    public function length($value, $rule): bool
1341
    {
1342
        if (is_array($value)) {
1343
            $length = count($value);
1344
        } elseif ($value instanceof File) {
1345
            $length = $value->getSize();
1346
        } else {
1347
            $length = mb_strlen((string) $value);
1348
        }
1349
1350
        if (is_string($rule) && strpos($rule, ',')) {
1351
            // 长度区间
1352
            [$min, $max] = explode(',', $rule);
1353
            return $length >= $min && $length <= $max;
1354
        }
1355
1356
        // 指定长度
1357
        return $length == $rule;
1358
    }
1359
1360
    /**
1361
     * 验证数据最大长度
1362
     * @access public
1363
     * @param mixed $value 字段值
1364
     * @param mixed $rule  验证规则
1365
     * @return bool
1366
     */
1367
    public function max($value, $rule): bool
1368
    {
1369
        if (is_array($value)) {
1370
            $length = count($value);
1371
        } elseif ($value instanceof File) {
1372
            $length = $value->getSize();
1373
        } else {
1374
            $length = mb_strlen((string) $value);
1375
        }
1376
1377
        return $length <= $rule;
1378
    }
1379
1380
    /**
1381
     * 验证数据最小长度
1382
     * @access public
1383
     * @param mixed $value 字段值
1384
     * @param mixed $rule  验证规则
1385
     * @return bool
1386
     */
1387
    public function min($value, $rule): bool
1388
    {
1389
        if (is_array($value)) {
1390
            $length = count($value);
1391
        } elseif ($value instanceof File) {
1392
            $length = $value->getSize();
1393
        } else {
1394
            $length = mb_strlen((string) $value);
1395
        }
1396
1397
        return $length >= $rule;
1398
    }
1399
1400
    /**
1401
     * 验证日期
1402
     * @access public
1403
     * @param mixed $value 字段值
1404
     * @param mixed $rule  验证规则
1405
     * @param array $data  数据
1406
     * @return bool
1407
     */
1408
    public function after($value, $rule, array $data = []): bool
1409
    {
1410
        return strtotime($value) >= strtotime($rule);
1411
    }
1412
1413
    /**
1414
     * 验证日期
1415
     * @access public
1416
     * @param mixed $value 字段值
1417
     * @param mixed $rule  验证规则
1418
     * @param array $data  数据
1419
     * @return bool
1420
     */
1421
    public function before($value, $rule, array $data = []): bool
1422
    {
1423
        return strtotime($value) <= strtotime($rule);
1424
    }
1425
1426
    /**
1427
     * 验证日期
1428
     * @access public
1429
     * @param mixed $value 字段值
1430
     * @param mixed $rule  验证规则
1431
     * @param array $data  数据
1432
     * @return bool
1433
     */
1434
    public function afterWith($value, $rule, array $data = []): bool
1435
    {
1436
        $rule = $this->getDataValue($data, $rule);
1437
        return !is_null($rule) && strtotime($value) >= strtotime($rule);
1438
    }
1439
1440
    /**
1441
     * 验证日期
1442
     * @access public
1443
     * @param mixed $value 字段值
1444
     * @param mixed $rule  验证规则
1445
     * @param array $data  数据
1446
     * @return bool
1447
     */
1448
    public function beforeWith($value, $rule, array $data = []): bool
1449
    {
1450
        $rule = $this->getDataValue($data, $rule);
1451
        return !is_null($rule) && strtotime($value) <= strtotime($rule);
1452
    }
1453
1454
    /**
1455
     * 验证有效期
1456
     * @access public
1457
     * @param mixed $value 字段值
1458
     * @param mixed $rule  验证规则
1459
     * @return bool
1460
     */
1461
    public function expire($value, $rule): bool
1462
    {
1463
        if (is_string($rule)) {
1464
            $rule = explode(',', $rule);
1465
        }
1466
1467
        [$start, $end] = $rule;
1468
1469
        if (!is_numeric($start)) {
1470
            $start = strtotime($start);
1471
        }
1472
1473
        if (!is_numeric($end)) {
1474
            $end = strtotime($end);
1475
        }
1476
1477
        return time() >= $start && time() <= $end;
1478
    }
1479
1480
    /**
1481
     * 验证IP许可
1482
     * @access public
1483
     * @param mixed $value 字段值
1484
     * @param mixed $rule  验证规则
1485
     * @return bool
1486
     */
1487
    public function allowIp($value, $rule): bool
1488
    {
1489
        return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1490
    }
1491
1492
    /**
1493
     * 验证IP禁用
1494
     * @access public
1495
     * @param mixed $value 字段值
1496
     * @param mixed $rule  验证规则
1497
     * @return bool
1498
     */
1499
    public function denyIp($value, $rule): bool
1500
    {
1501
        return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1502
    }
1503
1504
    /**
1505
     * 使用正则验证数据
1506
     * @access public
1507
     * @param mixed $value 字段值
1508
     * @param mixed $rule  验证规则 正则规则或者预定义正则名
1509
     * @return bool
1510
     */
1511
    public function regex($value, $rule): bool
1512
    {
1513
        if (isset($this->regex[$rule])) {
1514
            $rule = $this->regex[$rule];
1515
        } elseif (isset($this->defaultRegex[$rule])) {
1516
            $rule = $this->defaultRegex[$rule];
1517
        }
1518
1519
        if (is_string($rule) && 0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
1520
            // 不是正则表达式则两端补上/
1521
            $rule = '/^' . $rule . '$/';
1522
        }
1523
1524
        return is_scalar($value) && 1 === preg_match($rule, (string) $value);
1525
    }
1526
1527
    /**
1528
     * 获取错误键名
1529
     * @return string
1530
     */
1531
    public function getErrorKey()
1532
    {
1533
        return $this->errorKey;
1534
    }
1535
1536
    /**
1537
     * 获取错误信息
1538
     * @return array|string
1539
     */
1540
    public function getError()
1541
    {
1542
        return $this->error;
1543
    }
1544
1545
    /**
1546
     * 获取数据值
1547
     * @access protected
1548
     * @param array  $data 数据
1549
     * @param string $key  数据标识 支持二维
1550
     * @return mixed
1551
     */
1552
    protected function getDataValue(array $data, $key)
1553
    {
1554
        if (is_numeric($key)) {
1555
            $value = $key;
1556
        } elseif (is_string($key) && strpos($key, '.')) {
1557
            // 支持多维数组验证
1558
            foreach (explode('.', $key) as $key) {
0 ignored issues
show
introduced by
$key is overwriting one of the parameters of this function.
Loading history...
1559
                if (!isset($data[$key])) {
1560
                    $value = null;
1561
                    break;
1562
                }
1563
                $value = $data = $data[$key];
1564
            }
1565
        } else {
1566
            $value = $data[$key] ?? null;
1567
        }
1568
1569
        return $value;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.
Loading history...
1570
    }
1571
1572
    /**
1573
     * 获取验证规则的错误提示信息
1574
     * @access protected
1575
     * @param string $attribute 字段英文名
1576
     * @param string $title     字段描述名
1577
     * @param string $type      验证规则名称
1578
     * @param mixed  $rule      验证规则数据
1579
     * @return string|array
1580
     */
1581
    protected function getRuleMsg(string $attribute, string $title, string $type, $rule)
1582
    {
1583
        if (isset($this->message[$attribute . '.' . $type])) {
1584
            $msg = $this->message[$attribute . '.' . $type];
1585
        } elseif (isset($this->message[$attribute][$type])) {
1586
            $msg = $this->message[$attribute][$type];
1587
        } elseif (isset($this->message[$attribute])) {
1588
            $msg = $this->message[$attribute];
1589
        } elseif (isset($this->typeMsg[$type])) {
1590
            $msg = $this->typeMsg[$type];
1591
        } elseif (0 === strpos($type, 'require')) {
1592
            $msg = $this->typeMsg['require'];
1593
        } else {
1594
            $msg = $title . $this->lang->get('not conform to the rules');
1595
        }
1596
1597
        if (is_array($msg)) {
1598
            return $this->errorMsgIsArray($msg, $rule, $title);
1599
        }
1600
1601
        return $this->parseErrorMsg($msg, $rule, $title);
1602
    }
1603
1604
    /**
1605
     * 获取验证规则的错误提示信息
1606
     * @access protected
1607
     * @param string $msg   错误信息
1608
     * @param mixed  $rule  验证规则数据
1609
     * @param string $title 字段描述名
1610
     * @return string|array
1611
     */
1612
    protected function parseErrorMsg(string $msg, $rule, string $title)
1613
    {
1614
        if (0 === strpos($msg, '{%')) {
1615
            $msg = $this->lang->get(substr($msg, 2, -1));
1616
        } elseif ($this->lang->has($msg)) {
1617
            $msg = $this->lang->get($msg);
1618
        }
1619
1620
        if (is_array($msg)) {
1621
            return $this->errorMsgIsArray($msg, $rule, $title);
1622
        }
1623
1624
        // rule若是数组则转为字符串
1625
        if (is_array($rule)) {
1626
            $rule = implode(',', $rule);
1627
        }
1628
1629
        if (is_scalar($rule) && false !== strpos($msg, ':')) {
1630
            // 变量替换
1631
            if (is_string($rule) && strpos($rule, ',')) {
1632
                $array = array_pad(explode(',', $rule), 3, '');
1633
            } else {
1634
                $array = array_pad([], 3, '');
1635
            }
1636
1637
            $msg = str_replace(
1638
                [':attribute', ':1', ':2', ':3'],
1639
                [$title, $array[0], $array[1], $array[2]],
1640
                $msg
1641
            );
1642
1643
            if (strpos($msg, ':rule')) {
1644
                $msg = str_replace(':rule', (string) $rule, $msg);
1645
            }
1646
        }
1647
1648
        return $msg;
1649
    }
1650
1651
    /**
1652
     * 错误信息数组处理
1653
     * @access protected
1654
     * @param array $msg   错误信息
1655
     * @param mixed  $rule  验证规则数据
1656
     * @param string $title 字段描述名
1657
     * @return array
1658
     */
1659
    protected function errorMsgIsArray(array $msg, $rule, string $title)
1660
    {
1661
        foreach ($msg as $key => $val) {
1662
            if (is_string($val)) {
1663
                $msg[$key] = $this->parseErrorMsg($val, $rule, $title);
1664
            }
1665
        }
1666
        return $msg;
1667
    }
1668
1669
    /**
1670
     * 获取数据验证的场景
1671
     * @access protected
1672
     * @param string $scene 验证场景
1673
     * @return void
1674
     */
1675
    protected function getScene(string $scene): void
1676
    {
1677
        $this->only = $this->append = $this->remove = [];
1678
1679
        if (method_exists($this, 'scene' . $scene)) {
1680
            call_user_func([$this, 'scene' . $scene]);
1681
        } elseif (isset($this->scene[$scene])) {
1682
            // 如果设置了验证适用场景
1683
            $this->only = $this->scene[$scene];
1684
        }
1685
    }
1686
1687
    /**
1688
     * 动态方法 直接调用is方法进行验证
1689
     * @access public
1690
     * @param string $method 方法名
1691
     * @param array  $args   调用参数
1692
     * @return bool
1693
     */
1694
    public function __call($method, $args)
1695
    {
1696
        if ('is' == strtolower(substr($method, 0, 2))) {
1697
            $method = substr($method, 2);
1698
        }
1699
1700
        array_push($args, lcfirst($method));
1701
1702
        return call_user_func_array([$this, 'is'], $args);
1703
    }
1704
}
1705