Completed
Push — master ( aeb11c...f98da5 )
by Adrian
02:32
created

Validator::addMultiple()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

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