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,v 1.134 2009/01/28 12:27:33 davidc Exp $
36 * @link http://pear.php.net/package/Validate
40 * Methods for common data validations
42 define('VALIDATE_NUM', '0-9');
43 define('VALIDATE_SPACE', '\s');
44 define('VALIDATE_ALPHA_LOWER', 'a-z');
45 define('VALIDATE_ALPHA_UPPER', 'A-Z');
46 define('VALIDATE_ALPHA', VALIDATE_ALPHA_LOWER . VALIDATE_ALPHA_UPPER);
47 define('VALIDATE_EALPHA_LOWER', VALIDATE_ALPHA_LOWER . 'áéíóúýàèìòùäëïöüÿâêîôûãñõ¨åæç½ðøþß');
48 define('VALIDATE_EALPHA_UPPER', VALIDATE_ALPHA_UPPER . 'ÁÉÍÓÚÝÀÈÌÒÙÄËÏÖܾÂÊÎÔÛÃÑÕ¦ÅÆǼÐØÞ');
49 define('VALIDATE_EALPHA', VALIDATE_EALPHA_LOWER . VALIDATE_EALPHA_UPPER);
50 define('VALIDATE_PUNCTUATION', VALIDATE_SPACE . '\.,;\:&"\'\?\!\(\)');
51 define('VALIDATE_NAME', VALIDATE_EALPHA . VALIDATE_SPACE . "'" . "-");
52 define('VALIDATE_STREET', VALIDATE_NUM . VALIDATE_NAME . "/\\ºª\.");
54 define('VALIDATE_ITLD_EMAILS', 1);
55 define('VALIDATE_GTLD_EMAILS', 2);
56 define('VALIDATE_CCTLD_EMAILS', 4);
57 define('VALIDATE_ALL_EMAILS', 8);
62 * Package to validate various datas. It includes :
63 * - numbers (min/max, decimal or not)
64 * - email (syntax, domain check)
65 * - string (predifined type alpha upper and/or lowercase, numeric,...)
68 * - possibility valid multiple data with a single method call (::multiple)
72 * @author Tomas V.V.Cox <cox@idecnet.com>
73 * @author Pierre-Alain Joye <pajoye@php.net>
74 * @author Amir Mohammad Saied <amir@php.net>
75 * @copyright 1997-2006 Pierre-Alain Joye,Tomas V.V.Cox,Amir Mohammad Saied
76 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
77 * @version Release: @package_version@
78 * @link http://pear.php.net/package/Validate
83 * International Top-Level Domain
85 * This is an array of the known international
86 * top-level domain names.
89 * @var array $_iTld (International top-level domains)
97 * Generic top-level domain
99 * This is an array of the official
100 * generic top-level domains.
103 * @var array $_gTld (Generic top-level domains)
131 * Country code top-level domains
133 * This is an array of the official country
134 * codes top-level domains
137 * @var array $_ccTld (Country Code Top-Level Domain)
207 * Validate a tag URI (RFC4151)
209 * @param string $uri tag URI to validate
211 * @return boolean true if valid tag URI, false if not
215 function __uriRFC4151($uri)
219 '/^tag:(?<name>.*),(?<date>\d{4}-?\d{0,2}-?\d{0,2}):(?<specific>.*)(.*:)*$/', $uri, $matches)) {
220 $date = $matches['date'];
221 $date6 = strtotime($date);
222 if ((strlen($date) == 4) && $date <= date('Y')) {
224 } elseif ((strlen($date) == 7) && ($date6 < strtotime("now"))) {
226 } elseif ((strlen($date) == 10) && ($date6 < strtotime("now"))) {
229 if (self::email($matches['name'])) {
232 $namevalid = self::email('info@' . $matches['name']);
234 return $datevalid && $namevalid;
243 * @param string $number Number to validate
244 * @param array $options array where:
245 * 'decimal' is the decimal char or false when decimal
247 * i.e. ',.' to allow both ',' and '.'
248 * 'dec_prec' Number of allowed decimals
249 * 'min' minimum value
250 * 'max' maximum value
252 * @return boolean true if valid number, false if not
256 function number($number, $options = array())
258 $decimal = $dec_prec = $min = $max = null;
259 if (is_array($options)) {
263 $dec_prec = $dec_prec ? "{1,$dec_prec}" : '+';
264 $dec_regex = $decimal ? "[$decimal][0-9]$dec_prec" : '';
266 if (!preg_match("|^[-+]?\s*[0-9]+($dec_regex)?\$|", $number)) {
270 if ($decimal != '.') {
271 $number = strtr($number, $decimal, '.');
274 $number = (float)str_replace(' ', '', $number);
275 if ($min !== null && $min > $number) {
279 if ($max !== null && $max < $number) {
286 * Converting a string to UTF-7 (RFC 2152)
288 * @param string $string string to be converted
290 * @return string converted string
294 function __stringToUtf7($string)
298 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
299 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
300 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
301 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
302 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
303 '3', '4', '5', '6', '7', '8', '9', '+', ','
309 if (!empty($string)) {
311 while ($i <= strlen($string)) {
312 $char = substr($string, $i, 1);
314 if ((ord($char) >= 0x7F) || (ord($char) <= 0x1F)) {
319 } elseif ($char == '&') {
324 } elseif (($i == strlen($string) ||
325 !((ord($char) >= 0x7F)) || (ord($char) <= 0x1F))) {
327 if (ord($char) > 64) {
330 $return .= $utf7[ord($char)];
338 $return .= $utf7[ord($char) >> 2];
339 $residue = (ord($char) & 0x03) << 4;
343 $return .= $utf7[$residue | (ord($char) >> 4)];
344 $residue = (ord($char) & 0x0F) << 2;
348 $return .= $utf7[$residue | (ord($char) >> 6)];
349 $return .= $utf7[ord($char) & 0x3F];
362 * Validate an email according to full RFC822 (inclusive human readable part)
364 * @param string $email email to validate,
365 * will return the address for optional dns validation
366 * @param array $options email() options
368 * @return boolean true if valid email, false if not
372 function __emailRFC822(&$email, &$options)
374 static $address = null;
375 static $uncomment = null;
377 // atom = 1*<any CHAR except specials, SPACE and CTLs>
378 $atom = '[^][()<>@,;:\\".\s\000-\037\177-\377]+\s*';
379 // qtext = <any CHAR excepting <">, ; => may be folded
380 // "\" & CR, and including linear-white-space>
381 $qtext = '[^"\\\\\r]';
382 // quoted-pair = "\" CHAR ; may quote any char
383 $quoted_pair = '\\\\.';
384 // quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or
386 $quoted_string = '"(?:' . $qtext . '|' . $quoted_pair . ')*"\s*';
387 // word = atom / quoted-string
388 $word = '(?:' . $atom . '|' . $quoted_string . ')';
389 // local-part = word *("." word) ; uninterpreted
391 $local_part = $word . '(?:\.\s*' . $word . ')*';
392 // dtext = <any CHAR excluding "[", ; => may be folded
393 // "]", "\" & CR, & including linear-white-space>
394 $dtext = '[^][\\\\\r]';
395 // domain-literal = "[" *(dtext / quoted-pair) "]"
396 $domain_literal = '\[(?:' . $dtext . '|' . $quoted_pair . ')*\]\s*';
397 // sub-domain = domain-ref / domain-literal
398 // domain-ref = atom ; symbolic reference
399 $sub_domain = '(?:' . $atom . '|' . $domain_literal . ')';
400 // domain = sub-domain *("." sub-domain)
401 $domain = $sub_domain . '(?:\.\s*' . $sub_domain . ')*';
402 // addr-spec = local-part "@" domain ; global address
403 $addr_spec = $local_part . '@\s*' . $domain;
404 // route = 1#("@" domain) ":" ; path-relative
405 $route = '@' . $domain . '(?:,@\s*' . $domain . ')*:\s*';
406 // route-addr = "<" [route] addr-spec ">"
407 $route_addr = '<\s*(?:' . $route . ')?' . $addr_spec . '>\s*';
408 // phrase = 1*word ; Sequence of words
409 $phrase = $word . '+';
410 // mailbox = addr-spec ; simple address
411 // / phrase route-addr ; name & addr-spec
412 $mailbox = '(?:' . $addr_spec . '|' . $phrase . $route_addr . ')';
413 // group = phrase ":" [#mailbox] ";"
414 $group = $phrase . ':\s*(?:' . $mailbox . '(?:,\s*' . $mailbox . ')*)?;\s*';
415 // address = mailbox ; one addressee
416 // / group ; named list
417 $address = '/^\s*(?:' . $mailbox . '|' . $group . ')$/';
420 '/((?:(?:\\\\"|[^("])*(?:' . $quoted_string .
421 ')?)*)((?<!\\\\)\((?:(?2)|.)*?(?<!\\\\)\))/';
424 $email = preg_replace($uncomment, '$1 ', $email);
425 return preg_match($address, $email);
429 * Full TLD Validation function
431 * This function is used to make a much more proficient validation
432 * against all types of official domain names.
434 * @param string $email The email address to check.
435 * @param array $options The options for validation
439 * @return bool True if validating succeeds
441 function _fullTLDValidation($email, $options)
444 if(!empty($options["VALIDATE_ITLD_EMAILS"])) array_push($validate, 'itld');
445 if(!empty($options["VALIDATE_GTLD_EMAILS"])) array_push($validate, 'gtld');
446 if(!empty($options["VALIDATE_CCTLD_EMAILS"])) array_push($validate, 'cctld');
448 $self = new Validate;
450 $toValidate = array();
452 foreach ($validate as $valid) {
453 $tmpVar = '_' . (string)$valid;
455 $toValidate[$valid] = $self->{$tmpVar};
458 $e = $self->executeFullEmailValidation($email, $toValidate);
464 * Execute the validation
466 * This function will execute the full email vs tld
467 * validation using an array of tlds passed to it.
469 * @param string $email The email to validate.
470 * @param array $arrayOfTLDs The array of the TLDs to validate
474 * @return true or false (Depending on if it validates or if it does not)
476 function executeFullEmailValidation($email, $arrayOfTLDs)
478 $emailEnding = explode('.', $email);
479 $emailEnding = $emailEnding[count($emailEnding)-1];
480 foreach ($arrayOfTLDs as $validator => $keys) {
481 if (in_array($emailEnding, $keys)) {
491 * @param string $email email to validate
492 * @param mixed boolean (BC) $check_domain Check or not if the domain exists
493 * array $options associative array of options
494 * 'check_domain' boolean Check or not if the domain exists
495 * 'use_rfc822' boolean Apply the full RFC822 grammar
499 * 'check_domain' => 'true',
500 * 'fullTLDValidation' => 'true',
501 * 'use_rfc822' => 'true',
502 * 'VALIDATE_GTLD_EMAILS' => 'true',
503 * 'VALIDATE_CCTLD_EMAILS' => 'true',
504 * 'VALIDATE_ITLD_EMAILS' => 'true',
507 * @return boolean true if valid email, false if not
511 function email($email, $options = null)
513 $check_domain = false;
515 if (is_bool($options)) {
516 $check_domain = $options;
517 } elseif (is_array($options)) {
522 * Check for IDN usage so we can encode the domain as Punycode
527 if (@include_once('Net/IDNA.php')) {
531 if ($hasIDNA === true) {
532 if (strpos($email, '@') !== false) {
533 list($name, $domain) = explode('@', $email, 2);
535 // Check if the domain contains characters > 127 which means
536 // it's an idn domain name.
537 $chars = count_chars($domain, 1);
538 if (!empty($chars) && max(array_keys($chars)) > 127) {
539 $idna =& Net_IDNA::singleton();
540 $domain = $idna->encode($domain);
543 $email = "$name@$domain";
548 * @todo Fix bug here.. even if it passes this, it won't be passing
549 * The regular expression below
551 if (isset($fullTLDValidation)) {
552 //$valid = Validate::_fullTLDValidation($email, $fullTLDValidation);
553 $valid = Validate::_fullTLDValidation($email, $options);
560 // the base regexp for address
561 $regex = '&^(?: # recipient:
562 ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name
563 ([-\w!\#\$%\&\'*+~/^`|{}]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}]+)*)) #2 OR dot-atom
564 @(((\[)? #3 domain, 4 as IPv4, 5 optionally bracketed
565 (?:(?:(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.){3}
566 (?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))))(?(5)\])|
567 ((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z0-9](?:[-a-z0-9]*[a-z0-9])?) #6 domain as hostname
568 \.((?:([^- ])[-a-z]*[-a-z]))) #7 TLD
571 //checks if exists the domain (MX or A)
572 if ($use_rfc822? Validate::__emailRFC822($email, $options) :
573 preg_match($regex, $email)) {
574 if ($check_domain && function_exists('checkdnsrr')) {
575 list ($account, $domain) = explode('@', $email);
576 if (checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A')) {
587 * Validate a string using the given format 'format'
589 * @param string $string String to validate
590 * @param array $options Options array where:
591 * 'format' is the format of the string
592 * Ex:VALIDATE_NUM . VALIDATE_ALPHA (see constants)
593 * 'min_length' minimum length
594 * 'max_length' maximum length
596 * @return boolean true if valid string, false if not
600 function string($string, $options)
606 if (is_array($options)) {
610 if ($format && !preg_match("|^[$format]*\$|s", $string)) {
614 if ($min_length && strlen($string) < $min_length) {
618 if ($max_length && strlen($string) > $max_length) {
626 * Validate an URI (RFC2396)
627 * This function will validate 'foobarstring' by default, to get it to validate
628 * only http, https, ftp and such you have to pass it in the allowed_schemes
631 * $options = array('allowed_schemes' => array('http', 'https', 'ftp'))
632 * var_dump(Validate::uri('http://www.example.org', $options));
635 * NOTE 1: The rfc2396 normally allows middle '-' in the top domain
636 * e.g. http://example.co-m should be valid
637 * However, as '-' is not used in any known TLD, it is invalid
638 * NOTE 2: As double shlashes // are allowed in the path part, only full URIs
639 * including an authority can be valid, no relative URIs
640 * the // are mandatory (optionally preceeded by the 'sheme:' )
641 * NOTE 3: the full complience to rfc2396 is not achieved by default
642 * the characters ';/?:@$,' will not be accepted in the query part
643 * if not urlencoded, refer to the option "strict'"
645 * @param string $url URI to validate
646 * @param array $options Options used by the validation method.
648 * 'domain_check' => boolean
649 * Whether to check the DNS entry or not
650 * 'allowed_schemes' => array, list of protocols
651 * List of allowed schemes ('http',
653 * 'strict' => string the refused chars
654 * in query and fragment parts
656 * empty: accept all rfc2396 foreseen chars
658 * @return boolean true if valid uri, false if not
662 function uri($url, $options = null)
665 $domain_check = false;
666 $allowed_schemes = null;
667 if (is_array($options)) {
670 if (is_array($allowed_schemes) &&
671 in_array("tag", $allowed_schemes)
673 if (strpos($url, "tag:") === 0) {
674 return self::__uriRFC4151($url);
679 '&^(?:([a-z][-+.a-z0-9]*):)? # 1. scheme
680 (?:// # authority start
681 (?:((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();:\&=+$,])*)@)? # 2. authority-userinfo
682 (?:((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z](?:[a-z0-9]+)?\.?) # 3. authority-hostname OR
683 |([0-9]{1,3}(?:\.[0-9]{1,3}){3})) # 4. authority-ipv4
684 (?::([0-9]*))?) # 5. authority-port
685 ((?:/(?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'():@\&=+$,;])*)*/?)? # 6. path
686 (?:\?([^#]*))? # 7. query
687 (?:\#((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();/?:@\&=+$,])*))? # 8. fragment
688 $&xi', $url, $matches)) {
689 $scheme = isset($matches[1]) ? $matches[1] : '';
690 $authority = isset($matches[3]) ? $matches[3] : '' ;
691 if (is_array($allowed_schemes) &&
692 !in_array($scheme, $allowed_schemes)
696 if (!empty($matches[4])) {
697 $parts = explode('.', $matches[4]);
698 foreach ($parts as $part) {
703 } elseif ($domain_check && function_exists('checkdnsrr')) {
704 if (!checkdnsrr($authority, 'A')) {
709 $strict = '#[' . preg_quote($strict, '#') . ']#';
710 if ((!empty($matches[7]) && preg_match($strict, $matches[7]))
711 || (!empty($matches[8]) && preg_match($strict, $matches[8]))) {
721 * Validate date and times. Note that this method need the Date_Calc class
723 * @param string $date Date to validate
724 * @param array $options array options where :
725 * 'format' The format of the date (%d-%m-%Y)
726 * or rfc822_compliant
727 * 'min' The date has to be greater
728 * than this array($day, $month, $year)
729 * or PEAR::Date object
730 * 'max' The date has to be smaller than
731 * this array($day, $month, $year)
732 * or PEAR::Date object
734 * @return boolean true if valid date/time, false if not
738 function date($date, $options)
744 if (is_array($options)) {
748 if (strtolower($format) == 'rfc822_compliant') {
749 $preg = '&^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),) \s+
751 (?:(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)?) \s+
752 (?:(\d{2}(\d{2})?)?) \s+
753 (?:(\d{2}?)):(?:(\d{2}?))(:(?:(\d{2}?)))? \s+
754 (?:[+-]\d{4}|UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Za-ik-z])$&xi';
756 if (!preg_match($preg, $date, $matches)) {
760 $year = (int)$matches[4];
761 $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
762 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
763 $month = array_keys($months, $matches[3]);
764 $month = (int)$month[0]+1;
765 $day = (int)$matches[2];
766 $weekday = $matches[1];
767 $hour = (int)$matches[6];
768 $minute = (int)$matches[7];
769 isset($matches[9]) ? $second = (int)$matches[9] : $second = 0;
771 if ((strlen($year) != 4) ||
772 ($day > 31 || $day < 1)||
779 $date_len = strlen($format);
780 for ($i = 0; $i < $date_len; $i++) {
783 $next = $format{$i + 1};
788 $day = (int)Validate::_substr($date, 1, 2);
790 $day = (int)Validate::_substr($date, 0, 2);
792 if ($day < 1 || $day > 31) {
799 $month = (int)Validate::_substr($date, 0, 2);
801 $month = (int)Validate::_substr($date, 1, 2);
803 if ($month < 1 || $month > 12) {
810 $year = Validate::_substr($date, 4);
811 $year = (int)$year?$year:'';
813 $year = (int)(substr(date('Y'), 0, 2) .
814 Validate::_substr($date, 2));
816 if (strlen($year) != 4 || $year < 0 || $year > 9999) {
823 $hour = Validate::_substr($date, 1, 2);
825 $hour = Validate::_substr($date, 2);
827 if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 12) {
834 $hour = Validate::_substr($date, 1, 2);
836 $hour = Validate::_substr($date, 2);
838 if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 24) {
844 $t = Validate::_substr($date, 2);
845 if (!preg_match('/^\d+$/', $t) || $t < 0 || $t > 59) {
850 trigger_error("Not supported char `$next' after % in offset " . ($i+2), E_USER_WARNING);
855 if (Validate::_substr($date, 1) != $c) {
861 // there is remaing data, we don't want it
862 if (strlen($date) && (strtolower($format) != 'rfc822_compliant')) {
866 if (isset($day) && isset($month) && isset($year)) {
867 if (!checkdate($month, $day, $year)) {
871 if (strtolower($format) == 'rfc822_compliant') {
872 if ($weekday != date("D", mktime(0, 0, 0, $month, $day, $year))) {
878 include_once 'Date/Calc.php';
879 if (is_a($min, 'Date') &&
880 (Date_Calc::compareDates($day, $month, $year,
881 $min->getDay(), $min->getMonth(), $min->getYear()) < 0)
884 } elseif (is_array($min) &&
885 (Date_Calc::compareDates($day, $month, $year,
886 $min[0], $min[1], $min[2]) < 0)
893 include_once 'Date/Calc.php';
894 if (is_a($max, 'Date') &&
895 (Date_Calc::compareDates($day, $month, $year,
896 $max->getDay(), $max->getMonth(), $max->getYear()) > 0)
899 } elseif (is_array($max) &&
900 (Date_Calc::compareDates($day, $month, $year,
901 $max[0], $max[1], $max[2]) > 0)
914 * @param string &$date Date
915 * @param string $num Length
916 * @param string $opt Unknown
921 function _substr(&$date, $num, $opt = false)
923 if ($opt && strlen($date) >= $opt && preg_match('/^[0-9]{'.$opt.'}/', $date, $m)) {
926 $ret = substr($date, 0, $num);
928 $date = substr($date, strlen($ret));
932 function _modf($val, $div)
934 if (function_exists('bcmod')) {
935 return bcmod($val, $div);
936 } elseif (function_exists('fmod')) {
937 return fmod($val, $div);
941 return intval($val - $i * $div + .1);
945 * Calculates sum of product of number digits with weights
947 * @param string $number number string
948 * @param array $weights reference to array of weights
952 * @return int returns product of number digits with weights
954 function _multWeights($number, &$weights)
956 if (!is_array($weights)) {
961 $count = min(count($weights), strlen($number));
962 if ($count == 0) { // empty string or weights array
965 for ($i = 0; $i < $count; ++$i) {
966 $sum += intval(substr($number, $i, 1)) * $weights[$i];
973 * Calculates control digit for a given number
975 * @param string $number number string
976 * @param array $weights reference to array of weights
977 * @param int $modulo (optionsl) number
978 * @param int $subtract (optional) number
979 * @param bool $allow_high (optional) true if function can return number higher than 10
983 * @return int -1 calculated control number is returned
985 function _getControlNumber($number, &$weights, $modulo = 10, $subtract = 0, $allow_high = false)
988 $sum = Validate::_multWeights($number, $weights);
992 $mod = Validate::_modf($sum, $modulo); // calculate control digit
994 if ($subtract > $mod && $mod > 0) {
995 $mod = $subtract - $mod;
997 if ($allow_high === false) {
998 $mod %= 10; // change 10 to zero
1004 * Validates a number
1006 * @param string $number number to validate
1007 * @param array $weights reference to array of weights
1008 * @param int $modulo (optional) number
1009 * @param int $subtract (optional) number
1013 * @return bool true if valid, false if not
1015 function _checkControlNumber($number, &$weights, $modulo = 10, $subtract = 0)
1017 if (strlen($number) < count($weights)) {
1020 $target_digit = substr($number, count($weights), 1);
1021 $control_digit = Validate::_getControlNumber($number, $weights, $modulo, $subtract, $modulo > 10);
1023 if ($control_digit == -1) {
1026 if ($target_digit === 'X' && $control_digit == 10) {
1029 if ($control_digit != $target_digit) {
1036 * Bulk data validation for data introduced in the form of an
1037 * assoc array in the form $var_name => $value.
1038 * Can be used on any of Validate subpackages
1040 * @param array $data Ex: array('name' => 'toto', 'email' => 'toto@thing.info');
1041 * @param array $val_type Contains the validation type and all parameters used in.
1042 * 'val_type' is not optional
1043 * others validations properties must have the same name as the function
1045 * Ex: array('toto'=>array('type'=>'string','format'='toto@thing.info','min_length'=>5));
1046 * @param boolean $remove if set, the elements not listed in data will be removed
1048 * @return array value name => true|false the value name comes from the data key
1052 function multiple(&$data, &$val_type, $remove = false)
1054 $keys = array_keys($data);
1057 foreach ($keys as $var_name) {
1058 if (!isset($val_type[$var_name])) {
1060 unset($data[$var_name]);
1064 $opt = $val_type[$var_name];
1065 $methods = get_class_methods('Validate');
1066 $val2check = $data[$var_name];
1067 // core validation method
1068 if (in_array(strtolower($opt['type']), $methods)) {
1069 //$opt[$opt['type']] = $data[$var_name];
1070 $method = $opt['type'];
1071 unset($opt['type']);
1073 if (sizeof($opt) == 1 && is_array(reset($opt))) {
1074 $opt = array_pop($opt);
1076 $valid[$var_name] = call_user_func(array('Validate', $method), $val2check, $opt);
1079 * external validation method in the form:
1080 * "<class name><underscore><method name>"
1081 * Ex: us_ssn will include class Validate/US.php and call method ssn()
1083 } elseif (strpos($opt['type'], '_') !== false) {
1084 $validateType = explode('_', $opt['type']);
1085 $method = array_pop($validateType);
1086 $class = implode('_', $validateType);
1087 $classPath = str_replace('_', DIRECTORY_SEPARATOR, $class);
1088 $class = 'Validate_' . $class;
1089 if (!@include_once "Validate/$classPath.php") {
1090 trigger_error("$class isn't installed or you may have some permissoin issues", E_USER_ERROR);
1093 $ce = substr(phpversion(), 0, 1) > 4 ?
1094 class_exists($class, false) : class_exists($class);
1096 !in_array($method, get_class_methods($class))
1098 trigger_error("Invalid validation type $class::$method",
1102 unset($opt['type']);
1103 if (sizeof($opt) == 1) {
1104 $opt = array_pop($opt);
1106 $valid[$var_name] = call_user_func(array($class, $method),
1107 $data[$var_name], $opt);
1109 trigger_error("Invalid validation type {$opt['type']}",