1
<?php
2
/**
3
* Form Validation Library
4
*
5
* @author Zhou Yuan <yuanzhou19@gmail.com>
6
* @link http://www.infopotato.com/
7
* @copyright Copyright © 2009-2011 Zhou Yuan
8
* @license http://www.opensource.org/licenses/mit-license.php MIT Licence
9
*/
10
class Form_Validation_Library {
11
/**
12
* Key-value array of HTTP POST parameters to be validated
13
*
14
* @var array
15
*/
16
private $_post = array();
17
18
private $_field_data = array();
19
private $_error_array = array();
20
private $_error_messages = array();
21
private $_error_prefix = '<div>';
22
private $_error_suffix = '</div>';
23
private $_safe_form_data = FALSE;
24
25
/**
26
* Constructor
27
*
28
* $config must contain ['post']
29
*/
30
public function __construct(array $config = NULL) {
31
// Assign the $this->_POST_DATA array
32
$this->_post = $config['post'];
33
34
// Set the character encoding in MB.
35
if (function_exists('mb_internal_encoding')){
36
mb_internal_encoding('UTF-8');
37
}
38
39
$this->_error_messages = array(
40
'required' => "The %s field is required.",
41
'isset' => "The %s field must have a value.",
42
'valid_email' => "The %s field must contain a valid email address.",
43
'valid_emails' => "The %s field must contain a valid email address.",
44
'min_length' => "The %s field must be at least %s characters in length.",
45
'max_length' => "The %s field can not exceed %s characters in length.",
46
'exact_length' => "The %s field must be exactly %s characters in length.",
47
'equals' => "The %s field must equal the value designed.",
48
'form_token' => "Please do not resubmit the form data.",
49
'alpha' => "The %s field may only contain alphabetical characters.",
50
'alpha_numeric' => "The %s field may only contain alpha-numeric characters.",
51
'alpha_dash' => "The %s field may only contain alpha-numeric characters, underscores, and dashes.",
52
'numeric' => "The %s field must contain only numbers.",
53
'is_numeric' => "The %s field must contain only numeric characters.",
54
'is_integer' => "The %s field must contain an integer.",
55
'matches' => "The %s field does not match the %s field.",
56
'decimal' => "The %s field must contain a decimal number.",
57
'less_than' => "The %s field must contain a number less than %s.",
58
'greater_than' => "The %s field must contain a number greater than %s.",
59
'is_natural' => "The %s field must contain only positive numbers.",
60
'is_natural_no_zero' => "The %s field must contain a number greater than zero.",
61
);
62
}
63
64
/**
65
* Set Rules
66
*
67
* This function takes an array of field names and validation
68
* rules as input, validates the info, and stores it
69
*
70
* @param string the field name
71
* @param string
72
* @param string
73
* @return void
74
*/
75
public function set_rules($field = '', $label = '', $rules = '') {
76
// No reason to set rules if we have no POST data
77
if (count($this->_post) == 0) {
78
return;
79
}
80
81
// If the field label wasn't passed we use the field name
82
$label = ($label == '') ? $field : $label;
83
84
// Is the field name an array? We test for the existence of a bracket "[" in
85
// the field name to determine this. If it is an array, we break it apart
86
// into its components so that we can fetch the corresponding POST data later
87
if (strpos($field, '[') !== FALSE && preg_match_all('/\[(.*?)\]/', $field, $matches)) {
88
// Note: Due to a bug in current() that affects some versions
89
// of PHP we can not pass function call directly into it
90
$x = explode('[', $field);
91
$indexes[] = current($x);
92
93
for ($i = 0; $i < count($matches['0']); $i++) {
94
if ($matches['1'][$i] != '') {
95
$indexes[] = $matches['1'][$i];
96
}
97
}
98
99
$is_array = TRUE;
100
} else {
101
$indexes = array();
102
$is_array = FALSE;
103
}
104
105
// Build our master array
106
$this->_field_data[$field] = array(
107
'field' => $field,
108
'label' => $label,
109
'rules' => $rules,
110
'is_array' => $is_array,
111
'keys' => $indexes,
112
'postdata' => NULL,
113
'error' => ''
114
);
115
}
116
117
118
/**
119
* Set The Error Delimiter
120
*
121
* Permits a prefix/suffix to be added to each error message
122
*
123
* @param string
124
* @param string
125
* @return void
126
*/
127
public function set_error_delimiters($prefix = '<div>', $suffix = '</div>') {
128
$this->_error_prefix = $prefix;
129
$this->_error_suffix = $suffix;
130
}
131
132
133
/**
134
* Get Single Field Error Message
135
*
136
* Gets the error message associated with a particular field
137
*
138
* @param string the field name
139
* @param string
140
* @param string
141
* @return string
142
*/
143
public function field_error($field = '', $prefix = '', $suffix = '') {
144
if ( ! isset($this->_field_data[$field]['error']) || $this->_field_data[$field]['error'] == '') {
145
return '';
146
}
147
148
if ($prefix == '') {
149
$prefix = $this->_error_prefix;
150
}
151
152
if ($suffix == '') {
153
$suffix = $this->_error_suffix;
154
}
155
156
return $prefix.$this->_field_data[$field]['error'].$suffix;
157
}
158
159
160
/**
161
* Error String
162
*
163
* Returns the error messages as a string, wrapped in the error delimiters
164
* If there are no error messages it returns an empty string.
165
*
166
* @param string
167
* @param string
168
* @return string
169
*/
170
public function form_errors($prefix = '', $suffix = '') {
171
// No errrors, validation passes!
172
if (count($this->_error_array) === 0) {
173
return '';
174
}
175
176
if ($prefix == '') {
177
$prefix = $this->_error_prefix;
178
}
179
180
if ($suffix == '') {
181
$suffix = $this->_error_suffix;
182
}
183
184
// Generate the error string
185
$str = '';
186
foreach ($this->_error_array as $val) {
187
if ($val != '') {
188
$str .= $prefix.$val.$suffix."\n";
189
}
190
}
191
return $str;
192
}
193
194
195
/**
196
* Run the Validator
197
*
198
* This function does all the work.
199
*
200
* @return bool
201
*/
202
public function run() {
203
// Do we even have any data to process?
204
if (count($this->_post) == 0) {
205
return FALSE;
206
}
207
208
// Does the _field_data array containing the validation rules exist?
209
if (count($this->_field_data) == 0) {
210
return FALSE;
211
}
212
213
// Cycle through the rules for each field, match the
214
// corresponding $this->_post item and test for errors
215
foreach ($this->_field_data as $field => $row) {
216
// Fetch the data from the corresponding $this->_post array and cache it in the _field_data array.
217
// Depending on whether the field name is an array or a string will determine where we get it from.
218
if ($row['is_array'] == TRUE) {
219
$this->_field_data[$field]['postdata'] = $this->_reduce_array($this->_post, $row['keys']);
220
} else {
221
if (isset($this->_post[$field]) && $this->_post[$field] != '') {
222
$this->_field_data[$field]['postdata'] = $this->_post[$field];
223
}
224
}
225
226
$this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']);
227
}
228
229
// Did we end up with any errors?
230
$total_errors = count($this->_error_array);
231
232
if ($total_errors > 0) {
233
$this->_safe_form_data = TRUE;
234
}
235
236
// Now we need to re-set the POST data with the new, processed data
237
$this->_reset_post_array();
238
239
// No errors, validation passes!
240
if ($total_errors == 0) {
241
return TRUE;
242
}
243
244
// Validation fails
245
return FALSE;
246
}
247
248
249
/**
250
* Traverse a multidimensional $this->_post array index until the data is found
251
*
252
* @param array
253
* @param array
254
* @param integer
255
* @return mixed
256
*/
257
private function _reduce_array($array, $keys, $i = 0) {
258
if (is_array($array)) {
259
if (isset($keys[$i])) {
260
if (isset($array[$keys[$i]])) {
261
$array = $this->_reduce_array($array[$keys[$i]], $keys, ($i+1));
262
} else {
263
return NULL;
264
}
265
} else {
266
return $array;
267
}
268
}
269
return $array;
270
}
271
272
273
/**
274
* Re-populate the _POST array with our finalized and processed data
275
*
276
* @return null
277
*/
278
private function _reset_post_array() {
279
foreach ($this->_field_data as $field => $row) {
280
if ( ! is_null($row['postdata'])) {
281
if ($row['is_array'] == FALSE) {
282
if (isset($this->_post[$row['field']])) {
283
$this->_post[$row['field']] = $this->prep_for_form($row['postdata']);
284
}
285
} else {
286
// start with a reference
287
$post_ref =& $this->_post;
288
289
// before we assign values, make a reference to the right POST key
290
if (count($row['keys']) == 1) {
291
$post_ref =& $post_ref[current($row['keys'])];
292
} else {
293
foreach ($row['keys'] as $val) {
294
$post_ref =& $post_ref[$val];
295
}
296
}
297
298
if (is_array($row['postdata'])) {
299
$array = array();
300
foreach ($row['postdata'] as $k => $v) {
301
$array[$k] = $this->prep_for_form($v);
302
}
303
304
$post_ref = $array;
305
} else {
306
$post_ref = $this->prep_for_form($row['postdata']);
307
}
308
}
309
}
310
}
311
}
312
313
314
/**
315
* Executes the Validation routines
316
*
317
* @param array
318
* @param array
319
* @param mixed
320
* @param integer
321
* @return mixed
322
*/
323
private function _execute($row, $rules, $postdata = NULL, $cycles = 0) {
324
// If the $this->_post data is an array we will run a recursive call
325
if (is_array($postdata)) {
326
foreach ($postdata as $key => $val) {
327
$this->_execute($row, $rules, $val, $cycles);
328
$cycles++;
329
}
330
return;
331
}
332
333
// If the field is blank, but NOT required, no further tests are necessary
334
if ( ! in_array('required', $rules) && is_null($postdata)) {
335
return;
336
}
337
338
// Isset Test. Typically this rule will only apply to checkboxes.
339
if (is_null($postdata)) {
340
if (in_array('isset', $rules, TRUE) || in_array('required', $rules)) {
341
// Set the message type
342
$type = (in_array('required', $rules)) ? 'required' : 'isset';
343
344
if ( ! isset($this->_error_messages[$type])) {
345
$line = 'The error message field was not set';
346
} else {
347
$line = $this->_error_messages[$type];
348
}
349
350
// Build the error message
351
$message = sprintf($line, $row['label']);
352
353
// Save the error message
354
$this->_field_data[$row['field']]['error'] = $message;
355
356
if ( ! isset($this->_error_array[$row['field']])) {
357
$this->_error_array[$row['field']] = $message;
358
}
359
}
360
return;
361
}
362
363
// Cycle through each rule and run it
364
foreach ($rules as $rule) {
365
$_in_array = FALSE;
366
367
// We set the $postdata variable with the current data in our master array so that
368
// each cycle of the loop is dealing with the processed data from the last cycle
369
if ($row['is_array'] == TRUE && is_array($this->_field_data[$row['field']]['postdata'])) {
370
// We shouldn't need this safety, but just in case there isn't an array index
371
// associated with this cycle we'll bail out
372
if ( ! isset($this->_field_data[$row['field']]['postdata'][$cycles])) {
373
continue;
374
}
375
376
$postdata = $this->_field_data[$row['field']]['postdata'][$cycles];
377
$_in_array = TRUE;
378
} else {
379
$postdata = $this->_field_data[$row['field']]['postdata'];
380
}
381
382
// Strip the parameter (if exists) from the rule
383
// Rules can contain a parameter: max_length[5]
384
$param = FALSE;
385
if (preg_match("/(.*?)\[(.*?)\]/", $rule, $match)) {
386
$rule = $match[1];
387
$param = $match[2];
388
}
389
390
// Call the function that corresponds to the rule
391
if ( ! method_exists($this, $rule)) {
392
// If our own wrapper function doesn't exist we see if a native PHP function does.
393
// Users can use any native PHP function call that has one param.
394
if (function_exists($rule)) {
395
$result = $rule($postdata);
396
397
if ($_in_array == TRUE) {
398
$this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
399
} else {
400
$this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
401
}
402
}
403
404
continue;
405
}
406
407
$result = $this->$rule($postdata, $param);
408
409
if ($_in_array == TRUE) {
410
$this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
411
} else {
412
$this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
413
}
414
415
// Did the rule test negatively? If so, grab the error.
416
if ($result === FALSE) {
417
if ( ! isset($this->_error_messages[$rule])) {
418
$line = 'Unable to access an error message corresponding to your field name.';
419
} else {
420
$line = $this->_error_messages[$rule];
421
}
422
423
// Is the parameter we are inserting into the error message the name
424
// of another field? If so we need to grab its "field label"
425
if (isset($this->_field_data[$param]) && isset($this->_field_data[$param]['label'])) {
426
$param = $this->_field_data[$param]['label'];
427
}
428
429
// Build the error message
430
$message = sprintf($line, $row['label'], $param);
431
432
// Save the error message
433
$this->_field_data[$row['field']]['error'] = $message;
434
435
if ( ! isset($this->_error_array[$row['field']])) {
436
$this->_error_array[$row['field']] = $message;
437
}
438
439
return;
440
}
441
}
442
}
443
444
445
/**
446
* Get the value from a form
447
*
448
* Permits you to repopulate a form field with the value it was submitted
449
* with, or, if that value doesn't exist, with the default
450
*
451
* @param string the field name
452
* @param string
453
* @return void
454
*/
455
public function set_value($field = '', $default = '') {
456
if ( ! isset($this->_field_data[$field])) {
457
return $default;
458
}
459
460
return $this->_field_data[$field]['postdata'];
461
}
462
463
464
/**
465
* Set Select
466
*
467
* Enables pull-down lists to be set to the value the user
468
* selected in the event of an error
469
*
470
* @param string
471
* @param string
472
* @return string
473
*/
474
public function set_select($field = '', $value = '', $default = FALSE) {
475
if ( ! isset($this->_field_data[$field]) || ! isset($this->_field_data[$field]['postdata'])) {
476
if ($default === TRUE && count($this->_field_data) === 0) {
477
return ' selected="selected"';
478
}
479
return '';
480
}
481
482
$field = $this->_field_data[$field]['postdata'];
483
484
if (is_array($field)) {
485
if ( ! in_array($value, $field)) {
486
return '';
487
}
488
} else {
489
if (($field == '' || $value == '') || ($field != $value)) {
490
return '';
491
}
492
}
493
494
return ' selected="selected"';
495
}
496
497
498
/**
499
* Set Radio
500
*
501
* Enables radio buttons to be set to the value the user
502
* selected in the event of an error
503
*
504
* @param string
505
* @param string
506
* @return string
507
*/
508
public function set_radio($field = '', $value = '', $default = FALSE) {
509
if ( ! isset($this->_field_data[$field]) || ! isset($this->_field_data[$field]['postdata'])) {
510
if ($default === TRUE && count($this->_field_data) === 0) {
511
return ' checked="checked"';
512
}
513
return '';
514
}
515
516
$field = $this->_field_data[$field]['postdata'];
517
518
if (is_array($field)) {
519
if ( ! in_array($value, $field)) {
520
return '';
521
}
522
} else {
523
if (($field == '' || $value == '') || ($field != $value)) {
524
return '';
525
}
526
}
527
528
return ' checked="checked"';
529
}
530
531
532
/**
533
* Set Checkbox
534
*
535
* Enables checkboxes to be set to the value the user
536
* selected in the event of an error
537
*
538
* @param string
539
* @param string
540
* @return string
541
*/
542
public function set_checkbox($field = '', $value = '', $default = FALSE) {
543
if ( ! isset($this->_field_data[$field]) || ! isset($this->_field_data[$field]['postdata'])) {
544
if ($default === TRUE && count($this->_field_data) === 0) {
545
return ' checked="checked"';
546
}
547
return '';
548
}
549
550
$field = $this->_field_data[$field]['postdata'];
551
552
if (is_array($field)) {
553
if ( ! in_array($value, $field)) {
554
return '';
555
}
556
} else {
557
if (($field == '' || $value == '') || ($field != $value)) {
558
return '';
559
}
560
}
561
562
return ' checked="checked"';
563
}
564
565
566
/**
567
* Required
568
*
569
* @param string
570
* @return bool
571
*/
572
public function required($str) {
573
if ( ! is_array($str)) {
574
return (trim($str) == '') ? FALSE : TRUE;
575
} else {
576
return ( ! empty($str));
577
}
578
}
579
580
581
/**
582
* Match one field to another
583
*
584
* @param string
585
* @param field
586
* @return bool
587
*/
588
public function matches($str, $field) {
589
if ( ! isset($this->_post[$field])) {
590
return FALSE;
591
}
592
593
$field = $this->_post[$field];
594
595
return ($str !== $field) ? FALSE : TRUE;
596
}
597
598
599
/**
600
* Minimum Length
601
*
602
* @param string
603
* @param value
604
* @return bool
605
*/
606
public function min_length($str, $val) {
607
if (preg_match("/[^0-9]/", $val)) {
608
return FALSE;
609
}
610
611
if (function_exists('mb_strlen')) {
612
return (mb_strlen($str) < $val) ? FALSE : TRUE;
613
}
614
615
return (strlen($str) < $val) ? FALSE : TRUE;
616
}
617
618
619
/**
620
* Max Length
621
*
622
* @param string
623
* @param value
624
* @return bool
625
*/
626
public function max_length($str, $val) {
627
if (preg_match("/[^0-9]/", $val)) {
628
return FALSE;
629
}
630
631
if (function_exists('mb_strlen')) {
632
return (mb_strlen($str) > $val) ? FALSE : TRUE;
633
}
634
635
return (strlen($str) > $val) ? FALSE : TRUE;
636
}
637
638
639
/**
640
* Exact Length
641
*
642
* @param string
643
* @param value
644
* @return bool
645
*/
646
public function exact_length($str, $val) {
647
if (preg_match("/[^0-9]/", $val)) {
648
return FALSE;
649
}
650
651
if (function_exists('mb_strlen')) {
652
return (mb_strlen($str) != $val) ? FALSE : TRUE;
653
}
654
655
return (strlen($str) != $val) ? FALSE : TRUE;
656
}
657
658
/**
659
* Form Token check
660
*
661
* @param string
662
* @param value
663
* @return bool
664
*/
665
public function form_token($str, $val) {
666
return ($str !== $val) ? FALSE : TRUE;
667
}
668
669
/**
670
* Value equals $val
671
*
672
* @param string
673
* @param value
674
* @return bool
675
*/
676
public function equals($str, $val) {
677
return ($str !== $val) ? FALSE : TRUE;
678
}
679
680
/**
681
* Valid Email
682
*
683
* @param string
684
* @return bool
685
*/
686
public function valid_email($str) {
687
return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE;
688
}
689
690
691
/**
692
* Valid Emails
693
*
694
* @param string
695
* @return bool
696
*/
697
public function valid_emails($str) {
698
if (strpos($str, ',') === FALSE) {
699
return $this->valid_email(trim($str));
700
}
701
702
foreach (explode(',', $str) as $email) {
703
if (trim($email) != '' && $this->valid_email(trim($email)) === FALSE) {
704
return FALSE;
705
}
706
}
707
708
return TRUE;
709
}
710
711
712
/**
713
* Alpha
714
*
715
* @param string
716
* @return bool
717
*/
718
public function alpha($str) {
719
return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE;
720
}
721
722
723
/**
724
* Alpha-numeric
725
*
726
* @access public
727
* @param string
728
* @return bool
729
*/
730
public function alpha_numeric($str) {
731
return ( ! preg_match("/^([a-z0-9])+$/i", $str)) ? FALSE : TRUE;
732
}
733
734
735
/**
736
* Alpha-numeric with underscores and dashes
737
*
738
* @param string
739
* @return bool
740
*/
741
public function alpha_dash($str) {
742
return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str)) ? FALSE : TRUE;
743
}
744
745
746
/**
747
* Numeric
748
*
749
* @param string
750
* @return bool
751
*/
752
public function numeric($str) {
753
return (bool)preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str);
754
}
755
756
757
/**
758
* Finds whether a variable is a number or a numeric string
759
*
760
* @param string
761
* @return bool
762
*/
763
public function is_numeric($str) {
764
return ( ! is_numeric($str)) ? FALSE : TRUE;
765
}
766
767
768
/**
769
* Integer
770
*
771
* @param string
772
* @return bool
773
*/
774
public function is_integer($str) {
775
return (bool)preg_match( '/^[\-+]?[0-9]+$/', $str);
776
}
777
778
/**
779
* Decimal number
780
*
781
* @param string
782
* @return bool
783
*/
784
public function decimal($str) {
785
return (bool) preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str);
786
}
787
788
/**
789
* Greather than
790
*
791
* @param string
792
* @return bool
793
*/
794
public function greater_than($str, $min) {
795
if ( ! is_numeric($str)) {
796
return FALSE;
797
}
798
return $str > $min;
799
}
800
801
802
/**
803
* Less than
804
*
805
* @param string
806
* @return bool
807
*/
808
public function less_than($str, $max) {
809
if ( ! is_numeric($str)) {
810
return FALSE;
811
}
812
return $str < $max;
813
}
814
815
/**
816
* Is a Natural number (0,1,2,3, etc.)
817
*
818
* @param string
819
* @return bool
820
*/
821
public function is_natural($str) {
822
return (bool)preg_match( '/^[0-9]+$/', $str);
823
}
824
825
826
/**
827
* Is a Natural number, but not a zero (1,2,3, etc.)
828
*
829
* @param string
830
* @return bool
831
*/
832
public function is_natural_no_zero($str) {
833
if ( ! preg_match( '/^[0-9]+$/', $str)) {
834
return FALSE;
835
}
836
837
if ($str == 0) {
838
return FALSE;
839
}
840
return TRUE;
841
}
842
843
844
/**
845
* Valid Base64
846
*
847
* Tests a string for characters outside of the Base64 alphabet
848
* as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045
849
*
850
* @param string
851
* @return bool
852
*/
853
public function valid_base64($str) {
854
return (bool) ! preg_match('/[^a-zA-Z0-9\/\+=]/', $str);
855
}
856
857
858
/**
859
* Prep data for form
860
*
861
* This function allows HTML to be safely shown in a form.
862
* Special characters are converted.
863
*
864
* @param string
865
* @return string
866
*/
867
public function prep_for_form($data = '') {
868
if (is_array($data)) {
869
foreach ($data as $key => $val) {
870
$data[$key] = $this->prep_for_form($val);
871
}
872
return $data;
873
}
874
875
if ($this->_safe_form_data == FALSE || $data === '') {
876
return $data;
877
}
878
879
return str_replace(array("'", '"', '<', '>'), array("'", """, '<', '>'), stripslashes($data));
880
}
881
882
883
/**
884
* Convert PHP tags to entities
885
*
886
* @param string
887
* @return string
888
*/
889
public function encode_php_tags($str) {
890
return str_replace(array('<?php', '<?PHP', '<?', '?>'), array('<?php', '<?PHP', '<?', '?>'), $str);
891
}
892
893
}
894
895
/* End of file: ./system/libraries/form_validation/form_validation_library.php */
Page URI: http://www.infopotato.com/index.php/code/library/form_validation/
