source: branches/eli_branch/lib/FormValidator.inc.php @ 448

Last change on this file since 448 was 447, checked in by anonymous, 11 years ago

Changed some global constants to class constants (in cases where backwards compatability wouldn't break).

File size: 20.3 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
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/**
24 * FormValidator.inc.php
25 *
26 * The FormValidator class provides a method for validating input from
27 * http requests and displaying errors.
28 *
29 * @requires  codebase/lib/Validator.inc.php
30 * @author    Quinn Comendant <quinn@strangecode.com>
31 * @version   1.8
32 *
33 * Example of use:
34---------------------------------------------------------------------
35// The object that validates form input.
36require_once 'codebase/lib/FormValidator.inc.php';
37$fv = new FormValidator();
38
39$fv->empty('field_name', sprintf(_("%s cannot be blank."), _("Field name")));
40$fv->stringLength('field_name', 0, 255, sprintf(_("%s must be %d-to-%d characters in length."), _("Field name"), 0, 255));
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")));
43$fv->numericRange('field_name', 0, 65535, sprintf(_("%s must be a number between %d and %d."), _("Field name"), 0, 65535));
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---------------------------------------------------------------------
54 */
55
56// Credit card types are defined in class Validator.
57
58require_once 'codebase/lib/Validator.inc.php';
59
60class FormValidator extends Validator {
61
62    // Class parameters.
63    private $_params = array(
64        'error' => ' sc-msg-error ',
65        'warning' => ' sc-msg-warning ',
66        'notice' => ' sc-msg-notice ',
67        'success' => ' sc-msg-success ',
68    );
69
70    // Array filling with error messages.
71    public $errors = array();
72
73    /**
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    public 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    public 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    /**
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    public function getErrorList()
118    {
119        return $this->errors;
120    }
121
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    public 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    }
142
143    /**
144     * Check whether any errors have been triggered.
145     *
146     * @param  string $form_name the name of the incoming form variable
147     *
148     * @return bool   true if any errors were found, or if found for
149     *                a variable of $form_name, false otherwise
150     */
151    public function anyErrors($form_name=null)
152    {
153        if (isset($form_name)) {
154            foreach ($this->errors as $err) {
155                if ($err['name'] == $form_name) {
156                    return $err['type'];
157                }
158            }
159            return false;
160        } else {
161            return (sizeof($this->errors) > 0);
162        }
163    }
164
165    /**
166     * Reset the error list.
167     */
168    public function resetErrorList()
169    {
170        $this->errors = array();
171    }
172
173    /**
174     * Prints the HTML for displaying error messages.
175     *
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").
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.
180     * @access  public
181     * @author  Quinn Comendant <quinn@strangecode.com>
182     * @since   15 Jul 2005 01:39:14
183     */
184    public function printErrorMessages($above='', $below='', $print_gotohash_js=false, $hash='sc-msg-formvalidator')
185    {
186        $app =& App::getInstance();
187        if ($this->anyErrors()) {
188            ?><div class="sc-msg" id="sc-msg-formvalidator"><?php
189            if ('' != $above) {
190                ?><div class="sc-above"><?php echo oTxt($above); ?></div><?php
191            }
192            foreach ($this->getErrorList() as $e) {
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'] . '] -->';
196                    }
197                    switch ($e['type']) {
198                    case MSG_ERR:
199                        echo '<div class="sc-msg-error">' . $e['message'] . '</div>';
200                        break;
201
202                    case MSG_WARNING:
203                        echo '<div class="sc-msg-warning">' . $e['message'] . '</div>';
204                        break;
205
206                    case MSG_SUCCESS:
207                        echo '<div class="sc-msg-success">' . $e['message'] . '</div>';
208                        break;
209
210                    case MSG_NOTICE:
211                    default:
212                        echo '<div class="sc-msg-notice">' . $e['message'] . '</div>';
213                        break;
214                    }
215                }
216            }
217            if ('' != $below) {
218                ?><div class="sc-below"><?php echo oTxt($below); ?></div><?php
219            }
220            ?></div><?php
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            }
230        }
231    }
232
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     */
240    public function err($form_name, $marker=null)
241    {
242        if (false !== ($type = $this->anyErrors($form_name))) {
243            if (isset($marker)) {
244                echo $marker;
245            } else {
246                switch ($type) {
247                case MSG_ERR:
248                default:
249                    echo $this->getParam('error');
250                    break;
251
252                case MSG_WARNING:
253                    echo $this->getParam('warning');
254                    break;
255
256                case MSG_NOTICE:
257                    echo $this->getParam('notice');
258                    break;
259
260                case MSG_SUCCESS:
261                    echo $this->getParam('success');
262                    break;
263                }
264            }
265        }
266    }
267
268    /**
269     * Ensure the length of string is non-zero.
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     */
276    public function notEmpty($form_name, $msg='')
277    {
278        if (parent::notEmpty(getFormData($form_name))) {
279            return true;
280        } else {
281            $this->addError($form_name, $msg);
282            return false;
283        }
284    }
285
286    /*
287    * We were using the isEmpty method *wrong* all these years and should have been using notEmpty.
288    * But the fact is the only use is to ensure a value is not empty, so this function simply becomes
289    * an alias of the one-true notEmpty() function.
290    * @since    03 Jun 2006 22:56:46
291    */
292    public function isEmpty($form_name, $msg='')
293    {
294        $this->notEmpty($form_name, $msg);
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     */
305    public function isString($form_name, $msg='')
306    {
307        if (parent::isString(getFormData($form_name))) {
308            return true;
309        } else {
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     */
323    public function isNumber($form_name, $msg='')
324    {
325        if (parent::isNumber(getFormData($form_name))) {
326            return true;
327        } else {
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     */
342    public function isInteger($form_name, $msg='', $negative_ok=false)
343    {
344        if (parent::isInteger(getFormData($form_name), $negative_ok)) {
345            return true;
346        } else {
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
354     * data coming from the user is *really* a string. Integers will also
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     */
362    public function isFloat($form_name, $msg='', $negative_ok=false)
363    {
364        if (parent::isFloat(getFormData($form_name), $negative_ok)) {
365            return true;
366        } else {
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     */
380    public function isArray($form_name, $msg='')
381    {
382        if (parent::isArray(getFormData($form_name))) {
383            return true;
384        } else {
385            $this->addError($form_name, $msg);
386            return false;
387        }
388    }
389
390    /**
391     * Check whether input matches the specified perl regular expression
392     * pattern.
393     *
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
398     *
399     * @return bool   true if value passes regex test
400     */
401    public function checkRegex($form_name, $regex, $valid_on_match, $msg='')
402    {
403        if (parent::checkRegex(getFormData($form_name), $regex, $valid_on_match)) {
404            return true;
405        } else {
406            $this->addError($form_name, $msg);
407            return false;
408        }
409    }
410
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     */
421    public function stringLength($form_name, $min, $max, $msg='')
422    {
423        if (parent::stringLength(getFormData($form_name), $min, $max)) {
424            return true;
425        } else {
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     */
441    public function numericRange($form_name, $min, $max, $msg='')
442    {
443        if (parent::numericRange(getFormData($form_name), $min, $max)) {
444            return true;
445        } else {
446            $this->addError($form_name, $msg);
447            return false;
448        }
449    }
450
451    /**
452     * Validates an email address based on the recommendations in RFC 3696.
453     * Is more loose than restrictive, to allow the many valid variants of
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
459     *
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>
464     */
465    public function validateEmail($form_name)
466    {
467        $app =& App::getInstance();
468
469        $email = getFormData($form_name);
470
471        if ('' == trim($email)) {
472            // No email address provided, and that's okay.
473            return true;
474        }
475
476        // Validator::validateEmail() returns a value that relates to the Validate::EMAIL_* constants (defined in Validator.inc.php).
477        switch (parent::validateEmail($email)) {
478        case parent::EMAIL_REGEX_FAIL:
479            // Failed regex match.
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__);
482            return false;
483            break;
484
485        case parent::EMAIL_LENGTH_FAIL :
486            // Failed length requirements.
487            $this->addError($form_name, sprintf(_("The email address <em>%s</em> is too long (email addresses must have fewer than 256 characters)."), oTxt($email)));
488            $app->logMsg(sprintf('The email address %s must contain less than 256 characters.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__);
489            return false;
490            break;
491
492        case parent::EMAIL_MX_FAIL :
493            // Failed MX record test.
494            $this->addError($form_name, sprintf(_("The email address <em>%s</em> does not have a valid domain name"), oTxt($email)));
495            $app->logMsg(sprintf('The email address %s does not have a valid domain name.', oTxt($email)), LOG_INFO, __FILE__, __LINE__);
496            return false;
497            break;
498
499        case parent::EMAIL_SUCCESS :
500        default :
501            return true;
502            break;
503        }
504    }
505
506    /**
507     * Check whether input is a valid phone number. Notice: it is now set
508     * to allow characters like - or () or + so people can type in a phone
509     * number that looks like: +1 (530) 555-1212
510     *
511     * @param  string  $form_name the name of the incoming form variable
512     *
513     * @return bool    true if no errors found, false otherwise
514     */
515    public function validatePhone($form_name)
516    {
517        $phone = getFormData($form_name);
518
519        return (
520            $this->checkRegex($form_name, '/^[0-9 +().-]*$/', true, sprintf(_("The phone number <em>%s</em> is not valid."), $phone))
521            && $this->stringLength($form_name, 0, 25, sprintf(_("The phone number <em>%s</em> is too long"), $phone))
522        );
523    }
524
525    /**
526     * Verifies that date can be processed by the strtotime function.
527     *
528     * @param  string  $form_name the name of the incoming form variable
529     * @param  string  $msg       the message to display on error
530     *
531     * @return bool    true if no errors found, false otherwise
532     */
533    public function validateStrDate($form_name, $msg='')
534    {
535        $app =& App::getInstance();
536
537        if (parent::validateStrDate(getFormData($form_name))) {
538            return true;
539        } else {
540            $this->addError($form_name, $msg);
541            $app->logMsg(sprintf('The string date %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
542            return false;
543        }
544    }
545
546
547    /**
548     * Verifies credit card number using the Luhn (mod 10) algorithm.
549     * http://en.wikipedia.org/wiki/Luhn_algorithm
550     *
551     * @param  string  $form_name   The name of the incoming form variable.
552     * @param  string  $cc_type     Optional, card type to do specific checks. One of the Validator::CC_TYPE_* constants.
553     *
554     * @return bool    true if no errors found, false otherwise
555     */
556    public function validateCCNumber($form_name, $cc_type=null)
557    {
558        $cc_num = getFormData($form_name);
559
560        if (parent::validateCCNumber($cc_num, $cc_type)) {
561            return true;
562        } else {
563            $this->addError($form_name, sprintf(_("The credit card number you entered is not valid. Please check the number and try again."), $cc_num));
564            return false;
565        }
566    }
567
568    /**
569     * Check whether a file was selected for uploading. If file is missing, it's an error.
570     *
571     * @param  string $form_name the name of the incoming form variable
572     * @param  string $msg       the message to display on error
573     *
574     * @return bool   true if no errors found, false otherwise
575     */
576    public function fileUploaded($form_name, $msg='')
577    {
578        if (parent::fileUploaded($form_name)) {
579            return true;
580        } else {
581            $this->addError($form_name, $msg);
582            return false;
583        }
584    }
585
586} // THE END
587
Note: See TracBrowser for help on using the repository browser.