5 * Copyright (c) 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox, Amir Saied
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.
15 * Author: Tomas V.V.Cox <cox@idecnet.com>
16 * Pierre-Alain Joye <pajoye@php.net>
17 * Amir Mohammad Saied <amir@php.net>
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)
26 * - possibility valid multiple data with a single method call (::multiple)
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 302518 2010-08-20 01:58:15Z clockwerx $
36 * @link http://pear.php.net/package/Validate
41 * Methods for common data validations
43 define('VALIDATE_NUM', '0-9');
44 define('VALIDATE_SPACE', '\s');
45 define('VALIDATE_ALPHA_LOWER', 'a-z');
46 define('VALIDATE_ALPHA_UPPER', 'A-Z');
47 define('VALIDATE_ALPHA', VALIDATE_ALPHA_LOWER . VALIDATE_ALPHA_UPPER);
48 define('VALIDATE_EALPHA_LOWER', VALIDATE_ALPHA_LOWER . 'áéíóúýàèìòùäëïöüÿâêîôûãñõ¨åæç½ðøþß');
49 define('VALIDATE_EALPHA_UPPER', VALIDATE_ALPHA_UPPER . 'ÁÉÍÓÚÝÀÈÌÒÙÄËÏÖܾÂÊÎÔÛÃÑÕ¦ÅÆǼÐØÞ');
50 define('VALIDATE_EALPHA', VALIDATE_EALPHA_LOWER . VALIDATE_EALPHA_UPPER);
51 define('VALIDATE_PUNCTUATION', VALIDATE_SPACE . '\.,;\:&"\'\?\!\(\)');
52 define('VALIDATE_NAME', VALIDATE_EALPHA . VALIDATE_SPACE . "'" . '\-');
53 define('VALIDATE_STREET', VALIDATE_NUM . VALIDATE_NAME . "/\\ºª\.");
55 define('VALIDATE_ITLD_EMAILS', 1);
56 define('VALIDATE_GTLD_EMAILS', 2);
57 define('VALIDATE_CCTLD_EMAILS', 4);
58 define('VALIDATE_ALL_EMAILS', 8);
64 * Package to validate various datas. It includes :
65 * - numbers (min/max, decimal or not)
66 * - email (syntax, domain check)
67 * - string (predifined type alpha upper and/or lowercase, numeric,...)
70 * - possibility valid multiple data with a single method call (::multiple)
74 * @author Tomas V.V.Cox <cox@idecnet.com>
75 * @author Pierre-Alain Joye <pajoye@php.net>
76 * @author Amir Mohammad Saied <amir@php.net>
77 * @copyright 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox,Amir Mohammad Saied
78 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
79 * @version Release: @package_version@
80 * @link http://pear.php.net/package/Validate
84 // {{{ International, Generic and Country code TLDs
86 * International Top-Level Domain
88 * This is an array of the known international
89 * top-level domain names.
92 * @var array $_iTld (International top-level domains)
100 * Generic top-level domain
102 * This is an array of the official
103 * generic top-level domains.
106 * @var array $_gTld (Generic top-level domains)
134 * Country code top-level domains
136 * This is an array of the official country
137 * codes top-level domains
140 * @var array $_ccTld (Country Code Top-Level Domain)
211 * Validate a tag URI (RFC4151)
213 * @param string $uri tag URI to validate
215 * @return boolean true if valid tag URI, false if not
219 static function __uriRFC4151($uri)
223 '/^tag:(?<name>.*),(?<date>\d{4}-?\d{0,2}-?\d{0,2}):(?<specific>.*)(.*:)*$/', $uri, $matches)) {
224 $date = $matches['date'];
225 $date6 = strtotime($date);
226 if ((strlen($date) == 4) && $date <= date('Y')) {
228 } elseif ((strlen($date) == 7) && ($date6 < strtotime("now"))) {
230 } elseif ((strlen($date) == 10) && ($date6 < strtotime("now"))) {
233 if (self::email($matches['name'])) {
236 $namevalid = self::email('info@' . $matches['name']);
238 return $datevalid && $namevalid;
247 * @param string $number Number to validate
248 * @param array $options array where:
249 * 'decimal' is the decimal char or false when decimal
251 * i.e. ',.' to allow both ',' and '.'
252 * 'dec_prec' Number of allowed decimals
253 * 'min' minimum value
254 * 'max' maximum value
256 * @return boolean true if valid number, false if not
260 static function number($number, $options = array())
262 $decimal = $dec_prec = $min = $max = null;
263 if (is_array($options)) {
267 $dec_prec = $dec_prec ? "{1,$dec_prec}" : '+';
268 $dec_regex = $decimal ? "[$decimal][0-9]$dec_prec" : '';
270 if (!preg_match("|^[-+]?\s*[0-9]+($dec_regex)?\$|", $number)) {
274 if ($decimal != '.') {
275 $number = strtr($number, $decimal, '.');
278 $number = (float)str_replace(' ', '', $number);
279 if ($min !== null && $min > $number) {
283 if ($max !== null && $max < $number) {
290 * Converting a string to UTF-7 (RFC 2152)
292 * @param string $string string to be converted
294 * @return string converted string
298 static function __stringToUtf7($string)
302 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
303 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
304 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
305 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
306 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
307 '3', '4', '5', '6', '7', '8', '9', '+', ','
313 if (!empty($string)) {
315 while ($i <= strlen($string)) {
316 $char = substr($string, $i, 1);
318 if ((ord($char) >= 0x7F) || (ord($char) <= 0x1F)) {
323 } elseif ($char == '&') {
328 } elseif (($i == strlen($string) ||
329 !((ord($char) >= 0x7F)) || (ord($char) <= 0x1F))) {
331 if (ord($char) > 64) {
334 $return .= $utf7[ord($char)];
342 $return .= $utf7[ord($char) >> 2];
343 $residue = (ord($char) & 0x03) << 4;
347 $return .= $utf7[$residue | (ord($char) >> 4)];
348 $residue = (ord($char) & 0x0F) << 2;
352 $return .= $utf7[$residue | (ord($char) >> 6)];
353 $return .= $utf7[ord($char) & 0x3F];
366 * Validate an email according to full RFC822 (inclusive human readable part)
368 * @param string $email email to validate,
369 * will return the address for optional dns validation
370 * @param array $options email() options
372 * @return boolean true if valid email, false if not
376 static function __emailRFC822(&$email, &$options)
378 static $address = null;
379 static $uncomment = null;
381 // atom = 1*<any CHAR except specials, SPACE and CTLs>
382 $atom = '[^][()<>@,;:\\".\s\000-\037\177-\377]+\s*';
383 // qtext = <any CHAR excepting <">, ; => may be folded
384 // "\" & CR, and including linear-white-space>
385 $qtext = '[^"\\\\\r]';
386 // quoted-pair = "\" CHAR ; may quote any char
387 $quoted_pair = '\\\\.';
388 // quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or
390 $quoted_string = '"(?:' . $qtext . '|' . $quoted_pair . ')*"\s*';
391 // word = atom / quoted-string
392 $word = '(?:' . $atom . '|' . $quoted_string . ')';
393 // local-part = word *("." word) ; uninterpreted
395 $local_part = $word . '(?:\.\s*' . $word . ')*';
396 // dtext = <any CHAR excluding "[", ; => may be folded
397 // "]", "\" & CR, & including linear-white-space>
398 $dtext = '[^][\\\\\r]';
399 // domain-literal = "[" *(dtext / quoted-pair) "]"
400 $domain_literal = '\[(?:' . $dtext . '|' . $quoted_pair . ')*\]\s*';
401 // sub-domain = domain-ref / domain-literal
402 // domain-ref = atom ; symbolic reference
403 $sub_domain = '(?:' . $atom . '|' . $domain_literal . ')';
404 // domain = sub-domain *("." sub-domain)
405 $domain = $sub_domain . '(?:\.\s*' . $sub_domain . ')*';
406 // addr-spec = local-part "@" domain ; global address
407 $addr_spec = $local_part . '@\s*' . $domain;
408 // route = 1#("@" domain) ":" ; path-relative
409 $route = '@' . $domain . '(?:,@\s*' . $domain . ')*:\s*';
410 // route-addr = "<" [route] addr-spec ">"
411 $route_addr = '<\s*(?:' . $route . ')?' . $addr_spec . '>\s*';
412 // phrase = 1*word ; Sequence of words
413 $phrase = $word . '+';
414 // mailbox = addr-spec ; simple address
415 // / phrase route-addr ; name & addr-spec
416 $mailbox = '(?:' . $addr_spec . '|' . $phrase . $route_addr . ')';
417 // group = phrase ":" [#mailbox] ";"
418 $group = $phrase . ':\s*(?:' . $mailbox . '(?:,\s*' . $mailbox . ')*)?;\s*';
419 // address = mailbox ; one addressee
420 // / group ; named list
421 $address = '/^\s*(?:' . $mailbox . '|' . $group . ')$/';
424 '/((?:(?:\\\\"|[^("])*(?:' . $quoted_string .
425 ')?)*)((?<!\\\\)\((?:(?2)|.)*?(?<!\\\\)\))/';
428 $email = preg_replace($uncomment, '$1 ', $email);
429 return preg_match($address, $email);
433 * Full TLD Validation function
435 * This function is used to make a much more proficient validation
436 * against all types of official domain names.
438 * @param string $email The email address to check.
439 * @param array $options The options for validation
443 * @return bool True if validating succeeds
445 static function _fullTLDValidation($email, $options)
448 if(!empty($options["VALIDATE_ITLD_EMAILS"])) array_push($validate, 'itld');
449 if(!empty($options["VALIDATE_GTLD_EMAILS"])) array_push($validate, 'gtld');
450 if(!empty($options["VALIDATE_CCTLD_EMAILS"])) array_push($validate, 'cctld');
452 $self = new Validate;
454 $toValidate = array();
456 foreach ($validate as $valid) {
457 $tmpVar = '_' . (string)$valid;
459 $toValidate[$valid] = $self->{$tmpVar};
462 $e = $self->executeFullEmailValidation($email, $toValidate);
468 * Execute the validation
470 * This function will execute the full email vs tld
471 * validation using an array of tlds passed to it.
473 * @param string $email The email to validate.
474 * @param array $arrayOfTLDs The array of the TLDs to validate
478 * @return true or false (Depending on if it validates or if it does not)
480 static function executeFullEmailValidation($email, $arrayOfTLDs)
482 $emailEnding = explode('.', $email);
483 $emailEnding = $emailEnding[count($emailEnding)-1];
484 foreach ($arrayOfTLDs as $validator => $keys) {
485 if (in_array($emailEnding, $keys)) {
495 * @param string $email email to validate
496 * @param mixed boolean (BC) $check_domain Check or not if the domain exists
497 * array $options associative array of options
498 * 'check_domain' boolean Check or not if the domain exists
499 * 'use_rfc822' boolean Apply the full RFC822 grammar
503 * 'check_domain' => 'true',
504 * 'fullTLDValidation' => 'true',
505 * 'use_rfc822' => 'true',
506 * 'VALIDATE_GTLD_EMAILS' => 'true',
507 * 'VALIDATE_CCTLD_EMAILS' => 'true',
508 * 'VALIDATE_ITLD_EMAILS' => 'true',
511 * @return boolean true if valid email, false if not
515 static function email($email, $options = null)
517 static $dom_cache = array();
518 $check_domain = false;
520 if (is_bool($options)) {
521 $check_domain = $options;
522 } elseif (is_array($options)) {
527 * Check for IDN usage so we can encode the domain as Punycode
532 if (Validate::_includePathFileExists('Net/IDNA.php')) {
533 include_once('Net/IDNA.php');
537 if ($hasIDNA === true) {
538 if (strpos($email, '@') !== false) {
539 $tmpEmail = explode('@', $email);
540 $domain = array_pop($tmpEmail);
542 // Check if the domain contains characters > 127 which means
543 // it's an idn domain name.
544 $chars = count_chars($domain, 1);
545 if (!empty($chars) && max(array_keys($chars)) > 127) {
546 $idna =& Net_IDNA::singleton();
547 $domain = $idna->encode($domain);
550 array_push($tmpEmail, $domain);
551 $email = implode('@', $tmpEmail);
556 * @todo Fix bug here.. even if it passes this, it won't be passing
557 * The regular expression below
559 if (isset($fullTLDValidation)) {
560 //$valid = Validate::_fullTLDValidation($email, $fullTLDValidation);
561 $valid = Validate::_fullTLDValidation($email, $options);
568 // the base regexp for address
569 $regex = '&^(?: # recipient:
570 ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name
571 ([-\w!\#\$%\&\'*+~/^`|{}]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}]+)*)) #2 OR dot-atom
572 @(((\[)? #3 domain, 4 as IPv4, 5 optionally bracketed
573 (?:(?:(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.){3}
574 (?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))))(?(5)\])|
575 ((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z0-9](?:[-a-z0-9]*[a-z0-9])?) #6 domain as hostname
576 \.((?:([^- ])[-a-z]*[-a-z]))) #7 TLD
579 //checks if exists the domain (MX or A)
580 if ($use_rfc822? Validate::__emailRFC822($email, $options) :
581 preg_match($regex, $email)) {
582 if ($check_domain && function_exists('checkdnsrr')) {
583 $em = explode('@', $email);
584 $domain = preg_replace('/[^-a-z.0-9]/i', '', array_pop($em));
586 if (isset($dom_cache[$domain])) {
587 return $dom_cache[$domain];
590 if (checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A')) {
591 $dom_cache[$domain] = true;
594 $dom_cache[$domain] = false;
603 * Validate a string using the given format 'format'
605 * @param string $string String to validate
606 * @param array $options Options array where:
607 * 'format' is the format of the string
608 * Ex:VALIDATE_NUM . VALIDATE_ALPHA (see constants)
609 * 'min_length' minimum length
610 * 'max_length' maximum length
612 * @return boolean true if valid string, false if not
616 static function string($string, $options)
622 if (is_array($options)) {
626 if ($format && !preg_match("|^[$format]*\$|s", $string)) {
630 if ($min_length && strlen($string) < $min_length) {
634 if ($max_length && strlen($string) > $max_length) {
642 * Validate an URI (RFC2396)
643 * This function will validate 'foobarstring' by default, to get it to validate
644 * only http, https, ftp and such you have to pass it in the allowed_schemes
647 * $options = array('allowed_schemes' => array('http', 'https', 'ftp'))
648 * var_dump(Validate::uri('http://www.example.org', $options));
651 * NOTE 1: The rfc2396 normally allows middle '-' in the top domain
652 * e.g. http://example.co-m should be valid
653 * However, as '-' is not used in any known TLD, it is invalid
654 * NOTE 2: As double shlashes // are allowed in the path part, only full URIs
655 * including an authority can be valid, no relative URIs
656 * the // are mandatory (optionally preceeded by the 'sheme:' )
657 * NOTE 3: the full complience to rfc2396 is not achieved by default
658 * the characters ';/?:@$,' will not be accepted in the query part
659 * if not urlencoded, refer to the option "strict'"
661 * @param string $url URI to validate
662 * @param array $options Options used by the validation method.
664 * 'domain_check' => boolean
665 * Whether to check the DNS entry or not
666 * 'allowed_schemes' => array, list of protocols
667 * List of allowed schemes ('http',
669 * 'strict' => string the refused chars
670 * in query and fragment parts
672 * empty: accept all rfc2396 foreseen chars
674 * @return boolean true if valid uri, false if not
678 static function uri($url, $options = null)
681 $domain_check = false;
682 $allowed_schemes = null;
683 if (is_array($options)) {
686 if (is_array($allowed_schemes) &&
687 in_array("tag", $allowed_schemes)
689 if (strpos($url, "tag:") === 0) {
690 return self::__uriRFC4151($url);
695 '&^(?:([a-z][-+.a-z0-9]*):)? # 1. scheme
696 (?:// # authority start
697 (?:((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();:\&=+$,])*)@)? # 2. authority-userinfo
698 (?:((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z](?:[a-z0-9]+)?\.?) # 3. authority-hostname OR
699 |([0-9]{1,3}(?:\.[0-9]{1,3}){3})) # 4. authority-ipv4
700 (?::([0-9]*))?) # 5. authority-port
701 ((?:/(?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'():@\&=+$,;])*)*/?)? # 6. path
702 (?:\?([^#]*))? # 7. query
703 (?:\#((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();/?:@\&=+$,])*))? # 8. fragment
704 $&xi', $url, $matches)) {
705 $scheme = isset($matches[1]) ? $matches[1] : '';
706 $authority = isset($matches[3]) ? $matches[3] : '' ;
707 if (is_array($allowed_schemes) &&
708 !in_array($scheme, $allowed_schemes)
712 if (!empty($matches[4])) {
713 $parts = explode('.', $matches[4]);
714 foreach ($parts as $part) {
719 } elseif ($domain_check && function_exists('checkdnsrr')) {
720 if (!checkdnsrr($authority, 'A')) {
725 $strict = '#[' . preg_quote($strict, '#') . ']#';
726 if ((!empty($matches[7]) && preg_match($strict, $matches[7]))
727 || (!empty($matches[8]) && preg_match($strict, $matches[8]))) {
737 * Validate date and times. Note that this method need the Date_Calc class
739 * @param string $date Date to validate
740 * @param array $options array options where :
741 * 'format' The format of the date (%d-%m-%Y)
742 * or rfc822_compliant
743 * 'min' The date has to be greater
744 * than this array($day, $month, $year)
745 * or PEAR::Date object
746 * 'max' The date has to be smaller than
747 * this array($day, $month, $year)
748 * or PEAR::Date object
750 * @return boolean true if valid date/time, false if not
754 public static function date($date, $options)
760 if (is_array($options)) {
764 if (strtolower($format) == 'rfc822_compliant') {
765 $preg = '&^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),) \s+
767 (?:(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)?) \s+
768 (?:(\d{2}(\d{2})?)?) \s+
769 (?:(\d{2}?)):(?:(\d{2}?))(:(?:(\d{2}?)))? \s+
770 (?:[+-]\d{4}|UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Za-ik-z])$&xi';
772 if (!preg_match($preg, $date, $matches)) {
776 $year = (int)$matches[4];
777 $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
778 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
779 $month = array_keys($months, $matches[3]);
780 $month = (int)$month[0]+1;
781 $day = (int)$matches[2];
782 $weekday = $matches[1];
783 $hour = (int)$matches[6];
784 $minute = (int)$matches[7];
785 isset($matches[9]) ? $second = (int)$matches[9] : $second = 0;
787 if ((strlen($year) != 4) ||
788 ($day > 31 || $day < 1)||
795 $date_len = strlen($format);
796 for ($i = 0; $i < $date_len; $i++) {
799 $next = $format[$i + 1];
804 $day = (int)Validate::_substr($date, 1, 2);
806 $day = (int)Validate::_substr($date, 0, 2);
808 if ($day < 1 || $day > 31) {
815 $month = (int)Validate::_substr($date, 0, 2);
817 $month = (int)Validate::_substr($date, 1, 2);
819 if ($month < 1 || $month > 12) {
826 $year = Validate::_substr($date, 4);
827 $year = (int)$year?$year:'';
829 $year = (int)(substr(date('Y'), 0, 2) .
830 Validate::_substr($date, 2));
832 if (strlen($year) != 4 || $year < 0 || $year > 9999) {
839 $hour = Validate::_substr($date, 1, 2);
841 $hour = Validate::_substr($date, 2);
843 if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 12) {
850 $hour = Validate::_substr($date, 1, 2);
852 $hour = Validate::_substr($date, 2);
854 if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 24) {
860 $t = Validate::_substr($date, 2);
861 if (!preg_match('/^\d+$/', $t) || $t < 0 || $t > 59) {
866 trigger_error("Not supported char `$next' after % in offset " . ($i+2), E_USER_WARNING);
871 if (Validate::_substr($date, 1) != $c) {
877 // there is remaing data, we don't want it
878 if (strlen($date) && (strtolower($format) != 'rfc822_compliant')) {
882 if (isset($day) && isset($month) && isset($year)) {
883 if (!checkdate($month, $day, $year)) {
887 if (strtolower($format) == 'rfc822_compliant') {
888 if ($weekday != date("D", mktime(0, 0, 0, $month, $day, $year))) {
894 include_once 'Date/Calc.php';
895 if (is_a($min, 'Date') &&
896 (Date_Calc::compareDates($day, $month, $year,
897 $min->getDay(), $min->getMonth(), $min->getYear()) < 0)
900 } elseif (is_array($min) &&
901 (Date_Calc::compareDates($day, $month, $year,
902 $min[0], $min[1], $min[2]) < 0)
909 include_once 'Date/Calc.php';
910 if (is_a($max, 'Date') &&
911 (Date_Calc::compareDates($day, $month, $year,
912 $max->getDay(), $max->getMonth(), $max->getYear()) > 0)
915 } elseif (is_array($max) &&
916 (Date_Calc::compareDates($day, $month, $year,
917 $max[0], $max[1], $max[2]) > 0)
930 * @param string &$date Date
931 * @param string $num Length
932 * @param string $opt Unknown
937 static function _substr(&$date, $num, $opt = false)
939 if ($opt && strlen($date) >= $opt && preg_match('/^[0-9]{'.$opt.'}/', $date, $m)) {
942 $ret = substr($date, 0, $num);
944 $date = substr($date, strlen($ret));
948 static function _modf($val, $div)
950 if (function_exists('bcmod')) {
951 return bcmod($val, $div);
952 } elseif (function_exists('fmod')) {
953 return fmod($val, $div);
957 return intval($val - $i * $div + .1);
961 * Calculates sum of product of number digits with weights
963 * @param string $number number string
964 * @param array $weights reference to array of weights
968 * @return int returns product of number digits with weights
970 static function _multWeights($number, &$weights)
972 if (!is_array($weights)) {
977 $count = min(count($weights), strlen($number));
978 if ($count == 0) { // empty string or weights array
981 for ($i = 0; $i < $count; ++$i) {
982 $sum += intval(substr($number, $i, 1)) * $weights[$i];
989 * Calculates control digit for a given number
991 * @param string $number number string
992 * @param array $weights reference to array of weights
993 * @param int $modulo (optionsl) number
994 * @param int $subtract (optional) number
995 * @param bool $allow_high (optional) true if function can return number higher than 10
999 * @return int -1 calculated control number is returned
1001 static function _getControlNumber($number, &$weights, $modulo = 10, $subtract = 0, $allow_high = false)
1004 $sum = Validate::_multWeights($number, $weights);
1008 $mod = Validate::_modf($sum, $modulo); // calculate control digit
1010 if ($subtract > $mod && $mod > 0) {
1011 $mod = $subtract - $mod;
1013 if ($allow_high === false) {
1014 $mod %= 10; // change 10 to zero
1020 * Validates a number
1022 * @param string $number number to validate
1023 * @param array $weights reference to array of weights
1024 * @param int $modulo (optional) number
1025 * @param int $subtract (optional) number
1029 * @return bool true if valid, false if not
1031 static function _checkControlNumber($number, &$weights, $modulo = 10, $subtract = 0)
1033 if (strlen($number) < count($weights)) {
1036 $target_digit = substr($number, count($weights), 1);
1037 $control_digit = Validate::_getControlNumber($number, $weights, $modulo, $subtract, $modulo > 10);
1039 if ($control_digit == -1) {
1042 if ($target_digit === 'X' && $control_digit == 10) {
1045 if ($control_digit != $target_digit) {
1052 * Bulk data validation for data introduced in the form of an
1053 * assoc array in the form $var_name => $value.
1054 * Can be used on any of Validate subpackages
1056 * @param array $data Ex: array('name' => 'toto', 'email' => 'toto@thing.info');
1057 * @param array $val_type Contains the validation type and all parameters used in.
1058 * 'val_type' is not optional
1059 * others validations properties must have the same name as the function
1061 * Ex: array('toto'=>array('type'=>'string','format'='toto@thing.info','min_length'=>5));
1062 * @param boolean $remove if set, the elements not listed in data will be removed
1064 * @return array value name => true|false the value name comes from the data key
1068 static function multiple(&$data, &$val_type, $remove = false)
1070 $keys = array_keys($data);
1073 foreach ($keys as $var_name) {
1074 if (!isset($val_type[$var_name])) {
1076 unset($data[$var_name]);
1080 $opt = $val_type[$var_name];
1081 $methods = get_class_methods('Validate');
1082 $val2check = $data[$var_name];
1083 // core validation method
1084 if (in_array(strtolower($opt['type']), $methods)) {
1085 //$opt[$opt['type']] = $data[$var_name];
1086 $method = $opt['type'];
1087 unset($opt['type']);
1089 if (sizeof($opt) == 1 && is_array(reset($opt))) {
1090 $opt = array_pop($opt);
1092 $valid[$var_name] = call_user_func(array('Validate', $method), $val2check, $opt);
1095 * external validation method in the form:
1096 * "<class name><underscore><method name>"
1097 * Ex: us_ssn will include class Validate/US.php and call method ssn()
1099 } elseif (strpos($opt['type'], '_') !== false) {
1100 $validateType = explode('_', $opt['type']);
1101 $method = array_pop($validateType);
1102 $class = implode('_', $validateType);
1103 $classPath = str_replace('_', DIRECTORY_SEPARATOR, $class);
1104 $class = 'Validate_' . $class;
1105 if (!Validate::_includePathFileExists("Validate/$classPath.php")) {
1106 trigger_error("$class isn't installed or you may have some permission issues", E_USER_ERROR);
1109 $ce = substr(phpversion(), 0, 1) > 4 ?
1110 class_exists($class, false) : class_exists($class);
1112 !in_array($method, get_class_methods($class))
1114 trigger_error("Invalid validation type $class::$method",
1118 unset($opt['type']);
1119 if (sizeof($opt) == 1) {
1120 $opt = array_pop($opt);
1122 $valid[$var_name] = call_user_func(array($class, $method),
1123 $data[$var_name], $opt);
1125 trigger_error("Invalid validation type {$opt['type']}",
1133 * Determine whether specified file exists along the include path.
1135 * @param string $filename file to search for
1139 * @return bool true if file exists
1141 static function _includePathFileExists($filename)
1143 $paths = explode(":", ini_get("include_path"));
1145 foreach($paths as $key => $val) {
1146 $result = file_exists($val . "/" . $filename);