Passed
Pull Request — master (#98)
by Alexander
04:56 queued 02:02
created

Rule::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator;
6
7
use Yiisoft\Translator\TranslatorInterface;
8
9
/**
10
 * Rule represents a single value validation rule.
11
 */
12
abstract class Rule
13
{
14
    private ?TranslatorInterface $translator = null;
15
    private ?string $translationDomain = null;
16
    private ?string $translationLocale = null;
17
    private bool $skipOnEmpty = false;
18
    private bool $skipOnError = true;
19
20
    /**
21
     * @var callable|null
22
     */
23
    private $when = null;
24
25
    /**
26
     * Validates the value
27
     *
28
     * @param mixed $value value to be validated
29
     * @param DataSetInterface|null $dataSet optional data set that could be used for contextual validation
30
     * @param bool $previousRulesErrored set to true if rule is part of a group of rules and one of the previous validations failed
31
     *
32
     * @return Result
33
     */
34 146
    final public function validate($value, DataSetInterface $dataSet = null, bool $previousRulesErrored = false): Result
35
    {
36 146
        if ($this->skipOnEmpty && $this->isEmpty($value)) {
37 1
            return new Result();
38
        }
39
40
        if (
41 146
          ($this->skipOnError && $previousRulesErrored) ||
42 146
          (is_callable($this->when) && !($this->when)($value, $dataSet))
43
        ) {
44 6
            return new Result();
45
        }
46
47 146
        return $this->validateValue($value, $dataSet);
48
    }
49
50
    /**
51
     * Validates the value. The method should be implemented by concrete validation rules.
52
     *
53
     * @param mixed $value value to be validated
54
     * @param DataSetInterface|null $dataSet optional data set that could be used for contextual validation
55
     *
56
     * @return Result
57
     */
58
    abstract protected function validateValue($value, DataSetInterface $dataSet = null): Result;
59
60 1
    public function translator(TranslatorInterface $translator): self
61
    {
62 1
        $new = clone $this;
63 1
        $new->translator = $translator;
64 1
        return $new;
65
    }
66
67
    public function translationDomain(string $translation): self
68
    {
69
        $new = clone $this;
70
        $new->translationDomain = $translation;
71
        return $new;
72
    }
73
74
    public function translationLocale(string $locale): self
75
    {
76
        $new = clone $this;
77
        $new->translationLocale = $locale;
78
        return $new;
79
    }
80
81 175
    protected function translateMessage(string $message, array $parameters = []): string
82
    {
83 175
        if ($this->translator === null) {
84 174
            return $this->formatMessage($message, $parameters);
85
        }
86
87 1
        return $this->translator->translate(
88 1
            $message,
89
            $parameters,
90 1
            $this->translationDomain ?? 'validators',
91 1
            $this->translationLocale
92
        );
93
    }
94
95
    /**
96
     * Add a PHP callable whose return value determines whether this rule should be applied.
97
     * By default rule will be always applied.
98
     *
99
     * The signature of the callable should be `function ($value, DataSetInterface $dataSet): bool`, where $value and $dataSet
100
     * refer to the value validated and the data set in which context it is validated. The callable should return
101
     * a boolean value.
102
     *
103
     * The following example will enable the validator only when the country currently selected is USA:
104
     *
105
     * ```php
106
     * function ($value, DataSetInterface $dataSet) {
107
         return $dataSet->getAttributeValue('country') === Country::USA;
108
     }
109
     * ```
110
     *
111
     * @param callable $callback
112
     *
113
     * @return $this
114
     */
115 1
    public function when(callable $callback): self
116
    {
117 1
        $new = clone $this;
118 1
        $new->when = $callback;
119 1
        return $new;
120
    }
121
122 3
    public function skipOnError(bool $value): self
123
    {
124 3
        $new = clone $this;
125 3
        $new->skipOnError = $value;
126 3
        return $new;
127
    }
128
129
    /**
130
     * @param bool $value if validation should be skipped if value validated is empty
131
     *
132
     * @return self
133
     */
134 1
    public function skipOnEmpty(bool $value): self
135
    {
136 1
        $new = clone $this;
137 1
        $new->skipOnEmpty = $value;
138 1
        return $new;
139
    }
140
141 174
    private function formatMessage(string $message, array $arguments = []): string
142
    {
143 174
        $replacements = [];
144 174
        foreach ($arguments as $key => $value) {
145 74
            if (is_array($value)) {
146 1
                $value = 'array';
147 73
            } elseif (is_object($value)) {
148 2
                $value = 'object';
149 71
            } elseif (is_resource($value)) {
150 1
                $value = 'resource';
151
            }
152 74
            $replacements['{' . $key . '}'] = $value;
153
        }
154 174
        return strtr($message, $replacements);
155
    }
156
157
    /**
158
     * Checks if the given value is empty.
159
     * A value is considered empty if it is null, an empty array, or an empty string.
160
     * Note that this method is different from PHP empty(). It will return false when the value is 0.
161
     *
162
     * @param mixed $value the value to be checked
163
     *
164
     * @return bool whether the value is empty
165
     */
166 13
    protected function isEmpty($value): bool
167
    {
168 13
        return $value === null || $value === [] || $value === '';
169
    }
170
171
    /**
172
     * Get name of the rule to be used when rule is converted to array.
173
     * By default it returns base name of the class, first letter in lowercase.
174
     *
175
     * @return string
176
     */
177 21
    public function getName(): string
178
    {
179 21
        $className = static::class;
180 21
        return lcfirst(substr($className, strrpos($className, '\\') + 1));
181
    }
182
183
    /**
184
     * Returns rule options as array.
185
     *
186
     * @return array
187
     */
188 57
    public function getOptions(): array
189
    {
190
        return [
191 57
            'skipOnEmpty' => $this->skipOnEmpty,
192 57
            'skipOnError' => $this->skipOnError,
193
        ];
194
    }
195
}
196