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

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