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 * 9 * Copyright (c) 2014 - 2019, British Columbia Institute of Technology 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/) 32 * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/) 33 * @license https://opensource.org/licenses/MIT MIT License 34 * @link https://codeigniter.com 35 * @since Version 1.0.0 36 * @filesource 37 */ 38defined('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 */ 49class 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] = is_array($row['postdata']) ? NULL : $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 } 1203 elseif ( ! in_array(strtolower($matches[1]), array('http', 'https'), TRUE)) 1204 { 1205 return FALSE; 1206 } 1207 1208 $str = $matches[2]; 1209 } 1210 1211 // Apparently, FILTER_VALIDATE_URL doesn't reject digit-only names for some reason ... 1212 // See https://github.com/bcit-ci/CodeIgniter/issues/5755 1213 if (ctype_digit($str)) 1214 { 1215 return FALSE; 1216 } 1217 1218 // PHP 7 accepts IPv6 addresses within square brackets as hostnames, 1219 // but it appears that the PR that came in with https://bugs.php.net/bug.php?id=68039 1220 // was never merged into a PHP 5 branch ... https://3v4l.org/8PsSN 1221 if (preg_match('/^\[([^\]]+)\]/', $str, $matches) && ! is_php('7') && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE) 1222 { 1223 $str = 'ipv6.host'.substr($str, strlen($matches[1]) + 2); 1224 } 1225 1226 return (filter_var('http://'.$str, FILTER_VALIDATE_URL) !== FALSE); 1227 } 1228 1229 // -------------------------------------------------------------------- 1230 1231 /** 1232 * Valid Email 1233 * 1234 * @param string 1235 * @return bool 1236 */ 1237 public function valid_email($str) 1238 { 1239 if (function_exists('idn_to_ascii') && preg_match('#\A([^@]+)@(.+)\z#', $str, $matches)) 1240 { 1241 $domain = defined('INTL_IDNA_VARIANT_UTS46') 1242 ? idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46) 1243 : idn_to_ascii($matches[2]); 1244 1245 if ($domain !== FALSE) 1246 { 1247 $str = $matches[1].'@'.$domain; 1248 } 1249 } 1250 1251 return (bool) filter_var($str, FILTER_VALIDATE_EMAIL); 1252 } 1253 1254 // -------------------------------------------------------------------- 1255 1256 /** 1257 * Valid Emails 1258 * 1259 * @param string 1260 * @return bool 1261 */ 1262 public function valid_emails($str) 1263 { 1264 if (strpos($str, ',') === FALSE) 1265 { 1266 return $this->valid_email(trim($str)); 1267 } 1268 1269 foreach (explode(',', $str) as $email) 1270 { 1271 if (trim($email) !== '' && $this->valid_email(trim($email)) === FALSE) 1272 { 1273 return FALSE; 1274 } 1275 } 1276 1277 return TRUE; 1278 } 1279 1280 // -------------------------------------------------------------------- 1281 1282 /** 1283 * Validate IP Address 1284 * 1285 * @param string 1286 * @param string 'ipv4' or 'ipv6' to validate a specific IP format 1287 * @return bool 1288 */ 1289 public function valid_ip($ip, $which = '') 1290 { 1291 return $this->CI->input->valid_ip($ip, $which); 1292 } 1293 1294 // -------------------------------------------------------------------- 1295 1296 /** 1297 * Alpha 1298 * 1299 * @param string 1300 * @return bool 1301 */ 1302 public function alpha($str) 1303 { 1304 return ctype_alpha($str); 1305 } 1306 1307 // -------------------------------------------------------------------- 1308 1309 /** 1310 * Alpha-numeric 1311 * 1312 * @param string 1313 * @return bool 1314 */ 1315 public function alpha_numeric($str) 1316 { 1317 return ctype_alnum((string) $str); 1318 } 1319 1320 // -------------------------------------------------------------------- 1321 1322 /** 1323 * Alpha-numeric w/ spaces 1324 * 1325 * @param string 1326 * @return bool 1327 */ 1328 public function alpha_numeric_spaces($str) 1329 { 1330 return (bool) preg_match('/^[A-Z0-9 ]+$/i', $str); 1331 } 1332 1333 // -------------------------------------------------------------------- 1334 1335 /** 1336 * Alpha-numeric with underscores and dashes 1337 * 1338 * @param string 1339 * @return bool 1340 */ 1341 public function alpha_dash($str) 1342 { 1343 return (bool) preg_match('/^[a-z0-9_-]+$/i', $str); 1344 } 1345 1346 // -------------------------------------------------------------------- 1347 1348 /** 1349 * Numeric 1350 * 1351 * @param string 1352 * @return bool 1353 */ 1354 public function numeric($str) 1355 { 1356 return (bool) preg_match('/^[\-+]?[0-9]*\.?[0-9]+$/', $str); 1357 1358 } 1359 1360 // -------------------------------------------------------------------- 1361 1362 /** 1363 * Integer 1364 * 1365 * @param string 1366 * @return bool 1367 */ 1368 public function integer($str) 1369 { 1370 return (bool) preg_match('/^[\-+]?[0-9]+$/', $str); 1371 } 1372 1373 // -------------------------------------------------------------------- 1374 1375 /** 1376 * Decimal number 1377 * 1378 * @param string 1379 * @return bool 1380 */ 1381 public function decimal($str) 1382 { 1383 return (bool) preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str); 1384 } 1385 1386 // -------------------------------------------------------------------- 1387 1388 /** 1389 * Greater than 1390 * 1391 * @param string 1392 * @param int 1393 * @return bool 1394 */ 1395 public function greater_than($str, $min) 1396 { 1397 return is_numeric($str) ? ($str > $min) : FALSE; 1398 } 1399 1400 // -------------------------------------------------------------------- 1401 1402 /** 1403 * Equal to or Greater than 1404 * 1405 * @param string 1406 * @param int 1407 * @return bool 1408 */ 1409 public function greater_than_equal_to($str, $min) 1410 { 1411 return is_numeric($str) ? ($str >= $min) : FALSE; 1412 } 1413 1414 // -------------------------------------------------------------------- 1415 1416 /** 1417 * Less than 1418 * 1419 * @param string 1420 * @param int 1421 * @return bool 1422 */ 1423 public function less_than($str, $max) 1424 { 1425 return is_numeric($str) ? ($str < $max) : FALSE; 1426 } 1427 1428 // -------------------------------------------------------------------- 1429 1430 /** 1431 * Equal to or Less than 1432 * 1433 * @param string 1434 * @param int 1435 * @return bool 1436 */ 1437 public function less_than_equal_to($str, $max) 1438 { 1439 return is_numeric($str) ? ($str <= $max) : FALSE; 1440 } 1441 1442 // -------------------------------------------------------------------- 1443 1444 /** 1445 * Value should be within an array of values 1446 * 1447 * @param string 1448 * @param string 1449 * @return bool 1450 */ 1451 public function in_list($value, $list) 1452 { 1453 return in_array($value, explode(',', $list), TRUE); 1454 } 1455 1456 // -------------------------------------------------------------------- 1457 1458 /** 1459 * Is a Natural number (0,1,2,3, etc.) 1460 * 1461 * @param string 1462 * @return bool 1463 */ 1464 public function is_natural($str) 1465 { 1466 return ctype_digit((string) $str); 1467 } 1468 1469 // -------------------------------------------------------------------- 1470 1471 /** 1472 * Is a Natural number, but not a zero (1,2,3, etc.) 1473 * 1474 * @param string 1475 * @return bool 1476 */ 1477 public function is_natural_no_zero($str) 1478 { 1479 return ($str != 0 && ctype_digit((string) $str)); 1480 } 1481 1482 // -------------------------------------------------------------------- 1483 1484 /** 1485 * Valid Base64 1486 * 1487 * Tests a string for characters outside of the Base64 alphabet 1488 * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045 1489 * 1490 * @param string 1491 * @return bool 1492 */ 1493 public function valid_base64($str) 1494 { 1495 return (base64_encode(base64_decode($str)) === $str); 1496 } 1497 1498 // -------------------------------------------------------------------- 1499 1500 /** 1501 * Prep data for form 1502 * 1503 * This function allows HTML to be safely shown in a form. 1504 * Special characters are converted. 1505 * 1506 * @deprecated 3.0.6 Not used anywhere within the framework and pretty much useless 1507 * @param mixed $data Input data 1508 * @return mixed 1509 */ 1510 public function prep_for_form($data) 1511 { 1512 if ($this->_safe_form_data === FALSE OR empty($data)) 1513 { 1514 return $data; 1515 } 1516 1517 if (is_array($data)) 1518 { 1519 foreach ($data as $key => $val) 1520 { 1521 $data[$key] = $this->prep_for_form($val); 1522 } 1523 1524 return $data; 1525 } 1526 1527 return str_replace(array("'", '"', '<', '>'), array(''', '"', '<', '>'), stripslashes($data)); 1528 } 1529 1530 // -------------------------------------------------------------------- 1531 1532 /** 1533 * Prep URL 1534 * 1535 * @param string 1536 * @return string 1537 */ 1538 public function prep_url($str = '') 1539 { 1540 if ($str === 'http://' OR $str === '') 1541 { 1542 return ''; 1543 } 1544 1545 if (strpos($str, 'http://') !== 0 && strpos($str, 'https://') !== 0) 1546 { 1547 return 'http://'.$str; 1548 } 1549 1550 return $str; 1551 } 1552 1553 // -------------------------------------------------------------------- 1554 1555 /** 1556 * Strip Image Tags 1557 * 1558 * @param string 1559 * @return string 1560 */ 1561 public function strip_image_tags($str) 1562 { 1563 return $this->CI->security->strip_image_tags($str); 1564 } 1565 1566 // -------------------------------------------------------------------- 1567 1568 /** 1569 * Convert PHP tags to entities 1570 * 1571 * @param string 1572 * @return string 1573 */ 1574 public function encode_php_tags($str) 1575 { 1576 return str_replace(array('<?', '?>'), array('<?', '?>'), $str); 1577 } 1578 1579 // -------------------------------------------------------------------- 1580 1581 /** 1582 * Reset validation vars 1583 * 1584 * Prevents subsequent validation routines from being affected by the 1585 * results of any previous validation routine due to the CI singleton. 1586 * 1587 * @return CI_Form_validation 1588 */ 1589 public function reset_validation() 1590 { 1591 $this->_field_data = array(); 1592 $this->_error_array = array(); 1593 $this->_error_messages = array(); 1594 $this->error_string = ''; 1595 return $this; 1596 } 1597 1598} 1599