Passed
Push — main ( e4d7a2...ff0dd8 )
by Breno
01:45
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
        foreach ($this->rules as $rule) {
65
            if ($rule instanceof NotRequired) {
66
                return false;
67
            }
68
        }
69
70
        return true;
71
    }
72
73
    public function allowsEmpty(): bool
74
    {
75
        foreach ($this->rules as $rule) {
76
            if ($rule instanceof AllowsEmpty) {
77
                return true;
78
            }
79
        }
80
81
        return false;
82
    }
83
84
    public function notRequired(): self
85
    {
86
        if ($this->isRequired()) {
87
            $this->rules[] = new NotRequired;
88
        }
89
90
        return $this;
91
    }
92
93
    public function setAllowsEmpty(): self
94
    {
95
        if (!$this->allowsEmpty()) {
96
            $this->rules[] = new AllowsEmpty;
97
        }
98
99
        return $this;
100
    }
101
102
    public function isEmpty(): bool
103
    {
104
        return empty($this->rules);
105
    }
106
107
    /**
108
     * @param string|object $objectOrClass
109
     * @param int|null $filter filter properties, ex: ReflectionProperty::IS_PUBLIC|ReflectionProperty::IS_PRIVATE
110
     * @return ValidationSet[]
111
     * @throws ReflectionException if the class does not exist
112
     */
113
    public static function fromProperties(string|object $objectOrClass, ?int $filter = null): array
114
    {
115
        $ruleSets = [];
116
        foreach ((new ReflectionClass($objectOrClass))->getProperties($filter) as $property) {
117
            $ruleSets[$property->getName()] = ValidationSet::fromReflectionProperty($property);
118
        }
119
120
        return array_filter($ruleSets, fn(ValidationSet $c) => !$c->isEmpty());
121
    }
122
123
    /**
124
     * @param string|object $objectOrClass
125
     * @param int|null $filter
126
     * @return ValidationSet[]
127
     * @throws ReflectionException
128
     */
129
    public static function fromMethods(string|object $objectOrClass, ?int $filter = null): array
130
    {
131
        $ruleSets = [];
132
        foreach ((new ReflectionClass($objectOrClass))->getMethods($filter) as $method) {
133
            $ruleSets[$method->getName()] = ValidationSet::fromReflectionMethod($method);
134
        }
135
136
        return array_filter($ruleSets, fn(ValidationSet $c) => !$c->isEmpty());
137
    }
138
139
    /**
140
     * @param string|object $objectOrClass
141
     * @param string $property
142
     * @return static
143
     * @throws ReflectionException if the class or property does not exist.
144
     */
145
    public static function fromProperty(string|object $objectOrClass, string $property): self
146
    {
147
        return self::fromReflectionProperty(new ReflectionProperty($objectOrClass, $property));
148
    }
149
150
    public static function fromMethod(string|object $objectOrClass, string $method): self
151
    {
152
        return self::fromReflectionMethod(new ReflectionMethod($objectOrClass, $method));
153
    }
154
155
    /**
156
     * @param ReflectionProperty $property
157
     * @return static
158
     */
159
    public static function fromReflectionProperty(ReflectionProperty $property): self
160
    {
161
        return
162
            ValidationSet::forField(
163
                $property->getName(),
164
                ...array_map(
165
                    fn(ReflectionAttribute $attribute) => $attribute->newInstance(),
166
                    $property->getAttributes(Validation::class, ReflectionAttribute::IS_INSTANCEOF)
167
                )
168
            );
169
    }
170
171
    /**
172
     * @param ReflectionMethod $method
173
     * @return static
174
     */
175
    public static function fromReflectionMethod(ReflectionMethod $method): self
176
    {
177
        return
178
            ValidationSet::withRules(
179
                ...array_map(
180
                    fn(ReflectionAttribute $attribute) => $attribute->newInstance(),
181
                    $method->getAttributes(Validation::class, ReflectionAttribute::IS_INSTANCEOF)
182
                )
183
            );
184
    }
185
186
    /** @return Validation[] */
187
    public function rules(): array
188
    {
189
        return $this->rules;
190
    }
191
}
192