Project

General

Profile

inputfilter.php

fixed - Magh S, 01/03/2021 06:55 PM

 
1
<?php
2

    
3
/**
4
 * GUMP - A fast, extensible PHP input validation class.
5
 *
6
 * @author      Sean Nieuwoudt (http://twitter.com/SeanNieuwoudt)
7
 * @copyright   Copyright (c) 2015 wixelhq.com
8
 *
9
 * @link        http://github.com/Wixel/GUMP
10
 *
11
 * @version     1.0
12
 */
13
class InputFilter
14
{
15
    //Singleton instance of GUMP
16
    protected static $instance = null;
17

    
18
    // Validation rules for execution
19
    protected $validation_rules = array();
20

    
21
    // Filter rules for execution
22
    protected $filter_rules = array();
23

    
24
    // Instance attribute containing errors from last run
25
    protected $errors = array();
26

    
27
    // Contain readable field names that have been set manually
28
    protected static $fields = array();
29

    
30
    // Custom validation methods
31
    protected static $validation_methods = array();
32

    
33
    // Customer filter methods
34
    protected static $filter_methods = array();
35

    
36
    // ** ------------------------- Instance Helper ---------------------------- ** //
37
    /**
38
     * Function to create and return previously created instance
39
     *
40
     * @return InputFilter
41
     */
42

    
43
    public static function get_instance(){
44
        if(self::$instance === null)
45
        {
46
            self::$instance = new self();
47
        }
48
        return self::$instance;
49
    }
50

    
51

    
52

    
53
    // ** ------------------------- Validation Data ------------------------------- ** //
54

    
55
    public static $basic_tags = '<br><p><a><strong><b><i><em><img><blockquote><code><dd><dl><hr><h1><h2><h3><h4><h5><h6><label><ul><li><span><sub><sup>';
56

    
57
    public static $en_noise_words = "about,after,all,also,an,and,another,any,are,as,at,be,because,been,before,
58
                                                                             being,between,both,but,by,came,can,come,could,did,do,each,for,from,get,
59
                                                                             got,has,had,he,have,her,here,him,himself,his,how,if,in,into,is,it,its,it's,like,
60
                                                                         make,many,me,might,more,most,much,must,my,never,now,of,on,only,or,other,
61
                                                                             our,out,over,said,same,see,should,since,some,still,such,take,than,that,
62
                                                                             the,their,them,then,there,these,they,this,those,through,to,too,under,up,
63
                                                                             very,was,way,we,well,were,what,where,which,while,who,with,would,you,your,a,
64
                                                                             b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$,1,2,3,4,5,6,7,8,9,0,_";
65

    
66
    // field characters below will be replaced with a space.
67
    protected $fieldCharsToRemove = array('_', '-');
68

    
69
    // ** ------------------------- Validation Helpers ---------------------------- ** //
70

    
71
    /**
72
     * Shorthand method for inline validation.
73
     *
74
     * @param array $data       The data to be validated
75
     * @param array $validators The GUMP validators
76
     *
77
     * @return mixed True(boolean) or the array of error messages
78
     */
79
    public static function is_valid(array $data, array $validators)
80
    {
81
        $filter = self::get_instance();
82

    
83
        $filter->validation_rules($validators);
84

    
85
        if ($filter->run($data) === false) {
86
            return $filter->get_readable_errors(false);
87
        } else {
88
            return true;
89
        }
90
    }
91

    
92
    /**
93
     * Shorthand method for running only the data filters.
94
     *
95
     * @param array $data
96
     * @param array $filters
97
     *
98
     * @return mixed
99
     */
100
    public static function filter_input(array $data, array $filters)
101
    {
102
        $filter = self::get_instance();
103

    
104
        return $filter->filter($data, $filters);
105
    }
106

    
107
    /**
108
     * Magic method to generate the validation error messages.
109
     *
110
     * @return string
111
     */
112
    public function __toString()
113
    {
114
        return $this->get_readable_errors(true);
115
    }
116

    
117
    /**
118
     * Perform XSS clean to prevent cross site scripting.
119
     *
120
     * @static
121
     *
122
     * @param array $data
123
     *
124
     * @return array
125
     */
126
    public static function xss_clean(array $data)
127
    {
128
        foreach ($data as $k => $v) {
129
            $data[$k] = filter_var($v, FILTER_SANITIZE_STRING);
130
        }
131

    
132
        return $data;
133
    }
134

    
135
    /**
136
     * Adds a custom validation rule using a callback function.
137
     *
138
     * @param string   $rule
139
     * @param callable $callback
140
     *
141
     * @return bool
142
     *
143
     * @throws Exception
144
     */
145
    public static function add_validator($rule, $callback)
146
    {
147
        $method = 'validate_'.$rule;
148

    
149
        if (method_exists(__CLASS__, $method) || isset(self::$validation_methods[$rule])) {
150
            throw new Exception("Validator rule '$rule' already exists.");
151
        }
152

    
153
        self::$validation_methods[$rule] = $callback;
154

    
155
        return true;
156
    }
157

    
158
    /**
159
     * Adds a custom filter using a callback function.
160
     *
161
     * @param string   $rule
162
     * @param callable $callback
163
     *
164
     * @return bool
165
     *
166
     * @throws Exception
167
     */
168
    public static function add_filter($rule, $callback)
169
    {
170
        $method = 'filter_'.$rule;
171

    
172
        if (method_exists(__CLASS__, $method) || isset(self::$filter_methods[$rule])) {
173
            throw new Exception("Filter rule '$rule' already exists.");
174
        }
175

    
176
        self::$filter_methods[$rule] = $callback;
177

    
178
        return true;
179
    }
180

    
181
    /**
182
     * Helper method to extract an element from an array safely
183
     *
184
     * @param mixed $key
185
     * @param array $array
186
     * @param mixed $default
187
     * @return mixed
188
     */
189
    public static function field($key, array $array, $default = null)
190
    {
191
      if(!is_array($array)) {
192
        return null;
193
      }
194

    
195
      if(isset($array[$key])) {
196
        return $array[$key];
197
      } else {
198
        return $default;
199
      }
200
    }
201

    
202
    /**
203
     * Getter/Setter for the validation rules.
204
     *
205
     * @param array $rules
206
     *
207
     * @return array
208
     */
209
    public function validation_rules(array $rules = array())
210
    {
211
        if (empty($rules)) {
212
            return $this->validation_rules;
213
        }
214

    
215
        $this->validation_rules = $rules;
216
    }
217

    
218
    /**
219
     * Getter/Setter for the filter rules.
220
     *
221
     * @param array $rules
222
     *
223
     * @return array
224
     */
225
    public function filter_rules(array $rules = array())
226
    {
227
        if (empty($rules)) {
228
            return $this->filter_rules;
229
        }
230

    
231
        $this->filter_rules = $rules;
232
    }
233

    
234
    /**
235
     * Run the filtering and validation after each other.
236
     *
237
     * @param array $data
238
     * @param bool  $check_fields
239
     *
240
     * @return array
241
     *
242
     * @throws Exception
243
     */
244
    public function run(array $data, $check_fields = false)
245
    {
246
        $data = $this->filter($data, $this->filter_rules());
247

    
248
        $validated = $this->validate(
249
            $data, $this->validation_rules()
250
        );
251

    
252
        if ($check_fields === true) {
253
            $this->check_fields($data);
254
        }
255

    
256
        if ($validated !== true) {
257
            return false;
258
        }
259

    
260
        return $data;
261
    }
262

    
263
    /**
264
     * Ensure that the field counts match the validation rule counts.
265
     *
266
     * @param array $data
267
     */
268
    private function check_fields(array $data)
269
    {
270
        $ruleset = $this->validation_rules();
271
        $mismatch = array_diff_key($data, $ruleset);
272
        $fields = array_keys($mismatch);
273

    
274
        foreach ($fields as $field) {
275
            $this->errors[] = array(
276
                'field' => $field,
277
                'value' => $data[$field],
278
                'rule' => 'mismatch',
279
                'param' => null,
280
            );
281
        }
282
    }
283

    
284
    /**
285
     * Sanitize the input data.
286
     *
287
     * @param array $input
288
     * @param null  $fields
289
     * @param bool  $utf8_encode
290
     *
291
     * @return array
292
     */
293
    public function sanitize(array $input, array $fields = array(), $utf8_encode = true)
294
    {
295
//        $magic_quotes = (bool) get_magic_quotes_gpc();
296

    
297
        if (empty($fields)) {
298
            $fields = array_keys($input);
299
        }
300

    
301
        $return = array();
302

    
303
        foreach ($fields as $field) {
304
            if (!isset($input[$field])) {
305
                continue;
306
            } else {
307
                $value = $input[$field];
308
                if (is_array($value)) {
309
                    $value = null;
310
                }
311
                if (is_string($value)) {
312
                    if ($magic_quotes === true) {
313
                        $value = stripslashes($value);
314
                    }
315

    
316
                    if (strpos($value, "\r") !== false) {
317
                        $value = trim($value);
318
                    }
319

    
320
                    if (function_exists('iconv') && function_exists('mb_detect_encoding') && $utf8_encode) {
321
                        $current_encoding = mb_detect_encoding($value);
322

    
323
                        if ($current_encoding != 'UTF-8' && $current_encoding != 'UTF-16') {
324
                            $value = iconv($current_encoding, 'UTF-8', $value);
325
                        }
326
                    }
327

    
328
                    $value = filter_var($value, FILTER_SANITIZE_STRING);
329
                }
330

    
331
                $return[$field] = $value;
332
            }
333
        }
334

    
335
        return $return;
336
    }
337

    
338
    /**
339
     * Return the error array from the last validation run.
340
     *
341
     * @return array
342
     */
343
    public function errors()
344
    {
345
        return $this->errors;
346
    }
347

    
348
    /**
349
     * Perform data validation against the provided ruleset.
350
     *
351
     * @param mixed $input
352
     * @param array $ruleset
353
     *
354
     * @return mixed
355
     *
356
     * @throws Exception
357
     */
358
    public function validate(array $input, array $ruleset)
359
    {
360
        $this->errors = array();
361

    
362
        foreach ($ruleset as $field => $rules) {
363

    
364
            $rules = explode('|', $rules);
365

    
366
            if (in_array('required', $rules) || (isset($input[$field]) && !is_array($input[$field]))) {
367
                foreach ($rules as $rule) {
368
                    $method = null;
369
                    $param = null;
370

    
371
                    // Check if we have rule parameters
372
                    if (strstr($rule, ',') !== false) {
373
                        $rule   = explode(',', $rule);
374
                        $method = 'validate_'.$rule[0];
375
                        $param  = $rule[1];
376
                        $rule   = $rule[0];
377
                    } else {
378
                        $method = 'validate_'.$rule;
379
                    }
380

    
381
                    //self::$validation_methods[$rule] = $callback;
382

    
383
                    if (is_callable(array($this, $method))) {
384
                        $result = $this->$method(
385
                          $field, $input, $param
386
                        );
387

    
388
                        if (is_array($result)) {
389
                            $this->errors[] = $result;
390
                        }
391
                    } elseif(isset(self::$validation_methods[$rule])) {
392

    
393
                        $result = call_user_func(self::$validation_methods[$rule], $field, $input, $param);
394

    
395
                        if($result === false) {
396
                          $this->errors[] = array(
397
                            'field' => $field,
398
                            'value' => $input,
399
                            'rule' => self::$validation_methods[$rule],
400
                            'param' => $param,
401
                          );
402
                        }
403

    
404
                    } else {
405
                        throw new Exception("Validator method '$method' does not exist.");
406
                    }
407
                }
408
            }
409
        }
410

    
411
        return (count($this->errors) > 0) ? $this->errors : true;
412
    }
413

    
414
    /**
415
     * Overloadable method to invoke validation.
416
     *
417
     * @param array $input
418
     * @param $rules
419
     * @param $field
420
     *
421
     * @return bool
422
     */
423
    protected function shouldRunValidation(array $input, $rules, $field)
424
    {
425
        return in_array('required', $rules) || (isset($input[$field]) && trim($input[$field]) != '');
426
    }
427

    
428
    /**
429
     * Set a readable name for a specified field names.
430
     *
431
     * @param string $field
432
     * @param string $readable_name
433
     */
434
    public static function set_field_name($field, $readable_name)
435
    {
436
        self::$fields[$field] = $readable_name;
437
    }
438

    
439
    /**
440
     * Set readable name for specified fields in an array.
441
     *
442
     * Usage:
443
     *
444
     * GUMP::set_field_names(array(
445
     *         "name" => "My Lovely Name",
446
     *         "username" => "My Beloved Username",
447
     * ));
448
     *
449
     * @param array $array
450
     */
451
    public static function set_field_names(array $array)
452
    {
453
        foreach ($array as $field => $readable_name) {
454
            self::$fields[$field] = $readable_name;
455
        }
456
    }
457

    
458
    /**
459
     * Process the validation errors and return human readable error messages.
460
     *
461
     * @param bool   $convert_to_string = false
462
     * @param string $field_class
463
     * @param string $error_class
464
     *
465
     * @return array
466
     * @return string
467
     */
468
    public function get_readable_errors($convert_to_string = false, $field_class = 'gump-field', $error_class = 'gump-error-message')
469
    {
470
        if (empty($this->errors)) {
471
            return ($convert_to_string) ? null : array();
472
        }
473

    
474
        $resp = array();
475

    
476
        foreach ($this->errors as $e) {
477
            $field = ucwords(str_replace($this->fieldCharsToRemove, chr(32), $e['field']));
478
            $param = $e['param'];
479

    
480
            // Let's fetch explicit field names if they exist
481
            if (array_key_exists($e['field'], self::$fields)) {
482
                $field = self::$fields[$e['field']];
483
            }
484

    
485
            switch ($e['rule']) {
486
                case 'mismatch' :
487
                    $resp[] = "There is no validation rule for <span class=\"$field_class\">$field</span>";
488
                    break;
489
                case 'validate_required' :
490
                    $resp[] = "The <span class=\"$field_class\">$field</span> field is required";
491
                    break;
492
                case 'validate_valid_email':
493
                    $resp[] = "The <span class=\"$field_class\">$field</span> field is required to be a valid email address";
494
                    break;
495
                case 'validate_max_len':
496
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to be $param or shorter in length";
497
                    break;
498
                case 'validate_min_len':
499
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to be $param or longer in length";
500
                    break;
501
                case 'validate_exact_len':
502
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to be exactly $param characters in length";
503
                    break;
504
                case 'validate_alpha':
505
                    $resp[] = "The <span class=\"$field_class\">$field</span> field may only contain alpha characters(a-z)";
506
                    break;
507
                case 'validate_alpha_numeric':
508
                    $resp[] = "The <span class=\"$field_class\">$field</span> field may only contain alpha-numeric characters";
509
                    break;
510
                case 'validate_alpha_dash':
511
                    $resp[] = "The <span class=\"$field_class\">$field</span> field may only contain alpha characters &amp; dashes";
512
                    break;
513
                case 'validate_numeric':
514
                    $resp[] = "The <span class=\"$field_class\">$field</span> field may only contain numeric characters";
515
                    break;
516
                case 'validate_integer':
517
                    $resp[] = "The <span class=\"$field_class\">$field</span> field may only contain a numeric value";
518
                    break;
519
                case 'validate_boolean':
520
                    $resp[] = "The <span class=\"$field_class\">$field</span> field may only contain a true or false value";
521
                    break;
522
                case 'validate_float':
523
                    $resp[] = "The <span class=\"$field_class\">$field</span> field may only contain a float value";
524
                    break;
525
                case 'validate_valid_url':
526
                    $resp[] = "The <span class=\"$field_class\">$field</span> field is required to be a valid URL";
527
                    break;
528
                case 'validate_url_exists':
529
                    $resp[] = "The <span class=\"$field_class\">$field</span> URL does not exist";
530
                    break;
531
                case 'validate_valid_ip':
532
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to contain a valid IP address";
533
                    break;
534
                case 'validate_valid_cc':
535
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to contain a valid credit card number";
536
                    break;
537
                case 'validate_valid_name':
538
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to contain a valid human name";
539
                    break;
540
                case 'validate_contains':
541
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to contain one of these values: ".implode(', ', $param);
542
                    break;
543
                case 'validate_contains_list':
544
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs contain a value from its drop down list";
545
                    break;
546
                case 'validate_doesnt_contain_list':
547
                    $resp[] = "The <span class=\"$field_class\">$field</span> field contains a value that is not accepted";
548
                    break;
549
                case 'validate_street_address':
550
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to be a valid street address";
551
                    break;
552
                case 'validate_date':
553
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to be a valid date";
554
                    break;
555
                case 'validate_min_numeric':
556
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to be a numeric value, equal to, or higher than $param";
557
                    break;
558
                case 'validate_max_numeric':
559
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to be a numeric value, equal to, or lower than $param";
560
                    break;
561
                case 'validate_starts':
562
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to start with $param";
563
                    break;
564
                case 'validate_extension':
565
                    $resp[] = "The <span class\"$field_class\">$field</span> field can have the following extensions $param";
566
                    break;
567
                case 'validate_required_file':
568
                    $resp[] = "The <span class\"$field_class\">$field</span> field is required";
569
                    break;
570
                case 'validate_equalsfield':
571
                    $resp[] = "The <span class=\"$field_class\">$field</span> field does not equal $param field";
572
                    break;
573
                case 'validate_min_age':
574
                    $resp[] = "The <span class=\"$field_class\">$field</span> field needs to have an age greater than or equal to $param";
575
                    break;
576
                default:
577
                    $resp[] = "The <span class=\"$field_class\">$field</span> field is invalid";
578
            }
579
        }
580

    
581
        if (!$convert_to_string) {
582
            return $resp;
583
        } else {
584
            $buffer = '';
585
            foreach ($resp as $s) {
586
                $buffer .= "<span class=\"$error_class\">$s</span>";
587
            }
588

    
589
            return $buffer;
590
        }
591
    }
592

    
593
    /**
594
     * Process the validation errors and return an array of errors with field names as keys.
595
     *
596
     * @param $convert_to_string
597
     *
598
     * @return array | null (if empty)
599
     */
600
    public function get_errors_array($convert_to_string = null)
601
    {
602
        if (empty($this->errors)) {
603
            return ($convert_to_string) ? null : array();
604
        }
605

    
606
        $resp = array();
607

    
608
        foreach ($this->errors as $e) {
609
            $field = ucwords(str_replace(array('_', '-'), chr(32), $e['field']));
610
            $param = $e['param'];
611

    
612
            // Let's fetch explicit field names if they exist
613
            if (array_key_exists($e['field'], self::$fields)) {
614
                $field = self::$fields[$e['field']];
615
            }
616

    
617
            switch ($e['rule']) {
618
                case 'mismatch' :
619
                    $resp[$field] = "There is no validation rule for $field";
620
                    break;
621
                case 'validate_required':
622
                    $resp[$field] = "The $field field is required";
623
                    break;
624
                case 'validate_valid_email':
625
                    $resp[$field] = "The $field field is required to be a valid email address";
626
                    break;
627
                case 'validate_max_len':
628
                    $resp[$field] = "The $field field needs to be $param or shorter in length";
629
                    break;
630
                case 'validate_min_len':
631
                    $resp[$field] = "The $field field needs to be $param or longer in length";
632
                    break;
633
                case 'validate_exact_len':
634
                    $resp[$field] = "The $field field needs to be exactly $param characters in length";
635
                    break;
636
                case 'validate_alpha':
637
                    $resp[$field] = "The $field field may only contain alpha characters(a-z)";
638
                    break;
639
                case 'validate_alpha_numeric':
640
                    $resp[$field] = "The $field field may only contain alpha-numeric characters";
641
                    break;
642
                case 'validate_alpha_dash':
643
                    $resp[$field] = "The $field field may only contain alpha characters &amp; dashes";
644
                    break;
645
                case 'validate_numeric':
646
                    $resp[$field] = "The $field field may only contain numeric characters";
647
                    break;
648
                case 'validate_integer':
649
                    $resp[$field] = "The $field field may only contain a numeric value";
650
                    break;
651
                case 'validate_boolean':
652
                    $resp[$field] = "The $field field may only contain a true or false value";
653
                    break;
654
                case 'validate_float':
655
                    $resp[$field] = "The $field field may only contain a float value";
656
                    break;
657
                case 'validate_valid_url':
658
                    $resp[$field] = "The $field field is required to be a valid URL";
659
                    break;
660
                case 'validate_url_exists':
661
                    $resp[$field] = "The $field URL does not exist";
662
                    break;
663
                case 'validate_valid_ip':
664
                    $resp[$field] = "The $field field needs to contain a valid IP address";
665
                    break;
666
                case 'validate_valid_cc':
667
                    $resp[$field] = "The $field field needs to contain a valid credit card number";
668
                    break;
669
                case 'validate_valid_name':
670
                    $resp[$field] = "The $field field needs to contain a valid human name";
671
                    break;
672
                case 'validate_contains':
673
                    $resp[$field] = "The $field field needs to contain one of these values: ".implode(', ', $param);
674
                    break;
675
                case 'validate_street_address':
676
                    $resp[$field] = "The $field field needs to be a valid street address";
677
                    break;
678
                case 'validate_date':
679
                    $resp[$field] = "The $field field needs to be a valid date";
680
                    break;
681
                case 'validate_min_numeric':
682
                    $resp[$field] = "The $field field needs to be a numeric value, equal to, or higher than $param";
683
                    break;
684
                case 'validate_max_numeric':
685
                    $resp[$field] = "The $field field needs to be a numeric value, equal to, or lower than $param";
686
                    break;
687
                case 'validate_min_age':
688
                    $resp[$field] = "The $field field needs to have an age greater than or equal to $param";
689
                    break;
690
                default:
691
                    $resp[$field] = "The $field field is invalid";
692
            }
693
        }
694

    
695
        return $resp;
696
    }
697

    
698
    /**
699
     * Filter the input data according to the specified filter set.
700
     *
701
     * @param mixed $input
702
     * @param array $filterset
703
     *
704
     * @throws Exception
705
     *
706
     * @return mixed
707
     *
708
     * @throws Exception
709
     */
710
    public function filter(array $input, array $filterset)
711
    {
712
        foreach ($filterset as $field => $filters) {
713
            if (!array_key_exists($field, $input)) {
714
                continue;
715
            }
716

    
717
            $filters = explode('|', $filters);
718

    
719
            foreach ($filters as $filter) {
720
                $params = null;
721

    
722
                if (strstr($filter, ',') !== false) {
723
                    $filter = explode(',', $filter);
724

    
725
                    $params = array_slice($filter, 1, count($filter) - 1);
726

    
727
                    $filter = $filter[0];
728
                }
729

    
730
                if (is_callable(array($this, 'filter_'.$filter))) {
731
                    $method = 'filter_'.$filter;
732
                    $input[$field] = $this->$method($input[$field], $params);
733
                } elseif (function_exists($filter)) {
734
                    $input[$field] = $filter($input[$field]);
735
                } elseif (isset(self::$filter_methods[$filter])) {
736
                    $input[$field] = call_user_func(self::$filter_methods[$filter], $input[$field], $params);
737
                } else {
738
                    throw new Exception("Filter method '$filter' does not exist.");
739
                }
740
            }
741
        }
742

    
743
        return $input;
744
    }
745

    
746
    // ** ------------------------- Filters --------------------------------------- ** //
747

    
748
    /**
749
     * Replace noise words in a string (http://tax.cchgroup.com/help/Avoiding_noise_words_in_your_search.htm).
750
     *
751
     * Usage: '<index>' => 'noise_words'
752
     *
753
     * @param string $value
754
     * @param array  $params
755
     *
756
     * @return string
757
     */
758
    protected function filter_noise_words($value, $params = null)
759
    {
760
        $value = preg_replace('/\s\s+/u', chr(32), $value);
761

    
762
        $value = " $value ";
763

    
764
        $words = explode(',', self::$en_noise_words);
765

    
766
        foreach ($words as $word) {
767
            $word = trim($word);
768

    
769
            $word = " $word "; // Normalize
770

    
771
            if (stripos($value, $word) !== false) {
772
                $value = str_ireplace($word, chr(32), $value);
773
            }
774
        }
775

    
776
        return trim($value);
777
    }
778

    
779
    /**
780
     * Remove all known punctuation from a string.
781
     *
782
     * Usage: '<index>' => 'rmpunctuataion'
783
     *
784
     * @param string $value
785
     * @param array  $params
786
     *
787
     * @return string
788
     */
789
    protected function filter_rmpunctuation($value, $params = null)
790
    {
791
        return preg_replace("/(?![.=$'€%-])\p{P}/u", '', $value);
792
    }
793

    
794
    /**
795
     * Translate an input string to a desired language [DEPRECIATED].
796
     *
797
     * Any ISO 639-1 2 character language code may be used
798
     *
799
     * See: http://www.science.co.il/language/Codes.asp?s=code2
800
     *
801
     * @param string $value
802
     * @param array  $params
803
     *
804
     * @return string
805
     */
806
    /*
807
    protected function filter_translate($value, $params = NULL)
808
    {
809
        $input_lang  = 'en';
810
        $output_lang = 'en';
811

812
        if(is_null($params))
813
        {
814
            return $value;
815
        }
816

817
        switch(count($params))
818
        {
819
            case 1:
820
                $input_lang  = $params[0];
821
                break;
822
            case 2:
823
                $input_lang  = $params[0];
824
                $output_lang = $params[1];
825
                break;
826
        }
827

828
        $text = urlencode($value);
829

830
        $translation = file_get_contents(
831
            "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q={$text}&langpair={$input_lang}|{$output_lang}"
832
        );
833

834
        $json = json_decode($translation, true);
835

836
        if($json['responseStatus'] != 200)
837
        {
838
            return $value;
839
        }
840
        else
841
        {
842
            return $json['responseData']['translatedText'];
843
        }
844
    }
845
    */
846

    
847
    /**
848
     * Sanitize the string by removing any script tags.
849
     *
850
     * Usage: '<index>' => 'sanitize_string'
851
     *
852
     * @param string $value
853
     * @param array  $params
854
     *
855
     * @return string
856
     */
857
    protected function filter_sanitize_string($value, $params = null)
858
    {
859
        return filter_var($value, FILTER_SANITIZE_STRING);
860
    }
861

    
862
    /**
863
     * Sanitize the string by urlencoding characters.
864
     *
865
     * Usage: '<index>' => 'urlencode'
866
     *
867
     * @param string $value
868
     * @param array  $params
869
     *
870
     * @return string
871
     */
872
    protected function filter_urlencode($value, $params = null)
873
    {
874
        return filter_var($value, FILTER_SANITIZE_ENCODED);
875
    }
876

    
877
    /**
878
     * Sanitize the string by converting HTML characters to their HTML entities.
879
     *
880
     * Usage: '<index>' => 'htmlencode'
881
     *
882
     * @param string $value
883
     * @param array  $params
884
     *
885
     * @return string
886
     */
887
    protected function filter_htmlencode($value, $params = null)
888
    {
889
        return filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
890
    }
891

    
892
    /**
893
     * Sanitize the string by removing illegal characters from emails.
894
     *
895
     * Usage: '<index>' => 'sanitize_email'
896
     *
897
     * @param string $value
898
     * @param array  $params
899
     *
900
     * @return string
901
     */
902
    protected function filter_sanitize_email($value, $params = null)
903
    {
904
        return filter_var($value, FILTER_SANITIZE_EMAIL);
905
    }
906

    
907
    /**
908
     * Sanitize the string by removing illegal characters from numbers.
909
     *
910
     * @param string $value
911
     * @param array  $params
912
     *
913
     * @return string
914
     */
915
    protected function filter_sanitize_numbers($value, $params = null)
916
    {
917
        return filter_var($value, FILTER_SANITIZE_NUMBER_INT);
918
    }
919

    
920
    /**
921
     * Filter out all HTML tags except the defined basic tags.
922
     *
923
     * @param string $value
924
     * @param array  $params
925
     *
926
     * @return string
927
     */
928
    protected function filter_basic_tags($value, $params = null)
929
    {
930
        return strip_tags($value, self::$basic_tags);
931
    }
932

    
933
    /**
934
     * Convert the provided numeric value to a whole number.
935
     *
936
     * @param string $value
937
     * @param array  $params
938
     *
939
     * @return string
940
     */
941
    protected function filter_whole_number($value, $params = null)
942
    {
943
        return intval($value);
944
    }
945

    
946
    // ** ------------------------- Validators ------------------------------------ ** //
947

    
948

    
949
    /**
950
     * Verify that a value is contained within the pre-defined value set.
951
     *
952
     * Usage: '<index>' => 'contains,value value value'
953
     *
954
     * @param string $field
955
     * @param array  $input
956
     * @param null   $param
957
     *
958
     * @return mixed
959
     */
960
    protected function validate_contains($field, $input, $param = null)
961
    {
962
        if (!isset($input[$field])) {
963
            return;
964
        }
965

    
966
        $param = trim(strtolower($param));
967

    
968
        $value = trim(strtolower($input[$field]));
969

    
970
        if (preg_match_all('#\'(.+?)\'#', $param, $matches, PREG_PATTERN_ORDER)) {
971
            $param = $matches[1];
972
        } else {
973
            $param = explode(chr(32), $param);
974
        }
975

    
976
        if (in_array($value, $param)) { // valid, return nothing
977
            return;
978
        }
979

    
980
        return array(
981
            'field' => $field,
982
            'value' => $value,
983
            'rule' => __FUNCTION__,
984
            'param' => $param,
985
        );
986
    }
987

    
988
    /**
989
     * Verify that a value is contained within the pre-defined value set.
990
     * OUTPUT: will NOT show the list of values.
991
     *
992
     * Usage: '<index>' => 'contains_list,value;value;value'
993
     *
994
     * @param string $field
995
     * @param array  $input
996
     *
997
     * @return mixed
998
     */
999
    protected function validate_contains_list($field, $input, $param = null)
1000
    {
1001
        $param = trim(strtolower($param));
1002

    
1003
        $value = trim(strtolower($input[$field]));
1004

    
1005
        $param = explode(';', $param);
1006

    
1007
        // consider: in_array(strtolower($value), array_map('strtolower', $param)
1008

    
1009
        if (in_array($value, $param)) { // valid, return nothing
1010
            return;
1011
        } else {
1012
            return array(
1013
                    'field' => $field,
1014
                    'value' => $value,
1015
                    'rule' => __FUNCTION__,
1016
                    'param' => $param,
1017
            );
1018
        }
1019
    }
1020

    
1021
    /**
1022
     * Verify that a value is NOT contained within the pre-defined value set.
1023
     * OUTPUT: will NOT show the list of values.
1024
     *
1025
     * Usage: '<index>' => 'doesnt_contain_list,value;value;value'
1026
     *
1027
     * @param string $field
1028
     * @param array  $input
1029
     *
1030
     * @return mixed
1031
     */
1032
    protected function validate_doesnt_contain_list($field, $input, $param = null)
1033
    {
1034
        $param = trim(strtolower($param));
1035

    
1036
        $value = trim(strtolower($input[$field]));
1037

    
1038
        $param = explode(';', $param);
1039

    
1040
        if (!in_array($value, $param)) { // valid, return nothing
1041
            return;
1042
        } else {
1043
            return array(
1044
                    'field' => $field,
1045
                    'value' => $value,
1046
                    'rule' => __FUNCTION__,
1047
                    'param' => $param,
1048
            );
1049
        }
1050
    }
1051

    
1052
    /**
1053
     * Check if the specified key is present and not empty.
1054
     *
1055
     * Usage: '<index>' => 'required'
1056
     *
1057
     * @param string $field
1058
     * @param array  $input
1059
     * @param null   $param
1060
     *
1061
     * @return mixed
1062
     */
1063
    protected function validate_required($field, $input, $param = null)
1064
    {
1065
        if (isset($input[$field]) && ($input[$field] === false || $input[$field] === 0 || $input[$field] === 0.0 || $input[$field] === '0' || !empty($input[$field]))) {
1066
            return;
1067
        }
1068

    
1069
        return array(
1070
        'field' => $field,
1071
        'value' => null,
1072
        'rule' => __FUNCTION__,
1073
        'param' => $param,
1074
        );
1075
    }
1076

    
1077
    /**
1078
     * Determine if the provided email is valid.
1079
     *
1080
     * Usage: '<index>' => 'valid_email'
1081
     *
1082
     * @param string $field
1083
     * @param array  $input
1084
     * @param null   $param
1085
     *
1086
     * @return mixed
1087
     */
1088
    protected function validate_valid_email($field, $input, $param = null)
1089
    {
1090
        if (!isset($input[$field]) || empty($input[$field])) {
1091
            return;
1092
        }
1093

    
1094
        if (!filter_var($input[$field], FILTER_VALIDATE_EMAIL)) {
1095
            return array(
1096
                'field' => $field,
1097
                'value' => $input[$field],
1098
                'rule' => __FUNCTION__,
1099
                'param' => $param,
1100
            );
1101
        }
1102
    }
1103

    
1104
    /**
1105
     * Determine if the provided value length is less or equal to a specific value.
1106
     *
1107
     * Usage: '<index>' => 'max_len,240'
1108
     *
1109
     * @param string $field
1110
     * @param array  $input
1111
     * @param null   $param
1112
     *
1113
     * @return mixed
1114
     */
1115
    protected function validate_max_len($field, $input, $param = null)
1116
    {
1117
        if (!isset($input[$field])) {
1118
            return;
1119
        }
1120

    
1121
        if (function_exists('mb_strlen')) {
1122
            if (mb_strlen($input[$field]) <= (int) $param) {
1123
                return;
1124
            }
1125
        } else {
1126
            if (strlen($input[$field]) <= (int) $param) {
1127
                return;
1128
            }
1129
        }
1130

    
1131
        return array(
1132
            'field' => $field,
1133
            'value' => $input[$field],
1134
            'rule' => __FUNCTION__,
1135
            'param' => $param,
1136
        );
1137
    }
1138

    
1139
    /**
1140
     * Determine if the provided value length is more or equal to a specific value.
1141
     *
1142
     * Usage: '<index>' => 'min_len,4'
1143
     *
1144
     * @param string $field
1145
     * @param array  $input
1146
     * @param null   $param
1147
     *
1148
     * @return mixed
1149
     */
1150
    protected function validate_min_len($field, $input, $param = null)
1151
    {
1152
        if (!isset($input[$field])) {
1153
            return;
1154
        }
1155

    
1156
        if (function_exists('mb_strlen')) {
1157
            if (mb_strlen($input[$field]) >= (int) $param) {
1158
                return;
1159
            }
1160
        } else {
1161
            if (strlen($input[$field]) >= (int) $param) {
1162
                return;
1163
            }
1164
        }
1165

    
1166
        return array(
1167
            'field' => $field,
1168
            'value' => $input[$field],
1169
            'rule' => __FUNCTION__,
1170
            'param' => $param,
1171
        );
1172
    }
1173

    
1174
    /**
1175
     * Determine if the provided value length matches a specific value.
1176
     *
1177
     * Usage: '<index>' => 'exact_len,5'
1178
     *
1179
     * @param string $field
1180
     * @param array  $input
1181
     * @param null   $param
1182
     *
1183
     * @return mixed
1184
     */
1185
    protected function validate_exact_len($field, $input, $param = null)
1186
    {
1187
        if (!isset($input[$field])) {
1188
            return;
1189
        }
1190

    
1191
        if (function_exists('mb_strlen')) {
1192
            if (mb_strlen($input[$field]) == (int) $param) {
1193
                return;
1194
            }
1195
        } else {
1196
            if (strlen($input[$field]) == (int) $param) {
1197
                return;
1198
            }
1199
        }
1200

    
1201
        return array(
1202
            'field' => $field,
1203
            'value' => $input[$field],
1204
            'rule' => __FUNCTION__,
1205
            'param' => $param,
1206
        );
1207
    }
1208

    
1209
    /**
1210
     * Determine if the provided value contains only alpha characters.
1211
     *
1212
     * Usage: '<index>' => 'alpha'
1213
     *
1214
     * @param string $field
1215
     * @param array  $input
1216
     * @param null   $param
1217
     *
1218
     * @return mixed
1219
     */
1220
    protected function validate_alpha($field, $input, $param = null)
1221
    {
1222
        if (!isset($input[$field]) || empty($input[$field])) {
1223
            return;
1224
        }
1225

    
1226
        if (!preg_match('/^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$/i', $input[$field]) !== false) {
1227
            return array(
1228
                'field' => $field,
1229
                'value' => $input[$field],
1230
                'rule' => __FUNCTION__,
1231
                'param' => $param,
1232
            );
1233
        }
1234
    }
1235

    
1236
    /**
1237
     * Determine if the provided value contains only alpha-numeric characters.
1238
     *
1239
     * Usage: '<index>' => 'alpha_numeric'
1240
     *
1241
     * @param string $field
1242
     * @param array  $input
1243
     * @param null   $param
1244
     *
1245
     * @return mixed
1246
     */
1247
    protected function validate_alpha_numeric($field, $input, $param = null)
1248
    {
1249
        if (!isset($input[$field]) || empty($input[$field])) {
1250
            return;
1251
        }
1252

    
1253
        if (!preg_match('/^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$/i', $input[$field]) !== false) {
1254
            return array(
1255
                'field' => $field,
1256
                'value' => $input[$field],
1257
                'rule' => __FUNCTION__,
1258
                'param' => $param,
1259
            );
1260
        }
1261
    }
1262

    
1263
    /**
1264
     * Determine if the provided value contains only alpha characters with dashed and underscores.
1265
     *
1266
     * Usage: '<index>' => 'alpha_dash'
1267
     *
1268
     * @param string $field
1269
     * @param array  $input
1270
     * @param null   $param
1271
     *
1272
     * @return mixed
1273
     */
1274
    protected function validate_alpha_dash($field, $input, $param = null)
1275
    {
1276
        if (!isset($input[$field]) || empty($input[$field])) {
1277
            return;
1278
        }
1279

    
1280
        if (!preg_match('/^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ_-])+$/i', $input[$field]) !== false) {
1281
            return array(
1282
                'field' => $field,
1283
                'value' => $input[$field],
1284
                'rule' => __FUNCTION__,
1285
                'param' => $param,
1286
            );
1287
        }
1288
    }
1289

    
1290
    /**
1291
     * Determine if the provided value contains only alpha numeric characters with spaces.
1292
     *
1293
     * Usage: '<index>' => 'alpha_space'
1294
     *
1295
     * @param string $field
1296
     * @param array  $input
1297
     * @param null   $param
1298
     *
1299
     * @return mixed
1300
     */
1301
    protected function validate_alpha_space($field, $input, $param = null)
1302
    {
1303
        if (!isset($input[$field]) || empty($input[$field])) {
1304
            return;
1305
        }
1306

    
1307
        if (!preg_match("/^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\s])+$/i", $input[$field]) !== false) {
1308
            return array(
1309
                'field' => $field,
1310
                'value' => $input[$field],
1311
                'rule' => __FUNCTION__,
1312
                'param' => $param,
1313
            );
1314
        }
1315
    }
1316

    
1317
    /**
1318
     * Determine if the provided value is a valid number or numeric string.
1319
     *
1320
     * Usage: '<index>' => 'numeric'
1321
     *
1322
     * @param string $field
1323
     * @param array  $input
1324
     * @param null   $param
1325
     *
1326
     * @return mixed
1327
     */
1328
    protected function validate_numeric($field, $input, $param = null)
1329
    {
1330
        if (!isset($input[$field]) || empty($input[$field])) {
1331
            return;
1332
        }
1333

    
1334
        if (!is_numeric($input[$field])) {
1335
            return array(
1336
                'field' => $field,
1337
                'value' => $input[$field],
1338
                'rule' => __FUNCTION__,
1339
                'param' => $param,
1340
            );
1341
        }
1342
    }
1343

    
1344
    /**
1345
     * Determine if the provided value is a valid integer.
1346
     *
1347
     * Usage: '<index>' => 'integer'
1348
     *
1349
     * @param string $field
1350
     * @param array  $input
1351
     * @param null   $param
1352
     *
1353
     * @return mixed
1354
     */
1355
    protected function validate_integer($field, $input, $param = null)
1356
    {
1357
        if (!isset($input[$field]) || empty($input[$field])) {
1358
            return;
1359
        }
1360

    
1361
        if (filter_var($input[$field], FILTER_VALIDATE_INT) === false) {
1362
            return array(
1363
                'field' => $field,
1364
                'value' => $input[$field],
1365
                'rule' => __FUNCTION__,
1366
                'param' => $param,
1367
            );
1368
        }
1369
    }
1370

    
1371
    /**
1372
     * Determine if the provided value is a PHP accepted boolean.
1373
     *
1374
     * Usage: '<index>' => 'boolean'
1375
     *
1376
     * @param string $field
1377
     * @param array  $input
1378
     * @param null   $param
1379
     *
1380
     * @return mixed
1381
     */
1382
    protected function validate_boolean($field, $input, $param = null)
1383
    {
1384
        if (!isset($input[$field]) || empty($input[$field]) && $input[$field] !== 0) {
1385
            return;
1386
        }
1387

    
1388
        if($input[$field] === true || $input[$field] === false) {
1389
          return;
1390
        }
1391

    
1392
        return array(
1393
            'field' => $field,
1394
            'value' => $input[$field],
1395
            'rule' => __FUNCTION__,
1396
            'param' => $param,
1397
        );
1398
    }
1399

    
1400
    /**
1401
     * Determine if the provided value is a valid float.
1402
     *
1403
     * Usage: '<index>' => 'float'
1404
     *
1405
     * @param string $field
1406
     * @param array  $input
1407
     * @param null   $param
1408
     *
1409
     * @return mixed
1410
     */
1411
    protected function validate_float($field, $input, $param = null)
1412
    {
1413
        if (!isset($input[$field]) || empty($input[$field])) {
1414
            return;
1415
        }
1416

    
1417
        if (filter_var($input[$field], FILTER_VALIDATE_FLOAT) === false) {
1418
            return array(
1419
                'field' => $field,
1420
                'value' => $input[$field],
1421
                'rule' => __FUNCTION__,
1422
                'param' => $param,
1423
            );
1424
        }
1425
    }
1426

    
1427
    /**
1428
     * Determine if the provided value is a valid URL.
1429
     *
1430
     * Usage: '<index>' => 'valid_url'
1431
     *
1432
     * @param string $field
1433
     * @param array  $input
1434
     * @param null   $param
1435
     *
1436
     * @return mixed
1437
     */
1438
    protected function validate_valid_url($field, $input, $param = null)
1439
    {
1440
        if (!isset($input[$field]) || empty($input[$field])) {
1441
            return;
1442
        }
1443

    
1444
        if (!filter_var($input[$field], FILTER_VALIDATE_URL)) {
1445
            return array(
1446
                'field' => $field,
1447
                'value' => $input[$field],
1448
                'rule' => __FUNCTION__,
1449
                'param' => $param,
1450
            );
1451
        }
1452
    }
1453

    
1454
    /**
1455
     * Determine if a URL exists & is accessible.
1456
     *
1457
     * Usage: '<index>' => 'url_exists'
1458
     *
1459
     * @param string $field
1460
     * @param array  $input
1461
     * @param null   $param
1462
     *
1463
     * @return mixed
1464
     */
1465
    protected function validate_url_exists($field, $input, $param = null)
1466
    {
1467
        if (!isset($input[$field]) || empty($input[$field])) {
1468
            return;
1469
        }
1470

    
1471
        $url = parse_url(strtolower($input[$field]));
1472

    
1473
        if (isset($url['host'])) {
1474
            $url = $url['host'];
1475
        }
1476

    
1477
        if (function_exists('checkdnsrr')) {
1478
            if (checkdnsrr($url) === false) {
1479
                return array(
1480
                    'field' => $field,
1481
                    'value' => $input[$field],
1482
                    'rule' => __FUNCTION__,
1483
                    'param' => $param,
1484
                );
1485
            }
1486
        } else {
1487
            if (gethostbyname($url) == $url) {
1488
                return array(
1489
                    'field' => $field,
1490
                    'value' => $input[$field],
1491
                    'rule' => __FUNCTION__,
1492
                    'param' => $param,
1493
                );
1494
            }
1495
        }
1496
    }
1497

    
1498
    /**
1499
     * Determine if the provided value is a valid IP address.
1500
     *
1501
     * Usage: '<index>' => 'valid_ip'
1502
     *
1503
     * @param string $field
1504
     * @param array  $input
1505
     *
1506
     * @return mixed
1507
     */
1508
    protected function validate_valid_ip($field, $input, $param = null)
1509
    {
1510
        if (!isset($input[$field]) || empty($input[$field])) {
1511
            return;
1512
        }
1513

    
1514
        if (!filter_var($input[$field], FILTER_VALIDATE_IP) !== false) {
1515
            return array(
1516
                'field' => $field,
1517
                'value' => $input[$field],
1518
                'rule' => __FUNCTION__,
1519
                'param' => $param,
1520
            );
1521
        }
1522
    }
1523

    
1524
    /**
1525
     * Determine if the provided value is a valid IPv4 address.
1526
     *
1527
     * Usage: '<index>' => 'valid_ipv4'
1528
     *
1529
     * @param string $field
1530
     * @param array  $input
1531
     *
1532
     * @return mixed
1533
     *
1534
     * @see http://pastebin.com/UvUPPYK0
1535
     */
1536

    
1537
    /*
1538
     * What about private networks? http://en.wikipedia.org/wiki/Private_network
1539
     * What about loop-back address? 127.0.0.1
1540
     */
1541
    protected function validate_valid_ipv4($field, $input, $param = null)
1542
    {
1543
        if (!isset($input[$field]) || empty($input[$field])) {
1544
            return;
1545
        }
1546

    
1547
        if (!filter_var($input[$field], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
1548
            // removed !== FALSE
1549

    
1550
            return array(
1551
                'field' => $field,
1552
                'value' => $input[$field],
1553
                'rule' => __FUNCTION__,
1554
                'param' => $param,
1555
            );
1556
        }
1557
    }
1558

    
1559
    /**
1560
     * Determine if the provided value is a valid IPv6 address.
1561
     *
1562
     * Usage: '<index>' => 'valid_ipv6'
1563
     *
1564
     * @param string $field
1565
     * @param array  $input
1566
     *
1567
     * @return mixed
1568
     */
1569
    protected function validate_valid_ipv6($field, $input, $param = null)
1570
    {
1571
        if (!isset($input[$field]) || empty($input[$field])) {
1572
            return;
1573
        }
1574

    
1575
        if (!filter_var($input[$field], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
1576
            return array(
1577
                'field' => $field,
1578
                'value' => $input[$field],
1579
                'rule' => __FUNCTION__,
1580
                'param' => $param,
1581
            );
1582
        }
1583
    }
1584

    
1585
    /**
1586
     * Determine if the input is a valid credit card number.
1587
     *
1588
     * See: http://stackoverflow.com/questions/174730/what-is-the-best-way-to-validate-a-credit-card-in-php
1589
     * Usage: '<index>' => 'valid_cc'
1590
     *
1591
     * @param string $field
1592
     * @param array  $input
1593
     *
1594
     * @return mixed
1595
     */
1596
    protected function validate_valid_cc($field, $input, $param = null)
1597
    {
1598
        if (!isset($input[$field]) || empty($input[$field])) {
1599
            return;
1600
        }
1601

    
1602
        $number = preg_replace('/\D/', '', $input[$field]);
1603

    
1604
        if (function_exists('mb_strlen')) {
1605
            $number_length = mb_strlen($number);
1606
        } else {
1607
            $number_length = strlen($number);
1608
        }
1609

    
1610
        $parity = $number_length % 2;
1611

    
1612
        $total = 0;
1613

    
1614
        for ($i = 0; $i < $number_length; ++$i) {
1615
            $digit = $number[$i];
1616

    
1617
            if ($i % 2 == $parity) {
1618
                $digit *= 2;
1619

    
1620
                if ($digit > 9) {
1621
                    $digit -= 9;
1622
                }
1623
            }
1624

    
1625
            $total += $digit;
1626
        }
1627

    
1628
        if ($total % 10 == 0) {
1629
            return; // Valid
1630
        }
1631

    
1632
        return array(
1633
            'field' => $field,
1634
            'value' => $input[$field],
1635
            'rule' => __FUNCTION__,
1636
            'param' => $param,
1637
        );
1638
    }
1639

    
1640
    /**
1641
     * Determine if the input is a valid human name [Credits to http://github.com/ben-s].
1642
     *
1643
     * See: https://github.com/Wixel/GUMP/issues/5
1644
     * Usage: '<index>' => 'valid_name'
1645
     *
1646
     * @param string $field
1647
     * @param array  $input
1648
     *
1649
     * @return mixed
1650
     */
1651
    protected function validate_valid_name($field, $input, $param = null)
1652
    {
1653
        if (!isset($input[$field]) || empty($input[$field])) {
1654
            return;
1655
        }
1656

    
1657
        if (!preg_match("/^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïñðòóôõöùúûüýÿ '-])+$/i", $input[$field]) !== false) {
1658
            return array(
1659
                'field' => $field,
1660
                'value' => $input[$field],
1661
                'rule' => __FUNCTION__,
1662
                'param' => $param,
1663
            );
1664
        }
1665
    }
1666

    
1667
    /**
1668
     * Determine if the provided input is likely to be a street address using weak detection.
1669
     *
1670
     * Usage: '<index>' => 'street_address'
1671
     *
1672
     * @param string $field
1673
     * @param array  $input
1674
     *
1675
     * @return mixed
1676
     */
1677
    protected function validate_street_address($field, $input, $param = null)
1678
    {
1679
        if (!isset($input[$field]) || empty($input[$field])) {
1680
            return;
1681
        }
1682

    
1683
        // Theory: 1 number, 1 or more spaces, 1 or more words
1684
        $hasLetter = preg_match('/[a-zA-Z]/', $input[$field]);
1685
        $hasDigit = preg_match('/\d/', $input[$field]);
1686
        $hasSpace = preg_match('/\s/', $input[$field]);
1687

    
1688
        $passes = $hasLetter && $hasDigit && $hasSpace;
1689

    
1690
        if (!$passes) {
1691
            return array(
1692
                'field' => $field,
1693
                'value' => $input[$field],
1694
                'rule' => __FUNCTION__,
1695
                'param' => $param,
1696
            );
1697
        }
1698
    }
1699

    
1700
    /**
1701
     * Determine if the provided value is a valid IBAN.
1702
     *
1703
     * Usage: '<index>' => 'iban'
1704
     *
1705
     * @param string $field
1706
     * @param array  $input
1707
     *
1708
     * @return mixed
1709
     */
1710
    protected function validate_iban($field, $input, $param = null)
1711
    {
1712
        if (!isset($input[$field]) || empty($input[$field])) {
1713
            return;
1714
        }
1715

    
1716
        static $character = array(
1717
            'A' => 10, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15, 'G' => 16,
1718
            'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22,
1719
            'N' => 23, 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28,
1720
            'T' => 29, 'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34,
1721
            'Z' => 35, 'B' => 11
1722
        );
1723

    
1724
        if (!preg_match("/\A[A-Z]{2}\d{2} ?[A-Z\d]{4}( ?\d{4}){1,} ?\d{1,4}\z/", $input[$field])) {
1725
            return array(
1726
                'field' => $field,
1727
                'value' => $input[$field],
1728
                'rule' => __FUNCTION__,
1729
                'param' => $param,
1730
            );
1731
        }
1732

    
1733
        $iban = str_replace(' ', '', $input[$field]);
1734
        $iban = substr($iban, 4).substr($iban, 0, 4);
1735
        $iban = strtr($iban, $character);
1736

    
1737
        if (bcmod($iban, 97) != 1) {
1738
            return array(
1739
                'field' => $field,
1740
                'value' => $input[$field],
1741
                'rule' => __FUNCTION__,
1742
                'param' => $param,
1743
            );
1744
        }
1745
    }
1746

    
1747
    /**
1748
     * Determine if the provided input is a valid date (ISO 8601).
1749
     *
1750
     * Usage: '<index>' => 'date'
1751
     *
1752
     * @param string $field
1753
     * @param string $input date ('Y-m-d') or datetime ('Y-m-d H:i:s')
1754
     * @param null   $param
1755
     *
1756
     * @return mixed
1757
     */
1758
    protected function validate_date($field, $input, $param = null)
1759
    {
1760
        if (!isset($input[$field]) || empty($input[$field])) {
1761
            return;
1762
        }
1763

    
1764
        $cdate1 = date('Y-m-d', strtotime($input[$field]));
1765
        $cdate2 = date('Y-m-d H:i:s', strtotime($input[$field]));
1766

    
1767
        if ($cdate1 != $input[$field] && $cdate2 != $input[$field]) {
1768
            return array(
1769
                'field' => $field,
1770
                'value' => $input[$field],
1771
                'rule' => __FUNCTION__,
1772
                'param' => $param,
1773
            );
1774
        }
1775
    }
1776

    
1777
    /**
1778
     * Determine if the provided input meets age requirement (ISO 8601).
1779
     *
1780
     * Usage: '<index>' => 'min_age,13'
1781
     *
1782
     * @param string $field
1783
     * @param string $input date ('Y-m-d') or datetime ('Y-m-d H:i:s')
1784
     * @param string $param int
1785
     *
1786
     * @return mixed
1787
     */
1788
    protected function validate_min_age($field, $input, $param = null)
1789
    {
1790
        if (!isset($input[$field]) || empty($input[$field])) {
1791
            return;
1792
        }
1793

    
1794
        $cdate1 = new DateTime(date('Y-m-d', strtotime($input[$field])));
1795
        $today = new DateTime(date('d-m-Y'));
1796

    
1797
        $interval = $cdate1->diff($today);
1798
        $age = $interval->y;
1799

    
1800
        if ($age <= $param) {
1801
            return array(
1802
                'field' => $field,
1803
                'value' => $input[$field],
1804
                'rule' => __FUNCTION__,
1805
                'param' => $param,
1806
            );
1807
        }
1808
    }
1809

    
1810
    /**
1811
     * Determine if the provided numeric value is lower or equal to a specific value.
1812
     *
1813
     * Usage: '<index>' => 'max_numeric,50'
1814
     *
1815
     * @param string $field
1816
     * @param array  $input
1817
     * @param null   $param
1818
     *
1819
     * @return mixed
1820
     */
1821
    protected function validate_max_numeric($field, $input, $param = null)
1822
    {
1823
        if (!isset($input[$field]) || empty($input[$field])) {
1824
            return;
1825
        }
1826

    
1827
        if (is_numeric($input[$field]) && is_numeric($param) && ($input[$field] <= $param)) {
1828
            return;
1829
        }
1830

    
1831
        return array(
1832
            'field' => $field,
1833
            'value' => $input[$field],
1834
            'rule' => __FUNCTION__,
1835
            'param' => $param,
1836
        );
1837
    }
1838

    
1839
    /**
1840
     * Determine if the provided numeric value is higher or equal to a specific value.
1841
     *
1842
     * Usage: '<index>' => 'min_numeric,1'
1843
     *
1844
     * @param string $field
1845
     * @param array  $input
1846
     * @param null   $param
1847
     * @return mixed
1848
     */
1849
    protected function validate_min_numeric($field, $input, $param = null)
1850
    {
1851
        if (!isset($input[$field])) {
1852
            return;
1853
        }
1854

    
1855
        if (is_numeric($input[$field]) && is_numeric($param) && ($input[$field] >= $param)) {
1856
            return;
1857
        }
1858

    
1859
        return array(
1860
            'field' => $field,
1861
            'value' => $input[$field],
1862
            'rule' => __FUNCTION__,
1863
            'param' => $param,
1864
        );
1865
    }
1866

    
1867
    /**
1868
     * Determine if the provided value starts with param.
1869
     *
1870
     * Usage: '<index>' => 'starts,Z'
1871
     *
1872
     * @param string $field
1873
     * @param array  $input
1874
     *
1875
     * @return mixed
1876
     */
1877
    protected function validate_starts($field, $input, $param = null)
1878
    {
1879
        if (!isset($input[$field]) || empty($input[$field])) {
1880
            return;
1881
        }
1882

    
1883
        if (strpos($input[$field], $param) !== 0) {
1884
            return array(
1885
                'field' => $field,
1886
                'value' => $input[$field],
1887
                'rule' => __FUNCTION__,
1888
                'param' => $param,
1889
            );
1890
        }
1891
    }
1892

    
1893
      /**
1894
       * checks if a file was uploaded.
1895
       *
1896
       * Usage: '<index>' => 'required_file'
1897
       *
1898
       * @param  string $field
1899
       * @param  array $input
1900
       *
1901
       * @return mixed
1902
       */
1903
      protected function validate_required_file($field, $input, $param = null)
1904
      {
1905
          if ($input[$field]['error'] !== 4) {
1906
              return;
1907
          }
1908

    
1909
          return array(
1910
              'field' => $field,
1911
              'value' => $input[$field],
1912
              'rule' => __FUNCTION__,
1913
              'param' => $param,
1914
          );
1915
      }
1916

    
1917
    /**
1918
     * check the uploaded file for extension
1919
     * for now checks onlt the ext should add mime type check.
1920
     *
1921
     * Usage: '<index>' => 'starts,Z'
1922
     *
1923
     * @param string $field
1924
     * @param array  $input
1925
     *
1926
     * @return mixed
1927
     */
1928
    protected function validate_extension($field, $input, $param = null)
1929
    {
1930
        if ($input[$field]['error'] !== 4) {
1931
            $param = trim(strtolower($param));
1932
            $allowed_extensions = explode(';', $param);
1933

    
1934
            $path_info = pathinfo($input[$field]['name']);
1935
            $extension = $path_info['extension'];
1936

    
1937
            if (in_array($extension, $allowed_extensions)) {
1938
                return;
1939
            }
1940

    
1941
            return array(
1942
                'field' => $field,
1943
                'value' => $input[$field],
1944
                'rule' => __FUNCTION__,
1945
                'param' => $param,
1946
            );
1947
        }
1948
    }
1949

    
1950
    /**
1951
     * Determine if the provided field value equals current field value.
1952
     *
1953
     * Usage: '<index>' => 'equalsfield,Z'
1954
     *
1955
     * @param string $field
1956
     * @param string $input
1957
     * @param string $param field to compare with
1958
     *
1959
     * @return mixed
1960
     */
1961
    protected function validate_equalsfield($field, $input, $param = null)
1962
    {
1963
        if (!isset($input[$field]) || empty($input[$field])) {
1964
            return;
1965
        }
1966

    
1967
        if ($input[$field] == $input[$param]) {
1968
          return;
1969
        }
1970

    
1971
        return array(
1972
            'field' => $field,
1973
            'value' => $input[$field],
1974
            'rule' => __FUNCTION__,
1975
            'param' => $param,
1976
        );
1977
    }
1978

    
1979
    /**
1980
     * Determine if the provided field value is a valid GUID (v4)
1981
     *
1982
     * Usage: '<index>' => 'guidv4'
1983
     *
1984
     * @param string $field
1985
     * @param string $input
1986
     * @param string $param field to compare with
1987
     * @return mixed
1988
     */
1989
    protected function validate_guidv4($field, $input, $param = null)
1990
    {
1991
        if (!isset($input[$field]) || empty($input[$field])) {
1992
            return;
1993
        }
1994

    
1995
        if (preg_match("/\{?[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}?$/", $input[$field])) {
1996
          return;
1997
        }
1998

    
1999
        return array(
2000
            'field' => $field,
2001
            'value' => $input[$field],
2002
            'rule' => __FUNCTION__,
2003
            'param' => $param,
2004
        );
2005
    }
2006

    
2007
    /**
2008
     * Trims whitespace only when the value is a scalar.
2009
     *
2010
     * @param mixed $value
2011
     *
2012
     * @return mixed
2013
     */
2014
    private function trimScalar($value)
2015
    {
2016
        if (is_scalar($value)) {
2017
            $value = trim($value);
2018
        }
2019

    
2020
        return $value;
2021
    }
2022

    
2023
    /**
2024
     * Determine if the provided value is a valid phone number.
2025
     *
2026
     * Usage: '<index>' => 'phone_number'
2027
     *
2028
     * @param string $field
2029
     * @param array  $input
2030
     *
2031
     * @return mixed
2032
     *
2033
     * Examples:
2034
     *
2035
     *  555-555-5555: valid
2036
     *        5555425555: valid
2037
     *        555 555 5555: valid
2038
     *        1(519) 555-4444: valid
2039
     *        1 (519) 555-4422: valid
2040
     *        1-555-555-5555: valid
2041
     *        1-(555)-555-5555: valid
2042
     */
2043
    protected function validate_phone_number($field, $input, $param = null)
2044
    {
2045
        if (!isset($input[$field]) || empty($input[$field])) {
2046
            return;
2047
        }
2048

    
2049
        $regex = '/^(\d[\s-]?)?[\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4}$/i';
2050
        if (!preg_match($regex, $input[$field])) {
2051
            return array(
2052
          'field' => $field,
2053
          'value' => $input[$field],
2054
          'rule' => __FUNCTION__,
2055
          'param' => $param,
2056
        );
2057
        }
2058
    }
2059

    
2060
    /**
2061
     * Custom regex validator.
2062
     *
2063
     * Usage: '<index>' => 'regex,/your-regex-expression/'
2064
     *
2065
     * @param string $field
2066
     * @param array  $input
2067
     *
2068
     * @return mixed
2069
     */
2070
    protected function validate_regex($field, $input, $param = null)
2071
    {
2072
        if (!isset($input[$field]) || empty($input[$field])) {
2073
            return;
2074
        }
2075

    
2076
        $regex = $param;
2077
        if (!preg_match($regex, $input[$field])) {
2078
            return array(
2079
          'field' => $field,
2080
          'value' => $input[$field],
2081
          'rule' => __FUNCTION__,
2082
          'param' => $param,
2083
        );
2084
        }
2085
    }
2086

    
2087
    /**
2088
     * Json validatior.
2089
     *
2090
     * Usage: '<index>' => 'valid_json_string'
2091
     *
2092
     * @param string $field
2093
     * @param array  $input
2094
     *
2095
     * @return mixed
2096
     */
2097
    protected function validate_valid_json_string($field, $input, $param = null)
2098
    {
2099
        if (!isset($input[$field]) || empty($input[$field])) {
2100
            return;
2101
        }
2102

    
2103
        if (!is_string($input[$field]) || !is_object(json_decode($input[$field]))) {
2104
            return array(
2105
          'field' => $field,
2106
          'value' => $input[$field],
2107
          'rule' => __FUNCTION__,
2108
          'param' => $param,
2109
        );
2110
        }
2111
    }
2112
} // EOC