home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Validate.php < prev   
Encoding:
PHP Script  |  2008-07-02  |  39.9 KB  |  1,052 lines

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