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

Last change on this file since 534 was 534, checked in by anonymous, 9 years ago

Improved module maker validation output. Allow disabling cache at run time for ACL. Added ACL getList() method. Improved ACL CLI listing. Fixed app boomerang array initialization. Now retaining identical boomerang URLs if the key is different. Added a maximum boomerang time. Added a way to disable cache per request through a query string. Added validator isDecimal() method. Added disableSelectOptions() HTML method. Added getGravatarURL() method. Change how navigation page array is managed. Updated navigation currentPage() method to test an array of URLs.

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