Passed
Push — master ( 151935...bf98f6 )
by Smoren
02:25
created

Rule::falsy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Smoren\Validator\Rules;
6
7
use Smoren\Validator\Exceptions\CheckError;
8
use Smoren\Validator\Exceptions\ValidationError;
9
use Smoren\Validator\Interfaces\CheckInterface;
10
use Smoren\Validator\Interfaces\RuleInterface;
11
use Smoren\Validator\Interfaces\ExecutionResultInterface;
12
use Smoren\Validator\Structs\Check;
13
use Smoren\Validator\Structs\ExecutionResult;
14
use Smoren\Validator\Structs\RetrospectiveCheck;
15
16
class Rule extends BaseRule implements RuleInterface
17
{
18
    public const ERROR_NULL = 'null';
19
    public const ERROR_NOT_TRUTHY = 'not_truthy';
20
    public const ERROR_NOT_FALSY = 'not_falsy';
21
22
    /**
23
     * @var array<CheckInterface>
24
     */
25
    protected array $checks = [];
26
27
    /**
28
     * @var array<string, int>
29
     */
30
    protected array $usedCheckNames = [];
31
32
    /**
33
     * @var bool
34
     */
35
    protected bool $isNullable = false;
36
37
    /**
38
     * {@inheritDoc}
39
     *
40
     * @return static
41
     */
42
    public function nullable(): self
43
    {
44
        $this->isNullable = true;
45
        return $this;
46
    }
47
48
    /**
49
     * {@inheritDoc}
50
     *
51
     * @return static
52
     */
53
    public function truthy(): self
54
    {
55
        return $this->addCheck(new Check(
56
            self::ERROR_NOT_TRUTHY,
57
            fn ($value) => boolval($value),
58
        ));
59
    }
60
61
    /**
62
     * {@inheritDoc}
63
     *
64
     * @return static
65
     */
66
    public function falsy(): self
67
    {
68
        return $this->addCheck(new Check(
69
            self::ERROR_NOT_FALSY,
70
            fn ($value) => !boolval($value),
71
        ));
72
    }
73
74
    /**
75
     * {@inheritDoc}
76
     *
77
     * @return static
78
     */
79
    public function addCheck(CheckInterface $check): self
80
    {
81
        $this->checks[] = $check;
82
83
        $name = $check->getName();
84
        if ($this->isCheckNameUsed($name)) {
85
            $this->usedCheckNames[$name]++;
86
        } else {
87
            $this->usedCheckNames[$name] = 1;
88
        }
89
90
        return $this;
91
    }
92
93
    /**
94
     * {@inheritDoc}
95
     *
96
     * @return static
97
     */
98
    public function check(string $name, callable $predicate, array $params = [], bool $isInterrupting = false): self
99
    {
100
        return $this->addCheck(new Check($name, $predicate, $params, $isInterrupting));
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     *
106
     * @return static
107
     */
108
    public function stopOnViolation(): self
109
    {
110
        return $this->addCheck(new RetrospectiveCheck());
111
    }
112
113
    /**
114
     * {@inheritDoc}
115
     *
116
     * @return static
117
     */
118
    public function stopOnAnyPriorViolation(): self
119
    {
120
        foreach ($this->checks as $check) {
121
            $check->setInterrupting();
122
        }
123
        return $this;
124
    }
125
126
    /**
127
     * @param string $checkName
128
     *
129
     * @return bool
130
     */
131
    protected function isCheckNameUsed(string $checkName): bool
132
    {
133
        return array_key_exists($checkName, $this->usedCheckNames);
134
    }
135
136
    /**
137
     * {@inheritDoc}
138
     */
139 61
    protected function execute($value): ExecutionResultInterface
140
    {
141 61
        $result = parent::execute($value);
142 61
        if ($result->areChecksSufficient()) {
143
            return $result;
144
        }
145
146 61
        if ($value === null) {
147 8
            if ($this->isNullable) {
148 5
                return new ExecutionResult(true);
149
            }
150
151 3
            throw new ValidationError($value, [[self::ERROR_NULL, []]]);
152
        }
153
154 58
        $errors = [];
155
156 58
        foreach ($this->checks as $check) {
157
            try {
158 57
                $check->execute($value, $errors);
159 35
            } catch (CheckError $e) {
160 35
                $errors[] = $e;
161 35
                if ($check->isInterrupting()) {
162 10
                    throw ValidationError::fromCheckErrors($value, $errors);
163
                }
164
            }
165
        }
166
167 51
        if (\count($errors) > 0) {
168 24
            throw ValidationError::fromCheckErrors($value, $errors);
169
        }
170
171 29
        return new ExecutionResult(false);
172
    }
173
}
174