Passed
Push — master ( 401d75...a81b07 )
by AJ
02:09
created

Validation::requiredEmpty()   B

Complexity

Conditions 7
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 5
nc 2
nop 2
dl 0
loc 10
ccs 7
cts 7
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 *       __  ___      ____  _     ___                           _                    __
4
 *      /  |/  /_  __/ / /_(_)___/ (_)___ ___  ___  ____  _____(_)___  ____   ____ _/ /
5
 *     / /|_/ / / / / / __/ / __  / / __ `__ \/ _ \/ __ \/ ___/ / __ \/ __ \ / __ `/ /
6
 *    / /  / / /_/ / / /_/ / /_/ / / / / / / /  __/ / / (__  ) / /_/ / / / // /_/ / /
7
 *   /_/  /_/\__,_/_/\__/_/\__,_/_/_/ /_/ /_/\___/_/ /_/____/_/\____/_/ /_(_)__,_/_/
8
 *
9
 *  Array Validation Library
10
 *  Copyright (c) Multidimension.al (http://multidimension.al)
11
 *  Github : https://github.com/multidimension-al/array-validation
12
 *
13
 *  Licensed under The MIT License
14
 *  For full copyright and license information, please see the LICENSE file
15
 *  Redistributions of files must retain the above copyright notice.
16
 *
17
 *  @copyright  Copyright © 2017-2019 Multidimension.al (http://multidimension.al)
18
 *  @link       https://github.com/multidimension-al/array-validation Github
19
 *  @license    http://www.opensource.org/licenses/mit-license.php MIT License
20
 */
21
22
namespace Multidimensional\ArrayValidation;
23
24
use Exception;
25
26
class Validation
27
{
28
    /**
29
     * @param array $array
30
     * @param array $rules
31
     * @return true
32
     * @throws Exception
33
     */
34 56
    public static function validate($array, $rules)
35
    {
36 56
        if (is_array($array) && is_array($rules)) {
0 ignored issues
show
introduced by
The condition is_array($rules) is always true.
Loading history...
37 54
            self::checkRequired($array, $rules);
38 50
            foreach ($array as $key => $value) {
39 48
                if (!isset($rules[$key]) && !array_key_exists($key, $rules)) {
40 2
                    throw new Exception(sprintf('Unexpected key "%s" found in array.', $key));
41
                }
42
43 48
                if (is_array($value) && isset($rules[$key]['type']) && strtolower($rules[$key]['type']) == 'array' && isset($rules[$key]['fields'])) {
44 6
                    self::validate($value, $rules[$key]['fields']);
45 48
                } elseif (in_array($key, array_keys($rules))) {
46 48
                    self::validateField($value, $rules[$key], $key);
47 22
                }
48 23
            }
49 24
        } elseif (!is_array($array)) {
50 2
            throw new Exception('Validation array not found.');
51 2
        } elseif (!is_array($rules)) {
52 2
            throw new Exception('Validation rules array not found.');
53
        }
54
55 44
        return true;
56
    }
57
58
    /**
59
     * Main required check function. Must be fed with two variables, both of type array.
60
     * Returns void if all checks pass without a Exception being thrown. Only
61
     * performs checkRequired operation on values that are not set in the main $array.
62
     *
63
     * @param array $array Validation Array comprised of key / value pairs to be checked.
64
     * @param array $rules Rules Array comprised of properly formatted keys with rules.
65
     * @return void
66
     * @throws Exception
67
     */
68 54
    protected static function checkRequired($array, $rules)
69
    {
70 54
        if (is_array($rules)) {
0 ignored issues
show
introduced by
The condition is_array($rules) is always true.
Loading history...
71 54
            foreach ($rules as $key => $value) {
72 52
                if (self::requiredNull($array, $key) || self::requiredEmpty($array, $key)) {
73 32
                    if (isset($value['required'])) {
74 16
                        if (is_array($value['required']) && !self::requiredOne($array, $value['required'])) {
75
                            //
76 16
                        } elseif (($value['required'] == 'true' || $value['required'] === true) && isset($array[$key]) && (!empty($array[$key]) || is_numeric($array[$key]) )) {
77
                            //
78 3
                        } else {
79 12
                            throw new Exception(sprintf('Required value not found for key: %s.', $key));
80
                        }
81 5
                    }
82 13
                }
83 25
            }
84 25
        }
85
86 50
        return;
87
    }
88
89
    /**
90
     * Returns true when $key value is null, returns false when it is not.
91
     *
92
     * @param array $array
93
     * @param string $key
94
     * @return true|false
95
     */
96 52
    protected static function requiredNull($array, $key)
97
    {
98 52
        if ((!isset($array[$key]) && !array_key_exists($key, $array))
99 50
            || ((isset($array[$key]) || array_key_exists($key, $array))
100 51
                && ($array[$key] === null || $array[$key] === 'null'))
101 26
        ) {
102 20
            return true;
103
        }
104
105 46
        return false;
106
    }
107
108
    /**
109
     * Returns true when $key value is null, returns false when it is not.
110
     *
111
     * @param array $array
112
     * @param string $key
113
     * @return true|false
114
     */
115 46
    protected static function requiredEmpty($array, $key)
116
    {
117 46
        if ((!isset($array[$key]) && !array_key_exists($key, $array))
118 46
            || ((isset($array[$key]) || array_key_exists($key, $array))
119 46
                && (empty($array[$key] && !is_numeric($array[$key]))))
120 23
        ) {
121 16
            return true;
122
        }
123
124 44
        return false;
125
    }
126
127
    /**
128
     * Returns true if one of the conditions comes back as requiring the key being tested
129
     * to be required. Returns false if all the conditions find that checks do not require
130
     * the key value to be present.
131
     *
132
     * @param array $array
133
     * @param array $rules
134
     * @return true|false
135
     */
136 6
    protected static function requiredOne($array, $rules)
137
    {
138 6
        if (is_array($rules)) {
0 ignored issues
show
introduced by
The condition is_array($rules) is always true.
Loading history...
139 6
            foreach ($rules as $key => $value) {
140 6
                if ((is_array($value) && self::requiredAll($array, $value)) || self::requiredTest($array, $value, $key)) {
141 6
                    return true;
142
                }
143 2
            }
144 2
        }
145 4
        return false;
146
    }
147
148
    /**
149
     * Checks an array of $rules and returns false if any one of the rules fails.
150
     * If all rule tests pass, then the method returns true.
151
     *
152
     * @param array $array
153
     * @param array $rules
154
     * @return true|false
155
     */
156 2
    protected static function requiredAll($array, $rules)
157
    {
158 2
        if (is_array($rules)) {
0 ignored issues
show
introduced by
The condition is_array($rules) is always true.
Loading history...
159 2
            foreach ($rules as $key => $value) {
160 2
                if (!self::requiredTest($array, $value, $key)) {
161 2
                    return false;
162
                }
163 1
            }
164 1
        }
165
166 2
        return true;
167
    }
168
169
    /**
170
     * Returns output of testing functions. Will return true if $key is required
171
     * false if the $key is not required.
172
     *
173
     * @param $array
174
     * @param $value
175
     * @param $key
176
     * @return true|false
177
     */
178 6
    protected static function requiredTest($array, $value, $key)
179
    {
180 6
        if (is_null($value) || $value == 'null') {
181 4
            return self::requiredNull($array, $key);
182 2
        } elseif (empty($value) && !is_numeric($value)) {
183
            return self::requiredEmpty($array, $key);
184 2
        } elseif (isset($array[$key]) && $array[$key] == $value) {
185 2
            return true;
186
        }
187
188 2
        return false;
189
    }
190
191
    /**
192
     * @param string $value
193
     * @param array $rules
194
     * @param string $key
195
     * @return true
196
     * @throws Exception
197
     */
198 48
    public static function validateField($value, $rules, $key)
199
    {
200 48
        if (is_array($value) && isset($rules['type']) && strtolower($rules['type']) == 'group' && isset($rules['fields'])) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
201 6
            foreach ($value as $k => $v) {
202 6
                if (is_array($v) && !isset($rules['fields'][$k])) {
203 6
                    self::validate($v, $rules['fields']);
204 4
                } elseif (isset($rules['fields'][$k])) {
205 2
                    self::validateField($v, $rules['fields'][$k], $k);
206 1
                }
207 3
            }
208 48
        } elseif (is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
209 4
            throw new Exception(sprintf('Unexpected array found for key: %s.', $key));
210
        }
211
212 46
        if (isset($value) && $value !== null && $value != 'null' && (!empty($value) || is_numeric($value))) {
213 42
            if (isset($rules['type'])) {
214 42
                if ($rules['type'] === 'integer') {
215 6
                    self::validateInteger($value, $key);
216 41
                } elseif ($rules['type'] === 'decimal') {
217 6
                    self::validateDecimal($value, $key);
218 39
                } elseif ($rules['type'] === 'string') {
219 36
                    self::validateString($value, $key);
220 25
                } elseif ($rules['type'] === 'boolean') {
221 2
                    self::validateBoolean($value, $key);
222
                }
223 20
            }
224
225 40
            if (isset($rules['values'])) {
226 10
                self::validateValues($value, $rules['values'], $key);
227 4
            }
228
229 38
            if (isset($rules['pattern'])) {
230 16
                if (strtoupper($rules['pattern']) == 'ISO 8601' || strtoupper($rules['pattern']) == 'ISO8601') {
231 2
                    self::validateISO8601($value, $key);
232 16
                } else if (strtoupper($rules['pattern']) == 'URL') {
233 2
                    self::validateURL($value, $key);
234 15
                } else if (strtoupper($rules['pattern']) == 'EMAIL') {
235 2
                    self::validateEmail($value, $key);
236 13
                } else if (strtoupper($rules['pattern']) == 'IP') {
237 4
                    self::validateIP($value, $key);
238 11
                } else if (strtoupper($rules['pattern']) == 'MAC') {
239 2
                    self::validateMAC($value, $key);
240 1
                } else {
241 8
                    self::validatePattern($value, $rules['pattern'], $key);
242
                }
243 8
            }
244 19
        }
245
246 44
        return true;
247
    }
248
249
    /**
250
     * @param int $value
251
     * @param string|null $key
252
     * @return true|false
253
     * @throws Exception
254
     */
255 6
    protected static function validateInteger($value, $key)
256
    {
257 6
        if (!is_int($value)) {
0 ignored issues
show
introduced by
The condition is_int($value) is always true.
Loading history...
258 2
            throw new Exception(sprintf('Invalid integer "%s" for key: %s.', $value, $key));
259
        }
260
261 6
        return true;
262
    }
263
264
    /**
265
     * @param float $value
266
     * @param string|null $key
267
     * @return true|false
268
     * @throws Exception
269
     */
270 6
    protected static function validateDecimal($value, $key)
271
    {
272 6
        if (!is_float($value)) {
0 ignored issues
show
introduced by
The condition is_float($value) is always true.
Loading history...
273 2
            throw new Exception(sprintf('Invalid decimal "%s" for key: %s.', $value, $key));
274
        }
275
276 6
        return true;
277
    }
278
279
    /**
280
     * @param string $value
281
     * @param string|null $key
282
     * @return true|false
283
     * @throws Exception
284
     */
285 36
    protected static function validateString($value, $key)
286
    {
287 36
        if (!is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
288 2
            throw new Exception(sprintf('Invalid string "%s" for key: %s.', $value, $key));
289
        }
290
291 36
        return true;
292
    }
293
294
    /**
295
     * @param bool $value
296
     * @param string|null $key
297
     * @return true
298
     * @throws Exception
299
     */
300 2
    protected static function validateBoolean($value, $key)
301
    {
302 2
        if (!is_bool($value)) {
0 ignored issues
show
introduced by
The condition is_bool($value) is always true.
Loading history...
303 2
            throw new Exception(sprintf('Invalid boolean "%s" for key: %s.', $value, $key));
304
        }
305
306
        return true;
307
    }
308
309
    /**
310
     * @param string $value
311
     * @param array|string $array
312
     * @param string|null $key
313
     * @return bool
314
     * @throws Exception
315
     */
316 10
    protected static function validateValues($value, $array, $key)
317
    {
318 10
        $compareArray = [];
319 10
        if (is_array($array)) {
320 8
            foreach ($array as $compareKey) {
321 8
                $compareScore = levenshtein($value, $compareKey);
322 8
                $compareArray[$compareKey] = $compareScore;
323 8
                if ($compareScore === 0) {
324 6
                    return true;
325
                }
326 2
            }
327 3
        } elseif (strcasecmp($value, $array) === 0) {
328 2
            return true;
329
        }
330
331 2
        $errorMessage = sprintf('Invalid value "%s" for key: %s.', $value, $key);
332
333 1
        array_walk($compareArray, function(&$item) {
334 2
            $item = abs($item);
335 2
        });
336 2
        asort($compareArray);
337 2
        $compareArray = array_keys($compareArray);
338 2
        if (count($compareArray)) {
339 2
            $errorMessage .= sprintf(' Did you mean "%s"?', array_shift($compareArray));
340 1
        }
341
342 2
        throw new Exception($errorMessage);
343
    }
344
345
    /**
346
     * @param string $value
347
     * @param string $key
348
     * @return bool
349
     * @throws Exception
350
     */
351 2
    protected static function validateISO8601($value, $key)
352
    {
353 2
        $pattern = '(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:Z|[+-][01]\d:[0-5]\d)';
354 2
        if (!preg_match('/^'.$pattern.'$/', $value)) {
355 2
            throw new Exception(sprintf('Invalid value "%s" does not match ISO 8601 pattern for key: %s.', $value, $key));
356
        }
357
358 2
        return true;
359
    }
360
361
    /**
362
     * @param string $value
363
     * @param string $key
364
     * @return bool
365
     * @throws Exception
366
     */
367 2
    protected static function validateURL($value, $key)
368
    {
369 2
        if (!filter_var($value, FILTER_VALIDATE_URL)) {
370 2
            throw new Exception(sprintf('Invalid value "%s" does not match URL pattern for key: %s.', $value, $key));
371
        }
372
373 2
        return true;
374
    }
375
376
    /**
377
     * @param string $value
378
     * @param string $key
379
     * @return bool
380
     * @throws Exception
381
     */
382 2
    protected static function validateEmail($value, $key)
383
    {
384 2
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
385 2
            throw new Exception(sprintf('Invalid value "%s" does not match email pattern for key: %s.', $value, $key));
386
        }
387
388 2
        return true;
389
    }
390
391
    /**
392
     * @param string $value
393
     * @param string $key
394
     * @return bool
395
     * @throws Exception
396
     */
397 4
    protected static function validateIP($value, $key)
398
    {
399 4
        if (!filter_var($value, FILTER_VALIDATE_IP)) {
400 2
            throw new Exception(sprintf('Invalid value "%s" does not match IP address pattern for key: %s.', $value, $key));
401
        }
402
403 4
        return true;
404
    }
405
406
    /**
407
     * @param string $value
408
     * @param string $key
409
     * @return bool
410
     * @throws Exception
411
     */
412 2
    protected static function validateMAC($value, $key)
413
    {
414 2
        if (!filter_var($value, FILTER_VALIDATE_MAC)) {
415 2
            throw new Exception(sprintf('Invalid value "%s" does not match MAC address pattern for key: %s.', $value, $key));
416
        }
417
418 2
        return true;
419
    }
420
421
422
    /**
423
     * @param string $value
424
     * @param string $pattern
425
     * @param string $key
426
     * @return bool
427
     * @throws Exception
428
     */
429 8
    protected static function validatePattern($value, $pattern, $key)
430
    {
431 8
        if (!preg_match('/^'.$pattern.'$/', $value)) {
432 2
            throw new Exception(sprintf('Invalid value "%s" does not match pattern "%s" for key: %s.', $value, $pattern, $key));
433
        }
434
435 8
        return true;
436
    }
437
}
438