Passed
Push — main ( c6e7a1...7c50b2 )
by Breno
02:58
created

RuleSet::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 5
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\Factories\AllFactories;
7
use BrenoRoosevelt\Validation\Rules\AllowsEmpty;
8
use BrenoRoosevelt\Validation\Rules\AllowsNull;
9
use BrenoRoosevelt\Validation\Rules\IsEmpty;
10
use BrenoRoosevelt\Validation\Rules\NotRequired;
11
use Countable;
12
use IteratorAggregate;
13
use SplObjectStorage;
14
15
class RuleSet implements Rule, IteratorAggregate, Countable
16
{
17
    use GuardForValidation,
18
        AllFactories,
19
        MaybeBelongsToField {
20
        field as private;
21
    }
22
23
    private SplObjectStorage $rules;
24
25
    /**
26
     * @throws ValidationException
27
     */
28
    final public function __construct(?string $field = null, Rule|RuleSet ...$rules)
29
    {
30
        $this->rules = new SplObjectStorage;
31
        $this->setField($field);
32
        $this->attachRules(...$rules);
33
    }
34
35
    public static function new(): self
36
    {
37
        return new self;
38
    }
39
40
    public static function forField(string $field, Rule|RuleSet ...$rules): self
41
    {
42
        return new self($field, ...$rules);
43
    }
44
45
    public static function withRules(Rule|RuleSet ...$rules): self
46
    {
47
        return new self(null, ...$rules);
48
    }
49
50
    private function attachRules(Rule|RuleSet ...$rules): void
51
    {
52
        foreach ($rules as $validationOrSet) {
53
            if ($validationOrSet instanceof Rule) {
54
                $this->rules->attach($validationOrSet);
55
            }
56
57
            if ($validationOrSet instanceof RuleSet) {
58
                foreach ($validationOrSet as $validation) {
59
                    $this->rules->attach($validation);
60
                }
61
            }
62
        }
63
    }
64
65
    public function add(Rule|RuleSet ...$rules): static
66
    {
67
        $instance = clone $this;
68
        $instance->attachRules(...$rules);
69
        return $instance;
70
    }
71
72
    public function validate(mixed $input, array $context = []): ValidationResult|ValidationResultByField
73
    {
74
        $violations = $empty = $this->newEmptyValidationResult();
75
        if (!$this->shouldValidate($input)) {
76
            return $empty;
77
        }
78
79
        foreach ($this->rules as $rule) {
80
            $violations = $violations->error(...$rule->validate($input, $context)->getErrors());
81
        }
82
83
        return $violations;
84
    }
85
86
    private function shouldValidate(mixed $input): bool
87
    {
88
        if (null === $input && $this->isAllowsNull()) {
89
            return false;
90
        }
91
92
        $isEmptyInput = (new IsEmpty)->isValid($input);
93
        if ($isEmptyInput && $this->isAllowsEmpty()) {
94
            return false;
95
        }
96
97
        return true;
98
    }
99
100
    public function hasRule(string $ruleClass): bool
101
    {
102
        if (!class_exists($ruleClass)) {
103
            return false;
104
        }
105
106
        foreach ($this->rules as $rule) {
107
            if ($rule instanceof $ruleClass) {
108
                return true;
109
            }
110
        }
111
112
        return false;
113
    }
114
115
    public function isNotRequired(): bool
116
    {
117
        return !$this->isEmpty() && !$this->hasRule(NotRequired::class);
118
    }
119
120
    public function isAllowsEmpty(): bool
121
    {
122
        return $this->isEmpty() || $this->hasRule(AllowsEmpty::class);
123
    }
124
125
    public function isAllowsNull(): bool
126
    {
127
        return $this->isEmpty() || $this->hasRule(AllowsNull::class);
128
    }
129
130
    public function isEmpty(): bool
131
    {
132
        return $this->rules->count() === 0;
133
    }
134
135
    /** @return Rule[] */
136
    public function toArray(): array
137
    {
138
        return iterator_to_array($this->rules);
139
    }
140
141
    public function getIterator(): SplObjectStorage
142
    {
143
        return clone $this->rules;
144
    }
145
146
    public function count(): int
147
    {
148
        return $this->rules->count();
149
    }
150
}
151