Passed
Pull Request — master (#81)
by Def
01:31
created

Rule::formatMessage()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
nc 5
nop 2
dl 0
loc 14
ccs 11
cts 11
cp 1
crap 5
rs 9.6111
c 2
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator;
6
7
use Yiisoft\I18n\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
     * @return Result
32
     */
33 136
    final public function validate($value, DataSetInterface $dataSet = null, bool $previousRulesErrored = false): Result
34
    {
35 136
        if ($this->skipOnEmpty && $this->isEmpty($value)) {
36
            return new Result();
37
        }
38
39
        if (
40 136
          ($this->skipOnError && $previousRulesErrored) ||
41 136
          (is_callable($this->when) && !call_user_func($this->when, $value, $dataSet))
42
        ) {
43 6
            return new Result();
44
        }
45
46 136
        return $this->validateValue($value, $dataSet);
47
    }
48
49
    /**
50
     * Validates the value. The method should be implemented by concrete validation rules.
51
     *
52
     * @param mixed $value value to be validated
53
     * @param DataSetInterface|null $dataSet optional data set that could be used for contextual validation
54
     * @return Result
55
     */
56
    abstract protected function validateValue($value, DataSetInterface $dataSet = null): Result;
57
58 1
    public function translator(TranslatorInterface $translator): self
59
    {
60 1
        $new = clone $this;
61 1
        $new->translator = $translator;
62 1
        return $new;
63
    }
64
65
    public function translationDomain(string $translation): self
66
    {
67
        $new = clone $this;
68
        $new->translationDomain = $translation;
69
        return $new;
70
    }
71
72
    public function translationLocale(string $locale): self
73
    {
74
        $new = clone $this;
75
        $new->translationLocale = $locale;
76
        return $new;
77
    }
78
79 165
    protected function translateMessage(string $message, array $parameters = []): string
80
    {
81 165
        if ($this->translator === null) {
82 164
            return $this->formatMessage($message, $parameters);
83
        }
84
85 1
        return $this->translator->translate(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->translator...his->translationLocale) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
86 1
            $message,
87
            $parameters,
88 1
            $this->translationDomain ?? 'validators',
89 1
            $this->translationLocale
90
        );
91
    }
92
93
    /**
94
     * Add a PHP callable whose return value determines whether this rule should be applied.
95
     * By default rule will be always applied.
96
     *
97
     * The signature of the callable should be `function ($value, DataSetInterface $dataSet): bool`, where $value and $dataSet
98
     * refer to the value validated and the data set in which context it is validated. The callable should return
99
     * a boolean value.
100
     *
101
     * The following example will enable the validator only when the country currently selected is USA:
102
     *
103
     * ```php
104
     * function ($value, DataSetInterface $dataSet) {
105
         return $dataSet->getAttributeValue('country') === Country::USA;
106
     }
107
     * ```
108
     *
109
     * @param callable $callback
110
     * @return $this
111
     */
112 1
    public function when(callable $callback): self
113
    {
114 1
        $new = clone $this;
115 1
        $new->when = $callback;
116 1
        return $new;
117
    }
118
119 3
    public function skipOnError(bool $value): self
120
    {
121 3
        $new = clone $this;
122 3
        $new->skipOnError = $value;
123 3
        return $new;
124
    }
125
126
    /**
127
     * @param bool $value if validation should be skipped if value validated is empty
128
     * @return self
129
     */
130 1
    public function skipOnEmpty(bool $value): self
131
    {
132 1
        $new = clone $this;
133 1
        $new->skipOnEmpty = $value;
134 1
        return $new;
135
    }
136
137 164
    private function formatMessage(string $message, array $arguments = []): string
138
    {
139 164
        $replacements = [];
140 164
        foreach ($arguments as $key => $value) {
141 65
            if (is_array($value)) {
142 1
                $value = 'array';
143 64
            } elseif (is_object($value)) {
144 2
                $value = 'object';
145 62
            } elseif (is_resource($value)) {
146 1
                $value = 'resource';
147
            }
148 65
            $replacements['{' . $key . '}'] = $value;
149
        }
150 164
        return strtr($message, $replacements);
151
    }
152
153
    /**
154
     * Checks if the given value is empty.
155
     * A value is considered empty if it is null, an empty array, or an empty string.
156
     * Note that this method is different from PHP empty(). It will return false when the value is 0.
157
     * @param mixed $value the value to be checked
158
     * @return bool whether the value is empty
159
     */
160 10
    protected function isEmpty($value): bool
161
    {
162 10
        return $value === null || $value === [] || $value === '';
163
    }
164
165
    /**
166
     * Returns name of class in short format, lowercase
167
     * @return string
168
     */
169
    abstract public function getName(): string;
170
171
    /**
172
     * Returns rule options
173
     * @return array
174
     */
175 54
    public function getOptions(): array
176
    {
177 54
        return array_merge(
178 54
            $this->skipOnEmpty ? ['skipOnEmpty' => true] : [],
179 54
            $this->skipOnError ? [] : ['skipOnError' => false]
180
        );
181
    }
182
}
183