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