Passed
Push — main ( 5984a9...a02fd2 )
by Breno
01:39
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 (null === $input && $this->allowsNull()) {
75
            return $empty;
76
        }
77
78
        if ((is_string($input) || is_array($input)) && empty($input) && $this->allowsEmpty()) {
79
            return $empty;
80
        }
81
82
        foreach ($this->rules as $rule) {
83
            $violations = $violations->error(...$rule->validate($input, $context)->getErrors());
84
        }
85
86
        return $violations;
87
    }
88
89
    public function isRequired(): bool
90
    {
91
        if ($this->isEmpty()) {
92
            return false;
93
        }
94
95
        foreach ($this->rules as $rule) {
96
            if ($rule instanceof NotRequired) {
97
                return false;
98
            }
99
        }
100
101
        return true;
102
    }
103
104
    public function allowsEmpty(): bool
105
    {
106
        if ($this->isEmpty()) {
107
            return true;
108
        }
109
110
        foreach ($this->rules as $rule) {
111
            if ($rule instanceof AllowsEmpty) {
112
                return true;
113
            }
114
        }
115
116
        return false;
117
    }
118
119
    public function allowsNull(): bool
120
    {
121
        if ($this->isEmpty()) {
122
            return true;
123
        }
124
125
        foreach ($this->rules as $rule) {
126
            if ($rule instanceof AllowsNull) {
127
                return true;
128
            }
129
        }
130
131
        return false;
132
    }
133
134
    public function setNotRequired(): self
135
    {
136
        return $this->add(NotRequired::instance());
137
    }
138
139
    public function setAllowsEmpty(): self
140
    {
141
        return $this->add(AllowsEmpty::instance());
142
    }
143
144
    public function setAllowsNull(): self
145
    {
146
        return $this->add(AllowsNull::instance());
147
    }
148
149
    public function isEmpty(): bool
150
    {
151
        return $this->rules->count() === 0;
152
    }
153
154
    /** @return Rule[] */
155
    public function toArray(): array
156
    {
157
        return iterator_to_array($this->rules);
158
    }
159
160
    public function getIterator(): SplObjectStorage
161
    {
162
        return clone $this->rules;
163
    }
164
165
    public function count(): int
166
    {
167
        return $this->rules->count();
168
    }
169
}
170