Open-Source PHP Framework - Designed for rapid development of performance-oriented scalable applications

/mvc/components/validation

[return to app]
1 <?php
2
/**
3  * Validation Class.  Used for validation of model data
4  *
5  * PHP versions 5
6  *
7  * This is CakePHP's validation class updated to PHP5 syntax for Vork. It also has
8  * some imported methods as functions from multibyte class (see end of file).
9  *
10  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
11  * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
12  *
13  * Licensed under The MIT License
14  * Redistributions of files must retain the above copyright notice.
15  *
16  * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
17  * @link          http://cakephp.org CakePHP(tm) Project
18  * @package       cake
19  * @subpackage    cake.cake.libs
20  * @since         CakePHP(tm) v 1.2.0.3830
21  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
22  */
23 /**
24  * Offers different validation methods.
25  *
26  * @package       cake
27  * @subpackage    cake.cake.libs
28  * @since         CakePHP v 1.2.0.3830
29  */
30
class validationComponent {
31     
/**
32      * Set the value of methods $check param.
33      *
34      * @var string
35      * @access public
36      */
37     
public $check null;
38
39     
/**
40      * Set to a valid regular expression in the class methods.
41      * Can be set from $regex param also
42      *
43      * @var string
44      * @access public
45      */
46     
public $regex null;
47
48     
/**
49      * Some complex patterns needed in multiple places
50      *
51      * @var array
52      * @access private
53      */
54     
protected $_pattern = array(
55         
'hostname' =>
 
'(?:[a-z\d][-a-z\d]*\.)*(?:[a-z\d][-a-z\d]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)'
56     
);
57
58     
/**
59      * Some class methods use a country to determine proper validation.
60      * This can be passed to methods in the $country param
61      *
62      * @var string
63      * @access public
64      */
65     
public $country null;
66
67     
/**
68      * Some class methods use a deeper validation when set to true
69      *
70      * @var string
71      * @access public
72      */
73     
public $deep null;
74
75     
/**
76      * Some class methods use the $type param to determine which validation to perfom in the method
77      *
78      * @var string
79      * @access public
80      */
81     
public $type null;
82
83     
/**
84      * Holds an array of errors messages set in this class.
85      * These are used for debugging purposes
86      *
87      * @var array
88      * @access public
89      */
90     
public $errors = array();
91
92     
/**
93      * Checks that a string contains something other than whitespace
94      *
95      * Returns true if string contains something other than whitespace
96      *
97      * $check can be passed as an array:
98      * array('check' => 'valueToCheck');
99      *
100      * @param mixed $check Value to check
101      * @return boolean Success
102      * @access public
103      */
104     
public function notEmpty($check) {
105         
$this->_reset();
106         
$this->check $check;
107
108         if (
is_array($check)) {
109             
$this->_extract($check);
110         }
111
112         if (empty(
$this->check) && $this->check != '0') {
113             return 
false;
114         }
115         
$this->regex '/[^\s]+/m';
116         return 
$this->_check();
117     }
118
119     
/**
120      * Checks that a string contains only integer or letters
121      *
122      * Returns true if string contains only integer or letters
123      *
124      * $check can be passed as an array:
125      * array('check' => 'valueToCheck');
126      *
127      * @param mixed $check Value to check
128      * @return boolean Success
129      * @access public
130      */
131     
public function alphaNumeric($check) {
132         
$this->_reset();
133         
$this->check $check;
134
135         if (
is_array($check)) {
136             
$this->_extract($check);
137         }
138
139         if (empty(
$this->check) && $this->check != '0') {
140             return 
false;
141         }
142         
$this->regex '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/mu';
143         return 
$this->_check();
144     }
145
146     
/**
147      * Checks that a string length is within s specified range.
148      * Spaces are included in the character count.
149      * Returns true is string matches value min, max, or between min and max,
150      *
151      * @param string $check Value to check for length
152      * @param integer $min Minimum value in range (inclusive)
153      * @param integer $max Maximum value in range (inclusive)
154      * @return boolean Success
155      * @access public
156      */
157     
public function between($check$min$max) {
158         
$length self::mb_strlen($check);
159         return (
$length >= $min && $length <= $max);
160     }
161
162     
/**
163      * Returns true if field is left blank -OR- only whitespace characters are present in it's value
164      * Whitespace characters include Space, Tab, Carriage Return, Newline
165      *
166      * $check can be passed as an array:
167      * array('check' => 'valueToCheck');
168      *
169      * @param mixed $check Value to check
170      * @return boolean Success
171      * @access public
172      */
173     
public function blank($check) {
174         
$this->_reset();
175         
$this->check $check;
176
177         if (
is_array($check)) {
178             
$this->_extract($check);
179         }
180
181         
$this->regex '/[^\\s]/';
182         return !
$this->_check();
183     }
184
185     
/**
186      * Validation of credit card numbers.
187      * Returns true if $check is in the proper credit card format.
188      *
189      * @param mixed $check credit card number to validate
190      * @param mixed $type 'all' may be passed as a sting, defaults to fast which checks format of most major
 
credit
191      
*    cards if an array is used only the values of the array are checked.
192      *    
Example: array('amex''bankcard''maestro')
193      * @
param boolean $deep set to true this will check the Luhn algorithm of the credit card.
194      * @
param string $regex A custom regex can also be passedthis will be used instead of the defined regex
 
values
195      
* @return boolean Success
196      
* @access public
197      * @
see validationComponent::_luhn()
198      */
199     public function 
cc($check$type 'fast'$deep false$regex null) {
200         
$this->_reset();
201         
$this->check $check;
202         
$this->type $type;
203         
$this->deep $deep;
204         
$this->regex $regex;
205
206         if (
is_array($check)) {
207             
$this->_extract($check);
208         }
209         
$this->check str_replace(array('-'' '), ''$this->check);
210
211         if (
self::mb_strlen($this->check) < 13) {
212             return 
false;
213         }
214
215         if (!
is_null($this->regex)) {
216             if (
$this->_check()) {
217                 return 
$this->_luhn();
218             }
219         }
220         
$cards = array(
221             
'all' => array(
222                 
'amex' => '/^3[4|7]\\d{13}$/',
223                 
'bankcard' => '/^56(10\\d\\d|022[1-5])\\d{10}$/',
224                 
'diners'   => '/^(?:3(0[0-5]|[68]\\d)\\d{11})|(?:5[1-5]\\d{14})$/',
225                 
'disc'     => '/^(?:6011|650\\d)\\d{12}$/',
226                 
'electron' => '/^(?:417500|4917\\d{2}|4913\\d{2})\\d{10}$/',
227                 
'enroute'  => '/^2(?:014|149)\\d{11}$/',
228                 
'jcb'      => '/^(3\\d{4}|2100|1800)\\d{11}$/',
229                 
'maestro'  => '/^(?:5020|6\\d{3})\\d{12}$/',
230                 
'mc'       => '/^5[1-5]\\d{14}$/',
231                 
'solo'     => '/^(6334[5-9][0-9]|6767[0-9]{2})\\d{10}(\\d{2,3})?$/',
232                 
'switch'   => '/^(?:49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])' .
233                               
'|36[0-9]{2})\\d{10}(\\d{2,3})?)|(?:564182\\d{10}' .
234                               
'(\\d{2,3})?)|(6(3(33[0-4][0-9])|759[0-9]{2})\\d{10}(\\d{2,3})?)$/',
235                 
'visa'     => '/^4\\d{12}(\\d{3})?$/',
236                 
'voyager'  => '/^8699[0-9]{11}$/'
237             
),
238             
'fast'   => '/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}' .
239                         
'|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/'
240         
);
241
242         if (
is_array($this->type)) {
243             foreach (
$this->type as $value) {
244                 
$this->regex $cards['all'][strtolower($value)];
245
246                 if (
$this->_check()) {
247                     return 
$this->_luhn();
248                 }
249             }
250         } elseif (
$this->type == 'all') {
251             foreach (
$cards['all'] as $value) {
252                 
$this->regex $value;
253
254                 if (
$this->_check()) {
255                     return 
$this->_luhn();
256                 }
257             }
258         } else {
259             
$this->regex $cards['fast'];
260
261             if (
$this->_check()) {
262                 return 
$this->_luhn();
263             }
264         }
265     }
266
267     
/**
268      * Used to compare 2 numeric values.
269      *
270      * @param mixed $check1 if string is passed for a string must also be passed for $check2
271      *    used as an array it must be passed as array('check1' => value, 'operator' => 'value', 'check2' -> value)
272      * @param string $operator Can be either a word or operand
273      *    is greater >, is less <, greater or equal >=
274      *    less or equal <=, is less <, equal to ==, not equal !=
275      * @param integer $check2 only needed if $check1 is a string
276      * @return boolean Success
277      * @access public
278      */
279     
public function comparison($check1$operator null$check2 null) {
280         if (
is_array($check1)) {
281             
extract($check1EXTR_OVERWRITE);
282         }
283         
$operator str_replace(array(' '"\t""\n""\r""\0""\x0B"), ''strtolower($operator));
284
285         switch (
$operator) {
286             case 
'isgreater':
287             case 
'>':
288                 if (
$check1 $check2) {
289                     return 
true;
290                 }
291                 break;
292             case 
'isless':
293             case 
'<':
294                 if (
$check1 $check2) {
295                     return 
true;
296                 }
297                 break;
298             case 
'greaterorequal':
299             case 
'>=':
300                 if (
$check1 >= $check2) {
301                     return 
true;
302                 }
303                 break;
304             case 
'lessorequal':
305             case 
'<=':
306                 if (
$check1 <= $check2) {
307                     return 
true;
308                 }
309                 break;
310             case 
'equalto':
311             case 
'==':
312                 if (
$check1 == $check2) {
313                     return 
true;
314                 }
315                 break;
316             case 
'notequal':
317             case 
'!=':
318                 if (
$check1 != $check2) {
319                     return 
true;
320                 }
321                 break;
322             default:
323                 
$this->errors[] = 'You must define the $operator parameter for ' .
324                                    
'validationComponent::comparison()';
325                 break;
326         }
327         return 
false;
328     }
329
330     
/**
331      * Used when a custom regular expression is needed.
332      *
333      * @param mixed $check When used as a string, $regex must also be a valid regular expression.
334      *                              As and array: array('check' => value, 'regex' => 'valid regular expression')
335      * @param string $regex If $check is passed as a string, $regex must also be set to valid regular expression
336      * @return boolean Success
337      * @access public
338      */
339     
public function custom($check$regex null) {
340         
$this->_reset();
341         
$this->check $check;
342         
$this->regex $regex;
343         if (
is_array($check)) {
344             
$this->_extract($check);
345         }
346         if (
$this->regex === null) {
347             
$this->errors[] = 'You must define a regular expression for validationComponent::custom()';
348             return 
false;
349         }
350         return 
$this->_check();
351     }
352
353     
/**
354      * Date validation, determines if the string passed is a valid date.
355      * keys that expect full month, day and year will validate leap years
356      *
357      * @param string $check a valid date string
358      * @param mixed $format Use a string or an array of the keys below.
359      *                  Arrays should be passed as array('dmy', 'mdy', etc)
360      *                  Keys: dmy 27-12-2006 or 27-12-06 separators can be a space, period, dash, forward slash
361      *                        mdy 12-27-2006 or 12-27-06 separators can be a space, period, dash, forward slash
362      *                        ymd 2006-12-27 or 06-12-27 separators can be a space, period, dash, forward slash
363      *                        dMy 27 December 2006 or 27 Dec 2006
364      *                        Mdy December 27, 2006 or Dec 27, 2006 comma is optional
365      *                        My December 2006 or Dec 2006
366      *                        my 12/2006 separators can be a space, period, dash, forward slash
367      * @param string $regex If a custom regular expression is used this is the only validation that will occur.
368      * @return boolean Success
369      * @access public
370      */
371     
public function date($check$format 'ymd'$regex null) {
372         
$this->_reset();
373         
$this->check $check;
374         
$this->regex $regex;
375
376         if (!
is_null($this->regex)) {
377             return 
$this->_check();
378         }
379
380         
$regex['dmy'] = '%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\\/|-|\\.|\\x20)' .
381                         
'(?:0?[1,3-9]|1[0-2])\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(\\/|-|\\.|\\x20)0?2' .
382                         
'\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]' .
383                         
'|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(\\/|-|\\.|\\x20)(?:(?:0?[1-9])' .
384                         
'|(?:1[0-2]))\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%';
385         
$regex['mdy'] = '%^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.|\\x20)31)\\1|(?:(?:0?[13-9]|1[0-2])' .
386                         
'(\\/|-|\\.|\\x20)(?:29|30)\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$' .
387                         
'|^(?:0?2(\\/|-|\\.|\\x20)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]' .
388                         
'|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|' .
389                         
'^(?:(?:0?[1-9])|(?:1[0-2]))(\\/|-|\\.|\\x20)(?:0?[1-9]|1\\d|2[0-8])' .
390                         
'\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%';
391         
$regex['ymd'] = '%^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])' .
392                         
'|(?:(?:16|[2468][048]|[3579][26])00)))(\\/|-|\\.|\\x20)(?:0?2\\1(?:29)))' .
393                         
'|(?:(?:(?:1[6-9]|[2-9]\\d)?\\d{2})(\\/|-|\\.|\\x20)(?:(?:(?:0?[13578]|1[02])' .
394                         
'\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2' .
395                         
'(?:0?[1-9]|1\\d|2[0-8]))))$%';
396         
$regex['dMy'] = '/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))' .
397                         
'|((30|29)(?!\\ Feb(ruary)?))' .
398                         
'|(29(?=\\ Feb(ruary)?\\ (((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])' .
399                         
'|((16|[2468][048]|[3579][26])00)))))|(0?[1-9])|1\\d|2[0-8])\\ ' .
400                         
'(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?' .
401                         
'|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ((1[6-9]|[2-9]\\d)\\d{2})$/';
402         
$regex['Mdy'] = '/^(?:(((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?)\\ 31)' .
403                         
'|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?' .
404                         
'|(Sept|Nov|Dec)(ember)?)\\ (0?[1-9]|([12]\\d)|30))|(Feb(ruary)?\\ ' .
405                         
'(0?[1-9]|1\\d|2[0-8]|(29(?=,?\\ ((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])' .
406                         
'|((16|[2468][048]|[3579][26])00)))))))\\,?\\ ((1[6-9]|[2-9]\\d)\\d{2}))$/';
407         
$regex['My'] = '%^(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|' .
408                        
'(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)[ /]((1[6-9]|[2-9]\\d)\\d{2})$%';
409         
$regex['my'] = '%^(((0[123456789]|10|11|12)([- /.])(([1][9][0-9][0-9])|([2][0-9][0-9][0-9]))))$%';
410
411         
$format = (is_array($format)) ? array_values($format) : array($format);
412         foreach (
$format as $key) {
413             
$this->regex $regex[$key];
414
415             if (
$this->_check() === true) {
416                 return 
true;
417             }
418         }
419         return 
false;
420     }
421
422     
/**
423      * Time validation, determines if the string passed is a valid time.
424      * Validates time as 24hr (HH:MM) or am/pm ([H]H:MM[a|p]m)
425      * Does not allow/validate seconds.
426      *
427      * @param string $check a valid time string
428      * @return boolean Success
429      * @access public
430      */
431     
public function time($check) {
432         
$this->_reset();
433         
$this->check $check;
434         
$this->regex '%^((0?[1-9]|1[012])(:[0-5]\d){0,2}([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%';
435         return 
$this->_check();
436     }
437
438     
/**
439      * Boolean validation, determines if value passed is a boolean integer or true/false.
440      *
441      * @param string $check a valid boolean
442      * @return boolean Success
443      * @access public
444      */
445     
public function boolean($check) {
446         
$booleanList = array(01'0''1'truefalse);
447         return 
in_array($check$booleanListtrue);
448     }
449
450     
/**
451      * Checks that a value is a valid decimal. If $places is null, the $check is allowed to be a scientific float
452      * If no decimal point is found a false will be returned. Both the sign and exponent are optional.
453      *
454      * @param integer $check The value the test for decimal
455      * @param integer $places if set $check value must have exactly $places after the decimal point
456      * @param string $regex If a custom regular expression is used this is the only validation that will occur.
457      * @return boolean Success
458      * @access public
459      */
460     
public function decimal($check$places null$regex null) {
461         
$this->_reset();
462         
$this->regex $regex;
463         
$this->check $check;
464
465         if (
is_null($this->regex)) {
466             if (
is_null($places)) {
467                 
$this->regex '/^[-+]?[0-9]*\\.{1}[0-9]+(?:[eE][-+]?[0-9]+)?$/';
468             } else {
469                 
$this->regex '/^[-+]?[0-9]*\\.{1}[0-9]{'.$places.'}$/';
470             }
471         }
472         return 
$this->_check();
473     }
474
475     
/**
476      * Validates for an email address.
477      *
478      * @param string $check Value to check
479      * @param boolean $deep Perform a deeper validation (if true), by also checking availability of host
480      * @param string $regex Regex to use (if none it will use built in regex)
481      * @return boolean Success
482      * @access public
483      */
484     
public function email($check$deep false$regex null) {
485         
$this->_reset();
486         
$this->check $check;
487         
$this->regex $regex;
488         
$this->deep $deep;
489
490         if (
is_array($check)) {
491             
$this->_extract($check);
492         }
493
494         if (
is_null($this->regex)) {
495             
$this->regex '/^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@'
496                           
$this->_pattern['hostname'] . '$/i';
497         }
498         
$return $this->_check();
499
500         if (
$this->deep === false || $this->deep === null) {
501             return 
$return;
502         }
503
504         if (
$return === true && preg_match('/@(' $this->_pattern['hostname'] . ')$/i'$this->check$regs)) {
505             if (
function_exists('getmxrr') && getmxrr($regs[1], $mxhosts)) {
506                 return 
true;
507             }
508             if (
function_exists('checkdnsrr') && checkdnsrr($regs[1], 'MX')) {
509                 return 
true;
510             }
511             return 
is_array(gethostbynamel($regs[1]));
512         }
513         return 
false;
514     }
515
516     
/**
517      * Check that value is exactly $comparedTo.
518      *
519      * @param mixed $check Value to check
520      * @param mixed $comparedTo Value to compare
521      * @return boolean Success
522      * @access public
523      */
524     
public function equalTo($check$comparedTo) {
525         return (
$check === $comparedTo);
526     }
527
528     
/**
529      * Check that value has a valid file extension.
530      *
531      * @param mixed $check Value to check
532      * @param array $extensions file extenstions to allow
533      * @return boolean Success
534      * @access public
535      */
536     
public function extension($check$extensions = array('gif''jpeg''png''jpg')) {
537         if (
is_array($check)) {
538             return 
self::extension(array_shift($check), $extensions);
539         }
540         
$extension strtolower(array_pop(explode('.'$check)));
541         foreach (
$extensions as $value) {
542             if (
$extension == strtolower($value)) {
543                 return 
true;
544             }
545         }
546         return 
false;
547     }
548
549     
/**
550      * Validation of an IP address.
551      *
552      * Valid IP version strings for type restriction are:
553      * - both: Check both IPv4 and IPv6, return true if the supplied address matches either version
554      * - IPv4: Version 4 (Eg: 127.0.0.1, 192.168.10.123, 203.211.24.8)
555      * - IPv6: Version 6 (Eg: ::1, 2001:0db8::1428:57ab)
556      *
557      * @param string $check The string to test.
558      * @param string $type The IP Version to test against
559      * @return boolean Success
560      * @access public
561      */
562     
public function ip($check$type 'both') {
563         
$success false;
564         
$type strtolower($type);
565         if (
$type === 'ipv4' || $type === 'both') {
566             
$success |= $this->_ipv4($check);
567         }
568         if (
$type === 'ipv6' || $type === 'both') {
569             
$success |= $this->_ipv6($check);
570         }
571         return 
$success;
572     }
573
574     
/**
575      * Validation of IPv4 addresses.
576      *
577      * @param string $check IP Address to test
578      * @return boolean Success
579      * @access protected
580      */
581     
protected function _ipv4($check) {
582         if (
function_exists('filter_var')) {
583             return 
filter_var($checkFILTER_VALIDATE_IP, array('flags' => FILTER_FLAG_IPV4)) !== false;
584         }
585         
$this->_populateIp();
586         
$this->check $check;
587         
$this->regex '/^' $this->_pattern['IPv4'] . '$/';
588         return 
$this->_check();
589     }
590
591     
/**
592      * Validation of IPv6 addresses.
593      *
594      * @param string $check IP Address to test
595      * @return boolean Success
596      * @access protected
597      */
598     
protected function _ipv6($check) {
599         if (
function_exists('filter_var')) {
600             return 
filter_var($checkFILTER_VALIDATE_IP, array('flags' => FILTER_FLAG_IPV6)) !== false;
601         }
602         
$this->_populateIp();
603         
$this->check $check;
604         
$this->regex '/^' $this->_pattern['IPv6'] . '$/';
605         return 
$this->_check();
606     }
607
608     
/**
609      * Checks whether the length of a string is greater or equal to a minimal length.
610      *
611      * @param string $check The string to test
612      * @param integer $min The minimal string length
613      * @return boolean Success
614      * @access public
615      */
616     
public function minLength($check$min) {
617         
$length self::mb_strlen($check);
618         return (
$length >= $min);
619     }
620
621     
/**
622      * Checks whether the length of a string is smaller or equal to a maximal length..
623      *
624      * @param string $check The string to test
625      * @param integer $max The maximal string length
626      * @return boolean Success
627      * @access public
628      */
629     
public function maxLength($check$max) {
630         
$length self::mb_strlen($check);
631         return (
$length <= $max);
632     }
633
634     
/**
635      * Checks that a value is a monetary amount.
636      *
637      * @param string $check Value to check
638      * @param string $symbolPosition Where symbol is located (left/right)
639      * @return boolean Success
640      * @access public
641      */
642     
public function money($check$symbolPosition 'left') {
643         
$this->check $check;
644
645         if (
$symbolPosition == 'right') {
646             
$this->regex '/^(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*' .
647                             
'|(?:\d+))((?!\1)[,.]\d{2})?(?<!\x{00a2})\p{Sc}?$/u';
648         } else {
649             
$this->regex '/^(?!\x{00a2})\p{Sc}?(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?' .
650                             
'(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{2})?$/u';
651         }
652         return 
$this->_check();
653     }
654
655     
/**
656      * Validate a multiple select.
657      *
658      * Valid Options
659      *
660      * - in => provide a list of choices that selections must be made from
661      * - max => maximun number of non-zero choices that can be made
662      * - min => minimum number of non-zero choices that can be made
663      *
664      * @param mixed $check Value to check
665      * @param mixed $options Options for the check.
666      * @return boolean Success
667      * @access public
668      */
669     
public function multiple($check$options = array()) {
670         
$defaults = array('in' => null'max' => null'min' => null);
671         
$options array_merge($defaults$options);
672         
$check array_filter((array)$check);
673         if (empty(
$check)) {
674             return 
false;
675         }
676         if (
$options['max'] && count($check) > $options['max']) {
677             return 
false;
678         }
679         if (
$options['min'] && count($check) < $options['min']) {
680             return 
false;
681         }
682         if (
$options['in'] && is_array($options['in'])) {
683             foreach (
$check as $val) {
684                 if (!
in_array($val$options['in'])) {
685                     return 
false;
686                 }
687             }
688         }
689         return 
true;
690     }
691
692     
/**
693      * Checks if a value is numeric.
694      *
695      * @param string $check Value to check
696      * @return boolean Succcess
697      * @access public
698      */
699     
public function numeric($check) {
700         return 
is_numeric($check);
701     }
702
703     
/**
704      * Check that a value is a valid phone number.
705      *
706      * @param mixed $check Value to check (string or array)
707      * @param string $regex Regular expression to use
708      * @param string $country Country code (defaults to 'all')
709      * @return boolean Success
710      * @access public
711      */
712     
public function phone($check$regex null$country 'all') {
713         
$this->check $check;
714         
$this->regex $regex;
715         
$this->country $country;
716         if (
is_array($check)) {
717             
$this->_extract($check);
718         }
719
720         if (
is_null($this->regex)) {
721             switch (
$this->country) {
722                 case 
'us':
723                 case 
'all':
724                 case 
'can':
725         
// includes all NANPA members. see
726         // http://en.wikipedia.org/wiki/North_American_Numbering_Plan#List_of_NANPA_countries_and_territories
727                     
$this->regex '/^(?:\+?1)?[-. ]?\\(?[2-9][0-8][0-9]\\)?[-. ]?[2-9][0-9]{2}[-. ]?[0-9]{4}$/';
728                 break;
729             }
730         }
731         if (empty(
$this->regex)) {
732             return 
$this->_pass('phone'$check$country);
733         }
734         return 
$this->_check();
735     }
736
737     
/**
738      * Checks that a given value is a valid postal code.
739      *
740      * @param mixed $check Value to check
741      * @param string $regex Regular expression to use
742      * @param string $country Country to use for formatting
743      * @return boolean Success
744      * @access public
745      */
746     
public function postal($check$regex null$country null) {
747         
$this->check $check;
748         
$this->regex $regex;
749         
$this->country $country;
750         if (
is_array($check)) {
751             
$this->_extract($check);
752         }
753         if (empty(
$country)) {
754             
$this->country 'us';
755         }
756
757         if (
is_null($this->regex)) {
758             switch (
$this->country) {
759                 case 
'uk':
760                     
$this->regex  '/\\A\\b[A-Z]{1,2}[0-9][A-Z0-9]? [0-9][ABD-HJLNP-UW-Z]{2}\\b\\z/i';
761                     break;
762                 case 
'ca':
763                     
$this->regex  '/\\A\\b[ABCEGHJKLMNPRSTVXY][0-9][A-Z] ?[0-9][A-Z][0-9]\\b\\z/i';
764                     break;
765                 case 
'it':
766                 case 
'de':
767                     
$this->regex  '/^[0-9]{5}$/i';
768                     break;
769                 case 
'be':
770                     
$this->regex  '/^[1-9]{1}[0-9]{3}$/i';
771                     break;
772                 case 
'us':
773                     
$this->regex  '/\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z/i';
774                     break;
775             }
776         }
777         if (empty(
$this->regex)) {
778             return 
$this->_pass('postal'$check$country);
779         }
780         return 
$this->_check();
781     }
782
783     
/**
784      * Validate that a number is in specified range.
785      * if $lower and $upper are not set, will return true if
786      * $check is a legal finite on this platform
787      *
788      * @param string $check Value to check
789      * @param integer $lower Lower limit
790      * @param integer $upper Upper limit
791      * @return boolean Success
792      * @access public
793      */
794     
public function range($check$lower null$upper null) {
795         if (!
is_numeric($check)) {
796             return 
false;
797         }
798         if (isset(
$lower) && isset($upper)) {
799             return (
$check $lower && $check $upper);
800         }
801         return 
is_finite($check);
802     }
803
804     
/**
805      * Checks that a value is a valid Social Security Number.
806      *
807      * @param mixed $check Value to check
808      * @param string $regex Regular expression to use
809      * @param string $country Country
810      * @return boolean Success
811      * @access public
812      */
813     
public function ssn($check$regex null$country null) {
814         
$this->check $check;
815         
$this->regex $regex;
816         
$this->country $country;
817         if (
is_array($check)) {
818             
$this->_extract($check);
819         }
820
821         if (
is_null($this->regex)) {
822             switch (
$this->country) {
823                 case 
'dk':
824                     
$this->regex  '/\\A\\b[0-9]{6}-[0-9]{4}\\b\\z/i';
825                     break;
826                 case 
'nl':
827                     
$this->regex  '/\\A\\b[0-9]{9}\\b\\z/i';
828                     break;
829                 case 
'us':
830                     
$this->regex  '/\\A\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b\\z/i';
831                     break;
832             }
833         }
834         if (empty(
$this->regex)) {
835             return 
$this->_pass('ssn'$check$country);
836         }
837         return 
$this->_check();
838     }
839
840     
/**
841      * Checks that a value is a valid uuid - http://tools.ietf.org/html/rfc4122
842      * 
843      * @param string $check Value to check
844      * @return boolean Success
845      * @access public
846      */
847     
public function uuid($check) {
848         
$this->check $check;
849         
$this->regex '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i';
850         return 
$this->_check();
851     }
852
853     
/**
854      * Checks that a value is a valid URL according to http://www.w3.org/Addressing/URL/url-spec.txt
855      *
856      * The regex checks for the following component parts:
857      *
858      * - a valid, optional, scheme
859      * - a valid ip address OR
860      *   a valid domain name as defined by section 2.3.1 of http://www.ietf.org/rfc/rfc1035.txt
861      *   with an optional port number
862      * - an optional valid path
863      * - an optional query string (get parameters)
864      * - an optional fragment (anchor tag)
865      *
866      * @param string $check Value to check
867      * @param boolean $strict Require URL to be prefixed by a valid scheme (one of
 
http(s)/ftp(s)/file/news/gopher)
868      * @return 
boolean Success
869      
* @access public
870      */
871     public function 
url($check$strict false) {
872         
$this->_populateIp();
873         
$this->check $check;
874         
$validChars '([' preg_quote('!"$&\'()*+,-.@_:;=~') . '\/0-9a-z]|(%[0-9a-f]{2}))';
875         
$this->regex '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . (!empty($strict) ? '' '?') .
876             
'(?:' $this->_pattern['IPv4'] . '|\[' $this->_pattern['IPv6'] . '\]|' .
877             
$this->_pattern['hostname'] . ')' .
878             
'(?::[1-9][0-9]{0,4})?' .
879             
'(?:\/?|\/' $validChars '*)?' .
880             
'(?:\?' $validChars '*)?' .
881             
'(?:#' $validChars '*)?$/i';
882         return 
$this->_check();
883     }
884
885     
/**
886      * Checks if a value is in a given list.
887      *
888      * @param string $check Value to check
889      * @param array $list List to check against
890      * @return boolean Succcess
891      * @access public
892      */
893     
public function inList($check$list) {
894         return 
in_array($check$list);
895     }
896
897     
/**
898      * Runs an user-defined validation.
899      *
900      * @param mixed $check value that will be validated in user-defined methods.
901      * @param object $object class that holds validation method
902      * @param string $method class method name for validation to run
903      * @param array $args arguments to send to method
904      * @return mixed user-defined class class method returns
905      * @access public
906      */
907     
public function userDefined($check$object$method$args null) {
908         return 
call_user_func_array(array(&$object$method), array($check$args));
909     }
910
911     
/**
912      * Attempts to pass unhandled Validation locales to a class starting with $classPrefix
913      * and ending with ValidationComponent.  For example $classPrefix = 'nl', the class would be
914      * `NlValidationComponent`.
915      *
916      * @param string $method The method to call on the other class.
917      * @param mixed $check The value to check or an array of parameters for the method to be called.
918      * @param string $classPrefix The prefix for the class to do the validation.
919      * @return mixed Return of Passed method, false on failure
920      * @access protected
921      **/
922     
protected function _pass($method$check$classPrefix) {
923         
$className ucwords($classPrefix) . 'ValidationComponent';
924         if (!
class_exists($className)) {
925             
trigger_error(sprintf('Could not find %s class, unable to complete validation.',
926                                   
$className),
927                           
E_USER_WARNING);
928             return 
false;
929         }
930         if (!
is_callable(array($className$method))) {
931             
trigger_error(sprintf('Method %s does not exist on %s unable to complete validation.',
932                                   
$method$className),
933                           
E_USER_WARNING);
934             return 
false;
935         }
936         
$check = (array)$check;
937         return 
call_user_func_array(array($className$method), $check);
938     }
939
940     
/**
941      * Runs a regular expression match.
942      *
943      * @return boolean Success of match
944      * @access protected
945      */
946     
protected function _check() {
947         if (
preg_match($this->regex$this->check)) {
948             
$this->error[] = false;
949             return 
true;
950         } else {
951             
$this->error[] = true;
952             return 
false;
953         }
954     }
955
956     
/**
957      * Get the values to use when value sent to validation method is
958      * an array.
959      *
960      * @param array $params Parameters sent to validation method
961      * @return void
962      * @access protected
963      */
964     
protected function _extract($params) {
965         
extract($paramsEXTR_OVERWRITE);
966
967         if (isset(
$check)) {
968             
$this->check $check;
969         }
970         if (isset(
$regex)) {
971             
$this->regex $regex;
972         }
973         if (isset(
$country)) {
974             
$this->country mb_strtolower($country);
975         }
976         if (isset(
$deep)) {
977             
$this->deep $deep;
978         }
979         if (isset(
$type)) {
980             
$this->type $type;
981         }
982     }
983
984     
/**
985      * Luhn algorithm
986      *
987      * @see http://en.wikipedia.org/wiki/Luhn_algorithm
988      * @return boolean Success
989      * @access protected
990      */
991     
protected function _luhn() {
992         if (
$this->deep !== true) {
993             return 
true;
994         }
995         if (
$this->check == 0) {
996             return 
false;
997         }
998         
$sum 0;
999         
$length strlen($this->check);
1000
1001         for (
$position - ($length 2); $position $length$position += 2) {
1002             
$sum += $this->check[$position];
1003         }
1004
1005         for (
$position = ($length 2); $position $length$position += 2) {
1006             
$number $this->check[$position] * 2;
1007             
$sum += ($number 10) ? $number $number 9;
1008         }
1009
1010         return (
$sum 10 == 0);
1011     }
1012
1013     
/*
1014      * Lazily populate the IP address patterns used for validations
1015      *
1016      * @return void
1017      * @access private
1018      */
1019     
protected function _populateIp() {
1020         if (!isset(
$this->_pattern['IPv6'])) {
1021             
$pattern  '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}';
1022             
$pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})';
1023             
$pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})';
1024             
$pattern .=
 
'(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)';
1025             
$pattern .= '{4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d' .
1026                         
'|[01]?\d{1,2}))';
1027             
$pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}';
1028             
$pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|';
1029             
$pattern .= '((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}';
1030             
$pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
1031             
$pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4})';
1032             
$pattern .= '{0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)';
1033             
$pattern .= '|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]';
1034             
$pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})';
1035             
$pattern .=
 
'{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?';
1036
1037             
$this->_pattern['IPv6'] = $pattern;
1038         }
1039         if (!isset(
$this->_pattern['IPv4'])) {
1040             
$pattern '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}' .
1041                        
'(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])';
1042             
$this->_pattern['IPv4'] = $pattern;
1043         }
1044     }
1045
1046     
/**
1047      * Reset internal variables for another validation run.
1048      *
1049      * @return void
1050      * @access private
1051      */
1052     
protected function _reset() {
1053         
$this->check null;
1054         
$this->regex null;
1055         
$this->country null;
1056         
$this->deep null;
1057         
$this->type null;
1058         
$this->error = array();
1059         
$this->errors = array();
1060     }
1061
1062     
/**
1063      * mb_strlen() wrapper, in case mbstring module is not loaded
1064      *
1065      * @param string $string String to check for length
1066      * @param string $encoding String encoding
1067      * @return integer String length
1068      * @access public
1069      */
1070     
public function mb_strlen($string$encoding null) {
1071         if (
function_exists('mb_strlen')) {
1072             return 
mb_strlen($string$encoding);
1073         }
1074         if (
$this->mb_checkmultibyte($string)) {
1075             
$string $this->mb_utf8($string);
1076             return 
count($string);
1077         }
1078         return 
strlen($string);
1079     }
1080
1081     
/**
1082      * Check if a string is a multibyte string
1083      *
1084      * @param string $string String to check for
1085      * @return boolean True if it's multibyte
1086      * @access public
1087      */
1088     
public function mb_checkmultibyte($string) {
1089         
$length strlen($string);
1090
1091         for (
$i 0$i $length$i++ ) {
1092             
$value ord(($string[$i]));
1093             if (
$value 128) {
1094                 return 
true;
1095             }
1096         }
1097         return 
false;
1098     }
1099
1100     
/**
1101      * Converts a multibyte string into an array,
1102      * each character as an item of the array.
1103      *
1104      * @param string $string String to convert
1105      * @return array The characters of the string
1106      * @access public
1107      */
1108     
public function mb_utf8($string) {
1109         
$map = array();
1110
1111         
$values = array();
1112         
$find 1;
1113         
$length strlen($string);
1114
1115         for (
$i 0$i $length$i++) {
1116             
$value ord($string[$i]);
1117
1118             if (
$value 128) {
1119                 
$map[] = $value;
1120             } else {
1121                 if (empty(
$values)) {
1122                     
$find = ($value 224) ? 3;
1123                 }
1124                 
$values[] = $value;
1125
1126                 if (
count($values) === $find) {
1127                     if (
$find == 3) {
1128                         
$map[] = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64);
1129                     } else {
1130                         
$map[] = (($values[0] % 32) * 64) + ($values[1] % 64);
1131                     }
1132                     
$values = array();
1133                     
$find 1;
1134                 }
1135             }
1136         }
1137         return 
$map;
1138     }
1139 }
1140