Passed
Push — main ( 6abb46...867243 )
by Breno
01:45
created

ValidationSet::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 ReflectionAttribute;
12
use ReflectionClass;
13
use ReflectionException;
14
use ReflectionMethod;
15
use ReflectionProperty;
16
use SplObjectStorage;
17
18
class ValidationSet implements Validation, IteratorAggregate, Countable
19
{
20
    use GuardForValidation,
21
        MaybeBelongsToField;
22
23
    private SplObjectStorage $rules;
24
25
    final public function __construct(?string $field = null, Validation ...$rules)
26
    {
27
        $this->rules = new SplObjectStorage();
28
        $this->setField($field);
29
        $this->add(...$rules);
30
    }
31
32
    public static function empty(): self
33
    {
34
        return new self;
35
    }
36
37
    public static function forField(string $field, Validation ...$rules): self
38
    {
39
        return new self($field, ...$rules);
40
    }
41
42
    public static function withRules(Validation $validation, Validation ...$rules): self
43
    {
44
        return new self(null, $validation, ...$rules);
45
    }
46
47
    public function add(Validation ...$rules): self
48
    {
49
        foreach ($rules as $rule) {
50
            $this->rules->attach($rule);
51
        }
52
53
        return $this;
54
    }
55
56
    public function validate(mixed $input, array $context = []): ValidationResult|ValidationResultByField
57
    {
58
        $violations = $this->newEmptyValidationResult();
59
        if (null === $input && $this->allowsNull()) {
60
            return $violations;
61
        }
62
63
        if (empty($input) && $this->allowsEmpty()) {
64
            return $violations;
65
        }
66
67
        foreach ($this->rules as $rule) {
68
            $violations = $violations->error(...$rule->validate($input, $context)->getErrors());
69
        }
70
71
        return $violations;
72
    }
73
74
    public function isRequired(): bool
75
    {
76
        if ($this->isEmpty()) {
77
            return false;
78
        }
79
80
        foreach ($this->rules as $rule) {
81
            if ($rule instanceof NotRequired) {
82
                return false;
83
            }
84
        }
85
86
        return true;
87
    }
88
89
    public function allowsEmpty(): bool
90
    {
91
        if ($this->isEmpty()) {
92
            return true;
93
        }
94
95
        foreach ($this->rules as $rule) {
96
            if ($rule instanceof AllowsEmpty) {
97
                return true;
98
            }
99
        }
100
101
        return false;
102
    }
103
104
    public function allowsNull(): bool
105
    {
106
        if ($this->isEmpty()) {
107
            return true;
108
        }
109
110
        foreach ($this->rules as $rule) {
111
            if ($rule instanceof AllowsNull) {
112
                return true;
113
            }
114
        }
115
116
        return false;
117
    }
118
119
    public function setNotRequired(): self
120
    {
121
        return $this->add(new NotRequired);
122
    }
123
124
    public function setAllowsEmpty(): self
125
    {
126
        return $this->add(new AllowsEmpty);
127
    }
128
129
    public function setAllowsNull(): self
130
    {
131
        return $this->add(new AllowsNull);
132
    }
133
134
    public function isEmpty(): bool
135
    {
136
        return $this->rules->count() === 0;
137
    }
138
139
    /**
140
     * @param string|object $objectOrClass
141
     * @param int|null $filter filter properties, ex: ReflectionProperty::IS_PUBLIC|ReflectionProperty::IS_PRIVATE
142
     * @return ValidationSet[]
143
     * @throws ReflectionException if the class does not exist
144
     */
145
    public static function fromProperties(string|object $objectOrClass, ?int $filter = null): array
146
    {
147
        $ruleSets = [];
148
        foreach ((new ReflectionClass($objectOrClass))->getProperties($filter) as $property) {
149
            $ruleSets[$property->getName()] = ValidationSet::fromReflectionProperty($property);
150
        }
151
152
        return array_filter($ruleSets, fn(ValidationSet $c) => !$c->isEmpty());
153
    }
154
155
    /**
156
     * @param string|object $objectOrClass
157
     * @param int|null $filter
158
     * @return ValidationSet[]
159
     * @throws ReflectionException
160
     */
161
    public static function fromMethods(string|object $objectOrClass, ?int $filter = null): array
162
    {
163
        $ruleSets = [];
164
        foreach ((new ReflectionClass($objectOrClass))->getMethods($filter) as $method) {
165
            $ruleSets[$method->getName()] = ValidationSet::fromReflectionMethod($method);
166
        }
167
168
        return array_filter($ruleSets, fn(ValidationSet $c) => !$c->isEmpty());
169
    }
170
171
    /**
172
     * @param string|object $objectOrClass
173
     * @param string $property
174
     * @return static
175
     * @throws ReflectionException if the class or property does not exist.
176
     */
177
    public static function fromProperty(string|object $objectOrClass, string $property): self
178
    {
179
        return self::fromReflectionProperty(new ReflectionProperty($objectOrClass, $property));
180
    }
181
182
    public static function fromMethod(string|object $objectOrClass, string $method): self
183
    {
184
        return self::fromReflectionMethod(new ReflectionMethod($objectOrClass, $method));
185
    }
186
187
    /**
188
     * @param ReflectionProperty $property
189
     * @return static
190
     */
191
    public static function fromReflectionProperty(ReflectionProperty $property): self
192
    {
193
        return
194
            ValidationSet::forField(
195
                $property->getName(),
196
                ...array_map(
197
                    fn(ReflectionAttribute $attribute) => $attribute->newInstance(),
198
                    $property->getAttributes(Validation::class, ReflectionAttribute::IS_INSTANCEOF)
199
                )
200
            );
201
    }
202
203
    /**
204
     * @param ReflectionMethod $method
205
     * @return static
206
     */
207
    public static function fromReflectionMethod(ReflectionMethod $method): self
208
    {
209
        return
210
            ValidationSet::withRules(
211
                ...array_map(
212
                    fn(ReflectionAttribute $attribute) => $attribute->newInstance(),
213
                    $method->getAttributes(Validation::class, ReflectionAttribute::IS_INSTANCEOF)
214
                )
215
            );
216
    }
217
218
    /** @return Validation[] */
219
    public function toArray(): array
220
    {
221
        return iterator_to_array($this->rules);
222
    }
223
224
    public function getIterator(): SplObjectStorage
225
    {
226
        return clone $this->rules;
227
    }
228
229
    public function count(): int
230
    {
231
        return $this->rules->count();
232
    }
233
}
234