source: trunk/lib/Validator.inc.php @ 490

Last change on this file since 490 was 490, checked in by anonymous, 10 years ago

Increased robustness of Email(); added envelope_sender_address param. Clarity in Validator class.

File size: 13.0 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * Validator.inc.php
25 *
26 * The Validator class provides a methods for validating input against different criteria.
27 * All functions return true if the input passes the test.
28 *
29 * @author    Quinn Comendant <quinn@strangecode.com>
30 * @version   1.0
31 */
32
33class Validator {
34
35    // Known credit card types.
36    const CC_TYPE_VISA = 1;
37    const CC_TYPE_MASTERCARD = 2;
38    const CC_TYPE_AMEX = 3;
39    const CC_TYPE_DISCOVER = 4;
40    const CC_TYPE_DINERS = 5;
41    const CC_TYPE_JCB = 6;
42
43    // Validator::validateEmail() return types.
44    const EMAIL_SUCCESS = 0;
45    const EMAIL_REGEX_FAIL = 1;
46    const EMAIL_LENGTH_FAIL = 2;
47    const EMAIL_MX_FAIL = 3;
48
49    // Validator::validatePhone() return types.
50    const PHONE_SUCCESS = 0;
51    const PHONE_REGEX_FAIL = 1;
52    const PHONE_LENGTH_FAIL = 2;
53
54    /**
55     * Check if a value is not empty (just the opposite of isEmpty()).
56     *
57     * @param  string $val The input data to validate.
58     * @return bool   true if form is not empty, false otherwise.
59     */
60    static public function notEmpty($val)
61    {
62        return !self::isEmpty($val);
63    }
64
65    /**
66     * Check if a value is empty.
67     *
68     * @param  string $val The input data to validate.
69     * @return bool   true if form is empty, false otherwise.
70     */
71    static public function isEmpty($val)
72    {
73        if (is_array($val)) {
74            return empty($val);
75        } else {
76            return '' == trim((string)$val);
77        }
78    }
79
80    /**
81     * Check whether input is a string.
82     *
83     * @param  string $val The input data to validate.
84     * @return bool   true if form is a string, false otherwise.
85     */
86    static public function isString($val)
87    {
88        return '' == trim((string)$val) || is_string($val);
89    }
90
91    /**
92     * Check whether input is a number. Allows negative numbers.
93     *
94     * @param  string $val The input data to validate.
95     * @return bool   True if no errors found, false otherwise.
96     */
97    static public function isNumber($val)
98    {
99        return '' == trim((string)$val) || is_numeric($val);
100    }
101
102    /**
103     * addError if input is NOT an integer. Don't just use is_int() because the
104     * data coming from the user is *really* a string.
105     *
106     * @param  string $val The input data to validate.
107     * @return bool   true if value is an integer
108     */
109    static public function isInteger($val, $negative_ok=false)
110    {
111        $pattern = $negative_ok ? '/^-?[[:digit:]]+$/' : '/^[[:digit:]]+$/';
112        return '' == trim((string)$val) || (is_numeric($val) && preg_match($pattern, $val));
113    }
114
115    /**
116     * Check whether input is a float. Don't just use is_float() because the
117     * data coming from the user is *really* a string. Integers will also
118     * pass this test.
119     *
120     * @param  string $val The input data to validate.
121     * @return bool   true if value is a float
122     */
123    static public function isFloat($val, $negative_ok=false)
124    {
125        $pattern = $negative_ok ? '/^-?[[:digit:]]*(?:\.?[[:digit:]]+)$/' : '/^[[:digit:]]*(?:\.?[[:digit:]]+)$/';
126        return '' == trim((string)$val) || (is_numeric($val) && preg_match($pattern, $val));
127    }
128
129    /**
130     * Check whether input is an array.
131     *
132     * @param  string $val The input data to validate.
133     * @return bool   true if value is a float
134     */
135    static public function isArray($val)
136    {
137        return (is_string($val) && '' == trim((string)$val)) || is_array($val);
138    }
139
140    /**
141     * Check whether input matches the specified perl regular expression
142     * pattern.
143     *
144     * @param  string $val The input data to validate.
145     * @param  int    $regex            PREG that the string must match
146     * @param  bool   $valid_on_match   Set to true to be valid if match, or false to be valid if the match fails.
147     * @return bool   true if value passes regex test
148     */
149    static public function checkRegex($val, $regex, $valid_on_match=true)
150    {
151        return $valid_on_match ? preg_match($regex, $val) : !preg_match($regex, $val);
152    }
153
154    /**
155     * Tests if the string length is between specified values. Whitespace excluded for min.
156     *
157     * @param  string $val The input data to validate.
158     * @param  int    $min       minimum length of string, inclusive
159     * @param  int    $max       maximum length of string, inclusive
160     * @return bool   true if string length is within given boundaries
161     */
162    static public function stringLength($val, $min, $max)
163    {
164        return mb_strlen(trim((string)$val)) >= $min && mb_strlen($val) <= $max;
165    }
166
167    /**
168     * Check whether input is within a valid numeric range.
169     *
170     * @param  string $val The input data to validate.
171     * @param  int    $min       minimum value of number, inclusive
172     * @param  int    $max       maximum value of number, inclusive
173     * @return bool   True if no errors found, false otherwise.
174     */
175    static public function numericRange($val, $min, $max)
176    {
177        return '' == trim((string)$val) || (is_numeric($val) && $val >= $min && $val <= $max);
178    }
179
180    /**
181     * Validates an email address based on the recommendations in RFC 3696.
182     * Is more loose than restrictive, to allow the many valid variants of
183     * email addresses while catching the most common mistakes.
184     * http://www.faqs.org/rfcs/rfc822.html
185     * http://www.faqs.org/rfcs/rfc2822.html
186     * http://www.faqs.org/rfcs/rfc3696.html
187     * http://www.faqs.org/rfcs/rfc1035.html
188     *
189     * @access  public
190     * @param   string  $val    The input data to validate..
191     * @param   bool    $strict Do we run strict tests?
192     * @return  const           One of the constant values: Validate::EMAIL_SUCCESS|Validate::EMAIL_REGEX_FAIL|Validate::EMAIL_LENGTH_FAIL|Validate::EMAIL_MX_FAIL
193     * @author  Quinn Comendant <quinn@strangecode.com>
194     */
195    static public function validateEmail($val, $strict=false)
196    {
197        require_once 'codebase/lib/Email.inc.php';
198        $e = new Email();
199
200        // Test email address format.
201        if (!preg_match($e->getParam('regex'), $val, $e_parts)) {
202            return self::EMAIL_REGEX_FAIL;
203        }
204
205        // We have a match! Here are the captured subpatterns, on which further tests are run.
206        // The part before the @.
207        $local = $e_parts[2];
208
209        // The part after the @.
210        // If domain is an IP [XXX.XXX.XXX.XXX] strip off the brackets.
211        $domain = $e_parts[3]{0} == '[' ? mb_substr($e_parts[3], 1, -1) : $e_parts[3];
212
213        // Test length.
214        if (mb_strlen($local) > 64 || mb_strlen($domain) > 191) {
215            return self::EMAIL_LENGTH_FAIL;
216        }
217
218        // Strict tests below.
219
220        // Check domain exists: It's a domain if ip2long fails; checkdnsrr ensures a MX record exists; gethostbyname() ensures the domain exists.
221        if ($strict && ip2long($domain) === false && function_exists('checkdnsrr') && !checkdnsrr($domain . '.', 'MX') && gethostbyname($domain) == $domain) {
222            return self::EMAIL_MX_FAIL;
223        }
224
225        return self::EMAIL_SUCCESS;
226    }
227
228    /**
229     * Check whether input is a valid phone number. Notice: it is now set
230     * to allow characters like - or () or + so people can type in a phone
231     * number that looks like: +1 (530) 555-1212
232     *
233     * @param  string  $form_name the name of the incoming form variable
234     *
235     * @return bool    true if no errors found, false otherwise
236     */
237    static public function validatePhone($val)
238    {
239        if (!self::checkRegex($val, '/^[0-9 +().-]*$/', true)) {
240            return self::PHONE_REGEX_FAIL;
241        }
242        if (!self::stringLength($val, 0, 25)) {
243            return self::PHONE_LENGTH_FAIL;
244        }
245        return self::PHONE_SUCCESS;
246    }
247
248    /**
249     * Verifies that date can be processed by the strtotime function.
250     * Empty strings are considered valid. Other values are tested on their return value from strtotime(). Null values will fail.
251     *
252     * @param  string  $val The input data to validate.
253     * @return bool    True if no errors found, false otherwise.
254     */
255    static public function validateStrDate($val)
256    {
257        if (is_string($val) && '' === trim($val)) {
258            // Don't be too bothered about empty strings.
259            return true;
260        }
261
262        $timestamp = strtotime($val);
263        if (!$timestamp || $timestamp < 1) {
264            return false;
265        } else {
266            return true;
267        }
268    }
269
270    /**
271     * Verifies credit card number using the Luhn (mod 10) algorithm.
272     * http://en.wikipedia.org/wiki/Luhn_algorithm
273     *
274     * @param  string  $val   The input data to validate..
275     * @param  string  $cc_num      Card number to verify.
276     * @param  string  $cc_type     Optional, card type to do specific checks.
277     * @return bool    True if no errors found, false otherwise.
278     */
279    static public function validateCCNumber($val, $cc_type=null)
280    {
281        // Get rid of any non-digits
282        $cc_num = preg_replace('/[^\d]/', '', $val);
283
284        // Perform card-specific checks, if applicable
285        switch ($cc_type) {
286        case self::CC_TYPE_VISA :
287            $regex = '/^4\d{15}$|^4\d{12}$/';
288            break;
289        case self::CC_TYPE_MASTERCARD :
290            $regex = '/^5[1-5]\d{14}$/';
291            break;
292        case self::CC_TYPE_AMEX :
293            $regex = '/^3[47]\d{13}$/';
294            break;
295        case self::CC_TYPE_DISCOVER :
296            $regex = '/^6011\d{12}$/';
297            break;
298        case self::CC_TYPE_DINERS :
299            $regex = '/^30[0-5]\d{11}$|^3[68]\d{12}$/';
300            break;
301        case self::CC_TYPE_JCB :
302            $regex = '/^3\d{15}$|^2131|1800\d{11}$/';
303            break;
304        default :
305            $regex = '/\d{13,}/';
306            break;
307        }
308
309        if ('' != $regex && !preg_match($regex, $cc_num)) {
310            // Invalid format.
311            return false;
312        }
313
314        // The Luhn formula works right to left, so reverse the number.
315        $cc_num = strrev($cc_num);
316
317        $luhn_total = 0;
318
319        $num = mb_strlen($cc_num);
320        for ($i=0; $i<$num; $i++) {
321            // Get each digit.
322            $digit = mb_substr($cc_num, $i, 1);
323
324            //  If it's an odd digit, double it.
325            if ($i / 2 != floor($i / 2)) {
326                $digit *= 2;
327            }
328
329            //  If the result is two digits, add them.
330            if (mb_strlen($digit) == 2) {
331                $digit = mb_substr($digit, 0, 1) + mb_substr($digit, 1, 1);
332            }
333
334            //  Add the current digit to the $luhn_total.
335            $luhn_total += $digit;
336        }
337
338        // If the Total is evenly divisible by 10, it's cool!
339        return $luhn_total % 10 == 0;
340    }
341
342    /**
343     * Check whether a file was selected for uploading. If file is missing, it's an error.
344     *
345     * @param  string $form_name The input data to validate.
346     * @return bool   True if no errors found, false otherwise.
347     */
348    static public function fileUploaded($form_name)
349    {
350        if (!isset($_FILES[$form_name]['name']) || empty($_FILES[$form_name]['name'])) {
351            return false;
352        }
353
354        if (is_array($_FILES[$form_name]['name'])) {
355            foreach($_FILES[$form_name]['name'] as $f) {
356                if ('' == $f) {
357                    return false;
358                }
359            }
360        } else {
361            if ('' == $_FILES[$form_name]['name']) {
362                return false;
363            }
364        }
365
366        return true;
367    }
368
369    /*
370    * Check if the amount of content sent by the browser exceeds the upload_max_filesize value configured in php.ini.
371    * http://stackoverflow.com/a/24202363
372    *
373    * @access   public
374    * @param    string $form_name The input data to validate.
375    * @return   bool   True if no errors found, false otherwise.
376    * @author   Quinn Comendant <quinn@strangecode.com>
377    * @version  1.0
378    * @since    20 Aug 2014 14:44:23
379    */
380    static public function fileUploadSize($form_name)
381    {
382        $upload_max_filesize = phpIniGetBytes('upload_max_filesize');
383        if (isset($_SERVER['CONTENT_LENGTH']) && 0 != $upload_max_filesize && $_SERVER['CONTENT_LENGTH'] > $upload_max_filesize) {
384            return false;
385        }
386        return true;
387    }
388
389} // THE END
390
Note: See TracBrowser for help on using the repository browser.