Passed
Push — main ( 0606b0...817763 )
by Breno
01:46
created

RuleSet::getPriority()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 10
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace BrenoRoosevelt\Validation;
5
6
use BrenoRoosevelt\Validation\Exception\ValidateOrFailTrait;
7
use BrenoRoosevelt\Validation\Rules\AllowEmpty;
8
use BrenoRoosevelt\Validation\Rules\AllowNull;
9
use BrenoRoosevelt\Validation\Rules\IsEmpty;
10
11
class RuleSet implements Rule, BelongsToField, Priority
12
{
13
    use RuleChainTrait, BelongsToFieldTrait, ValidateOrFailTrait;
14
15
    /** @var Rule[] */
16
    private array $rules = [];
17
18
    final public function __construct(?string $field = null, Rule | RuleSet ...$rules)
19
    {
20
        $this->field = $field;
21
        foreach ($rules as $ruleOrRuleSet) {
22
            array_push(
23
                $this->rules,
24
                ...($ruleOrRuleSet instanceof RuleSet ? $ruleOrRuleSet->rules() : [$ruleOrRuleSet])
25
            );
26
        }
27
    }
28
29
    public static function new(): self
30
    {
31
        return new self;
32
    }
33
34
    public static function of(string $field, Rule | RuleSet ...$rules): self
35
    {
36
        return new self($field, ...$rules);
37
    }
38
39
    public static function withRules(Rule | RuleSet ...$rules): self
40
    {
41
        return new self(null, ...$rules);
42
    }
43
44
    public function add(Rule | RuleSet ...$rules): static
45
    {
46
        return new self($this->field, ...$this->rules, ...$rules);
47
    }
48
49
    /** @inheritDoc */
50
    public function validate(mixed $input, array $context = []): ErrorReporting
51
    {
52
        if (!$this->shouldValidate($input)) {
53
            return ErrorReporting::success();
54
        }
55
56
        PriorityTrait::sortByPriority($this->rules);
57
        $errorReporting = new ErrorReporting;
58
        foreach ($this->rules as $rule) {
59
            if ($rule instanceof BelongsToField) {
60
                $rule = $rule->setField($this->getField());
61
            }
62
63
            $result = $rule->validate($input, $context);
64
            $errorReporting = $errorReporting->add($result);
65
            $stopSign = $this->stopSign($rule, $result);
66
            if ($stopSign !== StopSign::DONT_STOP) {
67
                return $errorReporting->withStopSign($stopSign);
68
            }
69
        }
70
71
        return $errorReporting;
72
    }
73
74
    private function stopSign(Rule $rule, Result $result): int
75
    {
76
        if (! $rule instanceof Stoppable) {
77
            return StopSign::DONT_STOP;
78
        }
79
80
        $stopWhen = [StopSign::SAME_FIELD, StopSign::ALL];
81
        if (!$result->isOk() && in_array($rule->stopOnFailure(), $stopWhen)) {
82
            return $rule->stopOnFailure();
83
        }
84
85
        return StopSign::DONT_STOP;
86
    }
87
88
    private function shouldValidate(mixed $input): bool
89
    {
90
        if (null === $input && $this->containsRuleType(AllowNull::class)) {
91
            return false;
92
        }
93
94
        if ((new IsEmpty)->isValid($input) && $this->containsRuleType(AllowEmpty::class)) {
95
            return false;
96
        }
97
98
        return true;
99
    }
100
101
    public function containsRuleType(string $ruleClassName): bool
102
    {
103
        foreach ($this->rules as $rule) {
104
            if (is_a($rule, $ruleClassName, true)) {
105
                return true;
106
            }
107
        }
108
109
        return false;
110
    }
111
112
    public function isEmpty(): bool
113
    {
114
        return empty($this->rules);
115
    }
116
117
    /** @return Rule[] */
118
    public function rules(): array
119
    {
120
        return $this->rules;
121
    }
122
123
    public function getPriority(): int
124
    {
125
        $priority = 0;
126
        foreach ($this->rules as $rule) {
127
            if ($rule instanceof Priority && $rule->getPriority() > $priority) {
128
                $priority = $rule->getPriority();
129
            }
130
        }
131
132
        return $priority;
133
    }
134
}
135