Passed
Push — main ( a02fd2...0e9331 )
by Breno
01:47
created

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