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

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

Many minor fixes during pulso development

File size: 22.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 an array.
387     *
388     * @param  string $form_name the name of the incoming form variable
389     * @param  string $msg       the message to display on error
390     *
391     * @return bool   true if value is a float
392     */
393    public function isArray($form_name, $msg='')
394    {
395        if (Validator::isArray(getFormData($form_name))) {
396            return true;
397        } else {
398            $this->addError($form_name, $msg);
399            return false;
400        }
401    }
402
403    /**
404     * Check whether input matches the specified perl regular expression
405     * pattern.
406     *
407     * @param  string $form_name        The name of the incoming form variable
408     * @param  int    $regex            Perl regex that the string must match
409     * @param  bool   $valid_on_match   Set to true to be valid if match, or false to be valid if the match fails.
410     * @param  string $msg              The message to display on error
411     *
412     * @return bool   true if value passes regex test
413     */
414    public function checkRegex($form_name, $regex, $valid_on_match=true, $msg='')
415    {
416        if (Validator::checkRegex(getFormData($form_name), $regex, $valid_on_match)) {
417            return true;
418        } else {
419            $this->addError($form_name, $msg);
420            return false;
421        }
422    }
423
424    /**
425     * Tests if the string length is between specified values. Whitespace excluded for min.
426     *
427     * @param  string $form_name the name of the incoming form variable
428     * @param  int    $min       minimum length of string, inclusive
429     * @param  int    $max       maximum length of string, inclusive
430     * @param  string $msg       the message to display on error
431     *
432     * @return bool   true if string length is within given boundaries
433     */
434    public function stringLength($form_name, $min, $max, $msg='')
435    {
436        if (Validator::stringLength(getFormData($form_name), $min, $max)) {
437            return true;
438        } else {
439            $this->addError($form_name, $msg);
440            return false;
441        }
442    }
443
444    /**
445     * Check whether input is within a valid numeric range.
446     *
447     * @param  string $form_name the name of the incoming form variable
448     * @param  int    $min       minimum value of number, inclusive
449     * @param  int    $max       maximum value of number, inclusive
450     * @param  string $msg       the message to display on error
451     *
452     * @return bool   true if no errors found, false otherwise
453     */
454    public function numericRange($form_name, $min, $max, $msg='')
455    {
456        if (Validator::numericRange(getFormData($form_name), $min, $max)) {
457            return true;
458        } else {
459            $this->addError($form_name, $msg);
460            return false;
461        }
462    }
463
464    /**
465     * Validates an email address based on the recommendations in RFC 3696.
466     * Is more loose than restrictive, to allow the many valid variants of
467     * email addresses while catching the most common mistakes.
468     * http://www.faqs.org/rfcs/rfc822.html
469     * http://www.faqs.org/rfcs/rfc2822.html
470     * http://www.faqs.org/rfcs/rfc3696.html
471     * http://www.faqs.org/rfcs/rfc1035.html
472     *
473     * @access  public
474     * @param   string  $form_name  The name of the incoming form variable.
475     * @return  bool    Validity of address.
476     * @author  Quinn Comendant <quinn@strangecode.com>
477     */
478    public function validateEmail($form_name, $strict=false)
479    {
480        $app =& App::getInstance();
481
482        $email = getFormData($form_name);
483
484        if ('' == trim($email)) {
485            // No email address provided, and that's okay.
486            return true;
487        }
488
489        // Validator::validateEmail() returns a value that relates to the Validate::EMAIL_* constants (defined in Validator.inc.php).
490        switch (Validator::validateEmail($email, $strict)) {
491        case Validator::EMAIL_REGEX_FAIL:
492            // Failed regex match.
493            $this->addError($form_name, sprintf(_("The email address <em>%s</em> is formatted incorrectly."), oTxt($email)));
494            $app->logMsg(sprintf('The email address %s is not valid.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__);
495            return false;
496
497        case Validator::EMAIL_LENGTH_FAIL :
498            // Failed length requirements.
499            $this->addError($form_name, sprintf(_("The email address <em>%s</em> is too long (email addresses must have fewer than 256 characters)."), oTxt($email)));
500            $app->logMsg(sprintf('The email address %s must contain less than 256 characters.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__);
501            return false;
502
503        case Validator::EMAIL_MX_FAIL :
504            // Failed MX record test.
505            $this->addError($form_name, sprintf(_("The email address <em>%s</em> does not have a valid domain name"), oTxt($email)));
506            $app->logMsg(sprintf('The email address %s does not have a valid domain name.', oTxt($email)), LOG_INFO, __FILE__, __LINE__);
507            return false;
508
509        case Validator::EMAIL_SUCCESS :
510        default :
511            return true;
512        }
513    }
514
515    /**
516     * Check whether input is a valid phone number. Notice: it is now set
517     * to allow characters like - or () or + so people can type in a phone
518     * number that looks like: +1 (530) 555-1212
519     *
520     * @param  string  $form_name the name of the incoming form variable
521     *
522     * @return bool    true if no errors found, false otherwise
523     */
524    public function validatePhone($form_name)
525    {
526        $app =& App::getInstance();
527
528        $phone = getFormData($form_name);
529
530        // Validator::validateEmail() returns a value that relates to the Validate::PHONE_* constants (defined in Validator.inc.php).
531        switch (Validator::validatePhone($phone)) {
532        case Validator::PHONE_REGEX_FAIL:
533            // Failed regex match.
534            $this->addError($form_name, sprintf(_("The phone number <em>%s</em> is not valid."), oTxt($phone)));
535            $app->logMsg(sprintf('The phone number %s is not valid.', oTxt($phone)), LOG_DEBUG, __FILE__, __LINE__);
536            return false;
537
538        case Validator::PHONE_LENGTH_FAIL :
539            // Failed length requirements.
540            $this->addError($form_name, sprintf(_("The phone number <em>%s</em> is too long (phone number must have fewer than 25 characters)."), oTxt($phone)));
541            $app->logMsg(sprintf('The phone number %s must contain less than 256 characters.', oTxt($phone)), LOG_DEBUG, __FILE__, __LINE__);
542            return false;
543
544        case Validator::PHONE_SUCCESS :
545        default :
546            return true;
547        }
548    }
549
550    /**
551     * Verifies that date can be processed by the strtotime function.
552     *
553     * @param  string  $form_name the name of the incoming form variable
554     * @param  string  $msg       the message to display on error
555     *
556     * @return bool    true if no errors found, false otherwise
557     */
558    public function validateStrDate($form_name, $msg='')
559    {
560        $app =& App::getInstance();
561
562        if (Validator::validateStrDate(getFormData($form_name, ''))) {
563            return true;
564        } else {
565            $this->addError($form_name, $msg);
566            $app->logMsg(sprintf('The string date %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__);
567            return false;
568        }
569    }
570
571
572    /**
573     * Verifies credit card number using the Luhn (mod 10) algorithm.
574     * http://en.wikipedia.org/wiki/Luhn_algorithm
575     *
576     * @param  string  $form_name   The name of the incoming form variable.
577     * @param  string  $cc_type     Optional, card type to do specific checks. One of the Validator::CC_TYPE_* constants.
578     *
579     * @return bool    true if no errors found, false otherwise
580     */
581    public function validateCCNumber($form_name, $cc_type=null)
582    {
583        $cc_num = getFormData($form_name);
584
585        if (Validator::validateCCNumber($cc_num, $cc_type)) {
586            return true;
587        } else {
588            $this->addError($form_name, sprintf(_("The credit card number you entered is not valid. Please check the number and try again."), $cc_num));
589            return false;
590        }
591    }
592
593    /**
594     * Check whether a file was selected for uploading. If file is missing, it's an error.
595     *
596     * @param  string $form_name the name of the incoming form variable
597     * @param  string $msg       the message to display on error
598     *
599     * @return bool   true if no errors found, false otherwise
600     */
601    public function fileUploaded($form_name, $msg='')
602    {
603        if (Validator::fileUploaded($form_name)) {
604            return true;
605        } else {
606            $this->addError($form_name, $msg);
607            return false;
608        }
609    }
610    /**
611     * Check whether a file was selected for uploading. If file is missing, it's an error.
612     *
613     * @param  string $form_name the name of the incoming form variable
614     * @param  string $msg       the message to display on error
615     *
616     * @return bool   true if no errors found, false otherwise
617     */
618    public function fileUploadSize($form_name, $msg='')
619    {
620        if (Validator::fileUploadSize($form_name)) {
621            return true;
622        } else {
623            $msg = '' == $msg ? sprintf(_("Maximum filesize exceeded. Got %s, but limit is %s."), humanFileSize($_SERVER['CONTENT_LENGTH']), humanFileSize(phpIniGetBytes('upload_max_filesize'))) : $msg;
624            $this->addError($form_name, $msg);
625            return false;
626        }
627    }
628
629} // THE END
630
Note: See TracBrowser for help on using the repository browser.