Passed
Push — main ( b1f274...dccb3c )
by Breno
01:48
created

ValidationSet   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Importance

Changes 7
Bugs 2 Features 0
Metric Value
eloc 66
c 7
b 2
f 0
dl 0
loc 205
rs 9.6
wmc 35

20 Methods

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