Form_validation.php 36 KB

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