Completed
Push — 6.0 ( d45ec4...2c3776 )
by liu
05:39
created

Validate::checkRule()   B

Complexity

Conditions 8
Paths 22

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

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

1133
            /** @scrutinizer ignore-call */ 
1134
            $db = $this->db->name($rule[0]);
Loading history...
1134
        }
1135
1136
        $key = $rule[1] ?? $field;
1137
        $map = [];
1138
1139
        if (strpos($key, '^')) {
1140
            // 支持多个字段验证
1141
            $fields = explode('^', $key);
1142
            foreach ($fields as $key) {
1143
                if (isset($data[$key])) {
1144
                    $map[] = [$key, '=', $data[$key]];
1145
                }
1146
            }
1147
        } elseif (isset($data[$field])) {
1148
            $map[] = [$key, '=', $data[$field]];
1149
        } else {
1150
            $map = [];
1151
        }
1152
1153
        $pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
1154
1155
        if (is_string($pk)) {
1156
            if (isset($rule[2])) {
1157
                $map[] = [$pk, '<>', $rule[2]];
1158
            } elseif (isset($data[$pk])) {
1159
                $map[] = [$pk, '<>', $data[$pk]];
1160
            }
1161
        }
1162
1163
        if ($db->where($map)->field($pk)->find()) {
1164
            return false;
1165
        }
1166
1167
        return true;
1168
    }
1169
1170
    /**
1171
     * 使用filter_var方式验证
1172
     * @access public
1173
     * @param mixed $value 字段值
1174
     * @param mixed $rule  验证规则
1175
     * @return bool
1176
     */
1177
    public function filter($value, $rule): bool
1178
    {
1179
        if (is_string($rule) && strpos($rule, ',')) {
1180
            list($rule, $param) = explode(',', $rule);
1181
        } elseif (is_array($rule)) {
1182
            $param = $rule[1] ?? null;
1183
            $rule  = $rule[0];
1184
        } else {
1185
            $param = null;
1186
        }
1187
1188
        return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
1189
    }
1190
1191
    /**
1192
     * 验证某个字段等于某个值的时候必须
1193
     * @access public
1194
     * @param mixed $value 字段值
1195
     * @param mixed $rule  验证规则
1196
     * @param array $data  数据
1197
     * @return bool
1198
     */
1199
    public function requireIf($value, $rule, array $data = []): bool
1200
    {
1201
        list($field, $val) = explode(',', $rule);
1202
1203
        if ($this->getDataValue($data, $field) == $val) {
1204
            return !empty($value) || '0' == $value;
1205
        }
1206
1207
        return true;
1208
    }
1209
1210
    /**
1211
     * 通过回调方法验证某个字段是否必须
1212
     * @access public
1213
     * @param mixed $value 字段值
1214
     * @param mixed $rule  验证规则
1215
     * @param array $data  数据
1216
     * @return bool
1217
     */
1218
    public function requireCallback($value, $rule, array $data = []): bool
1219
    {
1220
        $result = call_user_func_array([$this, $rule], [$value, $data]);
1221
1222
        if ($result) {
1223
            return !empty($value) || '0' == $value;
1224
        }
1225
1226
        return true;
1227
    }
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 requireWith($value, $rule, array $data = []): bool
1238
    {
1239
        $val = $this->getDataValue($data, $rule);
1240
1241
        if (!empty($val)) {
1242
            return !empty($value) || '0' == $value;
1243
        }
1244
1245
        return true;
1246
    }
1247
1248
    /**
1249
     * 验证某个字段没有值的情况下必须
1250
     * @access public
1251
     * @param mixed $value 字段值
1252
     * @param mixed $rule  验证规则
1253
     * @param array $data  数据
1254
     * @return bool
1255
     */
1256
    public function requireWithout($value, $rule, array $data = []): bool
1257
    {
1258
        $val = $this->getDataValue($data, $rule);
1259
1260
        if (empty($val)) {
1261
            return !empty($value) || '0' == $value;
1262
        }
1263
1264
        return true;
1265
    }
1266
1267
    /**
1268
     * 验证是否在范围内
1269
     * @access public
1270
     * @param mixed $value 字段值
1271
     * @param mixed $rule  验证规则
1272
     * @return bool
1273
     */
1274
    public function in($value, $rule): bool
1275
    {
1276
        return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1277
    }
1278
1279
    /**
1280
     * 验证是否不在某个范围
1281
     * @access public
1282
     * @param mixed $value 字段值
1283
     * @param mixed $rule  验证规则
1284
     * @return bool
1285
     */
1286
    public function notIn($value, $rule): bool
1287
    {
1288
        return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1289
    }
1290
1291
    /**
1292
     * between验证数据
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
1293
     * @access public
1294
     * @param mixed $value 字段值
1295
     * @param mixed $rule  验证规则
1296
     * @return bool
1297
     */
1298
    public function between($value, $rule): bool
1299
    {
1300
        if (is_string($rule)) {
1301
            $rule = explode(',', $rule);
1302
        }
1303
        list($min, $max) = $rule;
1304
1305
        return $value >= $min && $value <= $max;
1306
    }
1307
1308
    /**
1309
     * 使用notbetween验证数据
1310
     * @access public
1311
     * @param mixed $value 字段值
1312
     * @param mixed $rule  验证规则
1313
     * @return bool
1314
     */
1315
    public function notBetween($value, $rule): bool
1316
    {
1317
        if (is_string($rule)) {
1318
            $rule = explode(',', $rule);
1319
        }
1320
        list($min, $max) = $rule;
1321
1322
        return $value < $min || $value > $max;
1323
    }
1324
1325
    /**
1326
     * 验证数据长度
1327
     * @access public
1328
     * @param mixed $value 字段值
1329
     * @param mixed $rule  验证规则
1330
     * @return bool
1331
     */
1332
    public function length($value, $rule): bool
1333
    {
1334
        if (is_array($value)) {
1335
            $length = count($value);
1336
        } elseif ($value instanceof File) {
1337
            $length = $value->getSize();
1338
        } else {
1339
            $length = mb_strlen((string) $value);
1340
        }
1341
1342
        if (is_string($rule) && strpos($rule, ',')) {
1343
            // 长度区间
1344
            list($min, $max) = explode(',', $rule);
1345
            return $length >= $min && $length <= $max;
1346
        }
1347
1348
        // 指定长度
1349
        return $length == $rule;
1350
    }
1351
1352
    /**
1353
     * 验证数据最大长度
1354
     * @access public
1355
     * @param mixed $value 字段值
1356
     * @param mixed $rule  验证规则
1357
     * @return bool
1358
     */
1359
    public function max($value, $rule): bool
1360
    {
1361
        if (is_array($value)) {
1362
            $length = count($value);
1363
        } elseif ($value instanceof File) {
1364
            $length = $value->getSize();
1365
        } else {
1366
            $length = mb_strlen((string) $value);
1367
        }
1368
1369
        return $length <= $rule;
1370
    }
1371
1372
    /**
1373
     * 验证数据最小长度
1374
     * @access public
1375
     * @param mixed $value 字段值
1376
     * @param mixed $rule  验证规则
1377
     * @return bool
1378
     */
1379
    public function min($value, $rule): bool
1380
    {
1381
        if (is_array($value)) {
1382
            $length = count($value);
1383
        } elseif ($value instanceof File) {
1384
            $length = $value->getSize();
1385
        } else {
1386
            $length = mb_strlen((string) $value);
1387
        }
1388
1389
        return $length >= $rule;
1390
    }
1391
1392
    /**
1393
     * 验证日期
1394
     * @access public
1395
     * @param mixed $value 字段值
1396
     * @param mixed $rule  验证规则
1397
     * @param array $data  数据
1398
     * @return bool
1399
     */
1400
    public function after($value, $rule, array $data = []): bool
1401
    {
1402
        return strtotime($value) >= strtotime($rule);
1403
    }
1404
1405
    /**
1406
     * 验证日期
1407
     * @access public
1408
     * @param mixed $value 字段值
1409
     * @param mixed $rule  验证规则
1410
     * @param array $data  数据
1411
     * @return bool
1412
     */
1413
    public function before($value, $rule, array $data = []): bool
1414
    {
1415
        return strtotime($value) <= strtotime($rule);
1416
    }
1417
1418
    /**
1419
     * 验证日期
1420
     * @access public
1421
     * @param mixed $value 字段值
1422
     * @param mixed $rule  验证规则
1423
     * @param array $data  数据
1424
     * @return bool
1425
     */
1426
    public function afterWith($value, $rule, array $data = []): bool
1427
    {
1428
        $rule = $this->getDataValue($data, $rule);
1429
        return !is_null($rule) && strtotime($value) >= strtotime($rule);
1430
    }
1431
1432
    /**
1433
     * 验证日期
1434
     * @access public
1435
     * @param mixed $value 字段值
1436
     * @param mixed $rule  验证规则
1437
     * @param array $data  数据
1438
     * @return bool
1439
     */
1440
    public function beforeWith($value, $rule, array $data = []): bool
1441
    {
1442
        $rule = $this->getDataValue($data, $rule);
1443
        return !is_null($rule) && strtotime($value) <= strtotime($rule);
1444
    }
1445
1446
    /**
1447
     * 验证有效期
1448
     * @access public
1449
     * @param mixed $value 字段值
1450
     * @param mixed $rule  验证规则
1451
     * @return bool
1452
     */
1453
    public function expire($value, $rule): bool
1454
    {
1455
        if (is_string($rule)) {
1456
            $rule = explode(',', $rule);
1457
        }
1458
1459
        list($start, $end) = $rule;
1460
1461
        if (!is_numeric($start)) {
1462
            $start = strtotime($start);
1463
        }
1464
1465
        if (!is_numeric($end)) {
1466
            $end = strtotime($end);
1467
        }
1468
1469
        return time() >= $start && time() <= $end;
1470
    }
1471
1472
    /**
1473
     * 验证IP许可
1474
     * @access public
1475
     * @param mixed $value 字段值
1476
     * @param mixed $rule  验证规则
1477
     * @return bool
1478
     */
1479
    public function allowIp($value, $rule): bool
1480
    {
1481
        return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1482
    }
1483
1484
    /**
1485
     * 验证IP禁用
1486
     * @access public
1487
     * @param mixed $value 字段值
1488
     * @param mixed $rule  验证规则
1489
     * @return bool
1490
     */
1491
    public function denyIp($value, $rule): bool
1492
    {
1493
        return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1494
    }
1495
1496
    /**
1497
     * 使用正则验证数据
1498
     * @access public
1499
     * @param mixed $value 字段值
1500
     * @param mixed $rule  验证规则 正则规则或者预定义正则名
1501
     * @return bool
1502
     */
1503
    public function regex($value, $rule): bool
1504
    {
1505
        if (isset($this->regex[$rule])) {
1506
            $rule = $this->regex[$rule];
1507
        } elseif (isset($this->defaultRegex[$rule])) {
1508
            $rule = $this->defaultRegex[$rule];
1509
        }
1510
1511
        if (is_string($rule) && 0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
1512
            // 不是正则表达式则两端补上/
1513
            $rule = '/^' . $rule . '$/';
1514
        }
1515
1516
        return is_scalar($value) && 1 === preg_match($rule, (string) $value);
1517
    }
1518
1519
    // 获取错误信息
1520
    public function getError()
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
1521
    {
1522
        return $this->error;
1523
    }
1524
1525
    /**
1526
     * 获取数据值
1527
     * @access protected
1528
     * @param array  $data 数据
1529
     * @param string $key  数据标识 支持二维
1530
     * @return mixed
1531
     */
1532
    protected function getDataValue(array $data, $key)
1533
    {
1534
        if (is_numeric($key)) {
1535
            $value = $key;
1536
        } elseif (is_string($key) && strpos($key, '.')) {
1537
            // 支持多维数组验证
1538
            foreach (explode('.', $key) as $key) {
0 ignored issues
show
introduced by
$key is overwriting one of the parameters of this function.
Loading history...
1539
                if (!isset($data[$key])) {
1540
                    $value = null;
1541
                    break;
1542
                }
1543
                $value = $data = $data[$key];
1544
            }
1545
        } else {
1546
            $value = $data[$key] ?? null;
1547
        }
1548
1549
        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...
1550
    }
1551
1552
    /**
1553
     * 获取验证规则的错误提示信息
1554
     * @access protected
1555
     * @param string $attribute 字段英文名
1556
     * @param string $title     字段描述名
1557
     * @param string $type      验证规则名称
1558
     * @param mixed  $rule      验证规则数据
1559
     * @return string|array
1560
     */
1561
    protected function getRuleMsg(string $attribute, string $title, string $type, $rule)
1562
    {
1563
        if (isset($this->message[$attribute . '.' . $type])) {
1564
            $msg = $this->message[$attribute . '.' . $type];
1565
        } elseif (isset($this->message[$attribute][$type])) {
1566
            $msg = $this->message[$attribute][$type];
1567
        } elseif (isset($this->message[$attribute])) {
1568
            $msg = $this->message[$attribute];
1569
        } elseif (isset($this->typeMsg[$type])) {
1570
            $msg = $this->typeMsg[$type];
1571
        } elseif (0 === strpos($type, 'require')) {
1572
            $msg = $this->typeMsg['require'];
1573
        } else {
1574
            $msg = $title . $this->lang->get('not conform to the rules');
1575
        }
1576
1577
        if (is_array($msg)) {
1578
            foreach ($msg as $key => $val) {
1579
                if (is_string($val)) {
1580
                    $msg[$key] = $this->parseErrorMsg($val, $rule, $title);
1581
                }
1582
            }
1583
            return $msg;
1584
        }
1585
1586
        return $this->parseErrorMsg($msg, $rule, $title);
1587
    }
1588
1589
    /**
1590
     * 获取验证规则的错误提示信息
1591
     * @access protected
1592
     * @param string $msg   错误信息
1593
     * @param mixed  $rule  验证规则数据
1594
     * @param string $title 字段描述名
1595
     * @return string
1596
     */
1597
    protected function parseErrorMsg(string $msg, $rule, string $title)
1598
    {
1599
        if (0 === strpos($msg, '{%')) {
1600
            $msg = $this->lang->get(substr($msg, 2, -1));
1601
        } elseif ($this->lang->has($msg)) {
1602
            $msg = $this->lang->get($msg);
1603
        }
1604
1605
        if (is_scalar($rule) && false !== strpos($msg, ':')) {
1606
            // 变量替换
1607
            if (is_string($rule) && strpos($rule, ',')) {
1608
                $array = array_pad(explode(',', $rule), 3, '');
1609
            } else {
1610
                $array = array_pad([], 3, '');
1611
            }
1612
1613
            $msg = str_replace(
1614
                [':attribute', ':1', ':2', ':3'],
1615
                [$title, $array[0], $array[1], $array[2]],
1616
                $msg);
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 12 spaces, but found 16.
Loading history...
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
1617
1618
            if (strpos($msg, ':rule')) {
1619
                $msg = str_replace(':rule', (string) $rule, $msg);
1620
            }
1621
        }
1622
1623
        return $msg;
1624
    }
1625
1626
    /**
1627
     * 获取数据验证的场景
1628
     * @access protected
1629
     * @param string $scene 验证场景
1630
     * @return void
1631
     */
1632
    protected function getScene(string $scene): void
1633
    {
1634
        $this->only = $this->append = $this->remove = [];
1635
1636
        if (method_exists($this, 'scene' . $scene)) {
1637
            call_user_func([$this, 'scene' . $scene]);
1638
        } elseif (isset($this->scene[$scene])) {
1639
            // 如果设置了验证适用场景
1640
            $this->only = $this->scene[$scene];
1641
        }
1642
    }
1643
1644
    /**
1645
     * 动态方法 直接调用is方法进行验证
1646
     * @access public
1647
     * @param string $method 方法名
1648
     * @param array  $args   调用参数
1649
     * @return bool
1650
     */
1651
    public function __call($method, $args)
1652
    {
1653
        if ('is' == strtolower(substr($method, 0, 2))) {
1654
            $method = substr($method, 2);
1655
        }
1656
1657
        array_push($args, lcfirst($method));
1658
1659
        return call_user_func_array([$this, 'is'], $args);
1660
    }
1661
}
1662