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

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

Improved module maker validation output. Allow disabling cache at run time for ACL. Added ACL getList() method. Improved ACL CLI listing. Fixed app boomerang array initialization. Now retaining identical boomerang URLs if the key is different. Added a maximum boomerang time. Added a way to disable cache per request through a query string. Added validator isDecimal() method. Added disableSelectOptions() HTML method. Added getGravatarURL() method. Change how navigation page array is managed. Updated navigation currentPage() method to test an array of URLs.

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