Merge branch 'package'
[speedfreak] / Server / system / helpers / valid.php
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3  * Validation helper class.
4  *
5  * $Id: valid.php 4367 2009-05-27 21:23:57Z samsoir $
6  *
7  * @package    Core
8  * @author     Kohana Team
9  * @copyright  (c) 2007-2008 Kohana Team
10  * @license    http://kohanaphp.com/license.html
11  */
12 class valid_Core {
13
14         /**
15          * Validate email, commonly used characters only
16          *
17          * @param   string   email address
18          * @return  boolean
19          */
20         public static function email($email)
21         {
22                 return (bool) preg_match('/^[-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*+@(?:(?![-.])[-a-z0-9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?$/iD', (string) $email);
23         }
24
25         /**
26          * Validate the domain of an email address by checking if the domain has a
27          * valid MX record.
28          *
29          * @param   string   email address
30          * @return  boolean
31          */
32         public static function email_domain($email)
33         {
34                 // If we can't prove the domain is invalid, consider it valid
35                 // Note: checkdnsrr() is not implemented on Windows platforms
36                 if ( ! function_exists('checkdnsrr'))
37                         return TRUE;
38
39                 // Check if the email domain has a valid MX record
40                 return (bool) checkdnsrr(preg_replace('/^[^@]+@/', '', $email), 'MX');
41         }
42
43         /**
44          * Validate email, RFC compliant version
45          * Note: This function is LESS strict than valid_email. Choose carefully.
46          *
47          * @see  Originally by Cal Henderson, modified to fit Kohana syntax standards:
48          * @see  http://www.iamcal.com/publish/articles/php/parsing_email/
49          * @see  http://www.w3.org/Protocols/rfc822/
50          *
51          * @param   string   email address
52          * @return  boolean
53          */
54         public static function email_rfc($email)
55         {
56                 $qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
57                 $dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
58                 $atom  = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
59                 $pair  = '\\x5c[\\x00-\\x7f]';
60
61                 $domain_literal = "\\x5b($dtext|$pair)*\\x5d";
62                 $quoted_string  = "\\x22($qtext|$pair)*\\x22";
63                 $sub_domain     = "($atom|$domain_literal)";
64                 $word           = "($atom|$quoted_string)";
65                 $domain         = "$sub_domain(\\x2e$sub_domain)*";
66                 $local_part     = "$word(\\x2e$word)*";
67                 $addr_spec      = "$local_part\\x40$domain";
68
69                 return (bool) preg_match('/^'.$addr_spec.'$/D', (string) $email);
70         }
71
72         /**
73          * Validate URL
74          *
75          * @param   string   URL
76          * @return  boolean
77          */
78         public static function url($url)
79         {
80                 return (bool) filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED);
81         }
82
83         /**
84          * Validate IP
85          *
86          * @param   string   IP address
87          * @param   boolean  allow IPv6 addresses
88          * @param   boolean  allow private IP networks
89          * @return  boolean
90          */
91         public static function ip($ip, $ipv6 = FALSE, $allow_private = TRUE)
92         {
93                 // By default do not allow private and reserved range IPs
94                 $flags = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
95                 if ($allow_private === TRUE)
96                         $flags =  FILTER_FLAG_NO_RES_RANGE;
97
98                 if ($ipv6 === TRUE)
99                         return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flags);
100
101                 return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flags | FILTER_FLAG_IPV4);
102         }
103
104         /**
105          * Validates a credit card number using the Luhn (mod10) formula.
106          * @see http://en.wikipedia.org/wiki/Luhn_algorithm
107          *
108          * @param   integer       credit card number
109          * @param   string|array  card type, or an array of card types
110          * @return  boolean
111          */
112         public static function credit_card($number, $type = NULL)
113         {
114                 // Remove all non-digit characters from the number
115                 if (($number = preg_replace('/\D+/', '', $number)) === '')
116                         return FALSE;
117
118                 if ($type == NULL)
119                 {
120                         // Use the default type
121                         $type = 'default';
122                 }
123                 elseif (is_array($type))
124                 {
125                         foreach ($type as $t)
126                         {
127                                 // Test each type for validity
128                                 if (valid::credit_card($number, $t))
129                                         return TRUE;
130                         }
131
132                         return FALSE;
133                 }
134
135                 $cards = Kohana::config('credit_cards');
136
137                 // Check card type
138                 $type = strtolower($type);
139
140                 if ( ! isset($cards[$type]))
141                         return FALSE;
142
143                 // Check card number length
144                 $length = strlen($number);
145
146                 // Validate the card length by the card type
147                 if ( ! in_array($length, preg_split('/\D+/', $cards[$type]['length'])))
148                         return FALSE;
149
150                 // Check card number prefix
151                 if ( ! preg_match('/^'.$cards[$type]['prefix'].'/', $number))
152                         return FALSE;
153
154                 // No Luhn check required
155                 if ($cards[$type]['luhn'] == FALSE)
156                         return TRUE;
157
158                 // Checksum of the card number
159                 $checksum = 0;
160
161                 for ($i = $length - 1; $i >= 0; $i -= 2)
162                 {
163                         // Add up every 2nd digit, starting from the right
164                         $checksum += $number[$i];
165                 }
166
167                 for ($i = $length - 2; $i >= 0; $i -= 2)
168                 {
169                         // Add up every 2nd digit doubled, starting from the right
170                         $double = $number[$i] * 2;
171
172                         // Subtract 9 from the double where value is greater than 10
173                         $checksum += ($double >= 10) ? $double - 9 : $double;
174                 }
175
176                 // If the checksum is a multiple of 10, the number is valid
177                 return ($checksum % 10 === 0);
178         }
179
180         /**
181          * Checks if a phone number is valid.
182          *
183          * @param   string   phone number to check
184          * @return  boolean
185          */
186         public static function phone($number, $lengths = NULL)
187         {
188                 if ( ! is_array($lengths))
189                 {
190                         $lengths = array(7,10,11);
191                 }
192
193                 // Remove all non-digit characters from the number
194                 $number = preg_replace('/\D+/', '', $number);
195
196                 // Check if the number is within range
197                 return in_array(strlen($number), $lengths);
198         }
199
200         /**
201          * Tests if a string is a valid date string.
202          * 
203          * @param   string   date to check
204          * @return  boolean
205          */
206         public static function date($str)
207         {
208                 return (strtotime($str) !== FALSE);
209         }
210
211         /**
212          * Checks whether a string consists of alphabetical characters only.
213          *
214          * @param   string   input string
215          * @param   boolean  trigger UTF-8 compatibility
216          * @return  boolean
217          */
218         public static function alpha($str, $utf8 = FALSE)
219         {
220                 return ($utf8 === TRUE)
221                         ? (bool) preg_match('/^\pL++$/uD', (string) $str)
222                         : ctype_alpha((string) $str);
223         }
224
225         /**
226          * Checks whether a string consists of alphabetical characters and numbers only.
227          *
228          * @param   string   input string
229          * @param   boolean  trigger UTF-8 compatibility
230          * @return  boolean
231          */
232         public static function alpha_numeric($str, $utf8 = FALSE)
233         {
234                 return ($utf8 === TRUE)
235                         ? (bool) preg_match('/^[\pL\pN]++$/uD', (string) $str)
236                         : ctype_alnum((string) $str);
237         }
238
239         /**
240          * Checks whether a string consists of alphabetical characters, numbers, underscores and dashes only.
241          *
242          * @param   string   input string
243          * @param   boolean  trigger UTF-8 compatibility
244          * @return  boolean
245          */
246         public static function alpha_dash($str, $utf8 = FALSE)
247         {
248                 return ($utf8 === TRUE)
249                         ? (bool) preg_match('/^[-\pL\pN_]++$/uD', (string) $str)
250                         : (bool) preg_match('/^[-a-z0-9_]++$/iD', (string) $str);
251         }
252
253         /**
254          * Checks whether a string consists of digits only (no dots or dashes).
255          *
256          * @param   string   input string
257          * @param   boolean  trigger UTF-8 compatibility
258          * @return  boolean
259          */
260         public static function digit($str, $utf8 = FALSE)
261         {
262                 return ($utf8 === TRUE)
263                         ? (bool) preg_match('/^\pN++$/uD', (string) $str)
264                         : ctype_digit((string) $str);
265         }
266
267         /**
268          * Checks whether a string is a valid number (negative and decimal numbers allowed).
269          *
270          * @see Uses locale conversion to allow decimal point to be locale specific.
271          * @see http://www.php.net/manual/en/function.localeconv.php
272          * 
273          * @param   string   input string
274          * @return  boolean
275          */
276         public static function numeric($str)
277         {
278                 // Use localeconv to set the decimal_point value: Usually a comma or period.
279                 $locale = localeconv();
280                 return (bool) preg_match('/^-?[0-9'.$locale['decimal_point'].']++$/D', (string) $str);
281         }
282
283         /**
284          * Checks whether a string is a valid text. Letters, numbers, whitespace,
285          * dashes, periods, and underscores are allowed.
286          *
287          * @param   string   text to check
288          * @return  boolean
289          */
290         public static function standard_text($str)
291         {
292                 // pL matches letters
293                 // pN matches numbers
294                 // pZ matches whitespace
295                 // pPc matches underscores
296                 // pPd matches dashes
297                 // pPo matches normal puncuation
298                 return (bool) preg_match('/^[\pL\pN\pZ\p{Pc}\p{Pd}\p{Po}]++$/uD', (string) $str);
299         }
300
301         /**
302          * Checks if a string is a proper decimal format. The format array can be
303          * used to specify a decimal length, or a number and decimal length, eg:
304          * array(2) would force the number to have 2 decimal places, array(4,2)
305          * would force the number to have 4 digits and 2 decimal places.
306          *
307          * @param   string   input string
308          * @param   array    decimal format: y or x,y
309          * @return  boolean
310          */
311         public static function decimal($str, $format = NULL)
312         {
313                 // Create the pattern
314                 $pattern = '/^[0-9]%s\.[0-9]%s$/';
315
316                 if ( ! empty($format))
317                 {
318                         if (count($format) > 1)
319                         {
320                                 // Use the format for number and decimal length
321                                 $pattern = sprintf($pattern, '{'.$format[0].'}', '{'.$format[1].'}');
322                         }
323                         elseif (count($format) > 0)
324                         {
325                                 // Use the format as decimal length
326                                 $pattern = sprintf($pattern, '+', '{'.$format[0].'}');
327                         }
328                 }
329                 else
330                 {
331                         // No format
332                         $pattern = sprintf($pattern, '+', '+');
333                 }
334
335                 return (bool) preg_match($pattern, (string) $str);
336         }
337
338 } // End valid