Completed
Push — master ( d45ca1...554243 )
by Adrian
01:42
created

ValueValidator::getMessages()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Validation;
5
6
use Sirius\Validation\Rule\AbstractRule;
7
8
class ValueValidator
9
{
10
11
    /**
12
     * The error messages generated after validation or set manually
13
     *
14
     * @var array
15
     */
16
    protected $messages = [];
17
18
    /**
19
     * Will be used to construct the rules
20
     *
21
     * @var \Sirius\Validation\RuleFactory
22
     */
23
    protected $ruleFactory;
24
25
    /**
26
     * The prototype that will be used to generate the error message
27
     *
28
     * @var \Sirius\Validation\ErrorMessage
29
     */
30
    protected $errorMessagePrototype;
31
32
    /**
33
     * The rule collections for the validation
34
     *
35
     * @var \Sirius\Validation\RuleCollection
36
     */
37
    protected $rules;
38
39
    /**
40
     * The label of the value to be validated
41
     *
42
     * @var string
43
     */
44
    protected $label;
45
46
47 24
    public function __construct(
48
        RuleFactory $ruleFactory = null,
49
        ErrorMessage $errorMessagePrototype = null,
50
        $label = null
51
    ) {
52 24
        if (!$ruleFactory) {
53 7
            $ruleFactory = new RuleFactory();
54
        }
55 24
        $this->ruleFactory = $ruleFactory;
56 24
        if (!$errorMessagePrototype) {
57 7
            $errorMessagePrototype = new ErrorMessage();
58
        }
59 24
        $this->errorMessagePrototype = $errorMessagePrototype;
60 24
        if ($label) {
61 2
            $this->label = $label;
62
        }
63 24
        $this->rules = new RuleCollection;
64 24
    }
65
66 1
    public function setLabel($label = null)
67
    {
68 1
        $this->label = $label;
69
70 1
        return $this;
71
    }
72
73
    /**
74
     * Add 1 or more validation rules
75
     *
76
     * @example
77
     * // add multiple rules at once
78
     * $validator->add(array(
79
     *   'required',
80
     *   ['required', ['email', null, '{label} must be an email', 'Field B']],
81
     * ));
82
     *
83
     * // add multiple rules using a string
84
     * $validator->add('required | email');
85
     *
86
     * // add validator with options
87
     * $validator->add('minlength', ['min' => 2], '{label} should have at least {min} characters', 'Field label');
88
     *
89
     * // add validator with string and parameters as JSON string
90
     * $validator->add('minlength({"min": 2})({label} should have at least {min} characters)(Field label)');
91
     *
92
     * // add validator with string and parameters as query string
93
     * $validator->add('minlength(min=2)({label} should have at least {min} characters)(Field label)');
94
     *
95
     * @param string|callback $name
96
     * @param string|array $options
97
     * @param string $messageTemplate
98
     * @param string $label
99
     *
100
     * @return ValueValidator
101
     */
102 24
    public function add($name, $options = null, $messageTemplate = null, $label = null)
103
    {
104 24
        if (is_array($name) && !is_callable($name)) {
105
            return $this->addMultiple($name);
106
        }
107 24
        if (is_string($name)) {
108
            // rule was supplied like 'required | email'
109 24
            if (strpos($name, ' | ') !== false) {
110 4
                return $this->addMultiple(explode(' | ', $name));
111
            }
112
            // rule was supplied like this 'length(2,10)(error message template)(label)'
113 24
            if (strpos($name, '(') !== false) {
114 7
                list($name, $options, $messageTemplate, $label) = $this->parseRule($name);
115
            }
116
        }
117
118
        // check for the default label
119 24
        if (!$label && $this->label) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $label of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
120 2
            $label = $this->label;
121
        }
122
123 24
        $validator = $this->ruleFactory->createRule($name, $options, $messageTemplate, $label);
124
125 22
        return $this->addRule($validator);
126
    }
127
128
    /**
129
     * @param array $rules
130
     *
131
     * @return ValueValidator
132
     */
133 4
    public function addMultiple($rules)
134
    {
135 4
        foreach ($rules as $singleRule) {
136
            // make sure the rule is an array (the parameters of subsequent calls);
137 4
            $singleRule = is_array($singleRule) ? $singleRule : [$singleRule];
138 4
            call_user_func_array([$this, 'add'], $singleRule);
139
        }
140
141 4
        return $this;
142
    }
143
144
    /**
145
     * @param AbstractValidator $validationRule
146
     *
147
     * @return ValueValidator
148
     */
149 22
    public function addRule(AbstractRule $validationRule)
150
    {
151 22
        $validationRule->setErrorMessagePrototype($this->errorMessagePrototype);
152 22
        $this->rules->attach($validationRule);
153
154 22
        return $this;
155
    }
156
157
    /**
158
     * Remove validation rule
159
     *
160
     * @param mixed $name
161
     *            rule name or true if all rules should be deleted for that selector
162
     * @param mixed $options
163
     *            rule options, necessary for rules that depend on params for their ID
164
     *
165
     * @throws \InvalidArgumentException
166
     * @internal param string $selector data selector
167
     * @return self
168
     */
169 4
    public function remove($name = true, $options = null)
170
    {
171 4
        if ($name === true) {
172 2
            $this->rules = new RuleCollection();
173
174 2
            return $this;
175
        }
176 2
        $validator = $this->ruleFactory->createRule($name, $options);
0 ignored issues
show
Documentation introduced by
$name is of type *, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
177 2
        $this->rules->detach($validator);
178
179 2
        return $this;
180
    }
181
182
    /**
183
     * Converts a rule that was supplied as string into a set of options that define the rule
184
     *
185
     * @example 'minLength({"min":2})({label} must have at least {min} characters)(Street)'
186
     *
187
     *          will be converted into
188
     *
189
     *          [
190
     *            'minLength', // validator name
191
     *            ['min' => 2'], // validator options
192
     *            '{label} must have at least {min} characters',
193
     *            'Street' // label
194
     *          ]
195
     *
196
     * @param string $ruleAsString
197
     *
198
     * @return array
199
     */
200 7
    protected function parseRule($ruleAsString)
201
    {
202 7
        $ruleAsString    = trim($ruleAsString);
203 7
        $options         = [];
204 7
        $messageTemplate = null;
205 7
        $label           = null;
206
207 7
        $name         = substr($ruleAsString, 0, strpos($ruleAsString, '('));
208 7
        $ruleAsString = substr($ruleAsString, strpos($ruleAsString, '('));
209 7
        $matches      = [];
210 7
        preg_match_all('/\(([^\)]*)\)/', $ruleAsString, $matches);
211
212 7
        if (isset($matches[1])) {
213 7
            if (isset($matches[1][0]) && $matches[1][0] !== '') {
214 7
                $options = $matches[1][0];
215
            }
216 7
            if (isset($matches[1][1]) && $matches[1][1]) {
217 2
                $messageTemplate = $matches[1][1];
218
            }
219 7
            if (isset($matches[1][2]) && $matches[1][2]) {
220 2
                $label = $matches[1][2];
221
            }
222
        }
223
224
        return [
225 7
            $name,
226 7
            $options,
227 7
            $messageTemplate,
228 7
            $label
229
        ];
230
    }
231
232
233 22
    public function validate($value, string $valueIdentifier = null, DataWrapper\WrapperInterface $context = null)
234
    {
235 22
        $this->messages = [];
236 22
        $isRequired     = false;
237
238
        // evaluate the required rules
239
        /* @var $rule \Sirius\Validation\Rule\AbstractValidator */
240 22
        foreach ($this->rules as $rule) {
241 22
            if ($rule instanceof Rule\Required) {
242 15
                $isRequired = true;
243
244 15
                if (!$this->validateRule($rule, $value, $valueIdentifier, $context)) {
245 9
                    return false;
246
                }
247
            }
248
        }
249
250
        // avoid future rule evaluations if value is null or empty string
251 20
        if ($this->isEmpty($value)) {
252 7
            return true;
253
        }
254
255
        // evaluate the non-required rules
256 15
        foreach ($this->rules as $rule) {
257 15
            if (!($rule instanceof Rule\Required)) {
258 15
                $this->validateRule($rule, $value, $valueIdentifier, $context);
259
260
                // if field is required and we have an error,
261
                // do not continue with the rest of rules
262 15
                if ($isRequired && count($this->messages)) {
263 8
                    break;
264
                }
265
            }
266
        }
267
268 15
        return count($this->messages) === 0;
269
    }
270
271 21
    private function validateRule($rule, $value, $valueIdentifier, $context)
272
    {
273 21
        $rule->setContext($context);
274 21
        if (!$rule->validate($value, $valueIdentifier)) {
275 20
            $this->addMessage($rule->getMessage());
276 20
            return false;
277
        }
278 12
        return true;
279
    }
280
281 19
    public function getMessages()
282
    {
283 19
        return $this->messages;
284
    }
285
286 20
    public function addMessage($message)
287
    {
288 20
        array_push($this->messages, $message);
289
290 20
        return $this;
291
    }
292
293 2
    public function getRules()
294
    {
295 2
        return $this->rules;
296
    }
297
298 20
    protected function isEmpty($value)
299
    {
300 20
        return in_array($value, [null, ''], true);
301
    }
302
}
303