source: tags/2.1.5/lib/FormValidator.inc.php

Last change on this file was 377, checked in by quinn, 14 years ago

Releasing trunk as stable version 2.1.5

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