source: trunk/lib/FormValidator.inc.php @ 1

Last change on this file since 1 was 1, checked in by scdev, 19 years ago

Initial import.

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