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

Last change on this file since 523 was 523, checked in by anonymous, 9 years ago

First set of changes towards 2.2.0. Improved functinoality with integration in wordpress; bugs fixed.

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