Passed
Push — main ( 63305f...7cc982 )
by Breno
01:54
created

RuleSet::rules()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
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\ValidateOrFail;
7
use BrenoRoosevelt\Validation\Rules\AllowsEmpty;
8
use BrenoRoosevelt\Validation\Rules\AllowsNull;
9
use BrenoRoosevelt\Validation\Rules\IsEmpty;
10
use BrenoRoosevelt\Validation\Rules\NotRequired;
11
use SplObjectStorage;
12
13
class RuleSet implements Rule
14
{
15
    use RuleChain,
16
        ValidateOrFail,
17
        MaybeBelongsToField;
18
19
    private SplObjectStorage $rules;
20
21
    final public function __construct(?string $field = null, Rule | RuleSet ...$rules)
22
    {
23
        $this->rules = new SplObjectStorage;
24
        $this->setField($field);
25
        $this->attachRules(...$rules);
26
    }
27
28
    public static function new(): self
29
    {
30
        return new self;
31
    }
32
33
    public static function of(string $field, Rule|RuleSet ...$rules): self
34
    {
35
        return new self($field, ...$rules);
36
    }
37
38
    public static function withRules(Rule|RuleSet ...$rules): self
39
    {
40
        return new self(null, ...$rules);
41
    }
42
43
    private function attachRules(Rule | RuleSet ...$rules): void
44
    {
45
        foreach ($rules as $validationOrSet) {
46
            if ($validationOrSet instanceof Rule) {
47
                $this->rules->attach($validationOrSet);
48
            }
49
50
            if ($validationOrSet instanceof RuleSet) {
51
                foreach ($validationOrSet as $validation) {
52
                    $this->rules->attach($validation);
53
                }
54
            }
55
        }
56
    }
57
58
    public function add(Rule | RuleSet ...$rules): static
59
    {
60
        $instance = clone $this;
61
        $instance->attachRules(...$rules);
62
        return $instance;
63
    }
64
65
    public function validate(mixed $input, array $context = []): ValidationResult
66
    {
67
        $violations = $empty = $this->newEmptyResult();
68
        if (!$this->shouldValidate($input)) {
69
            return $empty;
70
        }
71
72
        foreach ($this->rules as $rule) {
73
            $violations = $violations->addError(...$rule->validate($input, $context)->getErrors());
74
        }
75
76
        return $violations;
77
    }
78
79
    private function shouldValidate(mixed $input): bool
80
    {
81
        if (null === $input && $this->isAllowsNull()) {
82
            return false;
83
        }
84
85
        $isEmptyInput = (new IsEmpty)->isValid($input);
86
        if ($isEmptyInput && $this->isAllowsEmpty()) {
87
            return false;
88
        }
89
90
        return true;
91
    }
92
93
    public function hasRule(string $ruleClass): bool
94
    {
95
        if (!class_exists($ruleClass)) {
96
            return false;
97
        }
98
99
        foreach ($this->rules as $rule) {
100
            if ($rule instanceof $ruleClass) {
101
                return true;
102
            }
103
        }
104
105
        return false;
106
    }
107
108
    public function isNotRequired(): bool
109
    {
110
        return $this->hasRule(NotRequired::class);
111
    }
112
113
    public function isAllowsEmpty(): bool
114
    {
115
        return $this->isEmpty() || $this->hasRule(AllowsEmpty::class);
116
    }
117
118
    public function isAllowsNull(): bool
119
    {
120
        return $this->isEmpty() || $this->hasRule(AllowsNull::class);
121
    }
122
123
    public function isEmpty(): bool
124
    {
125
        return $this->rules->count() === 0;
126
    }
127
128
    /** @return Rule[] */
129
    public function rules(): array
130
    {
131
        return iterator_to_array($this->rules);
132
    }
133
}
134