Passed
Push — main ( 6335e2...7afbf7 )
by Breno
01:48
created

RuleSet::stopOnFailure()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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