PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/inc/Validate.php

http://github.com/chregu/rss2twi.php
PHP | 1116 lines | 893 code | 38 blank | 185 comment | 118 complexity | bccdfaedfbe218c1bceebbf7214e78f2 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. /**
  3. * Validation class
  4. *
  5. * Copyright (c) 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox, Amir Saied
  6. *
  7. * This source file is subject to the New BSD license, That is bundled
  8. * with this package in the file LICENSE, and is available through
  9. * the world-wide-web at
  10. * http://www.opensource.org/licenses/bsd-license.php
  11. * If you did not receive a copy of the new BSDlicense and are unable
  12. * to obtain it through the world-wide-web, please send a note to
  13. * pajoye@php.net so we can mail you a copy immediately.
  14. *
  15. * Author: Tomas V.V.Cox <cox@idecnet.com>
  16. * Pierre-Alain Joye <pajoye@php.net>
  17. * Amir Mohammad Saied <amir@php.net>
  18. *
  19. *
  20. * Package to validate various datas. It includes :
  21. * - numbers (min/max, decimal or not)
  22. * - email (syntax, domain check)
  23. * - string (predifined type alpha upper and/or lowercase, numeric,...)
  24. * - date (min, max, rfc822 compliant)
  25. * - uri (RFC2396)
  26. * - possibility valid multiple data with a single method call (::multiple)
  27. *
  28. * @category Validate
  29. * @package Validate
  30. * @author Tomas V.V.Cox <cox@idecnet.com>
  31. * @author Pierre-Alain Joye <pajoye@php.net>
  32. * @author Amir Mohammad Saied <amir@php.net>
  33. * @copyright 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox,Amir Mohammad Saied
  34. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  35. * @version CVS: $Id: Validate.php,v 1.134 2009/01/28 12:27:33 davidc Exp $
  36. * @link http://pear.php.net/package/Validate
  37. */
  38. /**
  39. * Methods for common data validations
  40. */
  41. define('VALIDATE_NUM', '0-9');
  42. define('VALIDATE_SPACE', '\s');
  43. define('VALIDATE_ALPHA_LOWER', 'a-z');
  44. define('VALIDATE_ALPHA_UPPER', 'A-Z');
  45. define('VALIDATE_ALPHA', VALIDATE_ALPHA_LOWER . VALIDATE_ALPHA_UPPER);
  46. define('VALIDATE_EALPHA_LOWER', VALIDATE_ALPHA_LOWER . 'áéíóúýàèìòùäëïöüÿâêîôûãñõ¨åæç½ðøþß');
  47. define('VALIDATE_EALPHA_UPPER', VALIDATE_ALPHA_UPPER . 'ÁÉÍÓÚÝÀÈÌÒÙÄËÏÖܾÂÊÎÔÛÃÑÕ¦ÅÆǼÐØÞ');
  48. define('VALIDATE_EALPHA', VALIDATE_EALPHA_LOWER . VALIDATE_EALPHA_UPPER);
  49. define('VALIDATE_PUNCTUATION', VALIDATE_SPACE . '\.,;\:&"\'\?\!\(\)');
  50. define('VALIDATE_NAME', VALIDATE_EALPHA . VALIDATE_SPACE . "'" . "-");
  51. define('VALIDATE_STREET', VALIDATE_NUM . VALIDATE_NAME . "/\\ºª\.");
  52. define('VALIDATE_ITLD_EMAILS', 1);
  53. define('VALIDATE_GTLD_EMAILS', 2);
  54. define('VALIDATE_CCTLD_EMAILS', 4);
  55. define('VALIDATE_ALL_EMAILS', 8);
  56. /**
  57. * Validation class
  58. *
  59. * Package to validate various datas. It includes :
  60. * - numbers (min/max, decimal or not)
  61. * - email (syntax, domain check)
  62. * - string (predifined type alpha upper and/or lowercase, numeric,...)
  63. * - date (min, max)
  64. * - uri (RFC2396)
  65. * - possibility valid multiple data with a single method call (::multiple)
  66. *
  67. * @category Validate
  68. * @package Validate
  69. * @author Tomas V.V.Cox <cox@idecnet.com>
  70. * @author Pierre-Alain Joye <pajoye@php.net>
  71. * @author Amir Mohammad Saied <amir@php.net>
  72. * @copyright 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox,Amir Mohammad Saied
  73. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  74. * @version Release: @package_version@
  75. * @link http://pear.php.net/package/Validate
  76. */
  77. class Validate
  78. {
  79. /**
  80. * International Top-Level Domain
  81. *
  82. * This is an array of the known international
  83. * top-level domain names.
  84. *
  85. * @access protected
  86. * @var array $_iTld (International top-level domains)
  87. */
  88. var $_itld = array(
  89. 'arpa',
  90. 'root',
  91. );
  92. /**
  93. * Generic top-level domain
  94. *
  95. * This is an array of the official
  96. * generic top-level domains.
  97. *
  98. * @access protected
  99. * @var array $_gTld (Generic top-level domains)
  100. */
  101. var $_gtld = array(
  102. 'aero',
  103. 'biz',
  104. 'cat',
  105. 'com',
  106. 'coop',
  107. 'edu',
  108. 'gov',
  109. 'info',
  110. 'int',
  111. 'jobs',
  112. 'mil',
  113. 'mobi',
  114. 'museum',
  115. 'name',
  116. 'net',
  117. 'org',
  118. 'pro',
  119. 'travel',
  120. 'asia',
  121. 'post',
  122. 'tel',
  123. 'geo',
  124. );
  125. /**
  126. * Country code top-level domains
  127. *
  128. * This is an array of the official country
  129. * codes top-level domains
  130. *
  131. * @access protected
  132. * @var array $_ccTld (Country Code Top-Level Domain)
  133. */
  134. var $_cctld = array(
  135. 'ac',
  136. 'ad','ae','af','ag',
  137. 'ai','al','am','an',
  138. 'ao','aq','ar','as',
  139. 'at','au','aw','ax',
  140. 'az','ba','bb','bd',
  141. 'be','bf','bg','bh',
  142. 'bi','bj','bm','bn',
  143. 'bo','br','bs','bt',
  144. 'bu','bv','bw','by',
  145. 'bz','ca','cc','cd',
  146. 'cf','cg','ch','ci',
  147. 'ck','cl','cm','cn',
  148. 'co','cr','cs','cu',
  149. 'cv','cx','cy','cz',
  150. 'de','dj','dk','dm',
  151. 'do','dz','ec','ee',
  152. 'eg','eh','er','es',
  153. 'et','eu','fi','fj',
  154. 'fk','fm','fo','fr',
  155. 'ga','gb','gd','ge',
  156. 'gf','gg','gh','gi',
  157. 'gl','gm','gn','gp',
  158. 'gq','gr','gs','gt',
  159. 'gu','gw','gy','hk',
  160. 'hm','hn','hr','ht',
  161. 'hu','id','ie','il',
  162. 'im','in','io','iq',
  163. 'ir','is','it','je',
  164. 'jm','jo','jp','ke',
  165. 'kg','kh','ki','km',
  166. 'kn','kp','kr','kw',
  167. 'ky','kz','la','lb',
  168. 'lc','li','lk','lr',
  169. 'ls','lt','lu','lv',
  170. 'ly','ma','mc','md',
  171. 'me','mg','mh','mk',
  172. 'ml','mm','mn','mo',
  173. 'mp','mq','mr','ms',
  174. 'mt','mu','mv','mw',
  175. 'mx','my','mz','na',
  176. 'nc','ne','nf','ng',
  177. 'ni','nl','no','np',
  178. 'nr','nu','nz','om',
  179. 'pa','pe','pf','pg',
  180. 'ph','pk','pl','pm',
  181. 'pn','pr','ps','pt',
  182. 'pw','py','qa','re',
  183. 'ro','rs','ru','rw',
  184. 'sa','sb','sc','sd',
  185. 'se','sg','sh','si',
  186. 'sj','sk','sl','sm',
  187. 'sn','so','sr','st',
  188. 'su','sv','sy','sz',
  189. 'tc','td','tf','tg',
  190. 'th','tj','tk','tl',
  191. 'tm','tn','to','tp',
  192. 'tr','tt','tv','tw',
  193. 'tz','ua','ug','uk',
  194. 'us','uy','uz','va',
  195. 'vc','ve','vg','vi',
  196. 'vn','vu','wf','ws',
  197. 'ye','yt','yu','za',
  198. 'zm','zw',
  199. );
  200. /**
  201. * Validate a tag URI (RFC4151)
  202. *
  203. * @param string $uri tag URI to validate
  204. *
  205. * @return boolean true if valid tag URI, false if not
  206. *
  207. * @access private
  208. */
  209. function __uriRFC4151($uri)
  210. {
  211. $datevalid = false;
  212. if (preg_match(
  213. '/^tag:(?<name>.*),(?<date>\d{4}-?\d{0,2}-?\d{0,2}):(?<specific>.*)(.*:)*$/', $uri, $matches)) {
  214. $date = $matches['date'];
  215. $date6 = strtotime($date);
  216. if ((strlen($date) == 4) && $date <= date('Y')) {
  217. $datevalid = true;
  218. } elseif ((strlen($date) == 7) && ($date6 < strtotime("now"))) {
  219. $datevalid = true;
  220. } elseif ((strlen($date) == 10) && ($date6 < strtotime("now"))) {
  221. $datevalid = true;
  222. }
  223. if (self::email($matches['name'])) {
  224. $namevalid = true;
  225. } else {
  226. $namevalid = self::email('info@' . $matches['name']);
  227. }
  228. return $datevalid && $namevalid;
  229. } else {
  230. return false;
  231. }
  232. }
  233. /**
  234. * Validate a number
  235. *
  236. * @param string $number Number to validate
  237. * @param array $options array where:
  238. * 'decimal' is the decimal char or false when decimal
  239. * not allowed.
  240. * i.e. ',.' to allow both ',' and '.'
  241. * 'dec_prec' Number of allowed decimals
  242. * 'min' minimum value
  243. * 'max' maximum value
  244. *
  245. * @return boolean true if valid number, false if not
  246. *
  247. * @access public
  248. */
  249. function number($number, $options = array())
  250. {
  251. $decimal = $dec_prec = $min = $max = null;
  252. if (is_array($options)) {
  253. extract($options);
  254. }
  255. $dec_prec = $dec_prec ? "{1,$dec_prec}" : '+';
  256. $dec_regex = $decimal ? "[$decimal][0-9]$dec_prec" : '';
  257. if (!preg_match("|^[-+]?\s*[0-9]+($dec_regex)?\$|", $number)) {
  258. return false;
  259. }
  260. if ($decimal != '.') {
  261. $number = strtr($number, $decimal, '.');
  262. }
  263. $number = (float)str_replace(' ', '', $number);
  264. if ($min !== null && $min > $number) {
  265. return false;
  266. }
  267. if ($max !== null && $max < $number) {
  268. return false;
  269. }
  270. return true;
  271. }
  272. /**
  273. * Converting a string to UTF-7 (RFC 2152)
  274. *
  275. * @param string $string string to be converted
  276. *
  277. * @return string converted string
  278. *
  279. * @access private
  280. */
  281. function __stringToUtf7($string)
  282. {
  283. $return = '';
  284. $utf7 = array(
  285. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
  286. 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
  287. 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  288. 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
  289. 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
  290. '3', '4', '5', '6', '7', '8', '9', '+', ','
  291. );
  292. $state = 0;
  293. if (!empty($string)) {
  294. $i = 0;
  295. while ($i <= strlen($string)) {
  296. $char = substr($string, $i, 1);
  297. if ($state == 0) {
  298. if ((ord($char) >= 0x7F) || (ord($char) <= 0x1F)) {
  299. if ($char) {
  300. $return .= '&';
  301. }
  302. $state = 1;
  303. } elseif ($char == '&') {
  304. $return .= '&-';
  305. } else {
  306. $return .= $char;
  307. }
  308. } elseif (($i == strlen($string) ||
  309. !((ord($char) >= 0x7F)) || (ord($char) <= 0x1F))) {
  310. if ($state != 1) {
  311. if (ord($char) > 64) {
  312. $return .= '';
  313. } else {
  314. $return .= $utf7[ord($char)];
  315. }
  316. }
  317. $return .= '-';
  318. $state = 0;
  319. } else {
  320. switch($state) {
  321. case 1:
  322. $return .= $utf7[ord($char) >> 2];
  323. $residue = (ord($char) & 0x03) << 4;
  324. $state = 2;
  325. break;
  326. case 2:
  327. $return .= $utf7[$residue | (ord($char) >> 4)];
  328. $residue = (ord($char) & 0x0F) << 2;
  329. $state = 3;
  330. break;
  331. case 3:
  332. $return .= $utf7[$residue | (ord($char) >> 6)];
  333. $return .= $utf7[ord($char) & 0x3F];
  334. $state = 1;
  335. break;
  336. }
  337. }
  338. $i++;
  339. }
  340. return $return;
  341. }
  342. return '';
  343. }
  344. /**
  345. * Validate an email according to full RFC822 (inclusive human readable part)
  346. *
  347. * @param string $email email to validate,
  348. * will return the address for optional dns validation
  349. * @param array $options email() options
  350. *
  351. * @return boolean true if valid email, false if not
  352. *
  353. * @access private
  354. */
  355. function __emailRFC822(&$email, &$options)
  356. {
  357. static $address = null;
  358. static $uncomment = null;
  359. if (!$address) {
  360. // atom = 1*<any CHAR except specials, SPACE and CTLs>
  361. $atom = '[^][()<>@,;:\\".\s\000-\037\177-\377]+\s*';
  362. // qtext = <any CHAR excepting <">, ; => may be folded
  363. // "\" & CR, and including linear-white-space>
  364. $qtext = '[^"\\\\\r]';
  365. // quoted-pair = "\" CHAR ; may quote any char
  366. $quoted_pair = '\\\\.';
  367. // quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or
  368. // ; quoted chars.
  369. $quoted_string = '"(?:' . $qtext . '|' . $quoted_pair . ')*"\s*';
  370. // word = atom / quoted-string
  371. $word = '(?:' . $atom . '|' . $quoted_string . ')';
  372. // local-part = word *("." word) ; uninterpreted
  373. // ; case-preserved
  374. $local_part = $word . '(?:\.\s*' . $word . ')*';
  375. // dtext = <any CHAR excluding "[", ; => may be folded
  376. // "]", "\" & CR, & including linear-white-space>
  377. $dtext = '[^][\\\\\r]';
  378. // domain-literal = "[" *(dtext / quoted-pair) "]"
  379. $domain_literal = '\[(?:' . $dtext . '|' . $quoted_pair . ')*\]\s*';
  380. // sub-domain = domain-ref / domain-literal
  381. // domain-ref = atom ; symbolic reference
  382. $sub_domain = '(?:' . $atom . '|' . $domain_literal . ')';
  383. // domain = sub-domain *("." sub-domain)
  384. $domain = $sub_domain . '(?:\.\s*' . $sub_domain . ')*';
  385. // addr-spec = local-part "@" domain ; global address
  386. $addr_spec = $local_part . '@\s*' . $domain;
  387. // route = 1#("@" domain) ":" ; path-relative
  388. $route = '@' . $domain . '(?:,@\s*' . $domain . ')*:\s*';
  389. // route-addr = "<" [route] addr-spec ">"
  390. $route_addr = '<\s*(?:' . $route . ')?' . $addr_spec . '>\s*';
  391. // phrase = 1*word ; Sequence of words
  392. $phrase = $word . '+';
  393. // mailbox = addr-spec ; simple address
  394. // / phrase route-addr ; name & addr-spec
  395. $mailbox = '(?:' . $addr_spec . '|' . $phrase . $route_addr . ')';
  396. // group = phrase ":" [#mailbox] ";"
  397. $group = $phrase . ':\s*(?:' . $mailbox . '(?:,\s*' . $mailbox . ')*)?;\s*';
  398. // address = mailbox ; one addressee
  399. // / group ; named list
  400. $address = '/^\s*(?:' . $mailbox . '|' . $group . ')$/';
  401. $uncomment =
  402. '/((?:(?:\\\\"|[^("])*(?:' . $quoted_string .
  403. ')?)*)((?<!\\\\)\((?:(?2)|.)*?(?<!\\\\)\))/';
  404. }
  405. // strip comments
  406. $email = preg_replace($uncomment, '$1 ', $email);
  407. return preg_match($address, $email);
  408. }
  409. /**
  410. * Full TLD Validation function
  411. *
  412. * This function is used to make a much more proficient validation
  413. * against all types of official domain names.
  414. *
  415. * @param string $email The email address to check.
  416. * @param array $options The options for validation
  417. *
  418. * @access protected
  419. *
  420. * @return bool True if validating succeeds
  421. */
  422. function _fullTLDValidation($email, $options)
  423. {
  424. $validate = array();
  425. if(!empty($options["VALIDATE_ITLD_EMAILS"])) array_push($validate, 'itld');
  426. if(!empty($options["VALIDATE_GTLD_EMAILS"])) array_push($validate, 'gtld');
  427. if(!empty($options["VALIDATE_CCTLD_EMAILS"])) array_push($validate, 'cctld');
  428. $self = new Validate;
  429. $toValidate = array();
  430. foreach ($validate as $valid) {
  431. $tmpVar = '_' . (string)$valid;
  432. $toValidate[$valid] = $self->{$tmpVar};
  433. }
  434. $e = $self->executeFullEmailValidation($email, $toValidate);
  435. return $e;
  436. }
  437. /**
  438. * Execute the validation
  439. *
  440. * This function will execute the full email vs tld
  441. * validation using an array of tlds passed to it.
  442. *
  443. * @param string $email The email to validate.
  444. * @param array $arrayOfTLDs The array of the TLDs to validate
  445. *
  446. * @access public
  447. *
  448. * @return true or false (Depending on if it validates or if it does not)
  449. */
  450. function executeFullEmailValidation($email, $arrayOfTLDs)
  451. {
  452. $emailEnding = explode('.', $email);
  453. $emailEnding = $emailEnding[count($emailEnding)-1];
  454. foreach ($arrayOfTLDs as $validator => $keys) {
  455. if (in_array($emailEnding, $keys)) {
  456. return true;
  457. }
  458. }
  459. return false;
  460. }
  461. /**
  462. * Validate an email
  463. *
  464. * @param string $email email to validate
  465. * @param mixed boolean (BC) $check_domain Check or not if the domain exists
  466. * array $options associative array of options
  467. * 'check_domain' boolean Check or not if the domain exists
  468. * 'use_rfc822' boolean Apply the full RFC822 grammar
  469. *
  470. * Ex.
  471. * $options = array(
  472. * 'check_domain' => 'true',
  473. * 'fullTLDValidation' => 'true',
  474. * 'use_rfc822' => 'true',
  475. * 'VALIDATE_GTLD_EMAILS' => 'true',
  476. * 'VALIDATE_CCTLD_EMAILS' => 'true',
  477. * 'VALIDATE_ITLD_EMAILS' => 'true',
  478. * );
  479. *
  480. * @return boolean true if valid email, false if not
  481. *
  482. * @access public
  483. */
  484. function email($email, $options = null)
  485. {
  486. $check_domain = false;
  487. $use_rfc822 = false;
  488. if (is_bool($options)) {
  489. $check_domain = $options;
  490. } elseif (is_array($options)) {
  491. extract($options);
  492. }
  493. /**
  494. * Check for IDN usage so we can encode the domain as Punycode
  495. * before continuing.
  496. */
  497. $hasIDNA = false;
  498. if (@include_once('Net/IDNA.php')) {
  499. $hasIDNA = true;
  500. }
  501. if ($hasIDNA === true) {
  502. if (strpos($email, '@') !== false) {
  503. list($name, $domain) = explode('@', $email, 2);
  504. // Check if the domain contains characters > 127 which means
  505. // it's an idn domain name.
  506. $chars = count_chars($domain, 1);
  507. if (!empty($chars) && max(array_keys($chars)) > 127) {
  508. $idna =& Net_IDNA::singleton();
  509. $domain = $idna->encode($domain);
  510. }
  511. $email = "$name@$domain";
  512. }
  513. }
  514. /**
  515. * @todo Fix bug here.. even if it passes this, it won't be passing
  516. * The regular expression below
  517. */
  518. if (isset($fullTLDValidation)) {
  519. //$valid = Validate::_fullTLDValidation($email, $fullTLDValidation);
  520. $valid = Validate::_fullTLDValidation($email, $options);
  521. if (!$valid) {
  522. return false;
  523. }
  524. }
  525. // the base regexp for address
  526. $regex = '&^(?: # recipient:
  527. ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name
  528. ([-\w!\#\$%\&\'*+~/^`|{}]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}]+)*)) #2 OR dot-atom
  529. @(((\[)? #3 domain, 4 as IPv4, 5 optionally bracketed
  530. (?:(?:(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.){3}
  531. (?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))))(?(5)\])|
  532. ((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z0-9](?:[-a-z0-9]*[a-z0-9])?) #6 domain as hostname
  533. \.((?:([^- ])[-a-z]*[-a-z]))) #7 TLD
  534. $&xi';
  535. //checks if exists the domain (MX or A)
  536. if ($use_rfc822? Validate::__emailRFC822($email, $options) :
  537. preg_match($regex, $email)) {
  538. if ($check_domain && function_exists('checkdnsrr')) {
  539. list ($account, $domain) = explode('@', $email);
  540. if (checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A')) {
  541. return true;
  542. }
  543. return false;
  544. }
  545. return true;
  546. }
  547. return false;
  548. }
  549. /**
  550. * Validate a string using the given format 'format'
  551. *
  552. * @param string $string String to validate
  553. * @param array $options Options array where:
  554. * 'format' is the format of the string
  555. * Ex:VALIDATE_NUM . VALIDATE_ALPHA (see constants)
  556. * 'min_length' minimum length
  557. * 'max_length' maximum length
  558. *
  559. * @return boolean true if valid string, false if not
  560. *
  561. * @access public
  562. */
  563. function string($string, $options)
  564. {
  565. $format = null;
  566. $min_length = 0;
  567. $max_length = 0;
  568. if (is_array($options)) {
  569. extract($options);
  570. }
  571. if ($format && !preg_match("|^[$format]*\$|s", $string)) {
  572. return false;
  573. }
  574. if ($min_length && strlen($string) < $min_length) {
  575. return false;
  576. }
  577. if ($max_length && strlen($string) > $max_length) {
  578. return false;
  579. }
  580. return true;
  581. }
  582. /**
  583. * Validate an URI (RFC2396)
  584. * This function will validate 'foobarstring' by default, to get it to validate
  585. * only http, https, ftp and such you have to pass it in the allowed_schemes
  586. * option, like this:
  587. * <code>
  588. * $options = array('allowed_schemes' => array('http', 'https', 'ftp'))
  589. * var_dump(Validate::uri('http://www.example.org', $options));
  590. * </code>
  591. *
  592. * NOTE 1: The rfc2396 normally allows middle '-' in the top domain
  593. * e.g. http://example.co-m should be valid
  594. * However, as '-' is not used in any known TLD, it is invalid
  595. * NOTE 2: As double shlashes // are allowed in the path part, only full URIs
  596. * including an authority can be valid, no relative URIs
  597. * the // are mandatory (optionally preceeded by the 'sheme:' )
  598. * NOTE 3: the full complience to rfc2396 is not achieved by default
  599. * the characters ';/?:@$,' will not be accepted in the query part
  600. * if not urlencoded, refer to the option "strict'"
  601. *
  602. * @param string $url URI to validate
  603. * @param array $options Options used by the validation method.
  604. * key => type
  605. * 'domain_check' => boolean
  606. * Whether to check the DNS entry or not
  607. * 'allowed_schemes' => array, list of protocols
  608. * List of allowed schemes ('http',
  609. * 'ssh+svn', 'mms')
  610. * 'strict' => string the refused chars
  611. * in query and fragment parts
  612. * default: ';/?:@$,'
  613. * empty: accept all rfc2396 foreseen chars
  614. *
  615. * @return boolean true if valid uri, false if not
  616. *
  617. * @access public
  618. */
  619. function uri($url, $options = null)
  620. {
  621. $strict = ';/?:@$,';
  622. $domain_check = false;
  623. $allowed_schemes = null;
  624. if (is_array($options)) {
  625. extract($options);
  626. }
  627. if (is_array($allowed_schemes) &&
  628. in_array("tag", $allowed_schemes)
  629. ) {
  630. if (strpos($url, "tag:") === 0) {
  631. return self::__uriRFC4151($url);
  632. }
  633. }
  634. if (preg_match(
  635. '&^(?:([a-z][-+.a-z0-9]*):)? # 1. scheme
  636. (?:// # authority start
  637. (?:((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();:\&=+$,])*)@)? # 2. authority-userinfo
  638. (?:((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z](?:[a-z0-9]+)?\.?) # 3. authority-hostname OR
  639. |([0-9]{1,3}(?:\.[0-9]{1,3}){3})) # 4. authority-ipv4
  640. (?::([0-9]*))?) # 5. authority-port
  641. ((?:/(?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'():@\&=+$,;])*)*/?)? # 6. path
  642. (?:\?([^#]*))? # 7. query
  643. (?:\#((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();/?:@\&=+$,])*))? # 8. fragment
  644. $&xi', $url, $matches)) {
  645. $scheme = isset($matches[1]) ? $matches[1] : '';
  646. $authority = isset($matches[3]) ? $matches[3] : '' ;
  647. if (is_array($allowed_schemes) &&
  648. !in_array($scheme, $allowed_schemes)
  649. ) {
  650. return false;
  651. }
  652. if (!empty($matches[4])) {
  653. $parts = explode('.', $matches[4]);
  654. foreach ($parts as $part) {
  655. if ($part > 255) {
  656. return false;
  657. }
  658. }
  659. } elseif ($domain_check && function_exists('checkdnsrr')) {
  660. if (!checkdnsrr($authority, 'A')) {
  661. return false;
  662. }
  663. }
  664. if ($strict) {
  665. $strict = '#[' . preg_quote($strict, '#') . ']#';
  666. if ((!empty($matches[7]) && preg_match($strict, $matches[7]))
  667. || (!empty($matches[8]) && preg_match($strict, $matches[8]))) {
  668. return false;
  669. }
  670. }
  671. return true;
  672. }
  673. return false;
  674. }
  675. /**
  676. * Validate date and times. Note that this method need the Date_Calc class
  677. *
  678. * @param string $date Date to validate
  679. * @param array $options array options where :
  680. * 'format' The format of the date (%d-%m-%Y)
  681. * or rfc822_compliant
  682. * 'min' The date has to be greater
  683. * than this array($day, $month, $year)
  684. * or PEAR::Date object
  685. * 'max' The date has to be smaller than
  686. * this array($day, $month, $year)
  687. * or PEAR::Date object
  688. *
  689. * @return boolean true if valid date/time, false if not
  690. *
  691. * @access public
  692. */
  693. function date($date, $options)
  694. {
  695. $max = false;
  696. $min = false;
  697. $format = '';
  698. if (is_array($options)) {
  699. extract($options);
  700. }
  701. if (strtolower($format) == 'rfc822_compliant') {
  702. $preg = '&^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),) \s+
  703. (?:(\d{2})?) \s+
  704. (?:(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)?) \s+
  705. (?:(\d{2}(\d{2})?)?) \s+
  706. (?:(\d{2}?)):(?:(\d{2}?))(:(?:(\d{2}?)))? \s+
  707. (?:[+-]\d{4}|UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Za-ik-z])$&xi';
  708. if (!preg_match($preg, $date, $matches)) {
  709. return false;
  710. }
  711. $year = (int)$matches[4];
  712. $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
  713. 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
  714. $month = array_keys($months, $matches[3]);
  715. $month = (int)$month[0]+1;
  716. $day = (int)$matches[2];
  717. $weekday = $matches[1];
  718. $hour = (int)$matches[6];
  719. $minute = (int)$matches[7];
  720. isset($matches[9]) ? $second = (int)$matches[9] : $second = 0;
  721. if ((strlen($year) != 4) ||
  722. ($day > 31 || $day < 1)||
  723. ($hour > 23) ||
  724. ($minute > 59) ||
  725. ($second > 59)) {
  726. return false;
  727. }
  728. } else {
  729. $date_len = strlen($format);
  730. for ($i = 0; $i < $date_len; $i++) {
  731. $c = $format{$i};
  732. if ($c == '%') {
  733. $next = $format{$i + 1};
  734. switch ($next) {
  735. case 'j':
  736. case 'd':
  737. if ($next == 'j') {
  738. $day = (int)Validate::_substr($date, 1, 2);
  739. } else {
  740. $day = (int)Validate::_substr($date, 0, 2);
  741. }
  742. if ($day < 1 || $day > 31) {
  743. return false;
  744. }
  745. break;
  746. case 'm':
  747. case 'n':
  748. if ($next == 'm') {
  749. $month = (int)Validate::_substr($date, 0, 2);
  750. } else {
  751. $month = (int)Validate::_substr($date, 1, 2);
  752. }
  753. if ($month < 1 || $month > 12) {
  754. return false;
  755. }
  756. break;
  757. case 'Y':
  758. case 'y':
  759. if ($next == 'Y') {
  760. $year = Validate::_substr($date, 4);
  761. $year = (int)$year?$year:'';
  762. } else {
  763. $year = (int)(substr(date('Y'), 0, 2) .
  764. Validate::_substr($date, 2));
  765. }
  766. if (strlen($year) != 4 || $year < 0 || $year > 9999) {
  767. return false;
  768. }
  769. break;
  770. case 'g':
  771. case 'h':
  772. if ($next == 'g') {
  773. $hour = Validate::_substr($date, 1, 2);
  774. } else {
  775. $hour = Validate::_substr($date, 2);
  776. }
  777. if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 12) {
  778. return false;
  779. }
  780. break;
  781. case 'G':
  782. case 'H':
  783. if ($next == 'G') {
  784. $hour = Validate::_substr($date, 1, 2);
  785. } else {
  786. $hour = Validate::_substr($date, 2);
  787. }
  788. if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 24) {
  789. return false;
  790. }
  791. break;
  792. case 's':
  793. case 'i':
  794. $t = Validate::_substr($date, 2);
  795. if (!preg_match('/^\d+$/', $t) || $t < 0 || $t > 59) {
  796. return false;
  797. }
  798. break;
  799. default:
  800. trigger_error("Not supported char `$next' after % in offset " . ($i+2), E_USER_WARNING);
  801. }
  802. $i++;
  803. } else {
  804. //literal
  805. if (Validate::_substr($date, 1) != $c) {
  806. return false;
  807. }
  808. }
  809. }
  810. }
  811. // there is remaing data, we don't want it
  812. if (strlen($date) && (strtolower($format) != 'rfc822_compliant')) {
  813. return false;
  814. }
  815. if (isset($day) && isset($month) && isset($year)) {
  816. if (!checkdate($month, $day, $year)) {
  817. return false;
  818. }
  819. if (strtolower($format) == 'rfc822_compliant') {
  820. if ($weekday != date("D", mktime(0, 0, 0, $month, $day, $year))) {
  821. return false;
  822. }
  823. }
  824. if ($min) {
  825. include_once 'Date/Calc.php';
  826. if (is_a($min, 'Date') &&
  827. (Date_Calc::compareDates($day, $month, $year,
  828. $min->getDay(), $min->getMonth(), $min->getYear()) < 0)
  829. ) {
  830. return false;
  831. } elseif (is_array($min) &&
  832. (Date_Calc::compareDates($day, $month, $year,
  833. $min[0], $min[1], $min[2]) < 0)
  834. ) {
  835. return false;
  836. }
  837. }
  838. if ($max) {
  839. include_once 'Date/Calc.php';
  840. if (is_a($max, 'Date') &&
  841. (Date_Calc::compareDates($day, $month, $year,
  842. $max->getDay(), $max->getMonth(), $max->getYear()) > 0)
  843. ) {
  844. return false;
  845. } elseif (is_array($max) &&
  846. (Date_Calc::compareDates($day, $month, $year,
  847. $max[0], $max[1], $max[2]) > 0)
  848. ) {
  849. return false;
  850. }
  851. }
  852. }
  853. return true;
  854. }
  855. /**
  856. * Substr
  857. *
  858. * @param string &$date Date
  859. * @param string $num Length
  860. * @param string $opt Unknown
  861. *
  862. * @access private
  863. * @return string
  864. */
  865. function _substr(&$date, $num, $opt = false)
  866. {
  867. if ($opt && strlen($date) >= $opt && preg_match('/^[0-9]{'.$opt.'}/', $date, $m)) {
  868. $ret = $m[0];
  869. } else {
  870. $ret = substr($date, 0, $num);
  871. }
  872. $date = substr($date, strlen($ret));
  873. return $ret;
  874. }
  875. function _modf($val, $div)
  876. {
  877. if (function_exists('bcmod')) {
  878. return bcmod($val, $div);
  879. } elseif (function_exists('fmod')) {
  880. return fmod($val, $div);
  881. }
  882. $r = $val / $div;
  883. $i = intval($r);
  884. return intval($val - $i * $div + .1);
  885. }
  886. /**
  887. * Calculates sum of product of number digits with weights
  888. *
  889. * @param string $number number string
  890. * @param array $weights reference to array of weights
  891. *
  892. * @access protected
  893. *
  894. * @return int returns product of number digits with weights
  895. */
  896. function _multWeights($number, &$weights)
  897. {
  898. if (!is_array($weights)) {
  899. return -1;
  900. }
  901. $sum = 0;
  902. $count = min(count($weights), strlen($number));
  903. if ($count == 0) { // empty string or weights array
  904. return -1;
  905. }
  906. for ($i = 0; $i < $count; ++$i) {
  907. $sum += intval(substr($number, $i, 1)) * $weights[$i];
  908. }
  909. return $sum;
  910. }
  911. /**
  912. * Calculates control digit for a given number
  913. *
  914. * @param string $number number string
  915. * @param array $weights reference to array of weights
  916. * @param int $modulo (optionsl) number
  917. * @param int $subtract (optional) number
  918. * @param bool $allow_high (optional) true if function can return number higher than 10
  919. *
  920. * @access protected
  921. *
  922. * @return int -1 calculated control number is returned
  923. */
  924. function _getControlNumber($number, &$weights, $modulo = 10, $subtract = 0, $allow_high = false)
  925. {
  926. // calc sum
  927. $sum = Validate::_multWeights($number, $weights);
  928. if ($sum == -1) {
  929. return -1;
  930. }
  931. $mod = Validate::_modf($sum, $modulo); // calculate control digit
  932. if ($subtract > $mod && $mod > 0) {
  933. $mod = $subtract - $mod;
  934. }
  935. if ($allow_high === false) {
  936. $mod %= 10; // change 10 to zero
  937. }
  938. return $mod;
  939. }
  940. /**
  941. * Validates a number
  942. *
  943. * @param string $number number to validate
  944. * @param array $weights reference to array of weights
  945. * @param int $modulo (optional) number
  946. * @param int $subtract (optional) number
  947. *
  948. * @access protected
  949. *
  950. * @return bool true if valid, false if not
  951. */
  952. function _checkControlNumber($number, &$weights, $modulo = 10, $subtract = 0)
  953. {
  954. if (strlen($number) < count($weights)) {
  955. return false;
  956. }
  957. $target_digit = substr($number, count($weights), 1);
  958. $control_digit = Validate::_getControlNumber($number, $weights, $modulo, $subtract, $modulo > 10);
  959. if ($control_digit == -1) {
  960. return false;
  961. }
  962. if ($target_digit === 'X' && $control_digit == 10) {
  963. return true;
  964. }
  965. if ($control_digit != $target_digit) {
  966. return false;
  967. }
  968. return true;
  969. }
  970. /**
  971. * Bulk data validation for data introduced in the form of an
  972. * assoc array in the form $var_name => $value.
  973. * Can be used on any of Validate subpackages
  974. *
  975. * @param array $data Ex: array('name' => 'toto', 'email' => 'toto@thing.info');
  976. * @param array $val_type Contains the validation type and all parameters used in.
  977. * 'val_type' is not optional
  978. * others validations properties must have the same name as the function
  979. * parameters.
  980. * Ex: array('toto'=>array('type'=>'string','format'='toto@thing.info','min_length'=>5));
  981. * @param boolean $remove if set, the elements not listed in data will be removed
  982. *
  983. * @return array value name => true|false the value name comes from the data key
  984. *
  985. * @access public
  986. */
  987. function multiple(&$data, &$val_type, $remove = false)
  988. {
  989. $keys = array_keys($data);
  990. $valid = array();
  991. foreach ($keys as $var_name) {
  992. if (!isset($val_type[$var_name])) {
  993. if ($remove) {
  994. unset($data[$var_name]);
  995. }
  996. continue;
  997. }
  998. $opt = $val_type[$var_name];
  999. $methods = get_class_methods('Validate');
  1000. $val2check = $data[$var_name];
  1001. // core validation method
  1002. if (in_array(strtolower($opt['type']), $methods)) {
  1003. //$opt[$opt['type']] = $data[$var_name];
  1004. $method = $opt['type'];
  1005. unset($opt['type']);
  1006. if (sizeof($opt) == 1 && is_array(reset($opt))) {
  1007. $opt = array_pop($opt);
  1008. }
  1009. $valid[$var_name] = call_user_func(array('Validate', $method), $val2check, $opt);
  1010. /**
  1011. * external validation method in the form:
  1012. * "<class name><underscore><method name>"
  1013. * Ex: us_ssn will include class Validate/US.php and call method ssn()
  1014. */
  1015. } elseif (strpos($opt['type'], '_') !== false) {
  1016. $validateType = explode('_', $opt['type']);
  1017. $method = array_pop($validateType);
  1018. $class = implode('_', $validateType);
  1019. $classPath = str_replace('_', DIRECTORY_SEPARATOR, $class);
  1020. $class = 'Validate_' . $class;
  1021. if (!@include_once "Validate/$classPath.php") {
  1022. trigger_error("$class isn't installed or you may have some permissoin issues", E_USER_ERROR);
  1023. }
  1024. $ce = substr(phpversion(), 0, 1) > 4 ?
  1025. class_exists($class, false) : class_exists($class);
  1026. if (!$ce ||
  1027. !in_array($method, get_class_methods($class))
  1028. ) {
  1029. trigger_error("Invalid validation type $class::$method",
  1030. E_USER_WARNING);
  1031. continue;
  1032. }
  1033. unset($opt['type']);
  1034. if (sizeof($opt) == 1) {
  1035. $opt = array_pop($opt);
  1036. }
  1037. $valid[$var_name] = call_user_func(array($class, $method),
  1038. $data[$var_name], $opt);
  1039. } else {
  1040. trigger_error("Invalid validation type {$opt['type']}",
  1041. E_USER_WARNING);
  1042. }
  1043. }
  1044. return $valid;
  1045. }
  1046. }