Passed
Push — main ( 7afbf7...1813d6 )
by Breno
01:57
created

RuleSet::stopSign()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 2
dl 0
loc 11
rs 10
c 0
b 0
f 0
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
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
        $errorReporting = new ErrorReporting;
57
        foreach ($this->rules as $rule) {
58
            if ($rule instanceof BelongsToField) {
59
                $rule = $rule->setField($this->getField());
60
            }
61
62
            $result = $rule->validate($input, $context);
63
            $errorReporting = $errorReporting->add($result);
64
            $stopSign = $this->stopSign($rule, $result);
65
            if ($stopSign !== StopSign::DONT_STOP) {
66
                return $errorReporting->withStopSign($stopSign);
67
            }
68
        }
69
70
        return $errorReporting;
71
    }
72
73
    private function stopSign(Rule $rule, Result $result): int
74
    {
75
        if (! $rule instanceof Stopable) {
76
            return StopSign::DONT_STOP;
77
        }
78
79
        if (!$result->isOk() && $rule->stopOnFailure() !== StopSign::DONT_STOP) {
80
            return $rule->stopOnFailure();
81
        }
82
83
        return StopSign::DONT_STOP;
84
    }
85
86
    private function shouldValidate(mixed $input): bool
87
    {
88
        if (null === $input && $this->containsRuleType(AllowNull::class)) {
89
            return false;
90
        }
91
92
        if ((new IsEmpty)->isValid($input) && $this->containsRuleType(AllowEmpty::class)) {
93
            return false;
94
        }
95
96
        return true;
97
    }
98
99
    public function containsRuleType(string $ruleClassName): bool
100
    {
101
        foreach ($this->rules as $rule) {
102
            if (is_a($rule, $ruleClassName, true)) {
103
                return true;
104
            }
105
        }
106
107
        return false;
108
    }
109
110
    public function isEmpty(): bool
111
    {
112
        return empty($this->rules);
113
    }
114
115
    /** @return Rule[] */
116
    public function rules(): array
117
    {
118
        return $this->rules;
119
    }
120
}
121