Passed
Push — 8.0 ( c06b7d...4441b4 )
by liu
10:33
created

Validate::maker()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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