Passed
Push — main ( 8eb503...b1f274 )
by Breno
02:08
created

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