Posted By


djenniex on 11/17/09

Tagged


Statistics


Viewed 811 times
Favorited by 1 user(s)

MY_Form_validation


/ Published in: PHP
Save to your folder(s)

Custom CodeIgniter form_validation class to validate uploaded files.


Copy this code and paste it in your HTML
  1. <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
  2.  
  3. /**
  4.  * CodeIgniter Form Validation
  5.  *
  6.  * @version $Id: MY_Form_validation.php 83 2009-07-17 22:07:43Z djenniex $
  7.  * @package
  8.  * @subpackage Library
  9.  * @category Validation
  10.  */
  11.  
  12. /**
  13.  * Form Validation
  14.  *
  15.  * This is an extension of the CodeIgniter Validation Class
  16.  *
  17.  * Adds validation rules
  18.  */
  19. class MY_Form_validation extends CI_Form_validation
  20. {
  21. const DUMMY_ITEM = 'DUMMY_ITEM';
  22. var $_error_prefix = '<span class="formerror small">';
  23. var $_error_suffix = '</span>';
  24.  
  25. /**
  26.   * Constructor
  27.   *
  28.   * @param array $rules
  29.   *
  30.   * @return MY_Form_validation
  31.   * @access public
  32.   */
  33. public function __construct($rules = array())
  34. {
  35. parent::CI_Form_validation($rules);
  36. $this->CI->lang->load('upload');
  37.  
  38. $this->CI->load->library('upload');
  39. }
  40.  
  41. /**
  42.   * PHP4 Constructor
  43.   *
  44.   * @param array $rules
  45.   *
  46.   * @return MY_Form_validation
  47.   * @access public
  48.   */
  49. public function MY_Form_validation($rules = array())
  50. {
  51. parent::CI_Form_validation($rules);
  52. $this->CI->lang->load('upload');
  53. $this->CI->load->library('upload');
  54. }
  55.  
  56. /**
  57. * Set Rules
  58. *
  59. * This function takes an array of field names and validation
  60. * rules as input, validates the info, and stores it
  61. *
  62. * @param mixed $field
  63. * @param string $label
  64. * @param mixed $rules
  65. *
  66. * @return void
  67. * @access public
  68. * @see set_rules()
  69. */
  70. function set_rules($field, $label = '', $rules = '')
  71. {
  72. // set a dummy post if we have only files that are processed
  73. if (count($_POST) == 0 && count($_FILES) > 0)
  74. {
  75. // Add a dummy $_POST variable
  76. $_POST[DUMMY_ITEM] = '';
  77. parent::set_rules($field, $label, $rules);
  78. unset($_POST[DUMMY_ITEM]);
  79. return;
  80. }
  81.  
  82. // we are safe to run as is
  83. parent::set_rules($field, $label, $rules);
  84. }
  85.  
  86. /**
  87. * Run the Validator
  88. *
  89. * This function does all the work.
  90. *
  91. * @param string $module
  92. * @param string $group
  93. *
  94. * @access public
  95. * @return bool
  96. * @see run()
  97. */
  98. public function run($group = '')
  99. {
  100. if (count($_POST) === 0 && count($_FILES) > 0)
  101. {
  102. // Add a dummy $_POST variable
  103. $_POST[DUMMY_ITEM] = '';
  104. $rc = parent::run($group);
  105. unset($_POST[DUMMY_ITEM]);
  106. return $rc;
  107. }
  108.  
  109. // we are safe to run as is
  110. return parent::run($group);
  111. }
  112.  
  113. /**
  114. * Executes the Validation routines
  115. *
  116. * @param array
  117. * @param array
  118. * @param mixed
  119. * @param integer
  120. *
  121. * @return mixed
  122. * @access private
  123. */
  124. public function _execute($row, $rules, $postdata = NULL, $cycles = 0)
  125. {
  126. if (isset($_FILES[$row['field']]))
  127. {
  128. // It's a file, so process it as a file
  129. $postdata = $_FILES[$row['field']];
  130.  
  131. // Before doing anything check for errors
  132. if ($postdata['error'] !== UPLOAD_ERR_OK)
  133. {
  134. switch ($postdata['error'])
  135. {
  136. case 1: // UPLOAD_ERR_INI_SIZE
  137. $error = $this->CI->lang->line('upload_file_exceeds_limit');
  138. break;
  139. case 2: // UPLOAD_ERR_FORM_SIZE
  140. $error = $this->CI->lang->line('upload_file_exceeds_form_limit');
  141. break;
  142. case 3: // UPLOAD_ERR_PARTIAL
  143. $error = $this->CI->lang->line('upload_file_partial');
  144. break;
  145. case 4: // UPLOAD_ERR_NO_FILE
  146. $error = $this->CI->lang->line('upload_no_file_selected');
  147. break;
  148. case 6: // UPLOAD_ERR_NO_TMP_DIR
  149. $error = $this->CI->lang->line('upload_no_temp_directory');
  150. break;
  151. case 7: // UPLOAD_ERR_CANT_WRITE
  152. $error = $this->CI->lang->line('upload_unable_to_write_file');
  153. break;
  154. case 8: // UPLOAD_ERR_EXTENSION
  155. $error = $this->CI->lang->line('upload_stopped_by_extension');
  156. break;
  157. default:
  158. $error = $this->CI->lang->line('upload_no_file_selected');
  159. break;
  160. }
  161. // Build the error message
  162. $message = sprintf($error, $this->_translate_fieldname($row['label']));
  163.  
  164. // Save the error message
  165. $this->_field_data[$row['field']]['error'] = $message;
  166.  
  167. if ( ! isset($this->_error_array[$row['field']]))
  168. {
  169. $this->_error_array[$row['field']] = $message;
  170. }
  171. return FALSE;
  172. }
  173.  
  174. $_in_array = FALSE;
  175.  
  176. // If the field is blank, but NOT required, no further tests are necessary
  177. $callback = FALSE;
  178. if ( !in_array('file_required', $rules) && $postdata['size'] == 0)
  179. {
  180. // Before we bail out, does the rule contain a callback?
  181. if (preg_match("/(callback_\w+)/", implode(' ', $rules), $match))
  182. {
  183. $callback = TRUE;
  184. $rules = (array('1' => $match[1]));
  185. }
  186. else
  187. {
  188. return;
  189. }
  190. }
  191.  
  192. // Cycle through each rule and run it
  193. foreach ($rules As $rule)
  194. {
  195. // Is the rule a callback?
  196. $callback = FALSE;
  197. if (substr($rule, 0, 9) == 'callback_')
  198. {
  199. $rule = substr($rule, 9);
  200. $callback = TRUE;
  201. }
  202.  
  203. // Strip the parameter (if exists) from the rule
  204. // Rules can contain a parameter: max_length[5]
  205. $param = FALSE;
  206. if (preg_match("/(.*?)\[(.*?)\]/", $rule, $match))
  207. {
  208. $rule = $match[1];
  209. $param = $match[2];
  210. }
  211.  
  212. // Call the function that corresponds to the rule
  213. if ($callback === TRUE)
  214. {
  215. if ( ! method_exists($this->CI, $rule))
  216. {
  217. continue;
  218. }
  219.  
  220. // Run the function and grab the result
  221. $result = $this->CI->$rule($postdata, $param);
  222.  
  223. // Re-assign the result to the master data array
  224. if ($_in_array == TRUE)
  225. {
  226. $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
  227. }
  228. else
  229. {
  230. $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
  231. }
  232.  
  233. // If the field isn't required and we just processed a callback we'll move on...
  234. if ( ! in_array('file_required', $rules, TRUE) AND $result !== FALSE)
  235. {
  236. // FIX: Should we continue or return here?
  237. continue;
  238. }
  239. }
  240. else
  241. {
  242. if ( ! method_exists($this, $rule))
  243. {
  244. // If our own wrapper function doesn't exist we see if a native PHP function does.
  245. // Users can use any native PHP function call that has one param.
  246. if (function_exists($rule))
  247. {
  248. $result = $rule($postdata);
  249.  
  250. if ($_in_array == TRUE)
  251. {
  252. $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
  253. }
  254. else
  255. {
  256. $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
  257. }
  258. }
  259.  
  260. continue;
  261. }
  262.  
  263. $result = $this->$rule($postdata, $param);
  264.  
  265. if ($_in_array == TRUE)
  266. {
  267. $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
  268. }
  269. else
  270. {
  271. $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
  272. }
  273. }
  274.  
  275. // Did the rule test negatively? If so, grab the error.
  276. if ($result === FALSE)
  277. {
  278. if ( ! isset($this->_error_messages[$rule]))
  279. {
  280. if (FALSE === ($line = $this->CI->lang->line($rule)))
  281. {
  282. $line = 'Unable to access an error message corresponding to your field name.';
  283. }
  284. }
  285. else
  286. {
  287. $line = $this->_error_messages[$rule];
  288. }
  289.  
  290. // Is the parameter we are inserting into the error message the name
  291. // of another field? If so we need to grab its "field label"
  292. if (isset($this->_field_data[$param]) AND isset($this->_field_data[$param]['label']))
  293. {
  294. $param = $this->_field_data[$param]['label'];
  295. }
  296.  
  297. // Build the error message
  298. $message = sprintf($line, $this->_translate_fieldname($row['label']), $param);
  299.  
  300. // Save the error message
  301. $this->_field_data[$row['field']]['error'] = $message;
  302.  
  303. if ( ! isset($this->_error_array[$row['field']]))
  304. {
  305. $this->_error_array[$row['field']] = $message;
  306. }
  307.  
  308. return;
  309. }
  310. }
  311. }
  312. else
  313. {
  314. parent::_execute($row, $rules, $postdata, $cycles);
  315. }
  316. }
  317.  
  318. // File Upload Validation
  319. // --------------------------------------------------------------------
  320.  
  321. /**
  322.   * Required file is uploaded
  323.   *
  324.   * @param mixed $file
  325.   *
  326.   * @return boolean
  327.   * @access public
  328.   */
  329. public function file_required($file)
  330. {
  331. if ($file['size'] === 0)
  332. {
  333. $this->set_message('file_required', 'Uploading a file for %s is required.');
  334. return FALSE;
  335. }
  336.  
  337. return TRUE;
  338. }
  339.  
  340. /**
  341.   * File is within expected file size limit
  342.   *
  343.   * @param mixed $file
  344.   * @param mixed $max_size
  345.   *
  346.   * @return boolean
  347.   * @access public
  348.   */
  349. public function max_file_size($file, $max_size)
  350. {
  351. $max_size_bit = $this->_let_to_bit($max_size);
  352. if ($file['size'] > $max_size_bit)
  353. {
  354. $this->set_message('max_file_size', '%s exceeds file size limit of ' . $max_size);
  355. return FALSE;
  356. }
  357. return TRUE;
  358. }
  359.  
  360. /**
  361.   * File is bigger than minimum size
  362.   *
  363.   * @param mixed $file
  364.   * @param mixed $min_size
  365.   *
  366.   * @return boolean
  367.   * @accesspublic
  368.   */
  369. public function min_file_size($file, $min_size)
  370. {
  371. $min_size_bit = $this->_let_to_bit($min_size);
  372. if ($file['size'] < $min_size_bit)
  373. {
  374. $this->set_message('min_file_size', '%s file size is smaller than minimum required of ' . $min_size);
  375. return FALSE;
  376. }
  377. return TRUE;
  378. }
  379.  
  380. /**
  381.   * File extension for valid file types
  382.   *
  383.   * @param mixed $file
  384.   * @param mixed $extensions
  385.   *
  386.   * @return boolean
  387.   * @access public
  388.   */
  389. public function valid_file_extension($file, $extensions)
  390. {
  391. // Check format type
  392. $allowed_types = explode('|', $extensions);
  393.  
  394. if (count($allowed_types) == 0 OR ! is_array($allowed_types))
  395. {
  396. $this->set_message('valid_file_extension', $this->CI->lang->line('upload_no_file_types'));
  397. return FALSE;
  398. }
  399.  
  400. $image_types = array('jpg', 'jpeg', 'gif', 'png', 'jpe');
  401.  
  402. foreach ($allowed_types as $val)
  403. {
  404. $mime = $this->CI->upload->mimes_types(strtolower($val));
  405.  
  406. // Images get some additional checks
  407. if (in_array($val, $image_types))
  408. {
  409. if (getimagesize($file['tmp_name']) === FALSE)
  410. {
  411. $this->set_message('valid_file_extension', "%s size is insufficient.");
  412. return FALSE;
  413. }
  414. }
  415.  
  416. if (is_array($mime))
  417. {
  418. if (in_array($file['type'], $mime, TRUE))
  419. {
  420. return TRUE;
  421. }
  422. }
  423. else
  424. {
  425. if ($mime == $file['type'])
  426. {
  427. return TRUE;
  428. }
  429. }
  430. }
  431.  
  432. $this->set_message('valid_file_extension', $this->CI->lang->line('upload_invalid_filetype'));
  433. return FALSE;
  434. }
  435.  
  436. /**
  437.   * Image is bigger than the dimensions given
  438.   *
  439.   * @param mixed $file
  440.   * @param array $dimensions
  441.   *
  442.   * @return boolean
  443.   * @access public
  444.   */
  445. public function max_image_dimension($file, $dimensions)
  446. {
  447. $dimensions = explode(',', $dimensions);
  448.  
  449. if (count($dimensions) !== 2)
  450. {
  451. // Bad size given
  452. $this->set_message('max_image_dimension', '%s has invalid rule expected similar to 150,300');
  453. return FALSE;
  454. }
  455.  
  456. // Get image size
  457. $image_dimensions = $this->_get_image_dimension($file['tmp_name']);
  458. if ( ! $image_dimensions)
  459. {
  460. $this->set_message('max_image_dimension', '%s dimensions was not detected.');
  461. return FALSE;
  462. }
  463.  
  464. if ($image_dimensions[0] < $dimensions[0] && $image_dimensions[1] < $dimensions[1])
  465. {
  466. return TRUE;
  467. }
  468.  
  469. $this->set_message('max_image_dimension', '%s image size is too big.');
  470. return FALSE;
  471. }
  472.  
  473. /**
  474.   * Image is smaller than given dimension
  475.   *
  476.   * @param mixed $file
  477.   * @param array $dim
  478.   *
  479.   * @return boolean
  480.   * @access public
  481.   */
  482. public function min_image_dimension($file, $dimensions)
  483. {
  484. $dimensions = explode(',', $dimensions);
  485.  
  486. if (count($dimensions) !== 2)
  487. {
  488. // Bad size given
  489. $this->set_message('min_image_dimension', '%s has invalid rule expected similar to 150,300');
  490. return FALSE;
  491. }
  492.  
  493. // Get image size
  494. $image_dimensions = $this->_get_image_dimension($file['tmp_name']);
  495. if ( ! $image_dimensions)
  496. {
  497. $this->set_message('min_image_dimension', '%s dimensions was not detected.');
  498. return FALSE;
  499. }
  500.  
  501. if ($image_dimensions[0] > $dimensions[0] && $image_dimensions[1] > $dimensions[1])
  502. {
  503. return TRUE;
  504. }
  505.  
  506. $this->set_message('min_image_dimension', '%s image size is too big.');
  507. return FALSE;
  508. }
  509.  
  510. /**
  511.   * Determine the image dimension
  512.   *
  513.   * @param mixed $file_name Path to the image file
  514.   *
  515.   * @return array
  516.   * @access private
  517.   */
  518. private function _get_image_dimension($file_name)
  519. {
  520. if (function_exists('getimagesize'))
  521. {
  522. return @getimagesize($file_name);
  523. }
  524.  
  525. return FALSE;
  526. }
  527.  
  528. /**
  529.   * Given an string in format of ###AA converts to number of bits it is assignin
  530.   *
  531.   * @param string $value
  532.   *
  533.   * @return integer
  534.   * @access private
  535.   */
  536. private function _let_to_bit($value)
  537. {
  538. // Split value from name
  539. if( ! preg_match('/([0-9]+)([ptgmkb]{1,2}|)/ui', $value, $matches))
  540. {
  541. // Invalid input
  542. return FALSE;
  543. }
  544.  
  545. if (empty($matches[2]))
  546. {
  547. // No name -> Enter default value
  548. $matches[2] = 'KB';
  549. }
  550.  
  551. if (strlen($matches[2]) == 1)
  552. {
  553. // Shorted name -> full name
  554. $matches[2] .= 'B';
  555. }
  556.  
  557. $bit = (substr($matches[2], -1) == 'B') ? 1024 : 1000;
  558.  
  559. // Calculate bits
  560. switch(strtoupper(substr($matches[2], 0, 1)))
  561. {
  562. case 'P':
  563. $matches[1] *= $bit;
  564. case 'T':
  565. $matches[1] *= $bit;
  566. case 'G':
  567. $matches[1] *= $bit;
  568. case 'M':
  569. $matches[1] *= $bit;
  570. case 'K':
  571. $matches[1] *= $bit;
  572. break;
  573. }
  574.  
  575. // Return the value in bits
  576. return $matches[1];
  577. }
  578.  
  579. // --------------------------------------------------------------------
  580. }
  581.  
  582. /* End of file MY_Form_validation.php */
  583. /* Location: ./system/application/library/MY_Form_validation.php */

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.