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

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

Bugfixes found during strangecode site upgrade.

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