Completed
Push — 6.0 ( ba3120...220eb5 )
by liu
03:53
created

Validate::append()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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

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

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

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
1548
1549
            if (strpos($msg, ':rule')) {
1550
                $msg = str_replace(':rule', (string) $rule, $msg);
1551
            }
1552
        }
1553
1554
        return $msg;
1555
    }
1556
1557
    /**
1558
     * 获取数据验证的场景
1559
     * @access protected
1560
     * @param  string $scene  验证场景
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
1561
     * @return void
1562
     */
1563
    protected function getScene(string $scene): void
1564
    {
1565
        $this->only = $this->append = $this->remove = [];
1566
1567
        if (method_exists($this, 'scene' . $scene)) {
1568
            call_user_func([$this, 'scene' . $scene]);
1569
        } elseif (isset($this->scene[$scene])) {
1570
            // 如果设置了验证适用场景
1571
            $this->only = $this->scene[$scene];
1572
        }
1573
    }
1574
1575
    /**
1576
     * 动态方法 直接调用is方法进行验证
1577
     * @access public
1578
     * @param  string $method  方法名
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
1579
     * @param  array $args  调用参数
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 2 found
Loading history...
1580
     * @return bool
1581
     */
1582
    public function __call($method, $args)
1583
    {
1584
        if ('is' == strtolower(substr($method, 0, 2))) {
1585
            $method = substr($method, 2);
1586
        }
1587
1588
        array_push($args, lcfirst($method));
1589
1590
        return call_user_func_array([$this, 'is'], $args);
1591
    }
1592
}
1593