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
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 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 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 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 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 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 if (count($validate) === 0) {
453 array_push($validate, 'itld', 'gtld', 'cctld');
456 $self = new Validate;
458 $toValidate = array();
460 foreach ($validate as $valid) {
461 $tmpVar = '_' . (string)$valid;
463 $toValidate[$valid] = $self->{$tmpVar};
466 $e = $self->executeFullEmailValidation($email, $toValidate);
472 * Execute the validation
474 * This function will execute the full email vs tld
475 * validation using an array of tlds passed to it.
477 * @param string $email The email to validate.
478 * @param array $arrayOfTLDs The array of the TLDs to validate
482 * @return true or false (Depending on if it validates or if it does not)
484 function executeFullEmailValidation($email, $arrayOfTLDs)
486 $emailEnding = explode('.', $email);
487 $emailEnding = $emailEnding[count($emailEnding)-1];
488 foreach ($arrayOfTLDs as $validator => $keys) {
489 if (in_array($emailEnding, $keys)) {
499 * @param string $email email to validate
500 * @param mixed boolean (BC) $check_domain Check or not if the domain exists
501 * array $options associative array of options
502 * 'check_domain' boolean Check or not if the domain exists
503 * 'use_rfc822' boolean Apply the full RFC822 grammar
507 * 'check_domain' => 'true',
508 * 'fullTLDValidation' => 'true',
509 * 'use_rfc822' => 'true',
510 * 'VALIDATE_GTLD_EMAILS' => 'true',
511 * 'VALIDATE_CCTLD_EMAILS' => 'true',
512 * 'VALIDATE_ITLD_EMAILS' => 'true',
515 * @return boolean true if valid email, false if not
519 function email($email, $options = null)
521 $check_domain = false;
523 if (is_bool($options)) {
524 $check_domain = $options;
525 } elseif (is_array($options)) {
530 * Check for IDN usage so we can encode the domain as Punycode
535 if (Validate::_includePathFileExists('Net/IDNA.php')) {
536 include_once('Net/IDNA.php');
540 if ($hasIDNA === true) {
541 if (strpos($email, '@') !== false) {
542 $tmpEmail = explode('@', $email);
543 $domain = array_pop($tmpEmail);
545 // Check if the domain contains characters > 127 which means
546 // it's an idn domain name.
547 $chars = count_chars($domain, 1);
548 if (!empty($chars) && max(array_keys($chars)) > 127) {
549 $idna =& Net_IDNA::singleton();
550 $domain = $idna->encode($domain);
553 array_push($tmpEmail, $domain);
554 $email = implode('@', $tmpEmail);
559 * @todo Fix bug here.. even if it passes this, it won't be passing
560 * The regular expression below
562 if (isset($fullTLDValidation)) {
563 //$valid = Validate::_fullTLDValidation($email, $fullTLDValidation);
564 $valid = Validate::_fullTLDValidation($email, $options);
571 // the base regexp for address
572 $regex = '&^(?: # recipient:
573 ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name
574 ([-\w!\#\$%\&\'*+~/^`|{}]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}]+)*)) #2 OR dot-atom
575 @(((\[)? #3 domain, 4 as IPv4, 5 optionally bracketed
576 (?:(?:(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.){3}
577 (?:(?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))))(?(5)\])|
578 ((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z0-9](?:[-a-z0-9]*[a-z0-9])?) #6 domain as hostname
579 \.((?:([^- ])[-a-z]*[-a-z]))) #7 TLD
582 //checks if exists the domain (MX or A)
583 if ($use_rfc822? Validate::__emailRFC822($email, $options) :
584 preg_match($regex, $email)) {
585 if ($check_domain && function_exists('checkdnsrr')) {
586 $domain = preg_replace('/[^-a-z.0-9]/i', '', array_pop(explode('@', $email)));
587 if (checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A')) {
598 * Validate a string using the given format 'format'
600 * @param string $string String to validate
601 * @param array $options Options array where:
602 * 'format' is the format of the string
603 * Ex:VALIDATE_NUM . VALIDATE_ALPHA (see constants)
604 * 'min_length' minimum length
605 * 'max_length' maximum length
607 * @return boolean true if valid string, false if not
611 function string($string, $options)
617 if (is_array($options)) {
621 if ($format && !preg_match("|^[$format]*\$|s", $string)) {
625 if ($min_length && strlen($string) < $min_length) {
629 if ($max_length && strlen($string) > $max_length) {
637 * Validate an URI (RFC2396)
638 * This function will validate 'foobarstring' by default, to get it to validate
639 * only http, https, ftp and such you have to pass it in the allowed_schemes
642 * $options = array('allowed_schemes' => array('http', 'https', 'ftp'))
643 * var_dump(Validate::uri('http://www.example.org', $options));
646 * NOTE 1: The rfc2396 normally allows middle '-' in the top domain
647 * e.g. http://example.co-m should be valid
648 * However, as '-' is not used in any known TLD, it is invalid
649 * NOTE 2: As double shlashes // are allowed in the path part, only full URIs
650 * including an authority can be valid, no relative URIs
651 * the // are mandatory (optionally preceeded by the 'sheme:' )
652 * NOTE 3: the full complience to rfc2396 is not achieved by default
653 * the characters ';/?:@$,' will not be accepted in the query part
654 * if not urlencoded, refer to the option "strict'"
656 * @param string $url URI to validate
657 * @param array $options Options used by the validation method.
659 * 'domain_check' => boolean
660 * Whether to check the DNS entry or not
661 * 'allowed_schemes' => array, list of protocols
662 * List of allowed schemes ('http',
664 * 'strict' => string the refused chars
665 * in query and fragment parts
667 * empty: accept all rfc2396 foreseen chars
669 * @return boolean true if valid uri, false if not
673 function uri($url, $options = null)
676 $domain_check = false;
677 $allowed_schemes = null;
678 if (is_array($options)) {
681 if (is_array($allowed_schemes) &&
682 in_array("tag", $allowed_schemes)
684 if (strpos($url, "tag:") === 0) {
685 return self::__uriRFC4151($url);
690 '&^(?:([a-z][-+.a-z0-9]*):)? # 1. scheme
691 (?:// # authority start
692 (?:((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();:\&=+$,])*)@)? # 2. authority-userinfo
693 (?:((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z](?:[a-z0-9]+)?\.?) # 3. authority-hostname OR
694 |([0-9]{1,3}(?:\.[0-9]{1,3}){3})) # 4. authority-ipv4
695 (?::([0-9]*))?) # 5. authority-port
696 ((?:/(?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'():@\&=+$,;])*)*/?)? # 6. path
697 (?:\?([^#]*))? # 7. query
698 (?:\#((?:%[0-9a-f]{2}|[-a-z0-9_.!~*\'();/?:@\&=+$,])*))? # 8. fragment
699 $&xi', $url, $matches)) {
700 $scheme = isset($matches[1]) ? $matches[1] : '';
701 $authority = isset($matches[3]) ? $matches[3] : '' ;
702 if (is_array($allowed_schemes) &&
703 !in_array($scheme, $allowed_schemes)
707 if (!empty($matches[4])) {
708 $parts = explode('.', $matches[4]);
709 foreach ($parts as $part) {
714 } elseif ($domain_check && function_exists('checkdnsrr')) {
715 if (!checkdnsrr($authority, 'A')) {
720 $strict = '#[' . preg_quote($strict, '#') . ']#';
721 if ((!empty($matches[7]) && preg_match($strict, $matches[7]))
722 || (!empty($matches[8]) && preg_match($strict, $matches[8]))) {
732 * Validate date and times. Note that this method need the Date_Calc class
734 * @param string $date Date to validate
735 * @param array $options array options where :
736 * 'format' The format of the date (%d-%m-%Y)
737 * or rfc822_compliant
738 * 'min' The date has to be greater
739 * than this array($day, $month, $year)
740 * or PEAR::Date object
741 * 'max' The date has to be smaller than
742 * this array($day, $month, $year)
743 * or PEAR::Date object
745 * @return boolean true if valid date/time, false if not
749 function date($date, $options)
755 if (is_array($options)) {
759 if (strtolower($format) == 'rfc822_compliant') {
760 $preg = '&^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),) \s+
762 (?:(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)?) \s+
763 (?:(\d{2}(\d{2})?)?) \s+
764 (?:(\d{2}?)):(?:(\d{2}?))(:(?:(\d{2}?)))? \s+
765 (?:[+-]\d{4}|UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Za-ik-z])$&xi';
767 if (!preg_match($preg, $date, $matches)) {
771 $year = (int)$matches[4];
772 $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
773 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
774 $month = array_keys($months, $matches[3]);
775 $month = (int)$month[0]+1;
776 $day = (int)$matches[2];
777 $weekday = $matches[1];
778 $hour = (int)$matches[6];
779 $minute = (int)$matches[7];
780 isset($matches[9]) ? $second = (int)$matches[9] : $second = 0;
782 if ((strlen($year) != 4) ||
783 ($day > 31 || $day < 1)||
790 $date_len = strlen($format);
791 for ($i = 0; $i < $date_len; $i++) {
794 $next = $format{$i + 1};
799 $day = (int)Validate::_substr($date, 1, 2);
801 $day = (int)Validate::_substr($date, 0, 2);
803 if ($day < 1 || $day > 31) {
810 $month = (int)Validate::_substr($date, 0, 2);
812 $month = (int)Validate::_substr($date, 1, 2);
814 if ($month < 1 || $month > 12) {
821 $year = Validate::_substr($date, 4);
822 $year = (int)$year?$year:'';
824 $year = (int)(substr(date('Y'), 0, 2) .
825 Validate::_substr($date, 2));
827 if (strlen($year) != 4 || $year < 0 || $year > 9999) {
834 $hour = Validate::_substr($date, 1, 2);
836 $hour = Validate::_substr($date, 2);
838 if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 12) {
845 $hour = Validate::_substr($date, 1, 2);
847 $hour = Validate::_substr($date, 2);
849 if (!preg_match('/^\d+$/', $hour) || $hour < 0 || $hour > 24) {
855 $t = Validate::_substr($date, 2);
856 if (!preg_match('/^\d+$/', $t) || $t < 0 || $t > 59) {
861 trigger_error("Not supported char `$next' after % in offset " . ($i+2), E_USER_WARNING);
866 if (Validate::_substr($date, 1) != $c) {
872 // there is remaing data, we don't want it
873 if (strlen($date) && (strtolower($format) != 'rfc822_compliant')) {
877 if (isset($day) && isset($month) && isset($year)) {
878 if (!checkdate($month, $day, $year)) {
882 if (strtolower($format) == 'rfc822_compliant') {
883 if ($weekday != date("D", mktime(0, 0, 0, $month, $day, $year))) {
889 include_once 'Date/Calc.php';
890 if (is_a($min, 'Date') &&
891 (Date_Calc::compareDates($day, $month, $year,
892 $min->getDay(), $min->getMonth(), $min->getYear()) < 0)
895 } elseif (is_array($min) &&
896 (Date_Calc::compareDates($day, $month, $year,
897 $min[0], $min[1], $min[2]) < 0)
904 include_once 'Date/Calc.php';
905 if (is_a($max, 'Date') &&
906 (Date_Calc::compareDates($day, $month, $year,
907 $max->getDay(), $max->getMonth(), $max->getYear()) > 0)
910 } elseif (is_array($max) &&
911 (Date_Calc::compareDates($day, $month, $year,
912 $max[0], $max[1], $max[2]) > 0)
925 * @param string &$date Date
926 * @param string $num Length
927 * @param string $opt Unknown
932 function _substr(&$date, $num, $opt = false)
934 if ($opt && strlen($date) >= $opt && preg_match('/^[0-9]{'.$opt.'}/', $date, $m)) {
937 $ret = substr($date, 0, $num);
939 $date = substr($date, strlen($ret));
943 function _modf($val, $div)
945 if (function_exists('bcmod')) {
946 return bcmod($val, $div);
947 } elseif (function_exists('fmod')) {
948 return fmod($val, $div);
952 return intval($val - $i * $div + .1);
956 * Calculates sum of product of number digits with weights
958 * @param string $number number string
959 * @param array $weights reference to array of weights
963 * @return int returns product of number digits with weights
965 function _multWeights($number, &$weights)
967 if (!is_array($weights)) {
972 $count = min(count($weights), strlen($number));
973 if ($count == 0) { // empty string or weights array
976 for ($i = 0; $i < $count; ++$i) {
977 $sum += intval(substr($number, $i, 1)) * $weights[$i];
984 * Calculates control digit for a given number
986 * @param string $number number string
987 * @param array $weights reference to array of weights
988 * @param int $modulo (optionsl) number
989 * @param int $subtract (optional) number
990 * @param bool $allow_high (optional) true if function can return number higher than 10
994 * @return int -1 calculated control number is returned
996 function _getControlNumber($number, &$weights, $modulo = 10, $subtract = 0, $allow_high = false)
999 $sum = Validate::_multWeights($number, $weights);
1003 $mod = Validate::_modf($sum, $modulo); // calculate control digit
1005 if ($subtract > $mod && $mod > 0) {
1006 $mod = $subtract - $mod;
1008 if ($allow_high === false) {
1009 $mod %= 10; // change 10 to zero
1015 * Validates a number
1017 * @param string $number number to validate
1018 * @param array $weights reference to array of weights
1019 * @param int $modulo (optional) number
1020 * @param int $subtract (optional) number
1024 * @return bool true if valid, false if not
1026 function _checkControlNumber($number, &$weights, $modulo = 10, $subtract = 0)
1028 if (strlen($number) < count($weights)) {
1031 $target_digit = substr($number, count($weights), 1);
1032 $control_digit = Validate::_getControlNumber($number, $weights, $modulo, $subtract, $modulo > 10);
1034 if ($control_digit == -1) {
1037 if ($target_digit === 'X' && $control_digit == 10) {
1040 if ($control_digit != $target_digit) {
1047 * Bulk data validation for data introduced in the form of an
1048 * assoc array in the form $var_name => $value.
1049 * Can be used on any of Validate subpackages
1051 * @param array $data Ex: array('name' => 'toto', 'email' => 'toto@thing.info');
1052 * @param array $val_type Contains the validation type and all parameters used in.
1053 * 'val_type' is not optional
1054 * others validations properties must have the same name as the function
1056 * Ex: array('toto'=>array('type'=>'string','format'='toto@thing.info','min_length'=>5));
1057 * @param boolean $remove if set, the elements not listed in data will be removed
1059 * @return array value name => true|false the value name comes from the data key
1063 function multiple(&$data, &$val_type, $remove = false)
1065 $keys = array_keys($data);
1068 foreach ($keys as $var_name) {
1069 if (!isset($val_type[$var_name])) {
1071 unset($data[$var_name]);
1075 $opt = $val_type[$var_name];
1076 $methods = get_class_methods('Validate');
1077 $val2check = $data[$var_name];
1078 // core validation method
1079 if (in_array(strtolower($opt['type']), $methods)) {
1080 //$opt[$opt['type']] = $data[$var_name];
1081 $method = $opt['type'];
1082 unset($opt['type']);
1084 if (sizeof($opt) == 1 && is_array(reset($opt))) {
1085 $opt = array_pop($opt);
1087 $valid[$var_name] = call_user_func(array('Validate', $method), $val2check, $opt);
1090 * external validation method in the form:
1091 * "<class name><underscore><method name>"
1092 * Ex: us_ssn will include class Validate/US.php and call method ssn()
1094 } elseif (strpos($opt['type'], '_') !== false) {
1095 $validateType = explode('_', $opt['type']);
1096 $method = array_pop($validateType);
1097 $class = implode('_', $validateType);
1098 $classPath = str_replace('_', DIRECTORY_SEPARATOR, $class);
1099 $class = 'Validate_' . $class;
1100 if (Validate::_includePathFileExists("Validate/$classPath.php")) {
1101 include_once "Validate/$classPath.php";
1103 trigger_error("$class isn't installed or you may have some permission issues", E_USER_ERROR);
1106 $ce = substr(phpversion(), 0, 1) > 4 ?
1107 class_exists($class, false) : class_exists($class);
1109 !in_array($method, get_class_methods($class))
1111 trigger_error("Invalid validation type $class::$method",
1115 unset($opt['type']);
1116 if (sizeof($opt) == 1) {
1117 $opt = array_pop($opt);
1119 $valid[$var_name] = call_user_func(array($class, $method),
1120 $data[$var_name], $opt);
1122 trigger_error("Invalid validation type {$opt['type']}",
1130 * Determine whether specified file exists along the include path.
1132 * @param string $filename file to search for
1136 * @return bool true if file exists
1138 function _includePathFileExists($filename)
1140 $paths = explode(":", ini_get("include_path"));
1143 while ((!($result)) && (list($key,$val) = each($paths))) {
1144 $result = file_exists($val . "/" . $filename);