Form_validation.php 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598
  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. */
  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] = is_array($row['postdata']) ? NULL : $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. // Apparently, FILTER_VALIDATE_URL doesn't reject digit-only names for some reason ...
  1066. // See https://github.com/bcit-ci/CodeIgniter/issues/5755
  1067. if (ctype_digit($str))
  1068. {
  1069. return FALSE;
  1070. }
  1071. // PHP 7 accepts IPv6 addresses within square brackets as hostnames,
  1072. // but it appears that the PR that came in with https://bugs.php.net/bug.php?id=68039
  1073. // was never merged into a PHP 5 branch ... https://3v4l.org/8PsSN
  1074. if (preg_match('/^\[([^\]]+)\]/', $str, $matches) && ! is_php('7') && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE)
  1075. {
  1076. $str = 'ipv6.host'.substr($str, strlen($matches[1]) + 2);
  1077. }
  1078. return (filter_var('http://'.$str, FILTER_VALIDATE_URL) !== FALSE);
  1079. }
  1080. // --------------------------------------------------------------------
  1081. /**
  1082. * Valid Email
  1083. *
  1084. * @param string
  1085. * @return bool
  1086. */
  1087. public function valid_email($str)
  1088. {
  1089. if (function_exists('idn_to_ascii') && preg_match('#\A([^@]+)@(.+)\z#', $str, $matches))
  1090. {
  1091. $domain = defined('INTL_IDNA_VARIANT_UTS46')
  1092. ? idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46)
  1093. : idn_to_ascii($matches[2]);
  1094. if ($domain !== FALSE)
  1095. {
  1096. $str = $matches[1].'@'.$domain;
  1097. }
  1098. }
  1099. return (bool) filter_var($str, FILTER_VALIDATE_EMAIL);
  1100. }
  1101. // --------------------------------------------------------------------
  1102. /**
  1103. * Valid Emails
  1104. *
  1105. * @param string
  1106. * @return bool
  1107. */
  1108. public function valid_emails($str)
  1109. {
  1110. if (strpos($str, ',') === FALSE)
  1111. {
  1112. return $this->valid_email(trim($str));
  1113. }
  1114. foreach (explode(',', $str) as $email)
  1115. {
  1116. if (trim($email) !== '' && $this->valid_email(trim($email)) === FALSE)
  1117. {
  1118. return FALSE;
  1119. }
  1120. }
  1121. return TRUE;
  1122. }
  1123. // --------------------------------------------------------------------
  1124. /**
  1125. * Validate IP Address
  1126. *
  1127. * @param string
  1128. * @param string 'ipv4' or 'ipv6' to validate a specific IP format
  1129. * @return bool
  1130. */
  1131. public function valid_ip($ip, $which = '')
  1132. {
  1133. return $this->CI->input->valid_ip($ip, $which);
  1134. }
  1135. // --------------------------------------------------------------------
  1136. /**
  1137. * Alpha
  1138. *
  1139. * @param string
  1140. * @return bool
  1141. */
  1142. public function alpha($str)
  1143. {
  1144. return ctype_alpha($str);
  1145. }
  1146. // --------------------------------------------------------------------
  1147. /**
  1148. * Alpha-numeric
  1149. *
  1150. * @param string
  1151. * @return bool
  1152. */
  1153. public function alpha_numeric($str)
  1154. {
  1155. return ctype_alnum((string) $str);
  1156. }
  1157. // --------------------------------------------------------------------
  1158. /**
  1159. * Alpha-numeric w/ spaces
  1160. *
  1161. * @param string
  1162. * @return bool
  1163. */
  1164. public function alpha_numeric_spaces($str)
  1165. {
  1166. return (bool) preg_match('/^[A-Z0-9 ]+$/i', $str);
  1167. }
  1168. // --------------------------------------------------------------------
  1169. /**
  1170. * Alpha-numeric with underscores and dashes
  1171. *
  1172. * @param string
  1173. * @return bool
  1174. */
  1175. public function alpha_dash($str)
  1176. {
  1177. return (bool) preg_match('/^[a-z0-9_-]+$/i', $str);
  1178. }
  1179. // --------------------------------------------------------------------
  1180. /**
  1181. * Numeric
  1182. *
  1183. * @param string
  1184. * @return bool
  1185. */
  1186. public function numeric($str)
  1187. {
  1188. return (bool) preg_match('/^[\-+]?[0-9]*\.?[0-9]+$/', $str);
  1189. }
  1190. // --------------------------------------------------------------------
  1191. /**
  1192. * Integer
  1193. *
  1194. * @param string
  1195. * @return bool
  1196. */
  1197. public function integer($str)
  1198. {
  1199. return (bool) preg_match('/^[\-+]?[0-9]+$/', $str);
  1200. }
  1201. // --------------------------------------------------------------------
  1202. /**
  1203. * Decimal number
  1204. *
  1205. * @param string
  1206. * @return bool
  1207. */
  1208. public function decimal($str)
  1209. {
  1210. return (bool) preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str);
  1211. }
  1212. // --------------------------------------------------------------------
  1213. /**
  1214. * Greater than
  1215. *
  1216. * @param string
  1217. * @param int
  1218. * @return bool
  1219. */
  1220. public function greater_than($str, $min)
  1221. {
  1222. return is_numeric($str) ? ($str > $min) : FALSE;
  1223. }
  1224. // --------------------------------------------------------------------
  1225. /**
  1226. * Equal to or Greater than
  1227. *
  1228. * @param string
  1229. * @param int
  1230. * @return bool
  1231. */
  1232. public function greater_than_equal_to($str, $min)
  1233. {
  1234. return is_numeric($str) ? ($str >= $min) : FALSE;
  1235. }
  1236. // --------------------------------------------------------------------
  1237. /**
  1238. * Less than
  1239. *
  1240. * @param string
  1241. * @param int
  1242. * @return bool
  1243. */
  1244. public function less_than($str, $max)
  1245. {
  1246. return is_numeric($str) ? ($str < $max) : FALSE;
  1247. }
  1248. // --------------------------------------------------------------------
  1249. /**
  1250. * Equal to or Less than
  1251. *
  1252. * @param string
  1253. * @param int
  1254. * @return bool
  1255. */
  1256. public function less_than_equal_to($str, $max)
  1257. {
  1258. return is_numeric($str) ? ($str <= $max) : FALSE;
  1259. }
  1260. // --------------------------------------------------------------------
  1261. /**
  1262. * Value should be within an array of values
  1263. *
  1264. * @param string
  1265. * @param string
  1266. * @return bool
  1267. */
  1268. public function in_list($value, $list)
  1269. {
  1270. return in_array($value, explode(',', $list), TRUE);
  1271. }
  1272. // --------------------------------------------------------------------
  1273. /**
  1274. * Is a Natural number (0,1,2,3, etc.)
  1275. *
  1276. * @param string
  1277. * @return bool
  1278. */
  1279. public function is_natural($str)
  1280. {
  1281. return ctype_digit((string) $str);
  1282. }
  1283. // --------------------------------------------------------------------
  1284. /**
  1285. * Is a Natural number, but not a zero (1,2,3, etc.)
  1286. *
  1287. * @param string
  1288. * @return bool
  1289. */
  1290. public function is_natural_no_zero($str)
  1291. {
  1292. return ($str != 0 && ctype_digit((string) $str));
  1293. }
  1294. // --------------------------------------------------------------------
  1295. /**
  1296. * Valid Base64
  1297. *
  1298. * Tests a string for characters outside of the Base64 alphabet
  1299. * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045
  1300. *
  1301. * @param string
  1302. * @return bool
  1303. */
  1304. public function valid_base64($str)
  1305. {
  1306. return (base64_encode(base64_decode($str)) === $str);
  1307. }
  1308. // --------------------------------------------------------------------
  1309. /**
  1310. * Prep data for form
  1311. *
  1312. * This function allows HTML to be safely shown in a form.
  1313. * Special characters are converted.
  1314. *
  1315. * @deprecated 3.0.6 Not used anywhere within the framework and pretty much useless
  1316. * @param mixed $data Input data
  1317. * @return mixed
  1318. */
  1319. public function prep_for_form($data)
  1320. {
  1321. if ($this->_safe_form_data === FALSE OR empty($data))
  1322. {
  1323. return $data;
  1324. }
  1325. if (is_array($data))
  1326. {
  1327. foreach ($data as $key => $val)
  1328. {
  1329. $data[$key] = $this->prep_for_form($val);
  1330. }
  1331. return $data;
  1332. }
  1333. return str_replace(array("'", '"', '<', '>'), array('&#39;', '&quot;', '&lt;', '&gt;'), stripslashes($data));
  1334. }
  1335. // --------------------------------------------------------------------
  1336. /**
  1337. * Prep URL
  1338. *
  1339. * @param string
  1340. * @return string
  1341. */
  1342. public function prep_url($str = '')
  1343. {
  1344. if ($str === 'http://' OR $str === '')
  1345. {
  1346. return '';
  1347. }
  1348. if (strpos($str, 'http://') !== 0 && strpos($str, 'https://') !== 0)
  1349. {
  1350. return 'http://'.$str;
  1351. }
  1352. return $str;
  1353. }
  1354. // --------------------------------------------------------------------
  1355. /**
  1356. * Strip Image Tags
  1357. *
  1358. * @param string
  1359. * @return string
  1360. */
  1361. public function strip_image_tags($str)
  1362. {
  1363. return $this->CI->security->strip_image_tags($str);
  1364. }
  1365. // --------------------------------------------------------------------
  1366. /**
  1367. * Convert PHP tags to entities
  1368. *
  1369. * @param string
  1370. * @return string
  1371. */
  1372. public function encode_php_tags($str)
  1373. {
  1374. return str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);
  1375. }
  1376. // --------------------------------------------------------------------
  1377. /**
  1378. * Reset validation vars
  1379. *
  1380. * Prevents subsequent validation routines from being affected by the
  1381. * results of any previous validation routine due to the CI singleton.
  1382. *
  1383. * @return CI_Form_validation
  1384. */
  1385. public function reset_validation()
  1386. {
  1387. $this->_field_data = array();
  1388. $this->_error_array = array();
  1389. $this->_error_messages = array();
  1390. $this->error_string = '';
  1391. return $this;
  1392. }
  1393. }