PageRenderTime 76ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/system/libraries/Validation.php

https://github.com/lmorchard/friendfeedarchiver
PHP | 1188 lines | 962 code | 71 blank | 155 comment | 47 complexity | b486edb8561af2586965c8a3fd47e82c MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Validation library.
  4. *
  5. * $Id: Validation.php 2111 2008-02-22 01:18:59Z Shadowhand $
  6. *
  7. * @package Validation
  8. * @author Kohana Team
  9. * @copyright (c) 2007-2008 Kohana Team
  10. * @license http://kohanaphp.com/license.html
  11. */
  12. class Validation_Core {
  13. // Instance count
  14. private static $instances = 0;
  15. // Currently validating field
  16. public $current_field = '';
  17. // Enable or disable safe form errors
  18. public $form_safe = FALSE;
  19. // Error message format
  20. public $error_format = '<p class="error">{message}</p>';
  21. public $newline_char = "\n";
  22. // Error messages
  23. public $messages = array();
  24. // Field names, rules, and errors
  25. protected $fields = array();
  26. protected $rules = array();
  27. protected $errors = array();
  28. // Data to validate
  29. protected $data = array();
  30. // Result from validation rules
  31. protected $result;
  32. /**
  33. * @param array array to validate
  34. */
  35. public function __construct( & $data = array())
  36. {
  37. $this->set_data($data);
  38. // Load the default error messages
  39. $this->messages = Kohana::lang('validation');
  40. // Add one more instance to the count
  41. self::$instances++;
  42. Log::add('debug', 'Validation Library Initialized, instance '.self::$instances);
  43. }
  44. /**
  45. * Magically gets a validation variable. This can be an error string or a
  46. * data field, or an array of all field data.
  47. *
  48. * @param string Variable name
  49. * @return string|array The variable contents or NULL if the variable does not exist
  50. */
  51. public function __get($key)
  52. {
  53. if ( ! isset($this->$key))
  54. {
  55. if ($key === 'error_string')
  56. {
  57. // Complete error message string
  58. $messages = FALSE;
  59. foreach(array_keys($this->errors) as $field)
  60. {
  61. $messages .= $this->__get($field.'_error');
  62. }
  63. return $messages;
  64. }
  65. elseif (substr($key, -6) === '_error')
  66. {
  67. // Get the field name
  68. $field = substr($key, 0, -6);
  69. // Return the error messages for this field
  70. $messages = FALSE;
  71. if (isset($this->errors[$field]) AND ! empty($this->errors[$field]))
  72. {
  73. foreach($this->errors[$field] as $error)
  74. {
  75. // Replace the message with the error in the html error string
  76. $messages .= str_replace('{message}', $error, $this->error_format).$this->newline_char;
  77. }
  78. }
  79. return $messages;
  80. }
  81. elseif (isset($this->data[$key]))
  82. {
  83. return $this->data[$key];
  84. }
  85. elseif ($key === 'data_array')
  86. {
  87. $data = array();
  88. foreach (array_keys($this->rules) as $key)
  89. {
  90. if (isset($this->data[$key]))
  91. {
  92. $data[$key] = $this->data[$key];
  93. }
  94. }
  95. return $data;
  96. }
  97. }
  98. }
  99. /**
  100. * This function takes an array of key names, rules, and field names as
  101. * input and sets internal field information.
  102. *
  103. * @param string|array Key names
  104. * @param string Rules
  105. * @param string Field name
  106. * @return void
  107. */
  108. public function set_rules($data, $rules = '', $field = FALSE)
  109. {
  110. // Normalize rules to an array
  111. if ( ! is_array($data))
  112. {
  113. if ($rules == '') return FALSE;
  114. // Make data into an array
  115. $data = array($data => array($field, $rules));
  116. }
  117. // Set the field information
  118. foreach ($data as $name => $rules)
  119. {
  120. if (is_array($rules))
  121. {
  122. if (count($rules) > 1)
  123. {
  124. $field = current($rules);
  125. $rules = next($rules);
  126. }
  127. else
  128. {
  129. $rules = current($rules);
  130. }
  131. }
  132. // Empty field names default to the name of the element
  133. $this->fields[$name] = empty($field) ? $name : $field;
  134. $this->rules[$name] = $rules;
  135. // Prevent fields from getting the wrong name
  136. unset($field);
  137. }
  138. }
  139. /**
  140. * Lets users set their own error messages on the fly.
  141. * Note - The key name has to match the function name that it corresponds to.
  142. *
  143. * @param string Function name
  144. * @param string Error message
  145. * @return void
  146. */
  147. public function set_message($func, $message = '')
  148. {
  149. if ( ! is_array($func))
  150. {
  151. $func = array($func => $message);
  152. }
  153. foreach($func as $name => $message)
  154. {
  155. $this->messages[$name] = $message;
  156. }
  157. }
  158. /**
  159. * @param array Data to validate
  160. * @return void
  161. */
  162. public function set_data( & $data)
  163. {
  164. if ( ! empty($data) AND is_array($data))
  165. {
  166. $this->data =& $data;
  167. }
  168. else
  169. {
  170. $this->data =& $_POST;
  171. }
  172. }
  173. /**
  174. * Allows the user to change the error message format. Error formats must
  175. * contain the string "{message}" or Kohana_Exception will be triggered.
  176. *
  177. * @param string Error message format
  178. * @return void
  179. */
  180. public function error_format($string = '')
  181. {
  182. if (strpos((string) $string, '{message}') === FALSE)
  183. throw new Kohana_Exception('validation.error_format');
  184. $this->error_format = $string;
  185. }
  186. /**
  187. * @param string Function name
  188. * @param string field name
  189. * @return void
  190. */
  191. public function add_error($func, $field)
  192. {
  193. // Set the friendly field name
  194. $friendly = isset($this->fields[$field]) ? $this->fields[$field] : $field;
  195. // Fetch the message
  196. $message = isset($this->messages[$func]) ? $this->messages[$func] : $this->messages['unknown_error'];
  197. // Replacements in strings
  198. $replace = array_slice(func_get_args(), 1);
  199. if ( ! empty($replace) AND $replace[0] === $field)
  200. {
  201. // Add the friendly name instead of the field name
  202. $replace[0] = $friendly;
  203. }
  204. // Add the field name into the message, if there is a place for it
  205. $message = (strpos($message, '%s') !== FALSE) ? vsprintf($message, $replace) : $message;
  206. $this->errors[$field][] = $message;
  207. }
  208. /**
  209. * This function does all the work.
  210. *
  211. * @return boolean The validation result
  212. */
  213. public function run()
  214. {
  215. // Do we even have any data to process? Mm?
  216. if (count($this->data) == 0 OR count($this->rules) == 0)
  217. {
  218. return FALSE;
  219. }
  220. // Cycle through the rules and test for errors
  221. foreach ($this->rules as $field => $rules)
  222. {
  223. // Set the current field, for other functions to use
  224. $this->current_field = $field;
  225. // Insert uploads into the data
  226. if (strpos($rules, 'upload') !== FALSE AND isset($_FILES[$field]))
  227. {
  228. if (is_array($_FILES[$field]['error']))
  229. {
  230. foreach($_FILES[$field]['error'] as $error)
  231. {
  232. if ($error !== UPLOAD_ERR_NO_FILE)
  233. {
  234. $this->data[$field] = $_FILES[$field];
  235. break;
  236. }
  237. }
  238. }
  239. elseif ($_FILES[$field]['error'] !== UPLOAD_ERR_NO_FILE)
  240. {
  241. $this->data[$field] = $_FILES[$field];
  242. }
  243. }
  244. // Process empty fields
  245. if ( ! isset($this->data[$field]) OR $this->data[$field] == NULL)
  246. {
  247. // This field is required
  248. if (strpos($rules, 'required') !== FALSE)
  249. {
  250. $this->add_error('required', $field);
  251. continue;
  252. }
  253. }
  254. // Loop through the rules and process each one
  255. foreach(explode('|', $rules) as $rule)
  256. {
  257. // To properly handle recursion
  258. $this->run_rule($rule, $field);
  259. // Stop validating when there is an error
  260. if ($this->result === FALSE)
  261. break;
  262. }
  263. }
  264. // Run validation finished Event and return
  265. if (count($this->errors) == 0)
  266. {
  267. Event::run('validation.success', $this->data);
  268. return TRUE;
  269. }
  270. else
  271. {
  272. Event::run('validation.failure', $this->data);
  273. return FALSE;
  274. }
  275. }
  276. /**
  277. * Handles recursively calling rules on arrays of data.
  278. *
  279. * @param string Validation rule to be run on the data
  280. * @param string Name of field
  281. * @return void
  282. */
  283. protected function run_rule($rule, $field)
  284. {
  285. // Use key_string to extract the field data
  286. $data = Kohana::key_string($field, $this->data);
  287. // Make sure that data input is not upload data
  288. if (is_array($data) AND ! (isset($data['tmp_name']) AND isset($data['error'])))
  289. {
  290. foreach($data as $key => $value)
  291. {
  292. // Recursion is fun!
  293. $this->run_rule($rule, $field.'.'.$key);
  294. if ($this->result === FALSE)
  295. break;
  296. }
  297. }
  298. else
  299. {
  300. if (strpos($rule, '=') === 0)
  301. {
  302. $rule = substr($rule, 1);
  303. $this->data[$field] = $rule($data);
  304. return;
  305. }
  306. // Handle callback rules
  307. $callback = FALSE;
  308. if (preg_match('/callback_(.+)/', $rule, $match))
  309. {
  310. $callback = $match[1];
  311. }
  312. // Handle params
  313. $params = FALSE;
  314. if (preg_match('/([^\[]*+)\[(.+)\]/', $rule, $match))
  315. {
  316. $rule = $match[1];
  317. $params = preg_split('/(?<!\\\\),/', $match[2]);
  318. $params = str_replace('\,', ',', $params);
  319. }
  320. // Process this field with the rule
  321. if ($callback !== FALSE)
  322. {
  323. if ( ! method_exists(Kohana::instance(), $callback))
  324. throw new Kohana_Exception('validation.invalid_rule', $callback);
  325. $this->result = Kohana::instance()->$callback($data, $params);
  326. }
  327. elseif ($rule === 'matches' OR $rule === 'depends_on')
  328. {
  329. $this->result = $this->$rule($field, $params);
  330. }
  331. elseif (method_exists($this, $rule))
  332. {
  333. $this->result = $this->$rule($data, $params);
  334. }
  335. elseif (is_callable($rule))
  336. {
  337. if (strpos($rule, '::') !== FALSE)
  338. {
  339. $this->result = call_user_func(explode('::', $rule), $data);
  340. }
  341. else
  342. {
  343. $this->result = $rule($data);
  344. }
  345. }
  346. else
  347. {
  348. // Trying to validate with a rule that does not exist? No way!
  349. throw new Kohana_Exception('validation.invalid_rule', $rule);
  350. }
  351. }
  352. }
  353. /**
  354. * @param mixed
  355. * @param array
  356. * @return boolean
  357. */
  358. public function in_array($data, $array = FALSE)
  359. {
  360. if (empty($array) OR ! is_array($array) OR ! in_array($data, $array))
  361. {
  362. $this->add_error('in_array', $this->current_field);
  363. return FALSE;
  364. }
  365. return TRUE;
  366. }
  367. /**
  368. * @param mixed Data to pass to the event
  369. * @param array Events name
  370. * @return boolean|void
  371. */
  372. public function event($data, $events = FALSE)
  373. {
  374. // Validate the events
  375. if (empty($events) OR ! is_array($events))
  376. {
  377. $this->add_error('event', $this->current_field);
  378. return FALSE;
  379. }
  380. // Run the requested events
  381. foreach($events as $event)
  382. {
  383. Event::run('validation.'.$event, $data);
  384. }
  385. }
  386. /**
  387. * @param array
  388. * @param array
  389. * @return boolean|void
  390. */
  391. public function upload($data, $params = FALSE)
  392. {
  393. // By default, nothing is allowed
  394. $allowed = FALSE;
  395. // Maximum sizes of various attributes
  396. $fileinfo = array
  397. (
  398. 'file' => FALSE,
  399. 'human' => FALSE,
  400. 'max_width' => FALSE,
  401. 'max_height' => FALSE,
  402. 'min_width' => FALSE,
  403. 'min_height' => FALSE
  404. );
  405. if ($data === $this->data[$this->current_field])
  406. {
  407. // Clear the raw upload data, it's internal now
  408. $this->data[$this->current_field] = NULL;
  409. }
  410. if (is_array($data['name']))
  411. {
  412. // Handle an array of inputs
  413. $files = $data;
  414. $total = count($files['name']);
  415. for ($i = 0; $i < $total; $i++)
  416. {
  417. if (empty($files['name'][$i]))
  418. continue;
  419. // Fake a single upload input
  420. $data = array
  421. (
  422. 'name' => $files['name'][$i],
  423. 'type' => $files['type'][$i],
  424. 'size' => $files['size'][$i],
  425. 'tmp_name' => $files['tmp_name'][$i],
  426. 'error' => $files['error'][$i]
  427. );
  428. // Recursion
  429. if ( ! $this->upload($data, $params))
  430. return FALSE;
  431. }
  432. // All files uploaded successfully
  433. return empty($this->errors);
  434. }
  435. // Parse addition parameters
  436. if (is_array($params) AND ! empty($params))
  437. {
  438. // Creates a mirrored array: foo=foo,bar=bar
  439. $params = array_combine($params, $params);
  440. foreach($params as $param)
  441. {
  442. if (preg_match('/[0-9]+x[0-9]+(?:-[0-9]+x[0-9]+)?/', $param))
  443. {
  444. // Image size, eg: 200x100, 20x10-200x100
  445. $param = (strpos($param, '-') === FALSE) ? $param.'-'.$param : $param;
  446. list($min, $max) = explode('-', $param);
  447. list($fileinfo['max_width'], $fileinfo['max_height']) = explode('x', $max);
  448. list($fileinfo['min_width'], $fileinfo['min_height']) = explode('x', $min);
  449. }
  450. elseif (preg_match('/[0-9]+[BKMG]/i', $param))
  451. {
  452. // Maximum file size, eg: 1M
  453. $fileinfo['human'] = strtoupper($param);
  454. switch(strtoupper(substr($param, -1)))
  455. {
  456. case 'G': $param = intval($param) * pow(1024, 3); break;
  457. case 'M': $param = intval($param) * pow(1024, 2); break;
  458. case 'K': $param = intval($param) * pow(1024, 1); break;
  459. default: $param = intval($param); break;
  460. }
  461. $fileinfo['file'] = $param;
  462. }
  463. else
  464. {
  465. $allowed[strtolower($param)] = strtolower($param);
  466. }
  467. }
  468. }
  469. // Uploads must use a white-list of allowed file types
  470. if (empty($allowed))
  471. throw new Kohana_Exception('upload.set_allowed');
  472. // Make sure that UPLOAD_ERR_EXTENSION is defined
  473. defined('UPLOAD_ERR_EXTENSION') or define('UPLOAD_ERR_EXTENSION', 8);
  474. // Fetch the real upload path
  475. if (($upload_path = str_replace('\\', '/', realpath(Config::item('upload.upload_directory')))) == FALSE)
  476. {
  477. $data['error'] = UPLOAD_ERR_NO_TMP_DIR;
  478. }
  479. // Validate the upload path
  480. if ( ! is_dir($upload_path) OR ! is_writable($upload_path))
  481. {
  482. $data['error'] = UPLOAD_ERR_CANT_WRITE;
  483. }
  484. // Error code definitions available at:
  485. // http://us.php.net/manual/en/features.file-upload.errors.php
  486. switch($data['error'])
  487. {
  488. // Valid upload
  489. case UPLOAD_ERR_OK:
  490. break;
  491. // Upload to large, based on php.ini settings
  492. case UPLOAD_ERR_INI_SIZE:
  493. if ($fileinfo['human'] == FALSE)
  494. {
  495. $fileinfo['human'] = ini_get('upload_max_filesize');
  496. }
  497. $this->add_error('max_size', $this->current_field, $fileinfo['human']);
  498. return FALSE;
  499. break;
  500. // Kohana does not allow the MAX_FILE_SIZE input to control filesize
  501. case UPLOAD_ERR_FORM_SIZE:
  502. throw new Kohana_Exception('upload.max_file_size');
  503. break;
  504. // User aborted the upload, or a connection error occurred
  505. case UPLOAD_ERR_PARTIAL:
  506. $this->add_error('user_aborted', $this->current_field);
  507. return FALSE;
  508. break;
  509. // No file was uploaded, or an extension blocked the upload
  510. case UPLOAD_ERR_NO_FILE:
  511. case UPLOAD_ERR_EXTENSION:
  512. return FALSE;
  513. break;
  514. // No temporary directory set in php.ini
  515. case UPLOAD_ERR_NO_TMP_DIR:
  516. throw new Kohana_Exception('upload.no_tmp_dir');
  517. break;
  518. // Could not write to the temporary directory
  519. case UPLOAD_ERR_CANT_WRITE:
  520. throw new Kohana_Exception('upload.tmp_unwritable', $upload_path);
  521. break;
  522. }
  523. // Validate the uploaded file
  524. if ( ! isset($data['tmp_name']) OR ! is_uploaded_file($data['tmp_name']))
  525. return FALSE;
  526. if ($fileinfo['file'] AND $data['size'] > $fileinfo['file'])
  527. {
  528. $this->add_error('max_size', $this->current_field, $fileinfo['human']);
  529. return FALSE;
  530. }
  531. // Find the MIME type of the file. Although the mime type is available
  532. // in the upload data, it can easily be faked. Instead, we use the
  533. // server filesystem functions (if possible) to determine the MIME type.
  534. if (preg_match('/jpe?g|png|[gt]if|bmp/', implode(' ', $allowed)))
  535. {
  536. // Use getimagesize() to find the mime type on images
  537. $mime = @getimagesize($data['tmp_name']);
  538. // Validate height and width
  539. if ($fileinfo['max_width'] AND $mime[0] > $fileinfo['max_width'])
  540. {
  541. $this->add_error('max_width', $this->current_field, $fileinfo['max_width']);
  542. return FALSE;
  543. }
  544. elseif ($fileinfo['min_width'] AND $mime[0] < $fileinfo['min_width'])
  545. {
  546. $this->add_error('min_width', $this->current_field, $fileinfo['min_width']);
  547. return FALSE;
  548. }
  549. elseif ($fileinfo['max_height'] AND $mime[1] > $fileinfo['max_height'])
  550. {
  551. $this->add_error('max_height', $this->current_field, $fileinfo['max_height']);
  552. return FALSE;
  553. }
  554. elseif ($fileinfo['min_height'] AND $mime[1] < $fileinfo['min_height'])
  555. {
  556. $this->add_error('min_height', $this->current_field, $fileinfo['min_height']);
  557. return FALSE;
  558. }
  559. // Set mime type
  560. $mime = isset($mime['mime']) ? $mime['mime'] : FALSE;
  561. }
  562. elseif (function_exists('finfo_open'))
  563. {
  564. // Try using the fileinfo extension
  565. $finfo = finfo_open(FILEINFO_MIME);
  566. $mime = finfo_file($finfo, $data['tmp_name']);
  567. finfo_close($finfo);
  568. }
  569. elseif (ini_get('mime_magic.magicfile') AND function_exists('mime_content_type'))
  570. {
  571. // Use mime_content_type(), deprecated by PHP
  572. $mime = mime_content_type($data['tmp_name']);
  573. }
  574. elseif (file_exists($cmd = trim(exec('which file'))))
  575. {
  576. // Use the UNIX 'file' command
  577. $mime = escapeshellarg($data['tmp_name']);
  578. $mime = trim(exec($cmd.' -bi '.$mime));
  579. }
  580. else
  581. {
  582. // Trust the browser, as a last resort
  583. $mime = $data['type'];
  584. }
  585. // Find the list of valid mime types by the extension of the file
  586. $ext = strtolower(end(explode('.', $data['name'])));
  587. // Validate file mime type based on the extension. Because the mime type
  588. // is trusted (validated by the server), we check if the mime is in the
  589. // list of known mime types for the current extension.
  590. if ($ext == FALSE OR ! in_array($ext, $allowed) OR array_search($mime, Config::item('mimes.'.$ext)) === NULL)
  591. {
  592. $this->add_error('invalid_type', $this->current_field);
  593. return FALSE;
  594. }
  595. // Removes spaces from the filename if configured to do so
  596. $filename = Config::item('upload.remove_spaces') ? preg_replace('/\s+/', '_', $data['name']) : $data['name'];
  597. // Change the filename to a full path name
  598. $filename = $upload_path.'/'.$filename;
  599. // Move the upload file to the new location
  600. move_uploaded_file($data['tmp_name'], $filename);
  601. if ( ! empty($this->data[$this->current_field]))
  602. {
  603. // Conver the returned data into an array
  604. $this->data[$this->current_field] = array($this->data[$this->current_field]);
  605. }
  606. // Set the data to the current field name
  607. if (is_array($this->data[$this->current_field]))
  608. {
  609. $this->data[$this->current_field][] = $filename;
  610. }
  611. else
  612. {
  613. $this->data[$this->current_field] = $filename;
  614. }
  615. return TRUE;
  616. }
  617. /**
  618. * @param string String to check
  619. * @param array Length
  620. * @return boolean
  621. */
  622. public function required($str, $length = FALSE)
  623. {
  624. if ($str === '' OR $str === FALSE OR (is_array($str) AND empty($str)))
  625. {
  626. $this->add_error('required', $this->current_field);
  627. return FALSE;
  628. }
  629. elseif ($length != FALSE AND is_array($length))
  630. {
  631. if (count($length) > 1)
  632. {
  633. // Get the min and max length
  634. list ($min, $max) = $length;
  635. // Change length to the length of the string
  636. $length = utf8::strlen($str);
  637. // Test min length
  638. if ($length < $min)
  639. {
  640. $this->add_error('min_length', $this->current_field, (int) $min);
  641. return FALSE;
  642. }
  643. // Test max length
  644. elseif ($length > $max)
  645. {
  646. $this->add_error('max_length', $this->current_field, (int) $max);
  647. return FALSE;
  648. }
  649. }
  650. elseif (strlen($str) !== (int) current($length))
  651. {
  652. // Test exact length
  653. $this->add_error('exact_length', $this->current_field, (int) current($length));
  654. return FALSE;
  655. }
  656. }
  657. else
  658. {
  659. return TRUE;
  660. }
  661. }
  662. /**
  663. * Match one field to another.
  664. *
  665. * @param string First field
  666. * @param string Field to match to first
  667. * @return boolean
  668. */
  669. public function matches($field, $match)
  670. {
  671. $match = trim(current($match));
  672. if ((isset($this->data[$field]) AND $this->data[$field] === $this->data[$match])
  673. OR ( ! isset($this->data[$field]) AND ! isset($this->data[$match])))
  674. {
  675. return TRUE;
  676. }
  677. else
  678. {
  679. $this->add_error('matches', $field, $match);
  680. return FALSE;
  681. }
  682. }
  683. /**
  684. * Check a string for a minimum length.
  685. *
  686. * @param string String to validate
  687. * @param integer Minimum length
  688. * @return boolean
  689. */
  690. public function min_length($str, $val)
  691. {
  692. $val = is_array($val) ? (string) current($val) : FALSE;
  693. if (ctype_digit($val))
  694. {
  695. if (utf8::strlen($str) >= $val)
  696. return TRUE;
  697. }
  698. $this->add_error('min_length', $this->current_field, (int) $val);
  699. return FALSE;
  700. }
  701. /**
  702. * Check a string for a maximum length.
  703. *
  704. * @param string String to validate
  705. * @param integer Maximum length
  706. * @return boolean
  707. */
  708. public function max_length($str, $val)
  709. {
  710. $val = is_array($val) ? (string) current($val) : FALSE;
  711. if (ctype_digit($val))
  712. {
  713. if (utf8::strlen($str) <= $val)
  714. return TRUE;
  715. }
  716. $this->add_error('max_length', $this->current_field, (int) $val);
  717. return FALSE;
  718. }
  719. /**
  720. * Check a string for an exact length.
  721. *
  722. * @param string String to validate
  723. * @param integer Length
  724. * @return boolean
  725. */
  726. public function exact_length($str, $val)
  727. {
  728. $val = is_array($val) ? (string) current($val) : FALSE;
  729. if (ctype_digit($val))
  730. {
  731. if (utf8::strlen($str) == $val)
  732. return TRUE;
  733. }
  734. $this->add_error('exact_length', $this->current_field, (int) $val);
  735. return FALSE;
  736. }
  737. /**
  738. * Validate URL.
  739. *
  740. * @param string URL
  741. * @param string Scheme
  742. * @return boolean
  743. */
  744. public function valid_url($url, $scheme = '')
  745. {
  746. if (empty($scheme))
  747. {
  748. $scheme = 'http';
  749. }
  750. if (is_array($scheme))
  751. {
  752. $scheme = current($scheme);
  753. }
  754. if (valid::url($url, $scheme))
  755. return TRUE;
  756. $this->add_error('valid_url', $this->current_field, $url);
  757. return FALSE;
  758. }
  759. /**
  760. * Valid Email, Commonly used characters only.
  761. *
  762. * @param string Email address
  763. * @return boolean
  764. */
  765. public function valid_email($email)
  766. {
  767. if (valid::email($email))
  768. return TRUE;
  769. $this->add_error('valid_email', $this->current_field);
  770. return FALSE;
  771. }
  772. /**
  773. * Valid Email, RFC compliant version
  774. *
  775. * @param string Email address
  776. * @return boolean
  777. */
  778. public function valid_email_rfc($email)
  779. {
  780. if (valid::email_rfc($email))
  781. return TRUE;
  782. $this->add_error('valid_email', $this->current_field);
  783. return FALSE;
  784. }
  785. /**
  786. * Validate IP Address.
  787. *
  788. * @param string IP address
  789. * @return boolean
  790. */
  791. public function valid_ip($ip)
  792. {
  793. if (valid::ip($ip))
  794. return TRUE;
  795. $this->add_error('valid_ip', $this->current_field);
  796. return FALSE;
  797. }
  798. /**
  799. * Alphabetic characters only.
  800. *
  801. * @param string String to validate
  802. * @return boolean
  803. */
  804. public function alpha($str)
  805. {
  806. if (valid::alpha($str))
  807. return TRUE;
  808. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.alpha'));
  809. return FALSE;
  810. }
  811. /**
  812. * Alphabetic characters only (UTF-8 compatible).
  813. *
  814. * @param string String to validate
  815. * @return boolean
  816. */
  817. public function utf8_alpha($str)
  818. {
  819. if (valid::alpha($str, TRUE))
  820. return TRUE;
  821. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.alpha'));
  822. return FALSE;
  823. }
  824. /**
  825. * Alphabetic and numeric characters only.
  826. *
  827. * @param string String to validate
  828. * @return boolean
  829. */
  830. public function alpha_numeric($str)
  831. {
  832. if (valid::alpha_numeric($str))
  833. return TRUE;
  834. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.alpha_numeric'));
  835. return FALSE;
  836. }
  837. /**
  838. * Alphabetic and numeric characters only (UTF-8 compatible).
  839. *
  840. * @param string String to validate
  841. * @return boolean
  842. */
  843. public function utf8_alpha_numeric($str)
  844. {
  845. if (valid::alpha_numeric($str, TRUE))
  846. return TRUE;
  847. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.alpha_numeric'));
  848. return FALSE;
  849. }
  850. /**
  851. * Alpha-numeric with underscores and dashes.
  852. *
  853. * @param string String to validate
  854. * @return boolean
  855. */
  856. public function alpha_dash($str)
  857. {
  858. if (valid::alpha_dash($str))
  859. return TRUE;
  860. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.alpha_dash'));
  861. return FALSE;
  862. }
  863. /**
  864. * Alpha-numeric with underscores and dashes (UTF-8 compatible).
  865. *
  866. * @param string String to validate
  867. * @return boolean
  868. */
  869. public function utf8_alpha_dash($str)
  870. {
  871. if (valid::alpha_dash($str, TRUE))
  872. return TRUE;
  873. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.alpha_dash'));
  874. return FALSE;
  875. }
  876. /**
  877. * Digits 0-9, no dots or dashes.
  878. *
  879. * @param string String to validate
  880. * @return boolean
  881. */
  882. public function digit($str)
  883. {
  884. if (valid::digit($str))
  885. return TRUE;
  886. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.digit'));
  887. return FALSE;
  888. }
  889. /**
  890. * Digits 0-9, no dots or dashes (UTF-8 compatible).
  891. *
  892. * @param string String to validate
  893. * @return boolean
  894. */
  895. public function utf8_digit($str)
  896. {
  897. if (valid::digit($str, TRUE))
  898. return TRUE;
  899. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.digit'));
  900. return FALSE;
  901. }
  902. /**
  903. * Digits 0-9 (negative and decimal numbers allowed).
  904. *
  905. * @param string String to validate
  906. * @return boolean
  907. */
  908. public function numeric($str)
  909. {
  910. if (valid::numeric($str))
  911. return TRUE;
  912. $this->add_error('valid_type', $this->current_field, Kohana::lang('validation.numeric'));
  913. return FALSE;
  914. }
  915. /**
  916. * Test that a field is between a range.
  917. *
  918. * @param integer Number to validate
  919. * @param array Renges
  920. * @return boolean
  921. */
  922. public function range($num, $ranges)
  923. {
  924. if (is_array($ranges) AND ! empty($ranges))
  925. {
  926. // Number is always an integer
  927. $num = (float) $num;
  928. foreach($ranges as $range)
  929. {
  930. list($low, $high) = explode(':', $range, 2);
  931. if ($low == 'FALSE' AND $num <= (float) $high)
  932. return TRUE;
  933. if ($high == 'FALSE' AND $num >= (float) $low)
  934. return TRUE;
  935. if ($num >= (float) $low AND $num <= (float) $high)
  936. return TRUE;
  937. }
  938. }
  939. $this->add_error('range', $this->current_field);
  940. return FALSE;
  941. }
  942. /**
  943. * Check dependency between fields.
  944. *
  945. * @param string First field
  946. * @param string Field which the first field is depend on it
  947. * @return boolean
  948. */
  949. public function depends_on($field, $depends_on)
  950. {
  951. $depends_on = trim(current($depends_on));
  952. if ($depends_on != NULL AND isset($this->data[$field]) AND isset($this->data[$depends_on]))
  953. {
  954. return TRUE;
  955. }
  956. $depends_on = isset($this->fields[$depends_on]) ? $this->fields[$depends_on] : $depends_on;
  957. $this->add_error('depends_on', $field, $depends_on);
  958. return FALSE;
  959. }
  960. /**
  961. * Test a field against a regex rule
  962. *
  963. * @param string String to test
  964. * @param string Regular expression to run
  965. * @return boolean
  966. */
  967. public function regex($str, $regex)
  968. {
  969. if ( ! empty($regex))
  970. {
  971. // Only one regex validation per field
  972. $regex = current($regex);
  973. if (preg_match($regex, $str))
  974. {
  975. // Regex matches, return
  976. return TRUE;
  977. }
  978. }
  979. $this->add_error('regex', $this->current_field);
  980. return FALSE;
  981. }
  982. /**
  983. * This function allows HTML to be safely shown in a form.
  984. * Special characters are converted.
  985. *
  986. * @param string HTML
  987. * @return string Prepped HTML
  988. */
  989. public function prep_for_form($str = '')
  990. {
  991. if ($this->form_safe == FALSE OR $str == '')
  992. return $str;
  993. return html::specialchars($str);
  994. }
  995. /**
  996. * @param string URL
  997. * @return void
  998. */
  999. public function prep_url($str = '')
  1000. {
  1001. if ($str == 'http://' OR $str == '')
  1002. {
  1003. $this->data[$this->current_field] = '';
  1004. return;
  1005. }
  1006. if (substr($str, 0, 7) != 'http://' AND substr($str, 0, 8) != 'https://')
  1007. {
  1008. $str = 'http://'.$str;
  1009. }
  1010. $this->data[$this->current_field] = $str;
  1011. }
  1012. /**
  1013. * Strip image tags from string.
  1014. *
  1015. * @param string HTML
  1016. * @return void
  1017. */
  1018. public function strip_image_tags($str)
  1019. {
  1020. $this->data[$this->current_field] = security::strip_image_tags($str);
  1021. }
  1022. /**
  1023. * XSS clean string.
  1024. *
  1025. * @param string String to be clean
  1026. * @return void
  1027. */
  1028. public function xss_clean($str)
  1029. {
  1030. $this->data[$this->current_field] = Kohana::instance()->input->xss_clean($str);
  1031. }
  1032. /**
  1033. * Convert PHP tags to entities.
  1034. *
  1035. * @param string
  1036. * @return void
  1037. */
  1038. public function encode_php_tags($str)
  1039. {
  1040. $this->data[$this->current_field] = security::encode_php_tags($str);
  1041. }
  1042. } // End Validation Class