Subversion-Projekte lars-tiefland.ci

Revision

Revision 2107 | Revision 2254 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
2242 lars 9
 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology
68 lars 10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
2242 lars 32
 * @copyright	Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
68 lars 33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * Form Validation Class
42
 *
43
 * @package		CodeIgniter
44
 * @subpackage	Libraries
45
 * @category	Validation
46
 * @author		EllisLab Dev Team
47
 * @link		https://codeigniter.com/user_guide/libraries/form_validation.html
48
 */
49
class CI_Form_validation {
50
 
51
	/**
52
	 * Reference to the CodeIgniter instance
53
	 *
54
	 * @var object
55
	 */
56
	protected $CI;
57
 
58
	/**
59
	 * Validation data for the current form submission
60
	 *
61
	 * @var array
62
	 */
63
	protected $_field_data		= array();
64
 
65
	/**
66
	 * Validation rules for the current form
67
	 *
68
	 * @var array
69
	 */
70
	protected $_config_rules	= array();
71
 
72
	/**
73
	 * Array of validation errors
74
	 *
75
	 * @var array
76
	 */
77
	protected $_error_array		= array();
78
 
79
	/**
80
	 * Array of custom error messages
81
	 *
82
	 * @var array
83
	 */
84
	protected $_error_messages	= array();
85
 
86
	/**
87
	 * Start tag for error wrapping
88
	 *
89
	 * @var string
90
	 */
91
	protected $_error_prefix	= '<p>';
92
 
93
	/**
94
	 * End tag for error wrapping
95
	 *
96
	 * @var string
97
	 */
98
	protected $_error_suffix	= '</p>';
99
 
100
	/**
101
	 * Custom error message
102
	 *
103
	 * @var string
104
	 */
105
	protected $error_string		= '';
106
 
107
	/**
108
	 * Whether the form data has been validated as safe
109
	 *
110
	 * @var bool
111
	 */
112
	protected $_safe_form_data	= FALSE;
113
 
114
	/**
115
	 * Custom data to validate
116
	 *
117
	 * @var array
118
	 */
119
	public $validation_data	= array();
120
 
121
	/**
122
	 * Initialize Form_Validation class
123
	 *
124
	 * @param	array	$rules
125
	 * @return	void
126
	 */
127
	public function __construct($rules = array())
128
	{
129
		$this->CI =& get_instance();
130
 
131
		// applies delimiters set in config file.
132
		if (isset($rules['error_prefix']))
133
		{
134
			$this->_error_prefix = $rules['error_prefix'];
135
			unset($rules['error_prefix']);
136
		}
137
		if (isset($rules['error_suffix']))
138
		{
139
			$this->_error_suffix = $rules['error_suffix'];
140
			unset($rules['error_suffix']);
141
		}
142
 
143
		// Validation rules can be stored in a config file.
144
		$this->_config_rules = $rules;
145
 
146
		// Automatically load the form helper
147
		$this->CI->load->helper('form');
148
 
149
		log_message('info', 'Form Validation Class Initialized');
150
	}
151
 
152
	// --------------------------------------------------------------------
153
 
154
	/**
155
	 * Set Rules
156
	 *
157
	 * This function takes an array of field names and validation
158
	 * rules as input, any custom error messages, validates the info,
159
	 * and stores it
160
	 *
161
	 * @param	mixed	$field
162
	 * @param	string	$label
163
	 * @param	mixed	$rules
164
	 * @param	array	$errors
165
	 * @return	CI_Form_validation
166
	 */
167
	public function set_rules($field, $label = '', $rules = array(), $errors = array())
168
	{
169
		// No reason to set rules if we have no POST data
170
		// or a validation array has not been specified
171
		if ($this->CI->input->method() !== 'post' && empty($this->validation_data))
172
		{
173
			return $this;
174
		}
175
 
176
		// If an array was passed via the first parameter instead of individual string
177
		// values we cycle through it and recursively call this function.
178
		if (is_array($field))
179
		{
180
			foreach ($field as $row)
181
			{
182
				// Houston, we have a problem...
183
				if ( ! isset($row['field'], $row['rules']))
184
				{
185
					continue;
186
				}
187
 
188
				// If the field label wasn't passed we use the field name
189
				$label = isset($row['label']) ? $row['label'] : $row['field'];
190
 
191
				// Add the custom error message array
192
				$errors = (isset($row['errors']) && is_array($row['errors'])) ? $row['errors'] : array();
193
 
194
				// Here we go!
195
				$this->set_rules($row['field'], $label, $row['rules'], $errors);
196
			}
197
 
198
			return $this;
199
		}
200
 
201
		// No fields or no rules? Nothing to do...
202
		if ( ! is_string($field) OR $field === '' OR empty($rules))
203
		{
204
			return $this;
205
		}
206
		elseif ( ! is_array($rules))
207
		{
208
			// BC: Convert pipe-separated rules string to an array
209
			if ( ! is_string($rules))
210
			{
211
				return $this;
212
			}
213
 
214
			$rules = preg_split('/\|(?![^\[]*\])/', $rules);
215
		}
216
 
217
		// If the field label wasn't passed we use the field name
218
		$label = ($label === '') ? $field : $label;
219
 
220
		$indexes = array();
221
 
222
		// Is the field name an array? If it is an array, we break it apart
223
		// into its components so that we can fetch the corresponding POST data later
224
		if (($is_array = (bool) preg_match_all('/\[(.*?)\]/', $field, $matches)) === TRUE)
225
		{
226
			sscanf($field, '%[^[][', $indexes[0]);
227
 
228
			for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
229
			{
230
				if ($matches[1][$i] !== '')
231
				{
232
					$indexes[] = $matches[1][$i];
233
				}
234
			}
235
		}
236
 
237
		// Build our master array
238
		$this->_field_data[$field] = array(
239
			'field'		=> $field,
240
			'label'		=> $label,
241
			'rules'		=> $rules,
242
			'errors'	=> $errors,
243
			'is_array'	=> $is_array,
244
			'keys'		=> $indexes,
245
			'postdata'	=> NULL,
246
			'error'		=> ''
247
		);
248
 
249
		return $this;
250
	}
251
 
252
	// --------------------------------------------------------------------
253
 
254
	/**
255
	 * By default, form validation uses the $_POST array to validate
256
	 *
257
	 * If an array is set through this method, then this array will
258
	 * be used instead of the $_POST array
259
	 *
260
	 * Note that if you are validating multiple arrays, then the
261
	 * reset_validation() function should be called after validating
262
	 * each array due to the limitations of CI's singleton
263
	 *
264
	 * @param	array	$data
265
	 * @return	CI_Form_validation
266
	 */
267
	public function set_data(array $data)
268
	{
269
		if ( ! empty($data))
270
		{
271
			$this->validation_data = $data;
272
		}
273
 
274
		return $this;
275
	}
276
 
277
	// --------------------------------------------------------------------
278
 
279
	/**
280
	 * Set Error Message
281
	 *
282
	 * Lets users set their own error messages on the fly. Note:
283
	 * The key name has to match the function name that it corresponds to.
284
	 *
285
	 * @param	array
286
	 * @param	string
287
	 * @return	CI_Form_validation
288
	 */
289
	public function set_message($lang, $val = '')
290
	{
291
		if ( ! is_array($lang))
292
		{
293
			$lang = array($lang => $val);
294
		}
295
 
296
		$this->_error_messages = array_merge($this->_error_messages, $lang);
297
		return $this;
298
	}
299
 
300
	// --------------------------------------------------------------------
301
 
302
	/**
303
	 * Set The Error Delimiter
304
	 *
305
	 * Permits a prefix/suffix to be added to each error message
306
	 *
307
	 * @param	string
308
	 * @param	string
309
	 * @return	CI_Form_validation
310
	 */
311
	public function set_error_delimiters($prefix = '<p>', $suffix = '</p>')
312
	{
313
		$this->_error_prefix = $prefix;
314
		$this->_error_suffix = $suffix;
315
		return $this;
316
	}
317
 
318
	// --------------------------------------------------------------------
319
 
320
	/**
321
	 * Get Error Message
322
	 *
323
	 * Gets the error message associated with a particular field
324
	 *
325
	 * @param	string	$field	Field name
326
	 * @param	string	$prefix	HTML start tag
327
	 * @param 	string	$suffix	HTML end tag
328
	 * @return	string
329
	 */
330
	public function error($field, $prefix = '', $suffix = '')
331
	{
332
		if (empty($this->_field_data[$field]['error']))
333
		{
334
			return '';
335
		}
336
 
337
		if ($prefix === '')
338
		{
339
			$prefix = $this->_error_prefix;
340
		}
341
 
342
		if ($suffix === '')
343
		{
344
			$suffix = $this->_error_suffix;
345
		}
346
 
347
		return $prefix.$this->_field_data[$field]['error'].$suffix;
348
	}
349
 
350
	// --------------------------------------------------------------------
351
 
352
	/**
353
	 * Get Array of Error Messages
354
	 *
355
	 * Returns the error messages as an array
356
	 *
357
	 * @return	array
358
	 */
359
	public function error_array()
360
	{
361
		return $this->_error_array;
362
	}
363
 
364
	// --------------------------------------------------------------------
365
 
366
	/**
367
	 * Error String
368
	 *
369
	 * Returns the error messages as a string, wrapped in the error delimiters
370
	 *
371
	 * @param	string
372
	 * @param	string
373
	 * @return	string
374
	 */
375
	public function error_string($prefix = '', $suffix = '')
376
	{
377
		// No errors, validation passes!
378
		if (count($this->_error_array) === 0)
379
		{
380
			return '';
381
		}
382
 
383
		if ($prefix === '')
384
		{
385
			$prefix = $this->_error_prefix;
386
		}
387
 
388
		if ($suffix === '')
389
		{
390
			$suffix = $this->_error_suffix;
391
		}
392
 
393
		// Generate the error string
394
		$str = '';
395
		foreach ($this->_error_array as $val)
396
		{
397
			if ($val !== '')
398
			{
399
				$str .= $prefix.$val.$suffix."\n";
400
			}
401
		}
402
 
403
		return $str;
404
	}
405
 
406
	// --------------------------------------------------------------------
407
 
408
	/**
409
	 * Run the Validator
410
	 *
411
	 * This function does all the work.
412
	 *
413
	 * @param	string	$group
414
	 * @return	bool
415
	 */
416
	public function run($group = '')
417
	{
418
		$validation_array = empty($this->validation_data)
419
			? $_POST
420
			: $this->validation_data;
421
 
422
		// Does the _field_data array containing the validation rules exist?
423
		// If not, we look to see if they were assigned via a config file
424
		if (count($this->_field_data) === 0)
425
		{
426
			// No validation rules?  We're done...
427
			if (count($this->_config_rules) === 0)
428
			{
429
				return FALSE;
430
			}
431
 
432
			if (empty($group))
433
			{
434
				// Is there a validation rule for the particular URI being accessed?
435
				$group = trim($this->CI->uri->ruri_string(), '/');
436
				isset($this->_config_rules[$group]) OR $group = $this->CI->router->class.'/'.$this->CI->router->method;
437
			}
438
 
439
			$this->set_rules(isset($this->_config_rules[$group]) ? $this->_config_rules[$group] : $this->_config_rules);
440
 
441
			// Were we able to set the rules correctly?
442
			if (count($this->_field_data) === 0)
443
			{
444
				log_message('debug', 'Unable to find validation rules');
445
				return FALSE;
446
			}
447
		}
448
 
449
		// Load the language file containing error messages
450
		$this->CI->lang->load('form_validation');
451
 
452
		// Cycle through the rules for each field and match the corresponding $validation_data item
453
		foreach ($this->_field_data as $field => &$row)
454
		{
455
			// Fetch the data from the validation_data array item and cache it in the _field_data array.
456
			// Depending on whether the field name is an array or a string will determine where we get it from.
457
			if ($row['is_array'] === TRUE)
458
			{
459
				$this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']);
460
			}
461
			elseif (isset($validation_array[$field]))
462
			{
463
				$this->_field_data[$field]['postdata'] = $validation_array[$field];
464
			}
465
		}
466
 
467
		// Execute validation rules
468
		// Note: A second foreach (for now) is required in order to avoid false-positives
469
		//	 for rules like 'matches', which correlate to other validation fields.
470
		foreach ($this->_field_data as $field => &$row)
471
		{
472
			// Don't try to validate if we have no rules set
473
			if (empty($row['rules']))
474
			{
475
				continue;
476
			}
477
 
478
			$this->_execute($row, $row['rules'], $row['postdata']);
479
		}
480
 
481
		// Did we end up with any errors?
482
		$total_errors = count($this->_error_array);
483
		if ($total_errors > 0)
484
		{
485
			$this->_safe_form_data = TRUE;
486
		}
487
 
488
		// Now we need to re-set the POST data with the new, processed data
489
		empty($this->validation_data) && $this->_reset_post_array();
490
 
491
		return ($total_errors === 0);
492
	}
493
 
494
	// --------------------------------------------------------------------
495
 
496
	/**
497
	 * Prepare rules
498
	 *
499
	 * Re-orders the provided rules in order of importance, so that
500
	 * they can easily be executed later without weird checks ...
501
	 *
502
	 * "Callbacks" are given the highest priority (always called),
503
	 * followed by 'required' (called if callbacks didn't fail),
504
	 * and then every next rule depends on the previous one passing.
505
	 *
506
	 * @param	array	$rules
507
	 * @return	array
508
	 */
509
	protected function _prepare_rules($rules)
510
	{
511
		$new_rules = array();
512
		$callbacks = array();
513
 
514
		foreach ($rules as &$rule)
515
		{
516
			// Let 'required' always be the first (non-callback) rule
517
			if ($rule === 'required')
518
			{
519
				array_unshift($new_rules, 'required');
520
			}
521
			// 'isset' is a kind of a weird alias for 'required' ...
522
			elseif ($rule === 'isset' && (empty($new_rules) OR $new_rules[0] !== 'required'))
523
			{
524
				array_unshift($new_rules, 'isset');
525
			}
526
			// The old/classic 'callback_'-prefixed rules
527
			elseif (is_string($rule) && strncmp('callback_', $rule, 9) === 0)
528
			{
529
				$callbacks[] = $rule;
530
			}
531
			// Proper callables
532
			elseif (is_callable($rule))
533
			{
534
				$callbacks[] = $rule;
535
			}
536
			// "Named" callables; i.e. array('name' => $callable)
537
			elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))
538
			{
539
				$callbacks[] = $rule;
540
			}
541
			// Everything else goes at the end of the queue
542
			else
543
			{
544
				$new_rules[] = $rule;
545
			}
546
		}
547
 
548
		return array_merge($callbacks, $new_rules);
549
	}
550
 
551
	// --------------------------------------------------------------------
552
 
553
	/**
554
	 * Traverse a multidimensional $_POST array index until the data is found
555
	 *
556
	 * @param	array
557
	 * @param	array
558
	 * @param	int
559
	 * @return	mixed
560
	 */
561
	protected function _reduce_array($array, $keys, $i = 0)
562
	{
563
		if (is_array($array) && isset($keys[$i]))
564
		{
565
			return isset($array[$keys[$i]]) ? $this->_reduce_array($array[$keys[$i]], $keys, ($i+1)) : NULL;
566
		}
567
 
568
		// NULL must be returned for empty fields
569
		return ($array === '') ? NULL : $array;
570
	}
571
 
572
	// --------------------------------------------------------------------
573
 
574
	/**
575
	 * Re-populate the _POST array with our finalized and processed data
576
	 *
577
	 * @return	void
578
	 */
579
	protected function _reset_post_array()
580
	{
581
		foreach ($this->_field_data as $field => $row)
582
		{
583
			if ($row['postdata'] !== NULL)
584
			{
585
				if ($row['is_array'] === FALSE)
586
				{
587
					isset($_POST[$field]) && $_POST[$field] = $row['postdata'];
588
				}
589
				else
590
				{
591
					// start with a reference
592
					$post_ref =& $_POST;
593
 
594
					// before we assign values, make a reference to the right POST key
595
					if (count($row['keys']) === 1)
596
					{
597
						$post_ref =& $post_ref[current($row['keys'])];
598
					}
599
					else
600
					{
601
						foreach ($row['keys'] as $val)
602
						{
603
							$post_ref =& $post_ref[$val];
604
						}
605
					}
606
 
607
					$post_ref = $row['postdata'];
608
				}
609
			}
610
		}
611
	}
612
 
613
	// --------------------------------------------------------------------
614
 
615
	/**
616
	 * Executes the Validation routines
617
	 *
618
	 * @param	array
619
	 * @param	array
620
	 * @param	mixed
621
	 * @param	int
622
	 * @return	mixed
623
	 */
624
	protected function _execute($row, $rules, $postdata = NULL, $cycles = 0)
625
	{
626
		// If the $_POST data is an array we will run a recursive call
627
		//
628
		// Note: We MUST check if the array is empty or not!
629
		//       Otherwise empty arrays will always pass validation.
630
		if (is_array($postdata) && ! empty($postdata))
631
		{
632
			foreach ($postdata as $key => $val)
633
			{
634
				$this->_execute($row, $rules, $val, $key);
635
			}
636
 
637
			return;
638
		}
639
 
640
		$rules = $this->_prepare_rules($rules);
641
		foreach ($rules as $rule)
642
		{
643
			$_in_array = FALSE;
644
 
645
			// We set the $postdata variable with the current data in our master array so that
646
			// each cycle of the loop is dealing with the processed data from the last cycle
647
			if ($row['is_array'] === TRUE && is_array($this->_field_data[$row['field']]['postdata']))
648
			{
649
				// We shouldn't need this safety, but just in case there isn't an array index
650
				// associated with this cycle we'll bail out
651
				if ( ! isset($this->_field_data[$row['field']]['postdata'][$cycles]))
652
				{
653
					continue;
654
				}
655
 
656
				$postdata = $this->_field_data[$row['field']]['postdata'][$cycles];
657
				$_in_array = TRUE;
658
			}
659
			else
660
			{
661
				// If we get an array field, but it's not expected - then it is most likely
662
				// somebody messing with the form on the client side, so we'll just consider
663
				// it an empty field
664
				$postdata = is_array($this->_field_data[$row['field']]['postdata'])
665
					? NULL
666
					: $this->_field_data[$row['field']]['postdata'];
667
			}
668
 
669
			// Is the rule a callback?
670
			$callback = $callable = FALSE;
671
			if (is_string($rule))
672
			{
673
				if (strpos($rule, 'callback_') === 0)
674
				{
675
					$rule = substr($rule, 9);
676
					$callback = TRUE;
677
				}
678
			}
679
			elseif (is_callable($rule))
680
			{
681
				$callable = TRUE;
682
			}
683
			elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))
684
			{
685
				// We have a "named" callable, so save the name
686
				$callable = $rule[0];
687
				$rule = $rule[1];
688
			}
689
 
690
			// Strip the parameter (if exists) from the rule
691
			// Rules can contain a parameter: max_length[5]
692
			$param = FALSE;
693
			if ( ! $callable && preg_match('/(.*?)\[(.*)\]/', $rule, $match))
694
			{
695
				$rule = $match[1];
696
				$param = $match[2];
697
			}
698
 
699
			// Ignore empty, non-required inputs with a few exceptions ...
700
			if (
701
				($postdata === NULL OR $postdata === '')
702
				&& $callback === FALSE
703
				&& $callable === FALSE
704
				&& ! in_array($rule, array('required', 'isset', 'matches'), TRUE)
705
			)
706
			{
707
				continue;
708
			}
709
 
710
			// Call the function that corresponds to the rule
711
			if ($callback OR $callable !== FALSE)
712
			{
713
				if ($callback)
714
				{
715
					if ( ! method_exists($this->CI, $rule))
716
					{
717
						log_message('debug', 'Unable to find callback validation rule: '.$rule);
718
						$result = FALSE;
719
					}
720
					else
721
					{
722
						// Run the function and grab the result
723
						$result = $this->CI->$rule($postdata, $param);
724
					}
725
				}
726
				else
727
				{
728
					$result = is_array($rule)
729
						? $rule[0]->{$rule[1]}($postdata)
730
						: $rule($postdata);
731
 
732
					// Is $callable set to a rule name?
733
					if ($callable !== FALSE)
734
					{
735
						$rule = $callable;
736
					}
737
				}
738
 
739
				// Re-assign the result to the master data array
740
				if ($_in_array === TRUE)
741
				{
742
					$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
743
				}
744
				else
745
				{
746
					$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
747
				}
748
			}
749
			elseif ( ! method_exists($this, $rule))
750
			{
751
				// If our own wrapper function doesn't exist we see if a native PHP function does.
752
				// Users can use any native PHP function call that has one param.
753
				if (function_exists($rule))
754
				{
755
					// Native PHP functions issue warnings if you pass them more parameters than they use
756
					$result = ($param !== FALSE) ? $rule($postdata, $param) : $rule($postdata);
757
 
758
					if ($_in_array === TRUE)
759
					{
760
						$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
761
					}
762
					else
763
					{
764
						$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
765
					}
766
				}
767
				else
768
				{
769
					log_message('debug', 'Unable to find validation rule: '.$rule);
770
					$result = FALSE;
771
				}
772
			}
773
			else
774
			{
775
				$result = $this->$rule($postdata, $param);
776
 
777
				if ($_in_array === TRUE)
778
				{
779
					$this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
780
				}
781
				else
782
				{
783
					$this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
784
				}
785
			}
786
 
787
			// Did the rule test negatively? If so, grab the error.
788
			if ($result === FALSE)
789
			{
790
				// Callable rules might not have named error messages
791
				if ( ! is_string($rule))
792
				{
793
					$line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)';
794
				}
795
				else
796
				{
797
					$line = $this->_get_error_message($rule, $row['field']);
798
				}
799
 
800
				// Is the parameter we are inserting into the error message the name
801
				// of another field? If so we need to grab its "field label"
802
				if (isset($this->_field_data[$param], $this->_field_data[$param]['label']))
803
				{
804
					$param = $this->_translate_fieldname($this->_field_data[$param]['label']);
805
				}
806
 
807
				// Build the error message
808
				$message = $this->_build_error_msg($line, $this->_translate_fieldname($row['label']), $param);
809
 
810
				// Save the error message
811
				$this->_field_data[$row['field']]['error'] = $message;
812
 
813
				if ( ! isset($this->_error_array[$row['field']]))
814
				{
815
					$this->_error_array[$row['field']] = $message;
816
				}
817
 
818
				return;
819
			}
820
		}
821
	}
822
 
823
	// --------------------------------------------------------------------
824
 
825
	/**
826
	 * Get the error message for the rule
827
	 *
828
	 * @param 	string $rule 	The rule name
829
	 * @param 	string $field	The field name
830
	 * @return 	string
831
	 */
832
	protected function _get_error_message($rule, $field)
833
	{
834
		// check if a custom message is defined through validation config row.
835
		if (isset($this->_field_data[$field]['errors'][$rule]))
836
		{
837
			return $this->_field_data[$field]['errors'][$rule];
838
		}
839
		// check if a custom message has been set using the set_message() function
840
		elseif (isset($this->_error_messages[$rule]))
841
		{
842
			return $this->_error_messages[$rule];
843
		}
844
		elseif (FALSE !== ($line = $this->CI->lang->line('form_validation_'.$rule)))
845
		{
846
			return $line;
847
		}
848
		// DEPRECATED support for non-prefixed keys, lang file again
849
		elseif (FALSE !== ($line = $this->CI->lang->line($rule, FALSE)))
850
		{
851
			return $line;
852
		}
853
 
854
		return $this->CI->lang->line('form_validation_error_message_not_set').'('.$rule.')';
855
	}
856
 
857
	// --------------------------------------------------------------------
858
 
859
	/**
860
	 * Translate a field name
861
	 *
862
	 * @param	string	the field name
863
	 * @return	string
864
	 */
865
	protected function _translate_fieldname($fieldname)
866
	{
867
		// Do we need to translate the field name? We look for the prefix 'lang:' to determine this
868
		// If we find one, but there's no translation for the string - just return it
869
		if (sscanf($fieldname, 'lang:%s', $line) === 1 && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE)))
870
		{
871
			return $line;
872
		}
873
 
874
		return $fieldname;
875
	}
876
 
877
	// --------------------------------------------------------------------
878
 
879
	/**
880
	 * Build an error message using the field and param.
881
	 *
882
	 * @param	string	The error message line
883
	 * @param	string	A field's human name
884
	 * @param	mixed	A rule's optional parameter
885
	 * @return	string
886
	 */
887
	protected function _build_error_msg($line, $field = '', $param = '')
888
	{
889
		// Check for %s in the string for legacy support.
890
		if (strpos($line, '%s') !== FALSE)
891
		{
892
			return sprintf($line, $field, $param);
893
		}
894
 
895
		return str_replace(array('{field}', '{param}'), array($field, $param), $line);
896
	}
897
 
898
	// --------------------------------------------------------------------
899
 
900
	/**
901
	 * Checks if the rule is present within the validator
902
	 *
903
	 * Permits you to check if a rule is present within the validator
904
	 *
905
	 * @param	string	the field name
906
	 * @return	bool
907
	 */
908
	public function has_rule($field)
909
	{
910
		return isset($this->_field_data[$field]);
911
	}
912
 
913
	// --------------------------------------------------------------------
914
 
915
	/**
916
	 * Get the value from a form
917
	 *
918
	 * Permits you to repopulate a form field with the value it was submitted
919
	 * with, or, if that value doesn't exist, with the default
920
	 *
921
	 * @param	string	the field name
922
	 * @param	string
923
	 * @return	string
924
	 */
925
	public function set_value($field = '', $default = '')
926
	{
927
		if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))
928
		{
929
			return $default;
930
		}
931
 
932
		// If the data is an array output them one at a time.
933
		//	E.g: form_input('name[]', set_value('name[]');
934
		if (is_array($this->_field_data[$field]['postdata']))
935
		{
936
			return array_shift($this->_field_data[$field]['postdata']);
937
		}
938
 
939
		return $this->_field_data[$field]['postdata'];
940
	}
941
 
942
	// --------------------------------------------------------------------
943
 
944
	/**
945
	 * Set Select
946
	 *
947
	 * Enables pull-down lists to be set to the value the user
948
	 * selected in the event of an error
949
	 *
950
	 * @param	string
951
	 * @param	string
952
	 * @param	bool
953
	 * @return	string
954
	 */
955
	public function set_select($field = '', $value = '', $default = FALSE)
956
	{
957
		if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))
958
		{
959
			return ($default === TRUE && count($this->_field_data) === 0) ? ' selected="selected"' : '';
960
		}
961
 
962
		$field = $this->_field_data[$field]['postdata'];
963
		$value = (string) $value;
964
		if (is_array($field))
965
		{
966
			// Note: in_array('', array(0)) returns TRUE, do not use it
967
			foreach ($field as &$v)
968
			{
969
				if ($value === $v)
970
				{
971
					return ' selected="selected"';
972
				}
973
			}
974
 
975
			return '';
976
		}
977
		elseif (($field === '' OR $value === '') OR ($field !== $value))
978
		{
979
			return '';
980
		}
981
 
982
		return ' selected="selected"';
983
	}
984
 
985
	// --------------------------------------------------------------------
986
 
987
	/**
988
	 * Set Radio
989
	 *
990
	 * Enables radio buttons to be set to the value the user
991
	 * selected in the event of an error
992
	 *
993
	 * @param	string
994
	 * @param	string
995
	 * @param	bool
996
	 * @return	string
997
	 */
998
	public function set_radio($field = '', $value = '', $default = FALSE)
999
	{
1000
		if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))
1001
		{
1002
			return ($default === TRUE && count($this->_field_data) === 0) ? ' checked="checked"' : '';
1003
		}
1004
 
1005
		$field = $this->_field_data[$field]['postdata'];
1006
		$value = (string) $value;
1007
		if (is_array($field))
1008
		{
1009
			// Note: in_array('', array(0)) returns TRUE, do not use it
1010
			foreach ($field as &$v)
1011
			{
1012
				if ($value === $v)
1013
				{
1014
					return ' checked="checked"';
1015
				}
1016
			}
1017
 
1018
			return '';
1019
		}
1020
		elseif (($field === '' OR $value === '') OR ($field !== $value))
1021
		{
1022
			return '';
1023
		}
1024
 
1025
		return ' checked="checked"';
1026
	}
1027
 
1028
	// --------------------------------------------------------------------
1029
 
1030
	/**
1031
	 * Set Checkbox
1032
	 *
1033
	 * Enables checkboxes to be set to the value the user
1034
	 * selected in the event of an error
1035
	 *
1036
	 * @param	string
1037
	 * @param	string
1038
	 * @param	bool
1039
	 * @return	string
1040
	 */
1041
	public function set_checkbox($field = '', $value = '', $default = FALSE)
1042
	{
1043
		// Logic is exactly the same as for radio fields
1044
		return $this->set_radio($field, $value, $default);
1045
	}
1046
 
1047
	// --------------------------------------------------------------------
1048
 
1049
	/**
1050
	 * Required
1051
	 *
1052
	 * @param	string
1053
	 * @return	bool
1054
	 */
1055
	public function required($str)
1056
	{
1057
		return is_array($str)
1058
			? (empty($str) === FALSE)
1059
			: (trim($str) !== '');
1060
	}
1061
 
1062
	// --------------------------------------------------------------------
1063
 
1064
	/**
1065
	 * Performs a Regular Expression match test.
1066
	 *
1067
	 * @param	string
1068
	 * @param	string	regex
1069
	 * @return	bool
1070
	 */
1071
	public function regex_match($str, $regex)
1072
	{
1073
		return (bool) preg_match($regex, $str);
1074
	}
1075
 
1076
	// --------------------------------------------------------------------
1077
 
1078
	/**
1079
	 * Match one field to another
1080
	 *
1081
	 * @param	string	$str	string to compare against
1082
	 * @param	string	$field
1083
	 * @return	bool
1084
	 */
1085
	public function matches($str, $field)
1086
	{
1087
		return isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])
1088
			? ($str === $this->_field_data[$field]['postdata'])
1089
			: FALSE;
1090
	}
1091
 
1092
	// --------------------------------------------------------------------
1093
 
1094
	/**
1095
	 * Differs from another field
1096
	 *
1097
	 * @param	string
1098
	 * @param	string	field
1099
	 * @return	bool
1100
	 */
1101
	public function differs($str, $field)
1102
	{
1103
		return ! (isset($this->_field_data[$field]) && $this->_field_data[$field]['postdata'] === $str);
1104
	}
1105
 
1106
	// --------------------------------------------------------------------
1107
 
1108
	/**
1109
	 * Is Unique
1110
	 *
1111
	 * Check if the input value doesn't already exist
1112
	 * in the specified database field.
1113
	 *
1114
	 * @param	string	$str
1115
	 * @param	string	$field
1116
	 * @return	bool
1117
	 */
1118
	public function is_unique($str, $field)
1119
	{
1120
		sscanf($field, '%[^.].%[^.]', $table, $field);
1121
		return isset($this->CI->db)
1122
			? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
1123
			: FALSE;
1124
	}
1125
 
1126
	// --------------------------------------------------------------------
1127
 
1128
	/**
1129
	 * Minimum Length
1130
	 *
1131
	 * @param	string
1132
	 * @param	string
1133
	 * @return	bool
1134
	 */
1135
	public function min_length($str, $val)
1136
	{
1137
		if ( ! is_numeric($val))
1138
		{
1139
			return FALSE;
1140
		}
1141
 
1142
		return ($val <= mb_strlen($str));
1143
	}
1144
 
1145
	// --------------------------------------------------------------------
1146
 
1147
	/**
1148
	 * Max Length
1149
	 *
1150
	 * @param	string
1151
	 * @param	string
1152
	 * @return	bool
1153
	 */
1154
	public function max_length($str, $val)
1155
	{
1156
		if ( ! is_numeric($val))
1157
		{
1158
			return FALSE;
1159
		}
1160
 
1161
		return ($val >= mb_strlen($str));
1162
	}
1163
 
1164
	// --------------------------------------------------------------------
1165
 
1166
	/**
1167
	 * Exact Length
1168
	 *
1169
	 * @param	string
1170
	 * @param	string
1171
	 * @return	bool
1172
	 */
1173
	public function exact_length($str, $val)
1174
	{
1175
		if ( ! is_numeric($val))
1176
		{
1177
			return FALSE;
1178
		}
1179
 
1180
		return (mb_strlen($str) === (int) $val);
1181
	}
1182
 
1183
	// --------------------------------------------------------------------
1184
 
1185
	/**
1186
	 * Valid URL
1187
	 *
1188
	 * @param	string	$str
1189
	 * @return	bool
1190
	 */
1191
	public function valid_url($str)
1192
	{
1193
		if (empty($str))
1194
		{
1195
			return FALSE;
1196
		}
1197
		elseif (preg_match('/^(?:([^:]*)\:)?\/\/(.+)$/', $str, $matches))
1198
		{
1199
			if (empty($matches[2]))
1200
			{
1201
				return FALSE;
1202
			}
1257 lars 1203
			elseif ( ! in_array(strtolower($matches[1]), array('http', 'https'), TRUE))
68 lars 1204
			{
1205
				return FALSE;
1206
			}
1207
 
1208
			$str = $matches[2];
1209
		}
1210
 
1211
		// PHP 7 accepts IPv6 addresses within square brackets as hostnames,
1212
		// but it appears that the PR that came in with https://bugs.php.net/bug.php?id=68039
1213
		// was never merged into a PHP 5 branch ... https://3v4l.org/8PsSN
1214
		if (preg_match('/^\[([^\]]+)\]/', $str, $matches) && ! is_php('7') && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE)
1215
		{
1216
			$str = 'ipv6.host'.substr($str, strlen($matches[1]) + 2);
1217
		}
1218
 
1257 lars 1219
		return (filter_var('http://'.$str, FILTER_VALIDATE_URL) !== FALSE);
68 lars 1220
	}
1221
 
1222
	// --------------------------------------------------------------------
1223
 
1224
	/**
1225
	 * Valid Email
1226
	 *
1227
	 * @param	string
1228
	 * @return	bool
1229
	 */
1230
	public function valid_email($str)
1231
	{
2107 lars 1232
		if (function_exists('idn_to_ascii') && preg_match('#\A([^@]+)@(.+)\z#', $str, $matches))
68 lars 1233
		{
2242 lars 1234
			$domain = defined('INTL_IDNA_VARIANT_UTS46')
1235
				? idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46)
1236
				: idn_to_ascii($matches[2]);
1237
 
1238
			if ($domain !== FALSE)
1239
			{
1240
				$str = $matches[1].'@'.$domain;
1241
			}
68 lars 1242
		}
1243
 
1244
		return (bool) filter_var($str, FILTER_VALIDATE_EMAIL);
1245
	}
1246
 
1247
	// --------------------------------------------------------------------
1248
 
1249
	/**
1250
	 * Valid Emails
1251
	 *
1252
	 * @param	string
1253
	 * @return	bool
1254
	 */
1255
	public function valid_emails($str)
1256
	{
1257
		if (strpos($str, ',') === FALSE)
1258
		{
1259
			return $this->valid_email(trim($str));
1260
		}
1261
 
1262
		foreach (explode(',', $str) as $email)
1263
		{
1264
			if (trim($email) !== '' && $this->valid_email(trim($email)) === FALSE)
1265
			{
1266
				return FALSE;
1267
			}
1268
		}
1269
 
1270
		return TRUE;
1271
	}
1272
 
1273
	// --------------------------------------------------------------------
1274
 
1275
	/**
1276
	 * Validate IP Address
1277
	 *
1278
	 * @param	string
1279
	 * @param	string	'ipv4' or 'ipv6' to validate a specific IP format
1280
	 * @return	bool
1281
	 */
1282
	public function valid_ip($ip, $which = '')
1283
	{
1284
		return $this->CI->input->valid_ip($ip, $which);
1285
	}
1286
 
1287
	// --------------------------------------------------------------------
1288
 
1289
	/**
1290
	 * Alpha
1291
	 *
1292
	 * @param	string
1293
	 * @return	bool
1294
	 */
1295
	public function alpha($str)
1296
	{
1297
		return ctype_alpha($str);
1298
	}
1299
 
1300
	// --------------------------------------------------------------------
1301
 
1302
	/**
1303
	 * Alpha-numeric
1304
	 *
1305
	 * @param	string
1306
	 * @return	bool
1307
	 */
1308
	public function alpha_numeric($str)
1309
	{
1310
		return ctype_alnum((string) $str);
1311
	}
1312
 
1313
	// --------------------------------------------------------------------
1314
 
1315
	/**
1316
	 * Alpha-numeric w/ spaces
1317
	 *
1318
	 * @param	string
1319
	 * @return	bool
1320
	 */
1321
	public function alpha_numeric_spaces($str)
1322
	{
1323
		return (bool) preg_match('/^[A-Z0-9 ]+$/i', $str);
1324
	}
1325
 
1326
	// --------------------------------------------------------------------
1327
 
1328
	/**
1329
	 * Alpha-numeric with underscores and dashes
1330
	 *
1331
	 * @param	string
1332
	 * @return	bool
1333
	 */
1334
	public function alpha_dash($str)
1335
	{
1336
		return (bool) preg_match('/^[a-z0-9_-]+$/i', $str);
1337
	}
1338
 
1339
	// --------------------------------------------------------------------
1340
 
1341
	/**
1342
	 * Numeric
1343
	 *
1344
	 * @param	string
1345
	 * @return	bool
1346
	 */
1347
	public function numeric($str)
1348
	{
1349
		return (bool) preg_match('/^[\-+]?[0-9]*\.?[0-9]+$/', $str);
1350
 
1351
	}
1352
 
1353
	// --------------------------------------------------------------------
1354
 
1355
	/**
1356
	 * Integer
1357
	 *
1358
	 * @param	string
1359
	 * @return	bool
1360
	 */
1361
	public function integer($str)
1362
	{
1363
		return (bool) preg_match('/^[\-+]?[0-9]+$/', $str);
1364
	}
1365
 
1366
	// --------------------------------------------------------------------
1367
 
1368
	/**
1369
	 * Decimal number
1370
	 *
1371
	 * @param	string
1372
	 * @return	bool
1373
	 */
1374
	public function decimal($str)
1375
	{
1376
		return (bool) preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str);
1377
	}
1378
 
1379
	// --------------------------------------------------------------------
1380
 
1381
	/**
1382
	 * Greater than
1383
	 *
1384
	 * @param	string
1385
	 * @param	int
1386
	 * @return	bool
1387
	 */
1388
	public function greater_than($str, $min)
1389
	{
1390
		return is_numeric($str) ? ($str > $min) : FALSE;
1391
	}
1392
 
1393
	// --------------------------------------------------------------------
1394
 
1395
	/**
1396
	 * Equal to or Greater than
1397
	 *
1398
	 * @param	string
1399
	 * @param	int
1400
	 * @return	bool
1401
	 */
1402
	public function greater_than_equal_to($str, $min)
1403
	{
1404
		return is_numeric($str) ? ($str >= $min) : FALSE;
1405
	}
1406
 
1407
	// --------------------------------------------------------------------
1408
 
1409
	/**
1410
	 * Less than
1411
	 *
1412
	 * @param	string
1413
	 * @param	int
1414
	 * @return	bool
1415
	 */
1416
	public function less_than($str, $max)
1417
	{
1418
		return is_numeric($str) ? ($str < $max) : FALSE;
1419
	}
1420
 
1421
	// --------------------------------------------------------------------
1422
 
1423
	/**
1424
	 * Equal to or Less than
1425
	 *
1426
	 * @param	string
1427
	 * @param	int
1428
	 * @return	bool
1429
	 */
1430
	public function less_than_equal_to($str, $max)
1431
	{
1432
		return is_numeric($str) ? ($str <= $max) : FALSE;
1433
	}
1434
 
1435
	// --------------------------------------------------------------------
1436
 
1437
	/**
1438
	 * Value should be within an array of values
1439
	 *
1440
	 * @param	string
1441
	 * @param	string
1442
	 * @return	bool
1443
	 */
1444
	public function in_list($value, $list)
1445
	{
1446
		return in_array($value, explode(',', $list), TRUE);
1447
	}
1448
 
1449
	// --------------------------------------------------------------------
1450
 
1451
	/**
1452
	 * Is a Natural number  (0,1,2,3, etc.)
1453
	 *
1454
	 * @param	string
1455
	 * @return	bool
1456
	 */
1457
	public function is_natural($str)
1458
	{
1459
		return ctype_digit((string) $str);
1460
	}
1461
 
1462
	// --------------------------------------------------------------------
1463
 
1464
	/**
1465
	 * Is a Natural number, but not a zero  (1,2,3, etc.)
1466
	 *
1467
	 * @param	string
1468
	 * @return	bool
1469
	 */
1470
	public function is_natural_no_zero($str)
1471
	{
1472
		return ($str != 0 && ctype_digit((string) $str));
1473
	}
1474
 
1475
	// --------------------------------------------------------------------
1476
 
1477
	/**
1478
	 * Valid Base64
1479
	 *
1480
	 * Tests a string for characters outside of the Base64 alphabet
1481
	 * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045
1482
	 *
1483
	 * @param	string
1484
	 * @return	bool
1485
	 */
1486
	public function valid_base64($str)
1487
	{
1488
		return (base64_encode(base64_decode($str)) === $str);
1489
	}
1490
 
1491
	// --------------------------------------------------------------------
1492
 
1493
	/**
1494
	 * Prep data for form
1495
	 *
1496
	 * This function allows HTML to be safely shown in a form.
1497
	 * Special characters are converted.
1498
	 *
1499
	 * @deprecated	3.0.6	Not used anywhere within the framework and pretty much useless
1500
	 * @param	mixed	$data	Input data
1501
	 * @return	mixed
1502
	 */
1503
	public function prep_for_form($data)
1504
	{
1505
		if ($this->_safe_form_data === FALSE OR empty($data))
1506
		{
1507
			return $data;
1508
		}
1509
 
1510
		if (is_array($data))
1511
		{
1512
			foreach ($data as $key => $val)
1513
			{
1514
				$data[$key] = $this->prep_for_form($val);
1515
			}
1516
 
1517
			return $data;
1518
		}
1519
 
1520
		return str_replace(array("'", '"', '<', '>'), array('&#39;', '&quot;', '&lt;', '&gt;'), stripslashes($data));
1521
	}
1522
 
1523
	// --------------------------------------------------------------------
1524
 
1525
	/**
1526
	 * Prep URL
1527
	 *
1528
	 * @param	string
1529
	 * @return	string
1530
	 */
1531
	public function prep_url($str = '')
1532
	{
1533
		if ($str === 'http://' OR $str === '')
1534
		{
1535
			return '';
1536
		}
1537
 
1538
		if (strpos($str, 'http://') !== 0 && strpos($str, 'https://') !== 0)
1539
		{
1540
			return 'http://'.$str;
1541
		}
1542
 
1543
		return $str;
1544
	}
1545
 
1546
	// --------------------------------------------------------------------
1547
 
1548
	/**
1549
	 * Strip Image Tags
1550
	 *
1551
	 * @param	string
1552
	 * @return	string
1553
	 */
1554
	public function strip_image_tags($str)
1555
	{
1556
		return $this->CI->security->strip_image_tags($str);
1557
	}
1558
 
1559
	// --------------------------------------------------------------------
1560
 
1561
	/**
1562
	 * Convert PHP tags to entities
1563
	 *
1564
	 * @param	string
1565
	 * @return	string
1566
	 */
1567
	public function encode_php_tags($str)
1568
	{
1569
		return str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);
1570
	}
1571
 
1572
	// --------------------------------------------------------------------
1573
 
1574
	/**
1575
	 * Reset validation vars
1576
	 *
1577
	 * Prevents subsequent validation routines from being affected by the
1578
	 * results of any previous validation routine due to the CI singleton.
1579
	 *
1580
	 * @return	CI_Form_validation
1581
	 */
1582
	public function reset_validation()
1583
	{
1584
		$this->_field_data = array();
1585
		$this->_error_array = array();
1586
		$this->_error_messages = array();
1587
		$this->error_string = '';
1588
		return $this;
1589
	}
1590
 
1591
}