Validate   F
last analyzed

Complexity

Total Complexity 266

Size/Duplication

Total Lines 1663
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 13
Bugs 0 Features 0
Metric Value
eloc 542
c 13
b 0
f 0
dl 0
loc 1663
ccs 0
cts 503
cp 0
rs 2
wmc 266

69 Methods

Rating   Name   Duplication   Size   Complexity  
A hasScene() 0 3 2
A append() 0 15 4
A setRequest() 0 3 1
A only() 0 5 1
A failException() 0 5 1
A setLang() 0 3 1
A scene() 0 6 1
A message() 0 5 1
A batch() 0 5 1
A getError() 0 3 1
A remove() 0 19 5
A rule() 0 12 3
A maker() 0 3 1
A setTypeMsg() 0 6 2
A setDb() 0 3 1
A extend() 0 9 2
A __construct() 0 5 3
A notBetween() 0 8 3
A notIn() 0 3 2
A after() 0 3 1
A requireIf() 0 9 3
A in() 0 3 2
A allowIp() 0 3 2
A requireCallback() 0 9 3
A expire() 0 17 5
A beforeWith() 0 4 2
A min() 0 11 3
A before() 0 3 1
A between() 0 8 3
B getRuleMsg() 0 21 7
A length() 0 18 6
B regex() 0 14 7
A requireWith() 0 9 3
A max() 0 11 3
A afterWith() 0 4 2
A getDataValue() 0 18 6
A denyIp() 0 3 2
A getScene() 0 9 3
A __call() 0 9 2
A errorMsgIsArray() 0 8 3
A getValidateType() 0 28 6
A different() 0 3 1
A ip() 0 7 3
A activeUrl() 0 7 2
A elt() 0 3 1
A confirm() 0 11 3
D is() 0 61 18
A fileSize() 0 14 6
B image() 0 29 7
A token() 0 4 2
A fileMime() 0 14 6
A checkMime() 0 7 2
A checkExt() 0 7 2
A fileExt() 0 14 6
A getImageType() 0 11 4
B checkRule() 0 32 8
A must() 0 3 2
C unique() 0 45 12
A egt() 0 3 1
A checkSize() 0 3 1
A lt() 0 3 1
F checkItem() 0 75 28
A eq() 0 3 1
A gt() 0 3 1
A dateFormat() 0 4 2
A requireWithout() 0 9 3
B parseErrorMsg() 0 37 10
A filter() 0 12 5
F check() 0 69 17

How to fix   Complexity   

Complex Class

Complex classes like Validate often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Validate, and based on these observations, apply Extract Interface, too.

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

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