source: branches/1.1dev/lib/FormValidator.inc.php @ 140

Last change on this file since 140 was 140, checked in by scdev, 18 years ago

Q - fixed bug in 1.1dev

File size: 18.1 KB
Line 
1<?php
2/**
3 * FormValidator.inc.php
4 * Code by Strangecode :: www.strangecode.com :: This document contains copyrighted information
5 */
6 
7//     Examples of use:
8//
9//     require_once CODE_BASE . '/lib/FormValidator.inc.php';
10//     $fv = new FormValidator();
11//
12//     $fv->isEmpty('location_name', _("<strong>Location name</strong> cannot be blank."));
13//     $fv->checkRegex('cc_exp', '/^\d{4}$|^$/', true, _("CC exp date must be in MMYY format."));
14//     $fv->isInteger('client_id', _("<strong>Client id</strong> must be an integer."));
15//     $fv->numericRange('client_id', -32768, 32767, _("<strong>Client id</strong> must be a number between -32768 and 32767."));
16//     $fv->stringLength('zip', 0, 255, _("<strong>Zip</strong> must contain less than 256 characters."));
17//     $fv->validateEmail('invoice_email');
18//     $fv->validatePhone('phone1');
19
20
21/**
22 * The FormValidator:: class provides a method for validating input from
23 * http requests and displaying errors.
24 *
25 * @requires  This class requires App.inc.php
26 * @requires  This class requires Utilities.inc.php
27 * @author    Quinn Comendant <quinn@strangecode.com>
28 * @version   1.5
29 */
30
31require_once dirname(__FILE__) . '/App.inc.php';
32require_once dirname(__FILE__) . '/Utilities.inc.php';
33require_once dirname(__FILE__) . '/Prefs.inc.php';
34
35class FormValidator
36{
37   
38    /**
39     * Array filling with errors. The key will be the name of the form where
40     * the data came from. The value will be a message to print to the user.
41     */
42    var $errors = array();
43   
44    /**
45     * Return the current list of errors.
46     *
47     * @return array    an array of errors in the following arrangement:
48     *                  keys: the name of the variable with an error
49     *                  vals: the message to display for that error
50     */
51    function getErrorList()
52    {
53        return $this->errors;
54    }
55   
56    /**
57     * Add an error to the errors stack.
58     *
59     * @param   string $form_name   The name of the incoming form variable.
60     * @param   string $msg         The error message for that form.
61     * @param   int    $type        The type of message: MSG_NOTICE,
62     *                              MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
63     * @param   string $file        __FILE__.
64     * @param   string $line        __LINE__.
65     */
66    function addError($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null)
67    {
68        $this->errors[] = array(
69            'name' => $form_name,
70            'message' => $msg,
71            'type' => $type,
72            'file' => $file,
73            'line' => $line
74        );
75    }
76   
77    /**
78     * Check whether any errors have been triggered.
79     *
80     * @param  string $form_name the name of the incoming form variable
81     *
82     * @return bool   true if any errors were found, or if found for
83     *                a variable of $form_name, false otherwise
84     */
85    function anyErrors($form_name=null)
86    {
87        if (isset($form_name)) {
88            foreach ($this->errors as $err) {
89                if ($err['name'] == $form_name) {
90                    return true;   
91                }
92            }
93            return false;
94        }
95        return (sizeof($this->errors) > 0);
96    }
97
98    /**
99     * Reset the error list.
100     */
101    function resetErrorList()
102    {
103        $this->errors = array();
104    }
105
106    /**
107     * If this form has an error, print an error marker like "<<".
108     *
109     * @param  string $form_name the name of the incoming form variable
110     * @param  string $marker    A string to print if there is an error. if
111     *                           not provided, use default.
112     */
113    function err($form_name, $marker=null)
114    {
115        global $CFG;
116        if ($this->anyErrors($form_name)) {
117            if (isset($marker)) {
118                echo $marker;
119            } else {
120                echo '<span style="color:red; font-size:2em;"><strong>&laquo;</strong></span>';
121            }
122        }
123    }
124
125    /**
126     * Check whether input has a value. To be used when a value must be empty
127     * under certain circumstances.
128     *
129     * @param  string $form_name the name of the incoming form variable
130     * @param  string $msg       the message to display on error
131     *
132     * @return bool   true if form is not empty, false otherwise.
133     */
134    function notEmpty($form_name, $msg='')
135    {
136   
137        $val = trim(getFormData($form_name));
138        if ($val != '') {
139            $this->addError($form_name, $msg);
140            return true;
141        } else {
142            return false;
143        }
144    }
145
146    /**
147     * Check whether input is blank.
148     *
149     * @param  string $form_name the name of the incoming form variable
150     * @param  string $msg       the message to display on error
151     *
152     * @return bool   true if form is empty, false otherwise.
153     */
154    function isEmpty($form_name, $msg='')
155    {
156   
157        $val = trim(getFormData($form_name));
158        if ($val == '') {
159            $this->addError($form_name, $msg);
160            return true;
161        } else {
162            return false;
163        }
164    }
165
166    /**
167     * Check whether input is a string.
168     *
169     * @param  string $form_name the name of the incoming form variable
170     * @param  string $msg       the message to display on error
171     *
172     * @return bool   true if form is a string, false otherwise.
173     */
174    function isString($form_name, $msg='')
175    {
176        $val = getFormData($form_name);
177        if (!is_string($val) && $val != '') {
178            $this->addError($form_name, $msg);
179            return false;
180        } else {
181            return true;
182        }
183    }
184
185    /**
186     * Check whether input is a number. Allows negative numbers.
187     *
188     * @param  string $form_name the name of the incoming form variable
189     * @param  string $msg       the message to display on error
190     *
191     * @return bool   true if no errors found, false otherwise
192     */
193    function isNumber($form_name, $msg='')
194    {
195        $val = getFormData($form_name);
196        if (!is_numeric($val) && $val != '') {
197            $this->addError($form_name, $msg);
198            return false;
199        } else {
200            return true;
201        }
202    }
203
204    /**
205     * addError if input is NOT an integer. Don't just use is_int() because the
206     * data coming from the user is *really* a string.
207     *
208     * @param  string $form_name the name of the incoming form variable
209     * @param  string $msg       the message to display on error
210     *
211     * @return bool   true if value is an integer
212     */
213    function isInteger($form_name, $msg='', $negative_ok=false)
214    {
215        $val = getFormData($form_name);
216        $pattern = $negative_ok ? '/^-?[[:digit:]]+$/' : '/^[[:digit:]]+$/';
217        if ((!is_numeric($val) || !preg_match($pattern, $val)) && $val != '') {
218            $this->addError($form_name, $msg);
219            return false;
220        } else {
221            return true;
222        }
223    }
224
225    /**
226     * Check whether input is a float. Don't just use is_float() because the
227     * data coming from the user is *really* a string. Integers will also
228     * pass this test.
229     *
230     * @param  string $form_name the name of the incoming form variable
231     * @param  string $msg       the message to display on error
232     *
233     * @return bool   true if value is a float
234     */
235    function isFloat($form_name, $msg='', $negative_ok=false)
236    {
237        $val = getFormData($form_name);
238        $pattern = $negative_ok ? '/^-?[[:digit:]]*(?:\.?[[:digit:]]+)$/' : '/^[[:digit:]]*(?:\.?[[:digit:]]+)$/';
239        if ((!is_numeric($val) || !preg_match($pattern, $val)) && $val != '') {
240            $this->addError($form_name, $msg);
241            return false;
242        } else {
243            return true;
244        }
245    }
246
247    /**
248     * Check whether input is an array.
249     *
250     * @param  string $form_name the name of the incoming form variable
251     * @param  string $msg       the message to display on error
252     *
253     * @return bool   true if value is a float
254     */
255    function isArray($form_name, $msg='')
256    {
257        $val = getFormData($form_name);
258        if (!is_array($val) && !empty($val)) {
259            $this->addError($form_name, $msg);
260            return false;
261        } else {
262            return true;
263        }
264    }
265   
266    /**
267     * Check whether input matches the specified perl regular expression
268     * pattern.
269     *
270     * @param  string $form_name the name of the incoming form variable
271     * @param  int    $regex     perl regex that the string must match
272     * @param  bool   $not       set to false to be valid if match, or true
273     *                           to be valid on no match
274     * @param  string $msg       the message to display on error
275     *
276     * @return bool   true if value passes regex test
277     */
278    function checkRegex($form_name, $regex, $not, $msg='')
279    {
280        $val = getFormData($form_name);
281        if ($not) {
282            if (!preg_match($regex, $val))  {
283                $this->addError($form_name, $msg);
284                return false;
285            } else {
286                return true;
287            }
288        } else {
289            if (preg_match($regex, $val))  {
290                $this->addError($form_name, $msg);
291                return false;
292            } else {
293                return true;
294            }
295        }
296    }
297   
298    /**
299     * Tests if the string length is between specified values. Whitespace excluded for min.
300     *
301     * @param  string $form_name the name of the incoming form variable
302     * @param  int    $min       minimum length of string, inclusive
303     * @param  int    $max       maximum length of string, inclusive
304     * @param  string $msg       the message to display on error
305     *
306     * @return bool   true if string length is within given boundaries
307     */
308    function stringLength($form_name, $min, $max, $msg='')
309    {
310        $val = getFormData($form_name);
311       
312        if (strlen(trim($val)) < $min || strlen($val) > $max) {
313            $this->addError($form_name, $msg);
314            return false;
315        } else {
316            return true;
317        }
318    }
319
320    /**
321     * Check whether input is within a valid numeric range.
322     *
323     * @param  string $form_name the name of the incoming form variable
324     * @param  int    $min       minimum value of number, inclusive
325     * @param  int    $max       maximum value of number, inclusive
326     * @param  string $msg       the message to display on error
327     *
328     * @return bool   true if no errors found, false otherwise
329     */
330    function numericRange($form_name, $min, $max, $msg='')
331    {
332        $val = getFormData($form_name);
333        if ($val != '' && is_numeric($val)) {
334            if ($val < $min || $val > $max) {
335                $this->addError($form_name, $msg);
336                return false;
337            }
338            return true;
339        } else {
340            // Not a number!
341            return false;
342        }
343    }
344
345    /**
346     * Validates email address length, domain name existance, format.
347     *
348     * @param  string  $form_name       The name of the incoming form variable
349     * @param  boolean $allow_fullname  Allow the use of rfc822 expanded email address with comment: Quinn Commie <quinn@strangecode.com>
350     *
351     * @return bool    true if no errors found, false otherwise
352     */
353    function validateEmail($form_name, $allow_fullname=false)
354    {
355        $email = getFormData($form_name);
356        if ('' == trim($email)) {
357            return false;
358        }
359       
360        // Test email address format.
361        if ($allow_fullname) {
362            if (!$this->checkRegex($form_name, '/^[\w\s]*<?php[A-Za-z0-9._-]{1,}\@[A-Za-z0-9.-]{1,}\.[A-Za-z]{2,5}>?$/i', true, sprintf(_("<strong>%s</strong> is not a valid email address."), $email))) {
363                logMsg(sprintf('The email address %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
364                return false;
365            }
366        } else {
367            if (!$this->checkRegex($form_name, '/^[A-Za-z0-9._-]{1,}\@[A-Za-z0-9.-]{1,}\.[A-Za-z]{2,5}$/i', true, sprintf(_("<strong>%s</strong> is not a valid email address."), $email))) {
368                logMsg(sprintf('The email address %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
369                return false;
370            }
371        }
372       
373        // Test length.
374        if (!$this->stringLength($form_name, 0, 128, sprintf(_("<strong>Email address</strong> must contain less than 128 characters."), $email))) {
375            logMsg(sprintf('The email address %s must contain less than 128 characters.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
376            return false;
377        }
378       
379        // Check domain exists and has valid MX record.
380        preg_match('/^[\w\s]*<?php[A-Za-z0-9._-]{1,}\@([A-Za-z0-9.-]{1,}\.[A-Za-z]{2,5})>?$/i', $email, $matches);
381        if (!empty($matches[1])) {
382            if (!checkdnsrr($matches[1] . '.', 'MX') && gethostbyname($matches[1]) == $matches[1]) {
383                $this->addError($form_name, sprintf(_("<strong>%s</strong> is not a valid email domain name"), $matches[1]));
384                logMsg(sprintf('The email address %s contains an invalid email domain name (%s).', getFormData($form_name), $matches[1]), LOG_DEBUG, __FILE__, __LINE__);
385                return false;
386            }
387        }
388       
389        return true;
390    }
391
392    /**
393     * Check whether input is a valid phone number. Notice: it is now set
394     * to allow characters like - or () or + so people can type in a phone
395     * number that looks like: +1 (530) 624-4410
396     *
397     * @param  string  $form_name the name of the incoming form variable
398     *
399     * @return bool    true if no errors found, false otherwise
400     */
401    function validatePhone($form_name)
402    {
403        $phone = getFormData($form_name);
404       
405        $this->checkRegex($form_name, '/^[0-9 +().-]*$/', true, sprintf(_("The phone number <strong>%s</strong> is not valid."), $phone));
406        $this->stringLength($form_name, 0, 25, sprintf(_("The phone number <strong>%s</strong> is too long"), $phone));
407    }
408
409    /**
410     * Verifies that date can be processed by the strtotime function.
411     *
412     * @param  string  $form_name the name of the incoming form variable
413     * @param  string  $msg       the message to display on error
414     *
415     * @return bool    true if no errors found, false otherwise
416     */
417    function validateStrDate($form_name, $msg='')
418    {
419        if (($timestamp = strtotime(getFormData($form_name))) === -1) {
420            $this->addError($form_name, $msg);
421            logMsg(sprintf('The string date %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
422            return false;
423        } else {
424            return true;
425        }
426    }
427   
428   
429    /**
430     * Verifies credit card number.
431     *
432     * @param  string  $form_name   The name of the incoming form variable.
433     * @param  string  $cc_num      Card number to verify.
434     * @param  string  $cc_type     Optional, card type to do specific checks.
435     *
436     * @return bool    true if no errors found, false otherwise
437     */
438    function validateCCNumber($form_name, $cc_num=null, $cc_type=null)
439    {
440        if (!isset($cc_num)) {
441            $cc_num = getFormData($form_name);
442        }
443       
444        if ('' == $cc_num) {
445            return false;
446        }
447       
448        // Innocent until proven guilty
449        $card_is_valid = true;
450   
451        // Get rid of any non-digits
452        $cc_num = preg_replace('/[^\d]/', '', $cc_num);
453   
454        // Perform card-specific checks, if applicable
455        switch (strtolower($cc_type)) {
456            case 'visa' :
457                $card_is_valid = preg_match('/^4\d{15}$|^4\d{12}$/', $cc_num);
458                break;
459            case 'mastercard' :
460            case 'mc' :
461                $card_is_valid = preg_match('/^5[1-5]\d{14}$/', $cc_num);
462                break;
463            case 'american_express' :
464            case 'american_ex' :
465            case 'americanexpress' :
466            case 'americanex' :
467            case 'am_ex' :
468            case 'amex' :
469            case 'ae' :
470                $card_is_valid = preg_match('/^3[47]\d{13}$/', $cc_num);
471                break;
472            case 'discover' :
473                $card_is_valid = preg_match('/^6011\d{12}$/', $cc_num);
474                break;
475            case 'diners_club' :
476            case 'dinersclub' :
477            case 'diners' :
478            case 'diner' :
479            case 'dc' :
480                $card_is_valid = preg_match('/^30[0-5]\d{11}$|^3[68]\d{12}$/', $cc_num);
481                break;
482            case 'jcb' :
483                $card_is_valid = preg_match('/^3\d{15}$|^2131|1800\d{11}$/', $cc_num);
484                break;
485        }
486   
487        // The Luhn formula works right to left, so reverse the number.
488        $cc_num = strrev($cc_num);
489       
490        $luhn_total = 0;
491
492        $num = strlen($cc_num);
493        for ($i=0; $i<$num; $i++) {
494            // Get each digit.
495            $digit = substr($cc_num,$i,1);
496
497            //  If it's an odd digit, double it.
498            if ($i / 2 != floor($i / 2)) {
499                $digit *= 2;
500            }
501   
502            //  If the result is two digits, add them.
503            if (strlen($digit) == 2) {
504                $digit = substr($digit,0,1) + substr($digit,1,1);
505            }
506   
507            //  Add the current digit to the $luhn_total.
508            $luhn_total += $digit;
509        }
510   
511        // If it passed (or bypassed) the card-specific check and the Total is evenly divisible by 10, it's cool!
512        if ($card_is_valid && $luhn_total % 10 == 0) {
513            return true;
514        } else {
515            $this->addError($form_name, _("The <strong>credit card number</strong> you entered is not valid."));
516            return false;
517        }
518    }
519
520    /**
521     * Check whether uploaded file is valid.
522     *
523     * @param  string $form_name the name of the incoming form variable
524     * @param  string $msg       the message to display on error
525     *
526     * @return bool   true if no errors found, false otherwise
527     */
528    function validateFile($form_name, $msg='')
529    {
530        if (!isset($_FILES[$form_name]['tmp_name']) || '' == trim($_FILES[$form_name]['tmp_name'])) {
531            $this->addError($form_name, $msg);
532            return false;
533        } else {
534            return true;
535        }
536    }
537   
538} // THE END
539
540?>
Note: See TracBrowser for help on using the repository browser.