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

Last change on this file since 408 was 396, checked in by anonymous, 12 years ago

Updated copyright date; comments elaboration; spelling fixes.

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