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

Last change on this file since 333 was 333, checked in by quinn, 16 years ago

Finished initial version of Cart.inc.php. Minor css changes.

File size: 18.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 * @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            foreach ($this->getErrorList() as $e) {
166                if ('' != $e['message'] && is_string($e['message'])) {
167                    if (error_reporting() > 0 && $app->getParam('display_errors') && isset($e['file']) && isset($e['line'])) {
168                        echo "\n<!-- [" . $e['file'] . ' : ' . $e['line'] . '] -->';
169                    }
170                    switch ($e['type']) {
171                    case MSG_ERR:
172                        echo '<div class="sc-msg-error">' . $e['message'] . '</div>';
173                        break;
174
175                    case MSG_WARNING:
176                        echo '<div class="sc-msg-warning">' . $e['message'] . '</div>';
177                        break;
178
179                    case MSG_SUCCESS:
180                        echo '<div class="sc-msg-success">' . $e['message'] . '</div>';
181                        break;
182
183                    case MSG_NOTICE:
184                    default:
185                        echo '<div class="sc-msg-notice">' . $e['message'] . '</div>';
186                        break;
187                    }
188                }
189            }
190            ?></div><?php
191        }
192    }
193
194    /**
195     * If this form has an error, print an error marker like "<<".
196     *
197     * @param  string $form_name the name of the incoming form variable
198     * @param  string $marker    A string to print if there is an error. if
199     *                           not provided, use default.
200     */
201    function err($form_name, $marker=null)
202    {
203        if (false !== ($type = $this->anyErrors($form_name))) {
204            if (isset($marker)) {
205                echo $marker;
206            } else {
207                switch ($type) {
208                case MSG_ERR:
209                default:
210                    echo $this->getParam('error');
211                    break;
212
213                case MSG_WARNING:
214                    echo $this->getParam('warning');
215                    break;
216
217                case MSG_NOTICE:
218                    echo $this->getParam('notice');
219                    break;
220
221                case MSG_SUCCESS:
222                    echo $this->getParam('success');
223                    break;
224                }
225            }
226        }
227    }
228
229    /**
230     * Ensure the length of string is non-zero.
231     *
232     * @param  string $form_name the name of the incoming form variable
233     * @param  string $msg       the message to display on error
234     *
235     * @return bool   true if form is not empty, false otherwise.
236     */
237    function notEmpty($form_name, $msg='')
238    {
239        if (parent::notEmpty(getFormData($form_name))) {
240            return true;
241        } else {
242            $this->addError($form_name, $msg);
243            return false;
244        }
245    }
246   
247    /*
248    * We were using the isEmpty method *wrong* all these years and should have been using notEmpty.
249    * But the fact is the only use is to ensure a value is not empty, so this function simply becomes
250    * an alias of the one-true notEmpty() function.
251    * @since    03 Jun 2006 22:56:46
252    */
253    function isEmpty($form_name, $msg='')
254    {
255        $this->notEmpty($form_name, $msg);
256    }
257
258    /**
259     * Check whether input is a string.
260     *
261     * @param  string $form_name the name of the incoming form variable
262     * @param  string $msg       the message to display on error
263     *
264     * @return bool   true if form is a string, false otherwise.
265     */
266    function isString($form_name, $msg='')
267    {
268        if (parent::isString(getFormData($form_name))) {
269            return true;
270        } else {
271            $this->addError($form_name, $msg);
272            return false;
273        }
274    }
275
276    /**
277     * Check whether input is a number. Allows negative numbers.
278     *
279     * @param  string $form_name the name of the incoming form variable
280     * @param  string $msg       the message to display on error
281     *
282     * @return bool   true if no errors found, false otherwise
283     */
284    function isNumber($form_name, $msg='')
285    {
286        if (parent::isNumber(getFormData($form_name))) {
287            return true;
288        } else {
289            $this->addError($form_name, $msg);
290            return false;
291        }
292    }
293
294    /**
295     * addError if input is NOT an integer. Don't just use is_int() because the
296     * data coming from the user is *really* a string.
297     *
298     * @param  string $form_name the name of the incoming form variable
299     * @param  string $msg       the message to display on error
300     *
301     * @return bool   true if value is an integer
302     */
303    function isInteger($form_name, $msg='', $negative_ok=false)
304    {
305        if (parent::isInteger(getFormData($form_name), $negative_ok)) {
306            return true;
307        } else {
308            $this->addError($form_name, $msg);
309            return false;
310        }
311    }
312
313    /**
314     * Check whether input is a float. Don't just use is_float() because the
315     * data coming from the user is *really* a string. Integers will also
316     * pass this test.
317     *
318     * @param  string $form_name the name of the incoming form variable
319     * @param  string $msg       the message to display on error
320     *
321     * @return bool   true if value is a float
322     */
323    function isFloat($form_name, $msg='', $negative_ok=false)
324    {
325        if (parent::isFloat(getFormData($form_name), $negative_ok)) {
326            return true;
327        } else {
328            $this->addError($form_name, $msg);
329            return false;
330        }
331    }
332
333    /**
334     * Check whether input is an array.
335     *
336     * @param  string $form_name the name of the incoming form variable
337     * @param  string $msg       the message to display on error
338     *
339     * @return bool   true if value is a float
340     */
341    function isArray($form_name, $msg='')
342    {
343        if (parent::isArray(getFormData($form_name))) {
344            return true;
345        } else {
346            $this->addError($form_name, $msg);
347            return false;
348        }
349    }
350
351    /**
352     * Check whether input matches the specified perl regular expression
353     * pattern.
354     *
355     * @param  string $form_name the name of the incoming form variable
356     * @param  int    $regex     perl regex that the string must match
357     * @param  bool   $valid_on_match       set to false to be valid if match, or true
358     *                           to be valid on no match
359     * @param  string $msg       the message to display on error
360     *
361     * @return bool   true if value passes regex test
362     */
363    function checkRegex($form_name, $regex, $valid_on_match, $msg='')
364    {
365        if (parent::checkRegex(getFormData($form_name), $regex, $valid_on_match)) {
366            return true;
367        } else {
368            $this->addError($form_name, $msg);
369            return false;
370        }
371    }
372
373    /**
374     * Tests if the string length is between specified values. Whitespace excluded for min.
375     *
376     * @param  string $form_name the name of the incoming form variable
377     * @param  int    $min       minimum length of string, inclusive
378     * @param  int    $max       maximum length of string, inclusive
379     * @param  string $msg       the message to display on error
380     *
381     * @return bool   true if string length is within given boundaries
382     */
383    function stringLength($form_name, $min, $max, $msg='')
384    {
385        if (parent::stringLength(getFormData($form_name), $min, $max)) {
386            return true;
387        } else {
388            $this->addError($form_name, $msg);
389            return false;
390        }
391    }
392
393    /**
394     * Check whether input is within a valid numeric range.
395     *
396     * @param  string $form_name the name of the incoming form variable
397     * @param  int    $min       minimum value of number, inclusive
398     * @param  int    $max       maximum value of number, inclusive
399     * @param  string $msg       the message to display on error
400     *
401     * @return bool   true if no errors found, false otherwise
402     */
403    function numericRange($form_name, $min, $max, $msg='')
404    {
405        if (parent::numericRange(getFormData($form_name), $min, $max)) {
406            return true;
407        } else {
408            $this->addError($form_name, $msg);
409            return false;
410        }
411    }
412
413    /**
414     * Validates an email address based on the recommendations in RFC 3696.
415     * Is more loose than restrictive, to allow the many valid variants of
416     * email addresses while catching the most common mistakes.
417     * http://www.faqs.org/rfcs/rfc822.html
418     * http://www.faqs.org/rfcs/rfc2822.html
419     * http://www.faqs.org/rfcs/rfc3696.html
420     * http://www.faqs.org/rfcs/rfc1035.html
421     *
422     * @access  public
423     * @param   string  $form_name  The name of the incoming form variable.
424     * @return  bool    Validity of address.
425     * @author  Quinn Comendant <quinn@strangecode.com>
426     */
427    function validateEmail($form_name)
428    {
429        $app =& App::getInstance();
430
431        $email = getFormData($form_name);
432
433        if ('' == trim($email)) {
434            // No email address provided, and that's okay.
435            return true;
436        }
437
438        // Validator::validateEmail() returns a value that relates to the VALIDATE_EMAIL_* constants (defined in Validator.inc.php).
439        switch (parent::validateEmail($email)) {
440        case VALIDATE_EMAIL_REGEX_FAIL:
441            // Failed regex match.
442            $this->addError($form_name, sprintf(_("The email address <em>%s</em> is formatted incorrectly."), oTxt($email)));
443            $app->logMsg(sprintf('The email address %s is not valid.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__);
444            return false;
445            break;
446           
447        case VALIDATE_EMAIL_LENGTH_FAIL :
448            // Failed length requirements.
449            $this->addError($form_name, sprintf(_("The email address <em>%s</em> is too long (email addresses must have fewer than 256 characters)."), oTxt($email)));
450            $app->logMsg(sprintf('The email address %s must contain less than 256 characters.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__);
451            return false;
452            break;
453           
454        case VALIDATE_EMAIL_MX_FAIL :
455            // Failed MX record test.
456            $this->addError($form_name, sprintf(_("The email address <em>%s</em> does not have a valid domain name"), oTxt($email)));
457            $app->logMsg(sprintf('The email address %s does not have a valid domain name.', oTxt($email)), LOG_INFO, __FILE__, __LINE__);
458            return false;
459            break;
460           
461        case VALIDATE_EMAIL_SUCCESS :
462        default :
463            return true;
464            break;
465        }
466    }
467
468    /**
469     * Check whether input is a valid phone number. Notice: it is now set
470     * to allow characters like - or () or + so people can type in a phone
471     * number that looks like: +1 (530) 555-1212
472     *
473     * @param  string  $form_name the name of the incoming form variable
474     *
475     * @return bool    true if no errors found, false otherwise
476     */
477    function validatePhone($form_name)
478    {
479        $phone = getFormData($form_name);
480
481        return (
482            $this->checkRegex($form_name, '/^[0-9 +().-]*$/', true, sprintf(_("The phone number <em>%s</em> is not valid."), $phone))
483            && $this->stringLength($form_name, 0, 25, sprintf(_("The phone number <em>%s</em> is too long"), $phone))
484        );
485    }
486
487    /**
488     * Verifies that date can be processed by the strtotime function.
489     *
490     * @param  string  $form_name the name of the incoming form variable
491     * @param  string  $msg       the message to display on error
492     *
493     * @return bool    true if no errors found, false otherwise
494     */
495    function validateStrDate($form_name, $msg='')
496    {
497        $app =& App::getInstance();
498
499        if (parent::validateStrDate(getFormData($form_name))) {
500            return true;
501        } else {
502            $this->addError($form_name, $msg);
503            $app->logMsg(sprintf('The string date %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
504            return false;
505        }
506    }
507
508
509    /**
510     * Verifies credit card number using the Luhn (mod 10) algorithm.
511     * http://en.wikipedia.org/wiki/Luhn_algorithm
512     *
513     * @param  string  $form_name   The name of the incoming form variable.
514     * @param  string  $cc_type     Optional, card type to do specific checks. One of the CC_TYPE_* constants.
515     *
516     * @return bool    true if no errors found, false otherwise
517     */
518    function validateCCNumber($form_name, $cc_type=null)
519    {
520        $cc_num = getFormData($form_name);
521       
522        if (parent::validateCCNumber($cc_num, $cc_type)) {
523            return true;
524        } else {
525            $this->addError($form_name, sprintf(_("The credit card number you entered is not valid. Please check the number and try again."), $cc_num));
526            return false;
527        }
528    }
529
530    /**
531     * Check whether a file was selected for uploading. If file is missing, it's an error.
532     *
533     * @param  string $form_name the name of the incoming form variable
534     * @param  string $msg       the message to display on error
535     *
536     * @return bool   true if no errors found, false otherwise
537     */
538    function fileUploaded($form_name, $msg='')
539    {
540        if (parent::fileUploaded($form_name)) {
541            return true;
542        } else {
543            $this->addError($form_name, $msg);
544            return false;
545        }
546    }
547
548} // THE END
549
550?>
Note: See TracBrowser for help on using the repository browser.