Completed
Branch master (b7a0b6)
by Adrian
04:26 queued 02:31
created

Validator::setData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
cc 1
eloc 5
nc 1
nop 1
crap 1
1
<?php
2
namespace Sirius\Validation;
3
4
use Sirius\Validation\ValidatorInterface;
5
6
class Validator implements ValidatorInterface
7
{
8
    const RULE_REQUIRED = 'required';
9
10
    const RULE_REQUIRED_WITH = 'requiredwith';
11
12
    const RULE_REQUIRED_WITHOUT = 'requiredwithout';
13
14
    const RULE_REQUIRED_WHEN = 'requiredwhen';
15
16
    // string rules
17
    const RULE_ALPHA = 'alpha';
18
19
    const RULE_ALPHANUMERIC = 'alphanumeric';
20
21
    const RULE_ALPHANUMHYPHEN = 'alphanumhyphen';
22
23
    const RULE_LENGTH = 'length';
24
25
    const RULE_MAX_LENGTH = 'maxlength';
26
27
    const RULE_MIN_LENGTH = 'minlength';
28
29
    const RULE_FULLNAME = 'fullname';
30
31
    // array rules
32
    const RULE_ARRAY_LENGTH = 'arraylength';
33
34
    const RULE_ARRAY_MIN_LENGTH = 'arrayminlength';
35
36
    const RULE_ARRAY_MAX_LENGTH = 'arraymaxlength';
37
38
    const RULE_IN_LIST = 'inlist';
39
40
    const RULE_NOT_IN_LIST = 'notinlist';
41
42
    // date rules
43
    const RULE_DATE = 'date';
44
45
    const RULE_DATETIME = 'datetime';
46
47
    const RULE_TIME = 'time';
48
49
    // number rules
50
    const RULE_BETWEEN = 'between';
51
52
    const RULE_GREATER_THAN = 'greaterthan';
53
54
    const RULE_LESS_THAN = 'lessthan';
55
56
    const RULE_NUMBER = 'number';
57
58
    const RULE_INTEGER = 'integer';
59
    // regular expression rules
60
    const RULE_REGEX = 'regex';
61
62
    const RULE_NOT_REGEX = 'notregex';
63
    // other rules
64
    const RULE_EMAIL = 'email';
65
66
    const RULE_EMAIL_DOMAIN = 'emaildomain';
67
68
    const RULE_URL = 'url';
69
70
    const RULE_WEBSITE = 'website';
71
72
    const RULE_IP = 'ipaddress';
73
74
    const RULE_MATCH = 'match';
75
76
    const RULE_EQUAL = 'equal';
77
78
    const RULE_CALLBACK = 'callback';
79
80
    // files rules
81
    const RULE_FILE_EXTENSION = 'fileextension';
82
    const RULE_FILE_SIZE = 'filesize';
83
    const RULE_IMAGE = 'image';
84
    const RULE_IMAGE_HEIGHT = 'imageheight';
85
    const RULE_IMAGE_WIDTH = 'imagewidth';
86
    const RULE_IMAGE_RATIO = 'imageratio';
87
    // upload rules
88
    const RULE_UPLOAD_EXTENSION = 'uploadextension';
89
    const RULE_UPLOAD_SIZE = 'uploadsize';
90
    const RULE_UPLOAD_IMAGE = 'uploadimage';
91
    const RULE_UPLOAD_IMAGE_HEIGHT = 'uploadimageheight';
92
    const RULE_UPLOAD_IMAGE_WIDTH = 'uploadimagewidth';
93
    const RULE_UPLOAD_IMAGE_RATIO = 'uploadimageratio';
94
95
    /**
96
     * @var boolean
97
     */
98
    protected $wasValidated = false;
99
100
    /**
101
     * @var array
102
     */
103
    protected $rules = array();
104
105
    /**
106
     * @var array
107
     */
108
    protected $messages = array();
109
110
    /**
111
     * @var \Sirius\Validation\RuleFactory
112
     */
113
    protected $ruleFactory;
114
115
    /**
116
     * @var ErrorMessage
117
     */
118
    protected $errorMessagePrototype;
119
120
    /**
121
     * The object that will contain the data
122
     *
123
     * @var \Sirius\Validation\DataWrapper\WrapperInterface
124
     */
125
    protected $dataWrapper;
126
127 19
    public function __construct(RuleFactory $ruleFactory = null, ErrorMessage $errorMessagePrototype = null)
128
    {
129 19
        if (!$ruleFactory) {
130 2
            $ruleFactory = new RuleFactory();
131 2
        }
132 19
        $this->ruleFactory = $ruleFactory;
133 19
        if (!$errorMessagePrototype) {
134 2
            $errorMessagePrototype = new ErrorMessage();
135 2
        }
136 19
        $this->errorMessagePrototype = $errorMessagePrototype;
137 19
    }
138
139
    /**
140
     * Retrieve the rule factory
141
     *
142
     * @return \Sirius\Validation\RuleFactory
143
     */
144 16
    public function getRuleFactory()
145
    {
146 16
        return $this->ruleFactory;
147
    }
148
149
    /**
150
     * @param ErrorMessage $errorMessagePrototype
151
     *
152
     * @throws \InvalidArgumentException
153
     *
154
     * @return \Sirius\Validation\Rule\AbstractValidator
155
     */
156 1
    public function setErrorMessagePrototype(ErrorMessage $errorMessagePrototype)
157
    {
158 1
        $this->errorMessagePrototype = $errorMessagePrototype;
159
160 1
        return $this;
161
    }
162
163
    /**
164
     * Retrieve the error message prototype
165
     *
166
     * @return ErrorMessage
167
     */
168 16
    public function getErroMessagePrototype()
169
    {
170 16
        return $this->errorMessagePrototype;
171
    }
172
173
    /**
174
     * @example
175
     * // add multiple rules at once
176
     * $validator->add(array(
177
     *   'field_a' => 'required',
178
     *   'field_b' => array('required', array('email', null, '{label} must be an email', 'Field B')),
179
     * ));
180
     *
181
     * // add multiple rules using arrays
182
     * $validator->add('field', array('required', 'email'));
183
     *
184
     * // add multiple rules using a string
185
     * $validator->add('field', 'required | email');
186
     *
187
     * // add validator with options
188
     * $validator->add('field:Label', 'minlength', array('min' => 2), '{label} should have at least {min} characters');
189
     *
190
     * // add validator with string and parameters as JSON string
191
     * $validator->add('field:Label', 'minlength({"min": 2})({label} should have at least {min} characters)');
192
     *
193
     * // add validator with string and parameters as query string
194
     * $validator->add('field:label', 'minlength(min=2)({label} should have at least {min} characters)');
195
     *
196
     * @param string $selector
197
     * @param string|callback $name
198
     * @param string|array $options
199
     * @param string $messageTemplate
200
     * @param string $label
201
     *
202
     * @throws \InvalidArgumentException
203
     *
204
     * @return Validator
205
     */
206 17
    public function add($selector, $name = null, $options = null, $messageTemplate = null, $label = null)
207
    {
208
        // the $selector is an associative array with $selector => $rules
209 17
        if (func_num_args() == 1) {
210 3
            if (!is_array($selector)) {
211 1
                throw new \InvalidArgumentException('If $selector is the only argument it must be an array');
212
            }
213
214 2
            return $this->addMultiple($selector);
215
        }
216
217
        // check if the selector is in the form of 'selector:Label'
218 16
        if (strpos($selector, ':') !== false) {
219 1
            list($selector, $label) = explode(':', $selector, 2);
220 1
        }
221
222 16
        $this->ensureSelectorRulesExist($selector, $label);
223 16
        call_user_func(array( $this->rules[$selector], 'add' ), $name, $options, $messageTemplate, $label);
224
225 14
        return $this;
226
    }
227
228
    /**
229
     * @param array $selectorRulesCollection
230
     *
231
     * @return Validator
232
     */
233 14
    public function addMultiple($selectorRulesCollection)
234
    {
235 2
        foreach ($selectorRulesCollection as $selector => $rules) {
236
            // a single rule was passed for the $valueSelector
237 2
            if (! is_array($rules)) {
238 1
                $this->add($selector, $rules);
239 1
                continue;
240
            }
241
242
            // multiple rules were passed for the same $valueSelector
243 2
            foreach ($rules as $rule) {
244
                // the rule is an array, this means it contains $name, $options, $messageTemplate, $label
245 2
                if (is_array($rule)) {
246 1
                    array_unshift($rule, $selector);
247 1
                    call_user_func_array(
248
                        array(
249 1
                            $this,
250
                            'add'
251 1
                        ),
252
                        $rule
253 1
                    );
254
                    // the rule is only the name of the validator
255 1
                } else {
256 14
                    $this->add($selector, $rule);
257
                }
258 2
            }
259 2
        }
260
261 2
        return $this;
262
    }
263
264
    /**
265
     * @param string $selector
266
     *            data selector
267
     * @param mixed $name
268
     *            rule name or true if all rules should be deleted for that selector
269
     * @param mixed $options
270
     *            rule options, necessary for rules that depend on params for their ID
271
     *
272
     * @return self
273
     */
274 2
    public function remove($selector, $name = true, $options = null)
275
    {
276 2
        if (!array_key_exists($selector, $this->rules)) {
277 1
            return $this;
278
        }
279
        /* @var $collection \Sirius\Validation\ValueValidator */
280 2
        $collection = $this->rules[$selector];
281 2
        $collection->remove($name, $options);
282
283 2
        return $this;
284
    }
285
286
    /**
287
     * The data wrapper will be used to wrap around the data passed to the validator
288
     * This way you can validate anything, not just arrays (which is the default)
289
     *
290
     * @param mixed $data
291
     *
292
     * @return \Sirius\Validation\DataWrapper\WrapperInterface
293
     */
294 15
    public function getDataWrapper($data = null)
295
    {
296
        // if $data is set reconstruct the data wrapper
297 15
        if (!$this->dataWrapper || $data) {
298 15
            $this->dataWrapper = new DataWrapper\ArrayWrapper($data);
299 14
        }
300
301 14
        return $this->dataWrapper;
302
    }
303
304 15
    public function setData($data)
305
    {
306 15
        $this->getDataWrapper($data);
307 14
        $this->wasValidated = false;
308
        // reset messages
309 14
        $this->messages = array();
310
311 14
        return $this;
312
    }
313
314
    /**
315
     * Performs the validation
316
     *
317
     * @param mixed $data
318
     *            array to be validated
319
     *
320
     * @return boolean
321
     */
322 15
    public function validate($data = null)
323
    {
324 15
        if ($data !== null) {
325 14
            $this->setData($data);
326 13
        }
327
        // data was already validated, return the results immediately
328 14
        if ($this->wasValidated === true) {
329 1
            return $this->wasValidated && count($this->messages) === 0;
330
        }
331 14
        foreach ($this->rules as $selector => $valueValidator) {
332 14
            foreach ($this->getDataWrapper()->getItemsBySelector($selector) as $valueIdentifier => $value) {
333
                /* @var $valueValidator \Sirius\Validation\ValueValidator */
334 14
                if (!$valueValidator->validate($value, $valueIdentifier, $this->getDataWrapper())) {
335 14
                    foreach ($valueValidator->getMessages() as $message) {
336 14
                        $this->addMessage($valueIdentifier, $message);
337 14
                    }
338 14
                }
339 14
            }
340 14
        }
341 14
        $this->wasValidated = true;
342
343 14
        return $this->wasValidated && count($this->messages) === 0;
344
    }
345
346
    /**
347
     * @param string $item
348
     *            data identifier (eg: 'email', 'addresses[0][state]')
349
     * @param string $message
350
     *
351
     * @return self
352
     */
353 15
    public function addMessage($item, $message = null)
354
    {
355 15
        if ($message === null || $message === '') {
356 1
            return $this;
357
        }
358 15
        if (!array_key_exists($item, $this->messages)) {
359 15
            $this->messages[$item] = array();
360 15
        }
361 15
        $this->messages[$item][] = $message;
362
363 15
        return $this;
364
    }
365
366
    /**
367
     * Clears the messages of an item
368
     *
369
     * @param string $item
370
     *
371
     * @return self
372
     */
373 1
    public function clearMessages($item = null)
374
    {
375 1
        if (is_string($item)) {
376 1
            if (array_key_exists($item, $this->messages)) {
377 1
                unset($this->messages[$item]);
378 1
            }
379 1
        } elseif ($item === null) {
380 1
            $this->messages = array();
381 1
        }
382
383 1
        return $this;
384
    }
385
386
    /**
387
     * @param string $item
388
     *            key of the messages array (eg: 'password', 'addresses[0][line_1]')
389
     *
390
     * @return array
391
     */
392 13
    public function getMessages($item = null)
393
    {
394 13
        if (is_string($item)) {
395 7
            return array_key_exists($item, $this->messages) ? $this->messages[$item] : array();
396
        }
397
398 6
        return $this->messages;
399
    }
400
401 1
    public function getRules()
402
    {
403 1
        return $this->rules;
404
    }
405
406
    /**
407
     * @param string $selector
408
     * @param string $label
409
     */
410 16
    protected function ensureSelectorRulesExist($selector, $label = null)
411
    {
412 16
        if (!isset($this->rules[$selector])) {
413 16
            $this->rules[$selector] = new ValueValidator(
414 16
                $this->getRuleFactory(),
415 16
                $this->getErroMessagePrototype(),
416
                $label
417 16
            );
418 16
        }
419 16
    }
420
}
421