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

Last change on this file since 475 was 468, checked in by anonymous, 10 years ago

Completed integrating /branches/eli_branch into /trunk. Changes include:

  • Removed closing ?> from end of files
  • Upgrade old-style contructor methods to use construct() instead.
  • Class properties and methods defined as public, private, static or protected
  • Ensure code runs under E_ALL with only mysql_* deprecated warnings
  • Search for the '@' symbol anywhere it might be used to supress runtime errors, then replace with proper error recovery.
  • Run the php cli -l option to check files for syntax errors.
  • Bring tests up-to-date with latest version and methods of PHPUnit
File size: 21.0 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
[459]6 *
[362]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.
[459]13 *
[362]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.
[459]18 *
[362]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
[468]60class FormValidator {
[144]61
[266]62    // Class parameters.
[468]63    private $_params = array(
[266]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.
[468]71    public $errors = array();
[459]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     */
[468]79    public function setParam($params)
[266]80    {
81        $app =& App::getInstance();
[459]82
[266]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     */
[468]98    public function getParam($param)
[266]99    {
100        $app =& App::getInstance();
[459]101
[266]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    }
[459]109
[266]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     */
[468]117    public function getErrorList()
[1]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     */
[468]132    public function addError($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null)
[1]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     */
[468]151    public function anyErrors($form_name=null)
[1]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 {
[459]161            return (sizeof($this->errors) > 0);
[1]162        }
163    }
164
165    /**
166     * Reset the error list.
167     */
[468]168    public function resetErrorList()
[1]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").
[413]178     * @param   string  $print_gotohash_js  Print a line of javascript that scrolls the browser window down to view any error messages.
179     * @param   string  $hash     The #hashtag to scroll to.
[1]180     * @access  public
181     * @author  Quinn Comendant <quinn@strangecode.com>
182     * @since   15 Jul 2005 01:39:14
183     */
[468]184    public function printErrorMessages($above='', $below='', $print_gotohash_js=false, $hash='sc-msg-formvalidator')
[1]185    {
[136]186        $app =& App::getInstance();
[1]187        if ($this->anyErrors()) {
[166]188            ?><div class="sc-msg" id="sc-msg-formvalidator"><?php
[393]189            if ('' != $above) {
190                ?><div class="sc-above"><?php echo oTxt($above); ?></div><?php
191            }
[333]192            foreach ($this->getErrorList() as $e) {
[136]193                if ('' != $e['message'] && is_string($e['message'])) {
194                    if (error_reporting() > 0 && $app->getParam('display_errors') && isset($e['file']) && isset($e['line'])) {
195                        echo "\n<!-- [" . $e['file'] . ' : ' . $e['line'] . '] -->';
[1]196                    }
[136]197                    switch ($e['type']) {
[1]198                    case MSG_ERR:
[136]199                        echo '<div class="sc-msg-error">' . $e['message'] . '</div>';
[1]200                        break;
[42]201
[1]202                    case MSG_WARNING:
[136]203                        echo '<div class="sc-msg-warning">' . $e['message'] . '</div>';
[1]204                        break;
[42]205
[1]206                    case MSG_SUCCESS:
[136]207                        echo '<div class="sc-msg-success">' . $e['message'] . '</div>';
[1]208                        break;
[42]209
[1]210                    case MSG_NOTICE:
211                    default:
[136]212                        echo '<div class="sc-msg-notice">' . $e['message'] . '</div>';
[1]213                        break;
214                    }
215                }
216            }
[393]217            if ('' != $below) {
218                ?><div class="sc-below"><?php echo oTxt($below); ?></div><?php
219            }
[1]220            ?></div><?php
[413]221            if ($print_gotohash_js) {
222                ?>
223                <script type="text/javascript">
224                /* <![CDATA[ */
225                window.location.hash = '#<?php echo urlencode($hash); ?>';
226                /* ]]> */
227                </script>
228                <?php
229            }
[1]230        }
231    }
[42]232
[1]233    /**
234     * If this form has an error, print an error marker like "<<".
235     *
236     * @param  string $form_name the name of the incoming form variable
237     * @param  string $marker    A string to print if there is an error. if
238     *                           not provided, use default.
239     */
[468]240    public function err($form_name, $marker=null)
[1]241    {
[178]242        if (false !== ($type = $this->anyErrors($form_name))) {
[1]243            if (isset($marker)) {
244                echo $marker;
245            } else {
[178]246                switch ($type) {
247                case MSG_ERR:
248                default:
[266]249                    echo $this->getParam('error');
[178]250                    break;
251
252                case MSG_WARNING:
[266]253                    echo $this->getParam('warning');
[178]254                    break;
255
[266]256                case MSG_NOTICE:
257                    echo $this->getParam('notice');
[178]258                    break;
259
[266]260                case MSG_SUCCESS:
261                    echo $this->getParam('success');
[178]262                    break;
263                }
[1]264            }
265        }
266    }
267
268    /**
[144]269     * Ensure the length of string is non-zero.
[1]270     *
271     * @param  string $form_name the name of the incoming form variable
272     * @param  string $msg       the message to display on error
273     *
274     * @return bool   true if form is not empty, false otherwise.
275     */
[468]276    public function notEmpty($form_name, $msg='')
[1]277    {
[468]278        if (Validator::notEmpty(getFormData($form_name))) {
[1]279            return true;
280        } else {
[144]281            $this->addError($form_name, $msg);
[1]282            return false;
283        }
284    }
[459]285
[144]286    /*
287    * We were using the isEmpty method *wrong* all these years and should have been using notEmpty.
[459]288    * But the fact is the only use is to ensure a value is not empty, so this function simply becomes
[144]289    * an alias of the one-true notEmpty() function.
290    * @since    03 Jun 2006 22:56:46
291    */
[468]292    public function isEmpty($form_name, $msg='')
[1]293    {
[241]294        $this->notEmpty($form_name, $msg);
[1]295    }
296
297    /**
298     * Check whether input is a string.
299     *
300     * @param  string $form_name the name of the incoming form variable
301     * @param  string $msg       the message to display on error
302     *
303     * @return bool   true if form is a string, false otherwise.
304     */
[468]305    public function isString($form_name, $msg='')
[1]306    {
[468]307        if (Validator::isString(getFormData($form_name))) {
[144]308            return true;
309        } else {
[1]310            $this->addError($form_name, $msg);
311            return false;
312        }
313    }
314
315    /**
316     * Check whether input is a number. Allows negative numbers.
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 no errors found, false otherwise
322     */
[468]323    public function isNumber($form_name, $msg='')
[1]324    {
[468]325        if (Validator::isNumber(getFormData($form_name))) {
[144]326            return true;
327        } else {
[1]328            $this->addError($form_name, $msg);
329            return false;
330        }
331    }
332
333    /**
334     * addError if input is NOT an integer. Don't just use is_int() because the
335     * data coming from the user is *really* a string.
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 an integer
341     */
[468]342    public function isInteger($form_name, $msg='', $negative_ok=false)
[1]343    {
[468]344        if (Validator::isInteger(getFormData($form_name), $negative_ok)) {
[144]345            return true;
346        } else {
[1]347            $this->addError($form_name, $msg);
348            return false;
349        }
350    }
351
352    /**
353     * Check whether input is a float. Don't just use is_float() because the
[42]354     * data coming from the user is *really* a string. Integers will also
[1]355     * pass this test.
356     *
357     * @param  string $form_name the name of the incoming form variable
358     * @param  string $msg       the message to display on error
359     *
360     * @return bool   true if value is a float
361     */
[468]362    public function isFloat($form_name, $msg='', $negative_ok=false)
[1]363    {
[468]364        if (Validator::isFloat(getFormData($form_name), $negative_ok)) {
[144]365            return true;
366        } else {
[1]367            $this->addError($form_name, $msg);
368            return false;
369        }
370    }
371
372    /**
373     * Check whether input is an array.
374     *
375     * @param  string $form_name the name of the incoming form variable
376     * @param  string $msg       the message to display on error
377     *
378     * @return bool   true if value is a float
379     */
[468]380    public function isArray($form_name, $msg='')
[1]381    {
[468]382        if (Validator::isArray(getFormData($form_name))) {
[144]383            return true;
384        } else {
[1]385            $this->addError($form_name, $msg);
386            return false;
387        }
388    }
[42]389
[1]390    /**
391     * Check whether input matches the specified perl regular expression
[42]392     * pattern.
[1]393     *
[366]394     * @param  string $form_name        The name of the incoming form variable
395     * @param  int    $regex            Perl regex that the string must match
396     * @param  bool   $valid_on_match   Set to true to be valid if match, or false to be valid if the match fails.
397     * @param  string $msg              The message to display on error
[1]398     *
399     * @return bool   true if value passes regex test
400     */
[468]401    public function checkRegex($form_name, $regex, $valid_on_match=true, $msg='')
[1]402    {
[468]403        if (Validator::checkRegex(getFormData($form_name), $regex, $valid_on_match)) {
[144]404            return true;
[1]405        } else {
[144]406            $this->addError($form_name, $msg);
407            return false;
[1]408        }
409    }
[42]410
[1]411    /**
412     * Tests if the string length is between specified values. Whitespace excluded for min.
413     *
414     * @param  string $form_name the name of the incoming form variable
415     * @param  int    $min       minimum length of string, inclusive
416     * @param  int    $max       maximum length of string, inclusive
417     * @param  string $msg       the message to display on error
418     *
419     * @return bool   true if string length is within given boundaries
420     */
[468]421    public function stringLength($form_name, $min, $max, $msg='')
[1]422    {
[468]423        if (Validator::stringLength(getFormData($form_name), $min, $max)) {
[144]424            return true;
425        } else {
[1]426            $this->addError($form_name, $msg);
427            return false;
428        }
429    }
430
431    /**
432     * Check whether input is within a valid numeric range.
433     *
434     * @param  string $form_name the name of the incoming form variable
435     * @param  int    $min       minimum value of number, inclusive
436     * @param  int    $max       maximum value of number, inclusive
437     * @param  string $msg       the message to display on error
438     *
439     * @return bool   true if no errors found, false otherwise
440     */
[468]441    public function numericRange($form_name, $min, $max, $msg='')
[1]442    {
[468]443        if (Validator::numericRange(getFormData($form_name), $min, $max)) {
[1]444            return true;
445        } else {
[144]446            $this->addError($form_name, $msg);
[1]447            return false;
448        }
449    }
450
451    /**
[23]452     * Validates an email address based on the recommendations in RFC 3696.
[42]453     * Is more loose than restrictive, to allow the many valid variants of
[23]454     * email addresses while catching the most common mistakes.
455     * http://www.faqs.org/rfcs/rfc822.html
456     * http://www.faqs.org/rfcs/rfc2822.html
457     * http://www.faqs.org/rfcs/rfc3696.html
458     * http://www.faqs.org/rfcs/rfc1035.html
[1]459     *
[23]460     * @access  public
461     * @param   string  $form_name  The name of the incoming form variable.
462     * @return  bool    Validity of address.
463     * @author  Quinn Comendant <quinn@strangecode.com>
[1]464     */
[468]465    public function validateEmail($form_name, $strict=false)
[1]466    {
[136]467        $app =& App::getInstance();
[144]468
[1]469        $email = getFormData($form_name);
[144]470
[1]471        if ('' == trim($email)) {
[305]472            // No email address provided, and that's okay.
[144]473            return true;
[1]474        }
[23]475
[468]476        // Validator::validateEmail() returns a value that relates to the Validate::EMAIL_* constants (defined in Validator.inc.php).
477        switch (Validator::validateEmail($email, $strict)) {
478        case Validator::EMAIL_REGEX_FAIL:
[144]479            // Failed regex match.
[305]480            $this->addError($form_name, sprintf(_("The email address <em>%s</em> is formatted incorrectly."), oTxt($email)));
481            $app->logMsg(sprintf('The email address %s is not valid.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__);
[23]482            return false;
[459]483
[468]484        case Validator::EMAIL_LENGTH_FAIL :
[144]485            // Failed length requirements.
[305]486            $this->addError($form_name, sprintf(_("The email address <em>%s</em> is too long (email addresses must have fewer than 256 characters)."), oTxt($email)));
487            $app->logMsg(sprintf('The email address %s must contain less than 256 characters.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__);
[1]488            return false;
[459]489
[468]490        case Validator::EMAIL_MX_FAIL :
[144]491            // Failed MX record test.
[314]492            $this->addError($form_name, sprintf(_("The email address <em>%s</em> does not have a valid domain name"), oTxt($email)));
[305]493            $app->logMsg(sprintf('The email address %s does not have a valid domain name.', oTxt($email)), LOG_INFO, __FILE__, __LINE__);
[23]494            return false;
[459]495
[468]496        case Validator::EMAIL_SUCCESS :
[144]497        default :
498            return true;
[1]499        }
500    }
501
502    /**
503     * Check whether input is a valid phone number. Notice: it is now set
504     * to allow characters like - or () or + so people can type in a phone
[144]505     * number that looks like: +1 (530) 555-1212
[1]506     *
507     * @param  string  $form_name the name of the incoming form variable
508     *
509     * @return bool    true if no errors found, false otherwise
510     */
[468]511    public function validatePhone($form_name)
[1]512    {
[468]513        $app =& App::getInstance();
514
[1]515        $phone = getFormData($form_name);
[42]516
[468]517        // Validator::validateEmail() returns a value that relates to the Validate::PHONE_* constants (defined in Validator.inc.php).
518        switch (Validator::validatePhone($phone)) {
519        case Validator::PHONE_REGEX_FAIL:
520            // Failed regex match.
521            $this->addError($form_name, sprintf(_("The phone number <em>%s</em> is not valid."), oTxt($phone)));
522            $app->logMsg(sprintf('The phone number %s is not valid.', oTxt($phone)), LOG_DEBUG, __FILE__, __LINE__);
523            return false;
524
525        case Validator::PHONE_LENGTH_FAIL :
526            // Failed length requirements.
527            $this->addError($form_name, sprintf(_("The phone number <em>%s</em> is too long (phone number must have fewer than 25 characters)."), oTxt($phone)));
528            $app->logMsg(sprintf('The phone number %s must contain less than 256 characters.', oTxt($phone)), LOG_DEBUG, __FILE__, __LINE__);
529            return false;
530
531        case Validator::PHONE_SUCCESS :
532        default :
533            return true;
534        }
[1]535    }
536
537    /**
538     * Verifies that date can be processed by the strtotime function.
539     *
540     * @param  string  $form_name the name of the incoming form variable
541     * @param  string  $msg       the message to display on error
542     *
543     * @return bool    true if no errors found, false otherwise
544     */
[468]545    public function validateStrDate($form_name, $msg='')
[1]546    {
[144]547        $app =& App::getInstance();
548
[468]549        if (Validator::validateStrDate(getFormData($form_name, ''))) {
[144]550            return true;
551        } else {
[1]552            $this->addError($form_name, $msg);
[136]553            $app->logMsg(sprintf('The string date %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
[1]554            return false;
555        }
556    }
[42]557
558
[1]559    /**
[279]560     * Verifies credit card number using the Luhn (mod 10) algorithm.
561     * http://en.wikipedia.org/wiki/Luhn_algorithm
[1]562     *
563     * @param  string  $form_name   The name of the incoming form variable.
[468]564     * @param  string  $cc_type     Optional, card type to do specific checks. One of the Validator::CC_TYPE_* constants.
[1]565     *
566     * @return bool    true if no errors found, false otherwise
567     */
[468]568    public function validateCCNumber($form_name, $cc_type=null)
[1]569    {
[144]570        $cc_num = getFormData($form_name);
[459]571
[468]572        if (Validator::validateCCNumber($cc_num, $cc_type)) {
[1]573            return true;
574        } else {
[282]575            $this->addError($form_name, sprintf(_("The credit card number you entered is not valid. Please check the number and try again."), $cc_num));
[1]576            return false;
577        }
578    }
579
580    /**
[136]581     * Check whether a file was selected for uploading. If file is missing, it's an error.
[1]582     *
583     * @param  string $form_name the name of the incoming form variable
584     * @param  string $msg       the message to display on error
585     *
586     * @return bool   true if no errors found, false otherwise
587     */
[468]588    public function fileUploaded($form_name, $msg='')
[1]589    {
[468]590        if (Validator::fileUploaded($form_name)) {
[144]591            return true;
592        } else {
[1]593            $this->addError($form_name, $msg);
594            return false;
595        }
596    }
[42]597
[1]598} // THE END
599
Note: See TracBrowser for help on using the repository browser.