Passed
Pull Request — 8.0 (#3079)
by wj
02:22
created

Validate::neq()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
// +----------------------------------------------------------------------
4
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
5
// +----------------------------------------------------------------------
6
// | Copyright (c) 2006~2023 http://thinkphp.cn All rights reserved.
7
// +----------------------------------------------------------------------
8
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
9
// +----------------------------------------------------------------------
10
// | Author: liu21st <[email protected]>
11
// +----------------------------------------------------------------------
12
declare (strict_types=1);
13
14
namespace think;
15
16
use BackedEnum;
0 ignored issues
show
Bug introduced by
The type BackedEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use Closure;
18
use think\contract\Enumable;
19
use think\exception\ValidateException;
20
use think\helper\Str;
21
use think\validate\ValidateRule;
22
use UnitEnum;
0 ignored issues
show
Bug introduced by
The type UnitEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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

1218
            /** @scrutinizer ignore-call */ 
1219
            $db = $this->db->name($rule[0]);
Loading history...
1219
        }
1220
1221
        $key = $rule[1] ?? $field;
1222
        $map = [];
1223
1224
        if (str_contains($key, '^')) {
1225
            // 支持多个字段验证
1226
            $fields = explode('^', $key);
1227
            foreach ($fields as $key) {
1228
                if (isset($data[$key])) {
1229
                    $map[] = [$key, '=', $data[$key]];
1230
                }
1231
            }
1232
        } elseif (strpos($key, '=')) {
1233
            // 支持复杂验证
1234
            parse_str($key, $array);
1235
            foreach ($array as $k => $val) {
1236
                $map[] = [$k, '=', $data[$k] ?? $val];
1237
            }
1238
        } elseif (isset($data[$field])) {
1239
            $map[] = [$key, '=', $data[$field]];
1240
        }
1241
1242
        $pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
1243
1244
        if (is_string($pk)) {
1245
            if (isset($rule[2])) {
1246
                $map[] = [$pk, '<>', $rule[2]];
1247
            } elseif (isset($data[$pk])) {
1248
                $map[] = [$pk, '<>', $data[$pk]];
1249
            }
1250
        }
1251
1252
        if ($db->where($map)->field($pk)->find()) {
1253
            return false;
1254
        }
1255
1256
        return true;
1257
    }
1258
1259
    /**
1260
     * 使用filter_var方式验证
1261
     * @access public
1262
     * @param mixed $value 字段值
1263
     * @param mixed $rule  验证规则
1264
     * @return bool
1265
     */
1266
    public function filter($value, $rule): bool
1267
    {
1268
        if (is_string($rule) && str_contains($rule, ',')) {
1269
            [$rule, $param] = explode(',', $rule);
1270
        } elseif (is_array($rule)) {
1271
            $param = $rule[1] ?? 0;
1272
            $rule  = $rule[0];
1273
        } else {
1274
            $param = 0;
1275
        }
1276
1277
        return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
1278
    }
1279
1280
    /**
1281
     * 验证某个字段等于某个值的时候必须
1282
     * @access public
1283
     * @param mixed $value 字段值
1284
     * @param mixed $rule  验证规则
1285
     * @param array $data  数据
1286
     * @return bool
1287
     */
1288
    public function requireIf($value, $rule, array $data = []): bool
1289
    {
1290
        [$field, $val] = is_string($rule) ? explode(',', $rule) : $rule;
1291
1292
        if ($this->getDataValue($data, $field) == $val) {
1293
            return !empty($value) || '0' == $value;
1294
        }
1295
1296
        return true;
1297
    }
1298
1299
    /**
1300
     * 通过回调方法验证某个字段是否必须
1301
     * @access public
1302
     * @param mixed        $value 字段值
1303
     * @param string|array $rule  验证规则
1304
     * @param array        $data  数据
1305
     * @return bool
1306
     */
1307
    public function requireCallback($value, string | array $rule, array $data = []): bool
1308
    {
1309
        $callback = is_array($rule) ? $rule : [$this, $rule];
0 ignored issues
show
introduced by
The condition is_array($rule) is always true.
Loading history...
1310
        $result   = call_user_func_array($callback, [$value, $data]);
1311
1312
        if ($result) {
1313
            return !empty($value) || '0' == $value;
1314
        }
1315
1316
        return true;
1317
    }
1318
1319
    /**
1320
     * 验证某个字段有值的情况下必须
1321
     * @access public
1322
     * @param mixed $value 字段值
1323
     * @param mixed $rule  验证规则
1324
     * @param array $data  数据
1325
     * @return bool
1326
     */
1327
    public function requireWith($value, $rule, array $data = []): bool
1328
    {
1329
        $val = $this->getDataValue($data, $rule);
1330
1331
        if (!empty($val)) {
1332
            return !empty($value) || '0' == $value;
1333
        }
1334
1335
        return true;
1336
    }
1337
1338
    /**
1339
     * 验证某个字段没有值的情况下必须
1340
     * @access public
1341
     * @param mixed $value 字段值
1342
     * @param mixed $rule  验证规则
1343
     * @param array $data  数据
1344
     * @return bool
1345
     */
1346
    public function requireWithout($value, $rule, array $data = []): bool
1347
    {
1348
        $val = $this->getDataValue($data, $rule);
1349
1350
        if (empty($val)) {
1351
            return !empty($value) || '0' == $value;
1352
        }
1353
1354
        return true;
1355
    }
1356
1357
    /**
1358
     * 验证是否为数组,支持检查键名
1359
     * @access public
1360
     * @param mixed $value 字段值
1361
     * @param mixed $rule  验证规则
1362
     * @return bool
1363
     */
1364
    public function array($value, $rule): bool
1365
    {
1366
        if (!is_array($value)) {
1367
            return false;
1368
        }
1369
        if ($rule) {
1370
            $keys = is_string($rule) ? explode(',', $rule) : $rule;
1371
            return empty(array_diff($keys, array_keys($value)));
1372
        } else {
1373
            return true;
1374
        }
1375
    }
1376
1377
    /**
1378
     * 验证是否在范围内
1379
     * @access public
1380
     * @param mixed $value 字段值
1381
     * @param mixed $rule  验证规则
1382
     * @return bool
1383
     */
1384
    public function in($value, $rule): bool
1385
    {
1386
        return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1387
    }
1388
1389
    /**
1390
     * 验证是否为枚举
1391
     * @access public
1392
     * @param mixed $value 字段值
1393
     * @param mixed $rule  验证规则
1394
     * @return bool
1395
     */
1396
    public function enum($value, $rule): bool
1397
    {
1398
        if (is_subclass_of($rule, BackedEnum::class)) {
1399
            $values = array_map(fn($case) => $case->value, $rule::cases());
1400
        } elseif (is_subclass_of($rule, UnitEnum::class)) {
1401
            $values = array_map(fn($case) => $case->name, $rule::cases());
1402
        } elseif (is_subclass_of($rule, Enumable::class)) {
1403
            $values = $rule::values();
1404
        } else {
1405
            $reflect = new \ReflectionClass($rule);
1406
            $values  = $reflect->getConstants();
1407
        }
1408
1409
        return in_array($value, $values ?? []);
1410
    }
1411
1412
    /**
1413
     * 验证是否不在某个范围
1414
     * @access public
1415
     * @param mixed $value 字段值
1416
     * @param mixed $rule  验证规则
1417
     * @return bool
1418
     */
1419
    public function notIn($value, $rule): bool
1420
    {
1421
        return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1422
    }
1423
1424
    /**
1425
     * between验证数据
1426
     * @access public
1427
     * @param mixed $value 字段值
1428
     * @param mixed $rule  验证规则
1429
     * @return bool
1430
     */
1431
    public function between($value, $rule): bool
1432
    {
1433
        [$min, $max] = is_string($rule) ? explode(',', $rule) : $rule;
1434
1435
        return $value >= $min && $value <= $max;
1436
    }
1437
1438
    /**
1439
     * 使用notbetween验证数据
1440
     * @access public
1441
     * @param mixed $value 字段值
1442
     * @param mixed $rule  验证规则
1443
     * @return bool
1444
     */
1445
    public function notBetween($value, $rule): bool
1446
    {
1447
        [$min, $max] = is_string($rule) ? explode(',', $rule) : $rule;
1448
1449
        return $value < $min || $value > $max;
1450
    }
1451
1452
    /**
1453
     * 验证数据长度
1454
     * @access public
1455
     * @param mixed $value 字段值
1456
     * @param mixed $rule  验证规则
1457
     * @return bool
1458
     */
1459
    public function length($value, $rule): bool
1460
    {
1461
        if (is_array($value)) {
1462
            $length = count($value);
1463
        } elseif ($value instanceof File) {
1464
            $length = $value->getSize();
1465
        } else {
1466
            $length = mb_strlen((string) $value);
1467
        }
1468
1469
        if (is_string($rule) && str_contains($rule, ',')) {
1470
            // 长度区间
1471
            [$min, $max] = explode(',', $rule);
1472
            return $length >= $min && $length <= $max;
1473
        }
1474
1475
        // 指定长度
1476
        return $length == $rule;
1477
    }
1478
1479
    /**
1480
     * 验证数据最大长度
1481
     * @access public
1482
     * @param mixed $value 字段值
1483
     * @param mixed $rule  验证规则
1484
     * @return bool
1485
     */
1486
    public function max($value, $rule): bool
1487
    {
1488
        if (is_array($value)) {
1489
            $length = count($value);
1490
        } elseif ($value instanceof File) {
1491
            $length = $value->getSize();
1492
        } else {
1493
            $length = mb_strlen((string) $value);
1494
        }
1495
1496
        return $length <= $rule;
1497
    }
1498
1499
    /**
1500
     * 验证数据最小长度
1501
     * @access public
1502
     * @param mixed $value 字段值
1503
     * @param mixed $rule  验证规则
1504
     * @return bool
1505
     */
1506
    public function min($value, $rule): bool
1507
    {
1508
        if (is_array($value)) {
1509
            $length = count($value);
1510
        } elseif ($value instanceof File) {
1511
            $length = $value->getSize();
1512
        } else {
1513
            $length = mb_strlen((string) $value);
1514
        }
1515
1516
        return $length >= $rule;
1517
    }
1518
1519
    /**
1520
     * 验证日期
1521
     * @access public
1522
     * @param mixed $value 字段值
1523
     * @param mixed $rule  验证规则
1524
     * @param array $data  数据
1525
     * @return bool
1526
     */
1527
    public function after($value, $rule, array $data = []): bool
1528
    {
1529
        return strtotime($value) >= strtotime($rule);
1530
    }
1531
1532
    /**
1533
     * 验证日期
1534
     * @access public
1535
     * @param mixed $value 字段值
1536
     * @param mixed $rule  验证规则
1537
     * @param array $data  数据
1538
     * @return bool
1539
     */
1540
    public function before($value, $rule, array $data = []): bool
1541
    {
1542
        return strtotime($value) <= strtotime($rule);
1543
    }
1544
1545
    /**
1546
     * 验证日期
1547
     * @access public
1548
     * @param mixed $value 字段值
1549
     * @param mixed $rule  验证规则
1550
     * @param array $data  数据
1551
     * @return bool
1552
     */
1553
    public function afterWith($value, $rule, array $data = []): bool
1554
    {
1555
        $rule = $this->getDataValue($data, $rule);
1556
        return !is_null($rule) && strtotime($value) >= strtotime($rule);
1557
    }
1558
1559
    /**
1560
     * 验证日期
1561
     * @access public
1562
     * @param mixed $value 字段值
1563
     * @param mixed $rule  验证规则
1564
     * @param array $data  数据
1565
     * @return bool
1566
     */
1567
    public function beforeWith($value, $rule, array $data = []): bool
1568
    {
1569
        $rule = $this->getDataValue($data, $rule);
1570
        return !is_null($rule) && strtotime($value) <= strtotime($rule);
1571
    }
1572
1573
    /**
1574
     * 验证有效期
1575
     * @access public
1576
     * @param mixed $value 字段值
1577
     * @param mixed $rule  验证规则
1578
     * @return bool
1579
     */
1580
    public function expire($value, $rule): bool
1581
    {
1582
        [$start, $end] = is_string($rule) ? explode(',', $rule) : $rule;
1583
1584
        if (!is_numeric($start)) {
1585
            $start = strtotime($start);
1586
        }
1587
1588
        if (!is_numeric($end)) {
1589
            $end = strtotime($end);
1590
        }
1591
1592
        return time() >= $start && time() <= $end;
1593
    }
1594
1595
    /**
1596
     * 验证IP许可
1597
     * @access public
1598
     * @param mixed $value 字段值
1599
     * @param mixed $rule  验证规则
1600
     * @return bool
1601
     */
1602
    public function allowIp($value, $rule): bool
1603
    {
1604
        return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1605
    }
1606
1607
    /**
1608
     * 验证IP禁用
1609
     * @access public
1610
     * @param mixed $value 字段值
1611
     * @param mixed $rule  验证规则
1612
     * @return bool
1613
     */
1614
    public function denyIp($value, $rule): bool
1615
    {
1616
        return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
1617
    }
1618
1619
    /**
1620
     * 验证某个字段等于指定的值,则验证中的字段必须为 yes、on、1 或 true
1621
     * @access public
1622
     * @param mixed $value 字段值
1623
     * @param mixed $rule 验证规则
1624
     * @param array $data 数据
1625
     * @return bool
1626
     */
1627 3
    public function acceptedIf($value, $rule, array $data = []): bool
1628
    {
1629 3
        [$field, $val] = is_string($rule) ? explode(',', $rule) : $rule;
1630
1631 3
        if ($this->getDataValue($data, $field) == $val) {
1632 3
            return in_array($value, ['1', 'on', 'yes', 'true', 1, true], true);
1633
        }
1634
1635
        return true;
1636
    }
1637
1638
    /**
1639
     * 验证某个字段等于指定的值,则验证中的字段必须为 no、off、0 或 false
1640
     * @access public
1641
     * @param mixed $value 字段值
1642
     * @param mixed $rule 验证规则
1643
     * @param array $data 数据
1644
     * @return bool
1645
     */
1646 3
    public function declinedIf($value, $rule, array $data = []): bool
1647
    {
1648 3
        [$field, $val] = is_string($rule) ? explode(',', $rule) : $rule;
1649
1650 3
        if ($this->getDataValue($data, $field) == $val) {
1651 3
            return in_array($value, ['0', 'off', 'no', 'false', 0, false], true);
1652
        }
1653
1654
        return true;
1655
    }
1656
1657
    /**
1658
     * 验证某个字段必须是指定值的倍数
1659
     * @param mixed $value 字段值
1660
     * * @param mixed $rule 验证规则
1661
     * @return bool
1662
     */
1663 3
    public function multipleOf($value, $rule): bool
1664
    {
1665 3
        if ('0' == $rule || $value < $rule) {
1666 3
            return false;
1667
        }
1668
1669 3
        return $value % $rule === 0;
1670
    }
1671
1672
    /**
1673
     * 使用正则验证数据
1674
     * @access public
1675
     * @param mixed $value 字段值
1676
     * @param mixed $rule  验证规则 正则规则或者预定义正则名
1677
     * @return bool
1678
     */
1679
    public function regex($value, $rule): bool
1680
    {
1681
        $rule = $this->regex[$rule] ?? $this->getDefaultRegexRule($rule);
1682
1683
        if (is_string($rule) && !str_starts_with($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
1684
            // 不是正则表达式则两端补上/
1685
            $rule = '/^' . $rule . '$/';
1686
        }
1687
1688
        return is_scalar($value) && 1 === preg_match($rule, (string) $value);
1689
    }
1690
1691
    /**
1692
     * 获取内置正则验证规则
1693
     * @access public
1694
     * @param string $rule  验证规则 正则规则或者预定义正则名
1695
     * @return bool
1696
     */
1697
    protected function getDefaultRegexRule(string $rule): string
1698
    {
1699
        $name = Str::camel($rule);
1700
        if (isset($this->defaultRegex[$name])) {
1701
            $rule = $this->defaultRegex[$name];
1702
        }
1703
        return $rule;
1704
    }
1705
1706
    /**
1707
     * 获取错误信息
1708
     * @return array|string
1709
     */
1710 6
    public function getError()
1711
    {
1712 6
        return $this->error;
1713
    }
1714
1715
    /**
1716
     * 获取数据集合
1717
     * @access protected
1718
     * @param array  $data 数据
1719
     * @param string $key  数据标识 支持二维
1720
     * @return array
1721
     */
1722 6
    protected function getDataSet(array $data, $key): array
1723
    {
1724 6
        if (is_string($key) && str_contains($key, '*')) {
1725
            if (str_ends_with($key, '*')) {
1726
                // user.id.*
1727
                [$key] = explode('.*', $key);
1728
                $value = $this->getRecursiveData($data, $key);
1729
                return is_array($value) ? $value : [$value];
1730
            }
1731
            // user.*.id
1732
            [$key, $column] = explode('.*.', $key);
1733
            return array_column($this->getRecursiveData($data, $key) ?: [], $column);
1734
        }
1735
1736 6
        return [$this->getDataValue($data, $key)];
1737
    }
1738
1739
    /**
1740
     * 获取数据值
1741
     * @access protected
1742
     * @param array  $data 数据
1743
     * @param string $key  数据标识 支持二维
1744
     * @return mixed
1745
     */
1746 9
    protected function getDataValue(array $data, $key)
1747
    {
1748 9
        if (is_numeric($key)) {
1749 3
            $value = $key;
1750 6
        } elseif (is_string($key) && str_contains($key, '.')) {
1751
            // 支持多维数组验证
1752
            $value = $this->getRecursiveData($data, $key);
1753
        } else {
1754 6
            $value = $data[$key] ?? null;
1755
        }
1756
1757 9
        return $value;
1758
    }
1759
1760
    /**
1761
     * 获取数据值
1762
     * @access protected
1763
     * @param array  $data 数据
1764
     * @param string $key  数据标识 支持二维
1765
     * @return mixed
1766
     */
1767
1768
    protected function getRecursiveData(array $data, string $key)
1769
    {
1770
        $keys = explode('.', $key);
1771
        foreach ($keys as $key) {
1772
            if (!isset($data[$key])) {
1773
                $value = null;
1774
                break;
1775
            }
1776
            $value = $data = $data[$key];
1777
        }
1778
        return $value;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value seems to be defined by a foreach iteration on line 1771. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1779
    }
1780
1781
    /**
1782
     * 获取验证规则的错误提示信息
1783
     * @access protected
1784
     * @param string $attribute 字段英文名
1785
     * @param string $title     字段描述名
1786
     * @param string $type      验证规则名称
1787
     * @param mixed  $rule      验证规则数据
1788
     * @return string|array
1789
     */
1790 6
    protected function getRuleMsg(string $attribute, string $title, string $type, $rule)
1791
    {
1792 6
        if (isset($this->message[$attribute . '.' . $type])) {
1793
            $msg = $this->message[$attribute . '.' . $type];
1794 6
        } elseif (isset($this->message[$attribute][$type])) {
1795
            $msg = $this->message[$attribute][$type];
1796 6
        } elseif (isset($this->message[$attribute])) {
1797
            $msg = $this->message[$attribute];
1798 6
        } elseif (isset($this->typeMsg[$type])) {
1799 6
            $msg = $this->typeMsg[$type];
1800
        } elseif (str_starts_with($type, 'require')) {
1801
            $msg = $this->typeMsg['require'];
1802
        } else {
1803
            $msg = $title . $this->lang->get('not conform to the rules');
1804
        }
1805
1806 6
        if (is_array($msg)) {
1807
            return $this->errorMsgIsArray($msg, $rule, $title);
1808
        }
1809
1810 6
        return $this->parseErrorMsg($msg, $rule, $title);
1811
    }
1812
1813
    /**
1814
     * 获取验证规则的错误提示信息
1815
     * @access protected
1816
     * @param string $msg   错误信息
1817
     * @param mixed  $rule  验证规则数据
1818
     * @param string $title 字段描述名
1819
     * @return string|array
1820
     */
1821 6
    protected function parseErrorMsg(string $msg, $rule, string $title)
1822
    {
1823 6
        if (str_starts_with($msg, '{%')) {
1824
            $msg = $this->lang->get(substr($msg, 2, -1));
1825 6
        } elseif ($this->lang->has($msg)) {
1826
            $msg = $this->lang->get($msg);
1827
        }
1828
1829 6
        if (is_array($msg)) {
1830
            return $this->errorMsgIsArray($msg, $rule, $title);
1831
        }
1832
1833
        // rule若是数组则转为字符串
1834 6
        if (is_array($rule)) {
1835
            $rule = implode(',', $rule);
1836
        }
1837
1838 6
        if (is_scalar($rule) && str_contains($msg, ':')) {
1839
            // 变量替换
1840 6
            if (is_string($rule) && str_contains($rule, ',')) {
1841 6
                $array = array_pad(explode(',', $rule), 3, '');
1842
            } else {
1843
                $array = array_pad([], 3, '');
1844
            }
1845
1846 6
            $msg = str_replace(
1847 6
                [':attribute', ':1', ':2', ':3'],
1848 6
                [$title, $array[0], $array[1], $array[2]],
1849 6
                $msg,
1850 6
            );
1851
1852 6
            if (str_contains($msg, ':rule')) {
1853
                $msg = str_replace(':rule', (string) $rule, $msg);
1854
            }
1855
        }
1856
1857 6
        return $msg;
1858
    }
1859
1860
    /**
1861
     * 错误信息数组处理
1862
     * @access protected
1863
     * @param array $msg   错误信息
1864
     * @param mixed  $rule  验证规则数据
1865
     * @param string $title 字段描述名
1866
     * @return array
1867
     */
1868
    protected function errorMsgIsArray(array $msg, $rule, string $title)
1869
    {
1870
        foreach ($msg as $key => $val) {
1871
            if (is_string($val)) {
1872
                $msg[$key] = $this->parseErrorMsg($val, $rule, $title);
1873
            }
1874
        }
1875
        return $msg;
1876
    }
1877
1878
    /**
1879
     * 获取数据验证的场景
1880
     * @access protected
1881
     * @param string $scene 验证场景
1882
     * @return void
1883
     */
1884
    protected function getScene(string $scene): void
1885
    {
1886
        if (method_exists($this, 'scene' . $scene)) {
1887
            call_user_func([$this, 'scene' . $scene]);
1888
        } elseif (isset($this->scene[$scene])) {
1889
            // 如果设置了验证适用场景
1890
            $this->only = $this->scene[$scene];
1891
        }
1892
    }
1893
1894
    /**
1895
     * 动态方法 直接调用is方法进行验证
1896
     * @access public
1897
     * @param string $method 方法名
1898
     * @param array  $args   调用参数
1899
     * @return bool
1900
     */
1901 6
    public function __call($method, $args)
1902
    {
1903 6
        if ('is' == strtolower(substr($method, 0, 2))) {
1904 6
            $method = substr($method, 2);
1905
        }
1906
1907 6
        array_push($args, lcfirst($method));
1908
1909 6
        return call_user_func_array([$this, 'is'], $args);
1910
    }
1911
}
1912