Passed
Push — 8.0 ( 6b1372...6fd544 )
by liu
11:54
created

Validate::endWith()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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

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