Passed
Push — main ( c8bc71...34003d )
by Breno
01:35
created

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