Passed
Push — main ( a1ad3c...963e70 )
by Breno
01:36
created

RuleSet::getIterator()   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\Rules\AllowsEmpty;
7
use BrenoRoosevelt\Validation\Rules\AllowsNull;
8
use BrenoRoosevelt\Validation\Rules\NotRequired;
9
use Countable;
10
use IteratorAggregate;
11
use SplObjectStorage;
12
13
class RuleSet implements Rule, IteratorAggregate, Countable
14
{
15
    use GuardForValidation,
16
        MaybeBelongsToField {
17
            setField as private;
18
        }
19
20
    private SplObjectStorage $rules;
21
22
    /**
23
     * @throws ValidationException if the field is provided and is blank
24
     */
25
    final public function __construct(?string $field = null, Rule|RuleSet ...$rules)
26
    {
27
        $this->rules = new SplObjectStorage;
28
        $this->setField($field);
29
        $this->attachRules(...$rules);
30
    }
31
32
    public static function empty(): self
33
    {
34
        return new self;
35
    }
36
37
    public static function forField(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
    private function attachRules(Rule|RuleSet ...$rules): void
48
    {
49
        foreach ($rules as $validationOrSet) {
50
            if ($validationOrSet instanceof Rule) {
51
                $this->rules->attach($validationOrSet);
52
            }
53
54
            if ($validationOrSet instanceof RuleSet) {
55
                foreach ($validationOrSet as $validation) {
56
                    $this->rules->attach($validation);
57
                }
58
            }
59
        }
60
    }
61
62
    public function add(Rule|RuleSet ...$rules): self
63
    {
64
        $instance = clone $this;
65
        $instance->attachRules(...$rules);
66
        return $instance;
67
    }
68
69
    public function validate(mixed $input, array $context = []): ValidationResult|ValidationResultByField
70
    {
71
        $violations = $empty = $this->newEmptyValidationResult();
72
        if (null === $input && $this->allowsNull()) {
73
            return $empty;
74
        }
75
76
        if (empty($input) && $this->allowsEmpty()) {
77
            return $empty;
78
        }
79
80
        foreach ($this->rules as $rule) {
81
            $violations = $violations->error(...$rule->validate($input, $context)->getErrors());
82
        }
83
84
        return $violations;
85
    }
86
87
    public function isRequired(): bool
88
    {
89
        if ($this->isEmpty()) {
90
            return false;
91
        }
92
93
        foreach ($this->rules as $rule) {
94
            if ($rule instanceof NotRequired) {
95
                return false;
96
            }
97
        }
98
99
        return true;
100
    }
101
102
    public function allowsEmpty(): bool
103
    {
104
        if ($this->isEmpty()) {
105
            return true;
106
        }
107
108
        foreach ($this->rules as $rule) {
109
            if ($rule instanceof AllowsEmpty) {
110
                return true;
111
            }
112
        }
113
114
        return false;
115
    }
116
117
    public function allowsNull(): bool
118
    {
119
        if ($this->isEmpty()) {
120
            return true;
121
        }
122
123
        foreach ($this->rules as $rule) {
124
            if ($rule instanceof AllowsNull) {
125
                return true;
126
            }
127
        }
128
129
        return false;
130
    }
131
132
    public function setNotRequired(): self
133
    {
134
        return $this->add(NotRequired::instance());
135
    }
136
137
    public function setAllowsEmpty(): self
138
    {
139
        return $this->add(AllowsEmpty::instance());
140
    }
141
142
    public function setAllowsNull(): self
143
    {
144
        return $this->add(AllowsNull::instance());
145
    }
146
147
    public function isEmpty(): bool
148
    {
149
        return $this->rules->count() === 0;
150
    }
151
152
    /** @return Rule[] */
153
    public function toArray(): array
154
    {
155
        return iterator_to_array($this->rules);
156
    }
157
158
    public function getIterator(): SplObjectStorage
159
    {
160
        return clone $this->rules;
161
    }
162
163
    public function count(): int
164
    {
165
        return $this->rules->count();
166
    }
167
}
168