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

Last change on this file since 499 was 497, checked in by anonymous, 10 years ago

Beginning the process of adapting codebase to foundation styles.

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