Passed
Push — main ( b9b194...557df8 )
by Breno
01:59
created

Validator   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 109
Duplicated Lines 0 %

Importance

Changes 27
Bugs 2 Features 0
Metric Value
eloc 39
dl 0
loc 109
rs 10
c 27
b 2
f 0
wmc 19

9 Methods

Rating   Name   Duplication   Size   Complexity  
A ruleSet() 0 3 1
A field() 0 5 1
A new() 0 3 1
A shouldStop() 0 3 2
A validateOrFail() 0 5 1
A validateObject() 0 20 5
A fromProperties() 0 5 1
A validate() 0 18 4
A shouldValidate() 0 6 3
1
<?php
2
declare(strict_types=1);
3
4
namespace BrenoRoosevelt\Validation;
5
6
use BrenoRoosevelt\Validation\Exception\GuardTrait;
7
use BrenoRoosevelt\Validation\Exception\ValidationExceptionFactoryInterface;
8
use BrenoRoosevelt\Validation\Exception\ValidationExceptionInterface;
9
use ReflectionClass;
10
use ReflectionException;
11
12
final class Validator
13
{
14
    use GuardTrait;
15
16
    /** @var RuleSet[] */
17
    private array $ruleSets;
18
19
    public static function new(): self
20
    {
21
        return new self;
22
    }
23
24
    /**
25
     * Returns an instance of RuleSet (immutable) or null if not set
26
     * @param string $field
27
     * @return RuleSet|null
28
     */
29
    public function ruleSet(string $field): ?RuleSet
30
    {
31
        return $this->ruleSets[$field] ?? null;
32
    }
33
34
    /**
35
     * Apply new rules to the field
36
     * @param string $field
37
     * @param Rule|RuleSet ...$rules
38
     * @return $this
39
     */
40
    public function field(string $field, Rule|RuleSet ...$rules): self
41
    {
42
        $ruleSet = $this->ruleSets[$field] ?? RuleSet::of($field);
43
        $this->ruleSets[$field] = $ruleSet->add(...$rules);
44
        return $this;
45
    }
46
47
    public function validate(array $data = []): ErrorReporting
48
    {
49
        $errorReporting = new ErrorReporting;
50
        foreach ($this->ruleSets as $field => $fieldRuleSet) {
51
            if ($this->shouldValidate($fieldRuleSet, $data)) {
52
                continue;
53
            }
54
55
            $fieldRuleSet = $fieldRuleSet->setField($field);
56
            $result = $fieldRuleSet->validate($data[$field] ?? null, $data);
57
            $errorReporting = $errorReporting->add($result);
58
59
            if ($this->shouldStop($fieldRuleSet, $result)) {
60
                break;
61
            }
62
        }
63
64
        return $errorReporting;
65
    }
66
67
    private function shouldValidate(RuleSet $fieldRuleSet, mixed $data): bool
68
    {
69
        $field = $fieldRuleSet->getField();
70
        $fieldIsPresent = $field && array_key_exists($field, $data);
71
72
        return !$fieldRuleSet->hasRequired() && !$fieldIsPresent;
73
    }
74
75
    private function shouldStop(RuleSet $fieldRuleSet, Result $result): bool
76
    {
77
        return $fieldRuleSet->stopOnFailure() && !$result->isOk();
78
    }
79
80
    /** @throws ValidationExceptionInterface */
81
    public function validateOrFail(
82
        array $data = [],
83
        ValidationExceptionFactoryInterface | ValidationExceptionInterface | string | null  $validationException = null
84
    ): void {
85
        $this->validate($data)->guard($validationException);
86
    }
87
88
    public static function validateObject(object $object): ErrorReporting
89
    {
90
        $data = [];
91
        $class = new ReflectionClass($object);
92
        foreach ($class->getProperties() as $property) {
93
            $data[$property->getName()] = $property->getValue($object);
94
        }
95
96
        $result = Validator::fromProperties($object)->validate($data);
97
98
        foreach ($class->getMethods() as $method) {
99
            $ruleSet = RuleSetFactory::fromReflectionMethod($method);
100
            $value = $method->invoke($method->isStatic() ? null : $object);
101
            $methodResult = $ruleSet->validate($value);
102
            if (!$methodResult->isOk()) {
103
                $result = $result->add($methodResult);
104
            }
105
        }
106
107
        return $result;
108
    }
109
110
    /**
111
     * @param string|object $objectOrClass
112
     * @param int|null $filter filter properties, ex: ReflectionProperty::IS_PUBLIC|ReflectionProperty::IS_PRIVATE
113
     * @return static
114
     * @throws ReflectionException if the class does not exist
115
     */
116
    public static function fromProperties(string|object $objectOrClass, ?int $filter = null): self
117
    {
118
        $instance = new self;
119
        $instance->ruleSets = RuleSetFactory::fromProperties($objectOrClass, $filter);
120
        return $instance;
121
    }
122
}
123