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

Last change on this file since 241 was 241, checked in by quinn, 17 years ago

Q - Fixed a few non-fatal but still annoying bugs.

File size: 16.8 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 * @requires  codebase/lib/Validator.inc.php
10 * @author    Quinn Comendant <quinn@strangecode.com>
11 * @version   1.8
12 *
13 * Example of use:
14---------------------------------------------------------------------
15// The object that validates form input.
16require_once 'codebase/lib/FormValidator.inc.php';
17$fv = new FormValidator();
18
19$fv->empty('field_name', sprintf(_("%s cannot be blank."), _("Field name")));
20$fv->stringLength('field_name', 0, 255, sprintf(_("%s must be %d-to-%d characters in length."), _("Field name"), 0, 255));
21$fv->isInteger('field_name', sprintf(_("%s must be an integer."), _("Field name")));
22$fv->checkRegex('field_name', '/^\d{4}$|^$/', true, sprintf(_("%s must be in MMYY format."), _("Field name")));
23$fv->numericRange('field_name', 0, 65535, sprintf(_("%s must be a number between %d and %d."), _("Field name"), 0, 65535));
24$fv->validatePhone('field_name');
25$fv->validateEmail('field_name');
26$fv->validateStrDate('field_name', sprintf(_("%s must be a valid date in YYYY-MM-DD format."), _("Field name")));
27if (is_null($var)) {
28    $fv->addError('field_name', sprintf(_("%s is invalid."), _("Field name")));
29}
30if ($fv->anyErrors()) {
31    // Errors!
32}
33---------------------------------------------------------------------
34 */
35
36// Credit card types are defined in class Validator.
37
38require_once 'codebase/lib/Validator.inc.php';
39
40class FormValidator extends Validator {
41
42    // Array filling with error messages.
43    var $errors = array();
44   
45    /**
46     * Return the current list of errors.
47     *
48     * @return array    an array of errors in the following arrangement:
49     *                  keys: the name of the variable with an error
50     *                  vals: the message to display for that error
51     */
52    function getErrorList()
53    {
54        return $this->errors;
55    }
56
57    /**
58     * Add an error to the errors stack.
59     *
60     * @param   string $form_name   The name of the incoming form variable.
61     * @param   string $msg         The error message for that form.
62     * @param   int    $type        The type of message: MSG_NOTICE,
63     *                              MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
64     * @param   string $file        __FILE__.
65     * @param   string $line        __LINE__.
66     */
67    function addError($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null)
68    {
69        $this->errors[] = array(
70            'name' => $form_name,
71            'message' => $msg,
72            'type' => $type,
73            'file' => $file,
74            'line' => $line
75        );
76    }
77
78    /**
79     * Check whether any errors have been triggered.
80     *
81     * @param  string $form_name the name of the incoming form variable
82     *
83     * @return bool   true if any errors were found, or if found for
84     *                a variable of $form_name, false otherwise
85     */
86    function anyErrors($form_name=null)
87    {
88        if (isset($form_name)) {
89            foreach ($this->errors as $err) {
90                if ($err['name'] == $form_name) {
91                    return $err['type'];
92                }
93            }
94            return false;
95        } else {
96            return (sizeof($this->errors) > 0);           
97        }
98    }
99
100    /**
101     * Reset the error list.
102     */
103    function resetErrorList()
104    {
105        $this->errors = array();
106    }
107
108    /**
109     * Prints the HTML for displaying error messages.
110     *
111     * @access  public
112     * @author  Quinn Comendant <quinn@strangecode.com>
113     * @since   15 Jul 2005 01:39:14
114     */
115    function printErrorMessages()
116    {
117        $app =& App::getInstance();
118        if ($this->anyErrors()) {
119            ?><div class="sc-msg" id="sc-msg-formvalidator"><?php
120            $errors = $this->getErrorList();
121            foreach ($errors as $e) {
122                if ('' != $e['message'] && is_string($e['message'])) {
123                    if (error_reporting() > 0 && $app->getParam('display_errors') && isset($e['file']) && isset($e['line'])) {
124                        echo "\n<!-- [" . $e['file'] . ' : ' . $e['line'] . '] -->';
125                    }
126                    switch ($e['type']) {
127                    case MSG_ERR:
128                        echo '<div class="sc-msg-error">' . $e['message'] . '</div>';
129                        break;
130
131                    case MSG_WARNING:
132                        echo '<div class="sc-msg-warning">' . $e['message'] . '</div>';
133                        break;
134
135                    case MSG_SUCCESS:
136                        echo '<div class="sc-msg-success">' . $e['message'] . '</div>';
137                        break;
138
139                    case MSG_NOTICE:
140                    default:
141                        echo '<div class="sc-msg-notice">' . $e['message'] . '</div>';
142                        break;
143                    }
144                }
145            }
146            ?></div><?php
147        }
148    }
149
150    /**
151     * If this form has an error, print an error marker like "<<".
152     *
153     * @param  string $form_name the name of the incoming form variable
154     * @param  string $marker    A string to print if there is an error. if
155     *                           not provided, use default.
156     */
157    function err($form_name, $marker=null)
158    {
159        if (false !== ($type = $this->anyErrors($form_name))) {
160            if (isset($marker)) {
161                echo $marker;
162            } else {
163                switch ($type) {
164                case MSG_ERR:
165                default:
166                    echo ' sc-msg-error ';
167                    break;
168
169                case MSG_WARNING:
170                    echo ' sc-msg-warning ';
171                    break;
172
173                case MSG_SUCCESS:
174                    echo ' sc-msg-success ';
175                    break;
176
177                case MSG_NOTICE:
178                    echo ' sc-msg-notice ';
179                    break;
180                }
181            }
182        }
183    }
184
185    /**
186     * Ensure the length of string is non-zero.
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 form is not empty, false otherwise.
192     */
193    function notEmpty($form_name, $msg='')
194    {
195        if (parent::notEmpty(getFormData($form_name))) {
196            return true;
197        } else {
198            $this->addError($form_name, $msg);
199            return false;
200        }
201    }
202   
203    /*
204    * We were using the isEmpty method *wrong* all these years and should have been using notEmpty.
205    * But the fact is the only use is to ensure a value is not empty, so this function simply becomes
206    * an alias of the one-true notEmpty() function.
207    * @since    03 Jun 2006 22:56:46
208    */
209    function isEmpty($form_name, $msg='')
210    {
211        $this->notEmpty($form_name, $msg);
212    }
213
214    /**
215     * Check whether input is a string.
216     *
217     * @param  string $form_name the name of the incoming form variable
218     * @param  string $msg       the message to display on error
219     *
220     * @return bool   true if form is a string, false otherwise.
221     */
222    function isString($form_name, $msg='')
223    {
224        if (parent::isString(getFormData($form_name))) {
225            return true;
226        } else {
227            $this->addError($form_name, $msg);
228            return false;
229        }
230    }
231
232    /**
233     * Check whether input is a number. Allows negative numbers.
234     *
235     * @param  string $form_name the name of the incoming form variable
236     * @param  string $msg       the message to display on error
237     *
238     * @return bool   true if no errors found, false otherwise
239     */
240    function isNumber($form_name, $msg='')
241    {
242        if (parent::isNumber(getFormData($form_name))) {
243            return true;
244        } else {
245            $this->addError($form_name, $msg);
246            return false;
247        }
248    }
249
250    /**
251     * addError if input is NOT an integer. Don't just use is_int() because the
252     * data coming from the user is *really* a string.
253     *
254     * @param  string $form_name the name of the incoming form variable
255     * @param  string $msg       the message to display on error
256     *
257     * @return bool   true if value is an integer
258     */
259    function isInteger($form_name, $msg='', $negative_ok=false)
260    {
261        if (parent::isInteger(getFormData($form_name), $negative_ok)) {
262            return true;
263        } else {
264            $this->addError($form_name, $msg);
265            return false;
266        }
267    }
268
269    /**
270     * Check whether input is a float. Don't just use is_float() because the
271     * data coming from the user is *really* a string. Integers will also
272     * pass this test.
273     *
274     * @param  string $form_name the name of the incoming form variable
275     * @param  string $msg       the message to display on error
276     *
277     * @return bool   true if value is a float
278     */
279    function isFloat($form_name, $msg='', $negative_ok=false)
280    {
281        if (parent::isFloat(getFormData($form_name), $negative_ok)) {
282            return true;
283        } else {
284            $this->addError($form_name, $msg);
285            return false;
286        }
287    }
288
289    /**
290     * Check whether input is an array.
291     *
292     * @param  string $form_name the name of the incoming form variable
293     * @param  string $msg       the message to display on error
294     *
295     * @return bool   true if value is a float
296     */
297    function isArray($form_name, $msg='')
298    {
299        if (parent::isArray(getFormData($form_name))) {
300            return true;
301        } else {
302            $this->addError($form_name, $msg);
303            return false;
304        }
305    }
306
307    /**
308     * Check whether input matches the specified perl regular expression
309     * pattern.
310     *
311     * @param  string $form_name the name of the incoming form variable
312     * @param  int    $regex     perl regex that the string must match
313     * @param  bool   $valid_on_match       set to false to be valid if match, or true
314     *                           to be valid on no match
315     * @param  string $msg       the message to display on error
316     *
317     * @return bool   true if value passes regex test
318     */
319    function checkRegex($form_name, $regex, $valid_on_match, $msg='')
320    {
321        if (parent::checkRegex(getFormData($form_name), $regex, $valid_on_match)) {
322            return true;
323        } else {
324            $this->addError($form_name, $msg);
325            return false;
326        }
327    }
328
329    /**
330     * Tests if the string length is between specified values. Whitespace excluded for min.
331     *
332     * @param  string $form_name the name of the incoming form variable
333     * @param  int    $min       minimum length of string, inclusive
334     * @param  int    $max       maximum length of string, inclusive
335     * @param  string $msg       the message to display on error
336     *
337     * @return bool   true if string length is within given boundaries
338     */
339    function stringLength($form_name, $min, $max, $msg='')
340    {
341        if (parent::stringLength(getFormData($form_name), $min, $max)) {
342            return true;
343        } else {
344            $this->addError($form_name, $msg);
345            return false;
346        }
347    }
348
349    /**
350     * Check whether input is within a valid numeric range.
351     *
352     * @param  string $form_name the name of the incoming form variable
353     * @param  int    $min       minimum value of number, inclusive
354     * @param  int    $max       maximum value of number, inclusive
355     * @param  string $msg       the message to display on error
356     *
357     * @return bool   true if no errors found, false otherwise
358     */
359    function numericRange($form_name, $min, $max, $msg='')
360    {
361        if (parent::numericRange(getFormData($form_name), $min, $max)) {
362            return true;
363        } else {
364            $this->addError($form_name, $msg);
365            return false;
366        }
367    }
368
369    /**
370     * Validates an email address based on the recommendations in RFC 3696.
371     * Is more loose than restrictive, to allow the many valid variants of
372     * email addresses while catching the most common mistakes.
373     * http://www.faqs.org/rfcs/rfc822.html
374     * http://www.faqs.org/rfcs/rfc2822.html
375     * http://www.faqs.org/rfcs/rfc3696.html
376     * http://www.faqs.org/rfcs/rfc1035.html
377     *
378     * @access  public
379     * @param   string  $form_name  The name of the incoming form variable.
380     * @return  bool    Validity of address.
381     * @author  Quinn Comendant <quinn@strangecode.com>
382     */
383    function validateEmail($form_name)
384    {
385        $app =& App::getInstance();
386
387        $email = getFormData($form_name);
388
389        if ('' == trim($email)) {
390            // No email address provided, and that's okay
391            return true;
392        }
393
394        // Validator::validateEmail() returns a value that relates to the VALIDATE_EMAIL_* constants (defined in Validator.inc.php).
395        switch (parent::validateEmail($email)) {
396        case VALIDATE_EMAIL_REGEX_FAIL:
397            // Failed regex match.
398            $this->addError($form_name, sprintf(_("<em>%s</em> is not a valid email address."), oTxt($email)));
399            $app->logMsg(sprintf('The email address %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
400            return false;
401            break;
402        case VALIDATE_EMAIL_LENGTH_FAIL :
403            // Failed length requirements.
404            $this->addError($form_name, sprintf(_("<em>Email address</em> must contain less than 256 characters."), oTxt($email)));
405            $app->logMsg(sprintf('The email address %s must contain less than 256 characters.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
406            return false;
407            break;
408        case VALIDATE_EMAIL_MX_FAIL :
409            // Failed MX record test.
410            $this->addError($form_name, sprintf(_("<em>%s</em> is not a valid email domain name"), oTxt($domain)));
411            $app->logMsg(sprintf('The email address %s contains an invalid email domain name (%s).', getFormData($form_name), $domain), LOG_INFO, __FILE__, __LINE__);
412            return false;
413            break;
414        case VALIDATE_EMAIL_SUCCESS :
415        default :
416            return true;
417            break;
418        }
419    }
420
421    /**
422     * Check whether input is a valid phone number. Notice: it is now set
423     * to allow characters like - or () or + so people can type in a phone
424     * number that looks like: +1 (530) 555-1212
425     *
426     * @param  string  $form_name the name of the incoming form variable
427     *
428     * @return bool    true if no errors found, false otherwise
429     */
430    function validatePhone($form_name)
431    {
432        $phone = getFormData($form_name);
433
434        return (
435            $this->checkRegex($form_name, '/^[0-9 +().-]*$/', true, sprintf(_("The phone number <em>%s</em> is not valid."), $phone))
436            && $this->stringLength($form_name, 0, 25, sprintf(_("The phone number <em>%s</em> is too long"), $phone))
437        );
438    }
439
440    /**
441     * Verifies that date can be processed by the strtotime function.
442     *
443     * @param  string  $form_name the name of the incoming form variable
444     * @param  string  $msg       the message to display on error
445     *
446     * @return bool    true if no errors found, false otherwise
447     */
448    function validateStrDate($form_name, $msg='')
449    {
450        $app =& App::getInstance();
451
452        if (parent::validateStrDate(getFormData($form_name))) {
453            return true;
454        } else {
455            $this->addError($form_name, $msg);
456            $app->logMsg(sprintf('The string date %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
457            return false;
458        }
459    }
460
461
462    /**
463     * Verifies credit card number.
464     *
465     * @param  string  $form_name   The name of the incoming form variable.
466     * @param  string  $cc_type     Optional, card type to do specific checks. One of the CC_TYPE_* constants.
467     *
468     * @return bool    true if no errors found, false otherwise
469     */
470    function validateCCNumber($form_name, $cc_type=null)
471    {
472        $cc_num = getFormData($form_name);
473       
474        if (parent::validateCCNumber($cc_num, $cc_type)) {
475            return true;
476        } else {
477            $this->addError($form_name, sprintf(_("<em>%s</em> is not a valid credit card number."), $cc_num));
478            return false;
479        }
480    }
481
482    /**
483     * Check whether a file was selected for uploading. If file is missing, it's an error.
484     *
485     * @param  string $form_name the name of the incoming form variable
486     * @param  string $msg       the message to display on error
487     *
488     * @return bool   true if no errors found, false otherwise
489     */
490    function fileUploaded($form_name, $msg='')
491    {
492        if (parent::fileUploaded($form_name)) {
493            return true;
494        } else {
495            $this->addError($form_name, $msg);
496            return false;
497        }
498    }
499
500} // THE END
501
502?>
Note: See TracBrowser for help on using the repository browser.