PageRenderTime 7ms CodeModel.GetById 41ms app.highlight 76ms RepoModel.GetById 15ms app.codeStats 1ms

/dooframework/helper/DooValidator.php

http://github.com/darkredz/DooPHP
PHP | 1221 lines | 538 code | 92 blank | 591 comment | 210 complexity | 7a1337eefb5b63e38a5aabf5ab35e415 MD5 | raw file
   1<?php
   2/**
   3 * DooValidator class file.
   4 *
   5 * @author Leng Sheng Hong <darkredz@gmail.com>
   6 * @link http://www.doophp.com/
   7 * @copyright Copyright &copy; 2009 Leng Sheng Hong
   8 * @license http://www.doophp.com/license
   9 */
  10
  11/**
  12 * A helper class that helps validating data.
  13 *
  14 * <p>DooValidator can be used for form and Model data validation before saving/inserting/deleting a data.</p>
  15 *
  16 * <p>To use DooValidator, you have to create an instance of it and defined the validation rules.
  17 * All the methods start with 'test' can be used as a rule for validating data. Rule names are case insensitive.</p>
  18 *
  19 * <p>You can pass in custom error message along with the rules. By default all fields in the rules are <b>required</b></p>
  20 * <code>
  21 * $rules = array(
  22 *              'creattime'=>array(
  23 *                    array('datetime'),
  24 *                    array('email'),
  25 *                    array('optional')   //Optional field
  26 *              ),
  27 *              'username'=>array(
  28 *                    array('username',6,16),
  29 *                    //Custom error message will be used
  30 *                    array('lowercase', 'Username must only be lowercase.')
  31 *               ),
  32 *
  33 *               //Only one rule
  34 *               'pwd'=>array('password'),
  35 *               'email'=>array('email'),
  36 *               'age'=>array('between',13,200),
  37 *               'today'=>array('date','mm/dd/yy'),
  38 *
  39 *               //Custom rules, static method required
  40 *               'a'=>array('custom', 'MainController::isA'),
  41 *
  42 *               //Custom Required field message.
  43 *               'content'=>array('required', 'Content is required!')
  44 *        );
  45 * </code>
  46 *
  47 * <p>Rules are defined based on the validation method's parameters. For example:</p>
  48 * <code>
  49 * //Validation method
  50 * testBetween($value, $min, $max, $msg=null)
  51 *
  52 * $rule = array(
  53 *     'field'=>array('between', 0, 20)
  54 *     'field2'=>array('between', 0, 20, 'Custom Err Msg!')
  55 * );
  56 * </code>
  57 *
  58 * <p>You can get the list of available validation rules by calling DooValidator::getAvailableRules()</p>
  59 *
  60 * <p>To validate the data, create an instance of DooValidator and call validate() in your Controller/Model.</p>
  61 * <code>
  62 * $v = new DooValidator();
  63 *
  64 * # There are 3 different validation Mode.
  65 * //$v->checkMode = DooValidator::CHECK_SKIP;
  66 * //$v->checkMode = DooValidator::CHECK_ALL_ONE;
  67 * //$v->checkMode = DooValidator::CHECK_ALL;
  68 *
  69 * //The $_POST or data pass in need to be an assoc array
  70 * //$data = array('username'=>'doophp', 'pwd'=>'12345');
  71 * if($error = $v->validate($_POST, $rules)){
  72 *      print_r($error);
  73 * }
  74 * </code>
  75 *
  76 * <p>You can pass in a string to load predefined Rules which located at SITE_PATH/protected/config/forms/</p>
  77 * <code>
  78 * <?php
  79 * //in protected/config/forms/example.php
  80 * return array(
  81 *      'username'=>array(
  82 *                      array('username',4,5,'username invalid'),
  83 *                      array('maxlength',6,'This is too long'),
  84 *                      array('minlength',6)
  85 *                  ),
  86 *      'pwd'=>array('password',3,5),
  87 *      'email'=>array('email')
  88 *  );
  89 * ?>
  90 *
  91 * //in your Controller/Model
  92 * $error = $v->validate($data, 'example');
  93 * </code>
  94 *
  95 * <p>If nothing is returned from the validate() call, it means that all the data passed the validation rules.</p>
  96 *
  97 * <p>The Model validation rules are automatically generated when you used the framework's model generator feature.
  98 * If your Models are extending DooModel or DooSmartModel, you can validate the data by calling DooModel::validate()</p>
  99 * <code>
 100 * $f = new Food;
 101 * $f->name = 'My Food name for validation';
 102 * $error = $f->validate();
 103 *
 104 * //Or...
 105 * $error = Food::_validate($f);
 106 * </code>
 107 *
 108 * @author Leng Sheng Hong <darkredz@gmail.com>
 109 * @version $Id: DooValidator.php 1000 2009-08-30 11:37:22
 110 * @package doo.helper
 111 * @since 1.2
 112 */
 113class DooValidator {
 114    /**
 115     * Checks All and returns All errors
 116     */
 117    const CHECK_ALL = 'all';
 118
 119    /**
 120     * Checks All and returns one error for each data field
 121     */
 122    const CHECK_ALL_ONE = 'all_one';
 123
 124    /**
 125     * Returns one error once the first is detected
 126     */
 127    const CHECK_SKIP = 'skip';
 128
 129	/**
 130	 * Use PHP empty method to test for required (or optional)
 131	 */
 132	const REQ_MODE_NULL_EMPTY = 'nullempty';
 133
 134	/**
 135	 * Only ensure required fields are non null / accept not null on required
 136	 */
 137	const REQ_MODE_NULL_ONLY = 'null';
 138    
 139    /**
 140     * Default require message to display field name "first_name is required."
 141     */
 142    const REQ_MSG_FIELDNAME = 'fieldname';
 143    
 144    /**
 145     * Default require message to display "This is required."
 146     */
 147    const REQ_MSG_THIS = 'this';
 148    
 149    /**
 150     * Default require message to convert field name with underscore to words. eg(field = first_name). "First name is required."
 151     */    
 152    const REQ_MSG_UNDERSCORE_TO_SPACE = 'underscore';
 153    
 154    /**
 155     * Default require message to convert field name with camelcase to words. eg(field = firstName). "First name is required."
 156     */    
 157    const REQ_MSG_CAMELCASE_TO_SPACE = 'camelcase';
 158
 159    /**
 160     * Validation mode
 161     * @var string all/all_one/skip
 162     */
 163    public $checkMode = 'all';
 164
 165	/**
 166	 * How should required fields be tested (or be considered left out on optional)
 167	 * @var string empty/null
 168	 */
 169	public $requireMode = 'nullempty';
 170    
 171    /**
 172     * Default method to generate error message for a required field.
 173     * @var string
 174     */
 175    public $requiredMsgDefaultMethod = 'underscore';
 176    
 177    /**
 178     * Default error message suffix for a required field.
 179     * @var string
 180     */
 181    public $requiredMsgDefaultSuffix = ' field is required';
 182
 183    /**
 184     * Trim the data fields. The data will be modified.
 185     * @param array $data assoc array to be trimmed
 186	 * @param int $maxDepth Maximum number of recursive calls. Defaults to a max nesting depth of 5.
 187     */
 188    public function trimValues(&$data, $maxDepth = 5) {
 189		foreach($data as $k=>&$v) {
 190			if (is_array($v)) {
 191				if ($maxDepth > 0) {
 192					$this->trimValues($v, $maxDepth - 1);
 193				}
 194			} else {
 195				$v = trim($v);
 196			}
 197		}
 198	}
 199
 200    /**
 201     * Get a list of available rules
 202     * @return array
 203     */
 204    public static function getAvailableRules(){
 205        return array('alpha', 'alphaNumeric', 'between', 'betweenInclusive', 'ccAmericanExpress', 'ccDinersClub', 'ccDiscover', 'ccMasterCard',
 206                    'ccVisa', 'colorHex', 'creditCard', 'custom', 'date', 'dateBetween', 'datetime', 'digit', 'email', 'equal', 'equalAs', 'float',
 207                    'greaterThan', 'greaterThanOrEqual', 'ip', 'integer', 'lessThan', 'lessThanOrEqual', 'lowercase', 'max',
 208                    'maxlength', 'min', 'minlength', 'notEmpty', 'notEqual', 'notNull', 'password', 'passwordComplex', 'price', 'regex',
 209                    'uppercase', 'url', 'username','dbExist','dbNotExist','alphaSpace','notInList','inList'
 210                );
 211    }
 212
 213    /**
 214     * Get appropriate rules for a certain DB data type
 215     * @param string $dataType
 216     * @return string Rule name for the data type
 217     */
 218    public static function dbDataTypeToRules($type){
 219        $dataType = array(
 220                        //integers
 221                        'tinyint'=>'integer',
 222                        'smallint'=>'integer',
 223                        'mediumint'=>'integer',
 224                        'int'=>'integer',
 225                        'bigint'=>'integer',
 226
 227                        //float
 228                        'float'=>'float',
 229                        'double'=>'float',
 230                        'decimal'=>'float',
 231
 232                        //datetime
 233                        'date'=>'date',
 234                        'datetime'=>'datetime',
 235                        'timestamp'=>'datetime',
 236                        'time'=>'datetime'
 237                    );
 238        if(isset($dataType[$type]))
 239            return $dataType[$type];
 240    }
 241
 242    /**
 243     * Validate the data with the defined rules.
 244     *
 245     * @param array $data Data to be validate. One dimension assoc array, eg. array('user'=>'leng', 'email'=>'abc@abc.com')
 246     * @param string|array $rules Validation rule. Pass in a string to load the predefined rules in SITE_PATH/protected/config/forms
 247     * @return array Returns an array of errors if errors exist.
 248     */
 249    public function validate($data=null, $rules=null){
 250        //$data = array('username'=>'leng s', 'pwd'=>'234231dfasd', 'email'=>'asdb12@#asd.com.my');
 251        //$rules = array('username'=>array('username'), 'pwd'=>array('password',6,32), 'email'=>array('email'));
 252        if(is_string($rules)){
 253            $rules = include(Doo::conf()->SITE_PATH .  Doo::conf()->PROTECTED_FOLDER . 'config/forms/'.$rules.'.php');
 254        }
 255
 256        $optErrorRemove = array();
 257
 258        foreach($data as $dk=>$dv){
 259            if($this->requireMode == DooValidator::REQ_MODE_NULL_EMPTY && ($dv === null || $dv === '') ||
 260			   $this->requireMode == DooValidator::REQ_MODE_NULL_ONLY  && $dv === null){
 261                unset($data[$dk]);
 262			}
 263        }
 264
 265        if($missingKey = array_diff_key($rules, $data) ){
 266                $fieldnames = array_keys($missingKey);
 267                $customRequireMsg = null;
 268
 269                foreach($fieldnames as $fieldname){
 270                    if(isset($missingKey[$fieldname])){
 271                        if( in_array('required', $missingKey[$fieldname]) ){
 272                            $customRequireMsg = $missingKey[$fieldname][1];
 273                        }
 274                        else if(is_array($missingKey[$fieldname][0])){
 275                            foreach($missingKey[$fieldname] as $f)
 276                                if($f[0]=='required'){
 277                                    if(isset($f[1]))
 278                                        $customRequireMsg = $f[1];
 279                                    break;
 280                                }
 281                        }
 282                    }
 283
 284                    //remove optional fields from error
 285                    if(is_array($missingKey[$fieldname][0])){
 286                       foreach($missingKey[$fieldname] as $innerArrayRules){
 287                           if($innerArrayRules[0]=='optional'){
 288                               //echo $fieldname.' - 1 this is not set and optional, should be removed from error';
 289                               $optErrorRemove[] = $fieldname;
 290                               break;
 291                           }
 292                       }
 293                    }
 294
 295                    if($this->checkMode==DooValidator::CHECK_ALL){
 296                        if($customRequireMsg!==null)
 297                            $errors[$fieldname] = $customRequireMsg;
 298                        else
 299                            $errors[$fieldname] = $this->getRequiredFieldDefaultMsg($fieldname);
 300                    }else if($this->checkMode==DooValidator::CHECK_SKIP){
 301                        if(in_array($fieldname, $optErrorRemove))
 302                            continue;
 303                        if($customRequireMsg!==null)
 304                            return $customRequireMsg;
 305                        return $this->getRequiredFieldDefaultMsg($fieldname);
 306                    }else if($this->checkMode==DooValidator::CHECK_ALL_ONE){
 307                        if($customRequireMsg!==null)
 308                            $errors[$fieldname] = $customRequireMsg;
 309                        else
 310                            $errors[$fieldname] = $this->getRequiredFieldDefaultMsg($fieldname);
 311                    }
 312                }
 313        }
 314
 315        foreach($data as $k=>$v){
 316            if(!isset($rules[$k])) continue;
 317            $cRule = $rules[$k];
 318            foreach($cRule as $v2){
 319                if(is_array($v2)){
 320                    //print_r(array_slice($v2, 1));
 321                    $vv = array_merge(array($v),array_slice($v2, 1));
 322
 323					$vIsEmpty = ($this->requireMode == DooValidator::REQ_MODE_NULL_EMPTY && ($v === null || $v === '') ||
 324								 $this->requireMode == DooValidator::REQ_MODE_NULL_ONLY  && $v === null) ? true : false;
 325
 326                    //call func
 327                    if($vIsEmpty && $v2[0]=='optional'){
 328                        //echo $k.' - this is not set and optional, should be removed from error';
 329                        $optErrorRemove[] = $k;
 330                    }
 331                    if($err = call_user_func_array(array(&$this, 'test'.$v2[0]), $vv) ){
 332                        if($this->checkMode==DooValidator::CHECK_ALL)
 333                            $errors[$k][$v2[0]] = $err;
 334                        else if($this->checkMode==DooValidator::CHECK_SKIP && !$vIsEmpty && $v2[0]!='optional'){
 335                            return $err;
 336                        }else if($this->checkMode==DooValidator::CHECK_ALL_ONE)
 337                            $errors[$k] = $err;
 338                    }
 339                }
 340                else if(is_string($cRule[0])){
 341                    if(sizeof($cRule)>1){
 342                        //print_r(array_slice($cRule, 1));
 343                        $vv = array_merge(array($v),array_slice($cRule, 1));
 344
 345                        if($err = call_user_func_array(array(&$this, 'test'.$cRule[0]), $vv) ){
 346                            if($this->checkMode==DooValidator::CHECK_ALL || $this->checkMode==DooValidator::CHECK_ALL_ONE)
 347                                $errors[$k] = $err;
 348                            else if($this->checkMode==DooValidator::CHECK_SKIP){
 349                                return $err;
 350                            }
 351                        }
 352                    }else{
 353                        if($err = $this->{'test'.$cRule[0]}($v) ){
 354                            if($this->checkMode==DooValidator::CHECK_ALL || $this->checkMode==DooValidator::CHECK_ALL_ONE)
 355                                $errors[$k] = $err;
 356                            else if($this->checkMode==DooValidator::CHECK_SKIP){
 357                                return $err;
 358                            }
 359                        }
 360                    }
 361                    continue 2;
 362                }
 363            }
 364        }
 365        if(isset($errors)){
 366            if(sizeof($optErrorRemove)>0){
 367                foreach($errors as $ek=>$ev){
 368                    if(in_array($ek, $optErrorRemove)){
 369                        //echo '<h3>Removing error '.$ek.'</h3>';
 370                        unset($errors[$ek]);
 371                    }
 372                }
 373            }
 374            return $errors;
 375        }
 376    }
 377    
 378    /**
 379     * Set default settings to display the default error message for required fields
 380     * @param type $displayMethod Default error message display method. use: DooValidator::REQ_MSG_UNDERSCORE_TO_SPACE, DooValidator::REQ_MSG_CAMELCASE_TO_SPACE, DooValidator::REQ_MSG_THIS, DooValidator::REQ_MSG_FIELDNAME
 381     * @param type $suffix suffix for the error message. Default is ' field is required'
 382     */
 383    public function setRequiredFieldDefaults( $displayMethod = DooValidator::REQ_MSG_UNDERSCORE_TO_SPACE, $suffix = ' field is required'){
 384        $this->requiredMsgDefaultMethod = $displayMethod;
 385        $this->requiredMsgDefaultSuffix = $suffix;
 386    }
 387    
 388    /**
 389     * Get the default error message for required field
 390     * @param string $fieldname Name of the field
 391     * @return string Error message
 392     */
 393    public function getRequiredFieldDefaultMsg($fieldname){
 394        if($this->requiredMsgDefaultMethod==DooValidator::REQ_MSG_UNDERSCORE_TO_SPACE)
 395            return ucfirst(str_replace('_', ' ', $fieldname)) . $this->requiredMsgDefaultSuffix;
 396        
 397        if($this->requiredMsgDefaultMethod==DooValidator::REQ_MSG_THIS)
 398            return 'This ' . $this->requiredMsgDefaultSuffix;        
 399        
 400        if($this->requiredMsgDefaultMethod==DooValidator::REQ_MSG_CAMELCASE_TO_SPACE)
 401            return ucfirst(strtolower(preg_replace('/([A-Z])/', ' $1', $fieldname))) . $this->requiredMsgDefaultSuffix;
 402        
 403        if($this->requiredMsgDefaultMethod==DooValidator::REQ_MSG_FIELDNAME)
 404            return $fieldname . $this->requiredMsgDefaultSuffix;
 405    }
 406
 407    public function testOptional($value){}
 408    public function testRequired($value, $msg){
 409		if ($this->requireMode == DooValidator::REQ_MODE_NULL_EMPTY && ($value === null || $value === '') ||
 410			$this->requireMode == DooValidator::REQ_MODE_NULL_ONLY  && $value === null) {
 411
 412            if($msg!==null) return $msg;
 413            return 'This field is required!';
 414        }
 415    }
 416
 417    /**
 418     * Validate data with your own custom rules.
 419     *
 420     * Usage in Controller:
 421     * <code>
 422     * public static function isA($value){
 423     *      if($value!='a'){
 424     *          return 'Value must be A';
 425     *      }
 426     * }
 427     *
 428     * public function test(){
 429     *     $rules = array(
 430     *          'email'=>array('custom', 'TestController::isA')
 431     *     );
 432     *
 433     *     $v = new DooValidator();
 434     *     if($error = $v->validate($_POST, $rules)){
 435     *          //display error
 436     *     }
 437     * }
 438     * </code>
 439     *
 440     * @param string $value Value of data to be validated
 441     * @param string $function Name of the custom function
 442     * @param string $msg Custom error message
 443     * @return string
 444     */
 445    public function testCustom($value, $function, $options=null ,$msg=null){
 446        if($options==null){
 447            if($err = call_user_func($function, $value)){
 448                if($err!==true){
 449                    if($msg!==null) return $msg;
 450                    return $err;
 451                }
 452            }
 453        }else{
 454            //if array, additional parameters
 455            if($err = call_user_func_array($function, array_merge(array($value), $options)) ){
 456                if($err!==true){
 457                    if($msg!==null) return $msg;
 458                    return $err;
 459                }
 460            }
 461        }
 462    }
 463
 464    /**
 465     * Validate against a Regex rule
 466     *
 467     * @param string $value Value of data to be validated
 468     * @param string $regex Regex rule to be tested against
 469     * @param string $msg Custom error message
 470     * @return string
 471     */
 472    public function testRegex($value, $regex, $msg=null){
 473        if(!preg_match($regex, $value) ){
 474            if($msg!==null) return $msg;
 475            return 'Error in field.';
 476        }
 477    }
 478
 479    /**
 480     * Validate username format.
 481     *
 482     * @param string $value Value of data to be validated
 483     * @param int $minLength Minimum length
 484     * @param int $maxLength Maximum length
 485     * @param string $msg Custom error message
 486     * @return string
 487     */
 488    public function testUsername($value, $minLength=4, $maxLength=12, $msg=null){
 489        if(!preg_match('/^[a-zA-Z][a-zA-Z.0-9_-]{'. ($minLength-1) .','.$maxLength.'}$/i', $value)){
 490            if($msg!==null) return $msg;
 491            return "User name must be $minLength-$maxLength characters. Only characters, dots, digits, underscore & hyphen are allowed.";
 492        }
 493        else if(strpos($value, '..')!==False){
 494            if($msg!==null) return $msg;
 495            return "User name cannot consist of 2 continuous dots.";
 496        }
 497        else if(strpos($value, '__')!==False){
 498            if($msg!==null) return $msg;
 499            return "User name cannot consist of 2 continuous underscore.";
 500        }
 501        else if(strpos($value, '--')!==False){
 502            if($msg!==null) return $msg;
 503            return "User name cannot consist of 2 continuous dash.";
 504        }
 505        else if(strpos($value, '.-')!==False || strpos($value, '-.')!==False ||
 506                strpos($value, '._')!==False || strpos($value, '_.')!==False ||
 507                strpos($value, '_-')!==False || strpos($value, '-_')!==False){
 508            if($msg!==null) return $msg;
 509            return "User name cannot consist of 2 continuous punctuation.";
 510        }
 511        else if(ctype_punct($value[0])){
 512            if($msg!==null) return $msg;
 513            return "User name cannot start with a punctuation.";
 514        }
 515        else if(ctype_punct( substr($value, strlen($value)-1) )){
 516            if($msg!==null) return $msg;
 517            return "User name cannot end with a punctuation.";
 518        }
 519    }
 520
 521    /**
 522     * Validate password format
 523     *
 524     * @param string $value Value of data to be validated
 525     * @param int $minLength Minimum length
 526     * @param int $maxLength Maximum length
 527     * @param string $msg Custom error message
 528     * @return string
 529     */
 530    public function testPassword($value, $minLength=6, $maxLength=32, $msg=null){
 531        if(!preg_match('/^[a-zA-Z.0-9_-]{'.$minLength.','.$maxLength.'}$/i', $value)){
 532            if($msg!==null) return $msg;
 533            return "Only characters, dots, digits, underscore & hyphen are allowed. Password must be at least $minLength characters long.";
 534        }
 535    }
 536
 537    /**
 538     * Validate against a complex password format
 539     *
 540     * @param string $value Value of data to be validated
 541     * @param string $msg Custom error message
 542     * @return string
 543     */
 544    public function testPasswordComplex($value, $msg=null){
 545        if(!preg_match('A(?=[-_a-zA-Z0-9]*?[A-Z])(?=[-_a-zA-Z0-9]*?[a-z])(?=[-_a-zA-Z0-9]*?[0-9])[-_a-zA-Z0-9]{6,32}z', $value)){
 546            if($msg!==null) return $msg;
 547            return 'Password must contain at least one upper case letter, one lower case letter and one digit. It must consists of 6 or more letters, digits, underscores and hyphens.';
 548        }
 549    }
 550
 551    /**
 552     * Validate email address
 553     *
 554     * @param string $value Value of data to be validated
 555     * @param string $msg Custom error message
 556     * @return string
 557     */
 558    public function testEmail($value, $msg=null){
 559		// Regex based on best solution from here: http://fightingforalostcause.net/misc/2006/compare-email-regex.php
 560        if(!preg_match('/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~\_]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i', $value) ||
 561            strpos($value, '--')!==False || strpos($value, '-.')!==False
 562        ){
 563            if($msg!==null) return $msg;
 564            return 'Invalid email format!';
 565        }
 566    }
 567
 568    /**
 569     * Validate a URL
 570     *
 571     * @param string $value Value of data to be validated
 572     * @param string $msg Custom error message
 573     * @return string
 574     */
 575    public function testUrl($value, $msg=null){
 576        if(!preg_match('/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i', $value)){
 577            if($msg!==null) return $msg;
 578            return 'Invalid URL!';
 579        }
 580    }
 581
 582    /**
 583     * Validate an IP address (198.168.1.101)
 584     *
 585     * @param string $value Value of data to be validated
 586     * @param string $msg Custom error message
 587     * @return string
 588     */
 589    public function testIP($value, $msg=null){
 590        //198.168.1.101
 591        if (!preg_match('/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/',$value)) {
 592            if($msg!==null) return $msg;
 593            return 'Invalid IP address!';
 594        }
 595    }
 596
 597    /**
 598     * Validate a credit card number
 599     *
 600     * @param string $value Value of data to be validated
 601     * @param string $msg Custom error message
 602     * @return string
 603     */
 604    public function testCreditCard($value, $msg=null){
 605        //568282246310632
 606        if (!preg_match('/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/', $value)) {
 607            if($msg!==null) return $msg;
 608            return 'Invalid credit card number!';
 609        }
 610    }
 611
 612    /**
 613     * Validate an American Express credit card number
 614     *
 615     * @param string $value Value of data to be validated
 616     * @param string $msg Custom error message
 617     * @return string
 618     */
 619    public function testCcAmericanExpress($value, $msg=null){
 620        if (!preg_match('/^3[47][0-9]{13}$/', $value)) {
 621            if($msg!==null) return $msg;
 622            return 'Invalid American Express credit card number!';
 623        }
 624    }
 625
 626    /**
 627     * Validate an Discover credit card number
 628     *
 629     * @param string $value Value of data to be validated
 630     * @param string $msg Custom error message
 631     * @return string
 632     */
 633    public function testCcDiscover($value, $msg=null){
 634        if (!preg_match('/^6011[0-9]{12}$/', $value)) {
 635            if($msg!==null) return $msg;
 636            return 'Invalid Discover credit card number!';
 637        }
 638    }
 639
 640    /**
 641     * Validate an Diners Club credit card number
 642     *
 643     * @param string $value Value of data to be validated
 644     * @param string $msg Custom error message
 645     * @return string
 646     */
 647    public function testCcDinersClub($value, $msg=null){
 648        if (!preg_match('/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/', $value)) {
 649            if($msg!==null) return $msg;
 650            return 'Invalid Diners Club credit card number!';
 651        }
 652    }
 653
 654    /**
 655     * Validate an Master Card number
 656     *
 657     * @param string $value Value of data to be validated
 658     * @param string $msg Custom error message
 659     * @return string
 660     */
 661    public function testCcMasterCard($value, $msg=null){
 662        if (!preg_match('/^5[1-5][0-9]{14}$/', $value)) {
 663            if($msg!==null) return $msg;
 664            return 'Invalid Master Card number!';
 665        }
 666    }
 667
 668    /**
 669     * Validate an Visa Card number
 670     *
 671     * @param string $value Value of data to be validated
 672     * @param string $msg Custom error message
 673     * @return string
 674     */
 675    public function testCcVisa($value, $msg=null){
 676        if (!preg_match('/^4[0-9]{12}(?:[0-9]{3})?$/', $value)) {
 677            if($msg!==null) return $msg;
 678            return 'Invalid Visa card number!';
 679        }
 680    }
 681
 682    /**
 683     * Validate Color hex #ff0000
 684     *
 685     * @param string $value Value of data to be validated
 686     * @param string $msg Custom error message
 687     * @return string
 688     */
 689    public function testColorHex($value, $msg=null){
 690        //#ff0000
 691        if (!preg_match('/^#([0-9a-f]{1,2}){3}$/i', $value)) {
 692            if($msg!==null) return $msg;
 693            return 'Invalid color code!';
 694        }
 695    }
 696
 697    //------------------- Common data validation ---------------------
 698
 699    /**
 700     * Validate Date Time
 701     *
 702     * @param string $value Value of data to be validated
 703     * @param string $msg Custom error message
 704     * @return string
 705     */
 706    public function testDateTime($value, $msg=null){
 707        $rs = strtotime($value);
 708
 709        if ($rs===false || $rs===-1){
 710            if($msg!==null) return $msg;
 711            return 'Invalid date time format!';
 712        }
 713    }
 714
 715    /**
 716     * Validate Date format. Default yyyy/mm/dd.
 717     *
 718     * <p>Date format: yyyy-mm-dd, yyyy/mm/dd, yyyy.mm.dd
 719     * Date valid from 1900-01-01 through 2099-12-31</p>
 720     *
 721     * @param string $value Value of data to be validated
 722     * @param string $dateFormat Date format
 723     * @param string $msg Custom error message
 724     * @return string
 725     */
 726    public function testDate($value, $format='yyyy/mm/dd', $msg=null, $forceYearLength=false){
 727        //Date yyyy-mm-dd, yyyy/mm/dd, yyyy.mm.dd
 728        //1900-01-01 through 2099-12-31
 729
 730		$yearFormat = "(19|20)?[0-9]{2}";
 731		if ($forceYearLength == true) {
 732			if (strpos($format, 'yyyy') !== false) {
 733				$yearFormat = "(19|20)[0-9]{2}";
 734			} else {
 735				$yearFormat = "[0-9]{2}";
 736			}
 737		}
 738
 739        switch($format){
 740            case 'dd/mm/yy':
 741                $format = "/^\b(0?[1-9]|[12][0-9]|3[01])[- \/.](0?[1-9]|1[012])[- \/.]{$yearFormat}\b$/";
 742                break;
 743            case 'mm/dd/yy':
 744                $format = "/^\b(0?[1-9]|1[012])[- \/.](0?[1-9]|[12][0-9]|3[01])[- \/.]{$yearFormat}\b$/";
 745                break;
 746            case 'mm/dd/yyyy':
 747                $format = "/^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.]{$yearFormat}$/";
 748                break;
 749            case 'dd/mm/yyyy':
 750                $format = "/^(0[1-9]|[12][0-9]|3[01])[- \/.](0[1-9]|1[012])[- \/.]{$yearFormat}$/";
 751                break;
 752            case 'yy/mm/dd':
 753                $format = "/^\b{$yearFormat}[- \/.](0?[1-9]|1[012])[- \/.](0?[1-9]|[12][0-9]|3[01])\b$/";
 754                break;
 755            case 'yyyy/mm/dd':
 756            default:
 757                $format = "/^\b{$yearFormat}[- \/.](0?[1-9]|1[012])[- \/.](0?[1-9]|[12][0-9]|3[01])\b$/";
 758        }
 759
 760        if (!preg_match($format, $value)) {
 761            if($msg!==null) return $msg;
 762            return 'Invalid date format!';
 763        }
 764    }
 765
 766    /**
 767     * Validate if given date is between 2 dates.
 768     *
 769     * @param string $value Value of data to be validated
 770     * @param string $dateStart Starting date
 771     * @param string $dateEnd Ending date
 772     * @param string $msg Custom error message
 773     * @return string
 774     */
 775    public function testDateBetween($value, $dateStart, $dateEnd, $msg=null){
 776		$value = strtotime($value);
 777        if(!( $value > strtotime($dateStart) && $value < strtotime($dateEnd) ) ) {
 778            if($msg!==null) return $msg;
 779            return "Date must be between $dateStart and $dateEnd";
 780        }
 781    }
 782
 783    /**
 784     * Validate integer
 785     *
 786     * @param string $value Value of data to be validated
 787     * @param string $msg Custom error message
 788     * @return string
 789     */
 790    public function testInteger($value, $msg=null){
 791        if(intval($value)!=$value || strlen(intval($value))!=strlen($value)){
 792            if($msg!==null) return $msg;
 793            return 'Input is not an integer.';
 794        }
 795    }
 796
 797    /**
 798     * Validate price. 2 decimal points only
 799     *
 800     * @param string $value Value of data to be validated
 801     * @param string $msg Custom error message
 802     * @return string
 803     */
 804    public function testPrice($value, $msg=null){
 805        // 2 decimal
 806        if (!preg_match('/^[0-9]*\\.?[0-9]{0,2}$/', $value)){
 807            if($msg!==null) return $msg;
 808            return 'Input is not a valid price amount.';
 809        }
 810    }
 811
 812    /**
 813     * Validate float value.
 814     *
 815     * @param string $value Value of data to be validated
 816     * @param int $decimal Number of Decimal points
 817     * @param string $msg Custom error message
 818     * @return string
 819     */
 820    public function testFloat($value, $decimal='', $msg=null){
 821        // any amount of decimal
 822        if (!preg_match('/^[0-9]*\\.?[0-9]{0,'.$decimal.'}$/', $value)){
 823            if($msg!==null) return $msg;
 824            return 'Input is not a valid float value.';
 825        }
 826    }
 827
 828    /**
 829     * Validate digits.
 830     *
 831     * @param string $value Value of data to be validated
 832     * @param string $msg Custom error message
 833     * @return string
 834     */
 835    public function testDigit($value, $msg=null){
 836        if(!ctype_digit($value)){
 837            if($msg!==null) return $msg;
 838            return 'Input is not a digit.';
 839        }
 840    }
 841
 842    /**
 843     * Validate Alpha numeric values.
 844     *
 845     * Input string can only consist of only Letters or Digits.
 846     *
 847     * @param string $value Value of data to be validated
 848     * @param string $msg Custom error message
 849     * @return string
 850     */
 851    public function testAlphaNumeric($value, $msg=null){
 852        if(!ctype_alnum($value)){
 853            if($msg!==null) return $msg;
 854            return 'Input can only consist of letters or digits.';
 855        }
 856    }
 857
 858    /**
 859     * Validate Alpha values.
 860     *
 861     * Input string can only consist of only Letters.
 862     *
 863     * @param string $value Value of data to be validated
 864     * @param string $msg Custom error message
 865     * @return string
 866     */
 867    public function testAlpha($value, $msg=null){
 868        if(!ctype_alpha($value)){
 869            if($msg!==null) return $msg;
 870            return 'Input can only consist of letters.';
 871        }
 872    }
 873
 874    /**
 875     * Validate if string only consist of letters and spaces
 876     *
 877     * Input string can only consist of only Letters and spaces.
 878     *
 879     * @param string $value Value of data to be validated
 880     * @param string $msg Custom error message
 881     * @return string
 882     */
 883    public function testAlphaSpace($value, $msg=null){
 884        if(!ctype_alpha(str_replace(' ','',$value))){
 885            if($msg!==null) return $msg;
 886            return 'Input can only consist of letters and spaces.';
 887        }
 888    }
 889
 890
 891    /**
 892     * Validate lowercase string.
 893     *
 894     * Input string can only be lowercase letters.
 895     *
 896     * @param string $value Value of data to be validated
 897     * @param string $msg Custom error message
 898     * @return string
 899     */
 900    public function testLowercase($value, $msg=null){
 901        if(!ctype_lower($value)){
 902            if($msg!==null) return $msg;
 903            return 'Input can only consists of lowercase letters.';
 904        }
 905    }
 906
 907    /**
 908     * Validate uppercase string.
 909     *
 910     * Input string can only be uppercase letters.
 911     *
 912     * @param string $value Value of data to be validated
 913     * @param string $msg Custom error message
 914     * @return string
 915     */
 916    public function testUppercase($value, $msg=null){
 917        if(!ctype_upper($value)){
 918            if($msg!==null) return $msg;
 919            return 'Input can only consists of uppercase letters.';
 920        }
 921    }
 922
 923    /**
 924     * Validate Not Empty. Input cannot be empty.
 925     *
 926     * @param string $value Value of data to be validated
 927     * @param string $msg Custom error message
 928     * @return string
 929     */
 930    public function testNotEmpty($value, $msg=null){
 931        if(empty($value)){
 932            if($msg!==null) return $msg;
 933            return 'Value cannot be empty!';
 934        }
 935    }
 936
 937    /**
 938     * Validate Max length of a string.
 939     *
 940     * @param string $value Value of data to be validated
 941     * @param int $length Maximum length of the string
 942     * @param string $msg Custom error message
 943     * @return string
 944     */
 945    public function testMaxLength($value, $length=0, $msg=null){
 946        if(mb_strlen($value) > $length){
 947            if($msg!==null) return $msg;
 948            return "Input cannot be longer than $length characters.";
 949        }
 950    }
 951
 952    /**
 953     * Validate Minimum length of a string.
 954     *
 955     * @param string $value Value of data to be validated
 956     * @param int $length Minimum length of the string
 957     * @param string $msg Custom error message
 958     * @return string
 959     */
 960    public function testMinLength($value, $length=0, $msg=null){
 961        if(strlen($value) < $length){
 962            if($msg!==null) return $msg;
 963            return "Input cannot be shorter than $length characters.";
 964        }
 965    }
 966
 967    /**
 968     * Validate Not Null. Value cannot be null.
 969     *
 970     * @param string $value Value of data to be validated
 971     * @param string $msg Custom error message
 972     * @return string
 973     */
 974    public function testNotNull($value, $msg=null){
 975        if(is_null($value)){
 976            if($msg!==null) return $msg;
 977            return 'Value cannot be null.';
 978        }
 979    }
 980
 981    /**
 982     * Validate Minimum value of a number.
 983     *
 984     * @param string $value Value of data to be validated
 985     * @param int $min Minimum value
 986     * @param string $msg Custom error message
 987     * @return string
 988     */
 989    public function testMin($value, $min, $msg=null){
 990        if( $value < $min){
 991            if($msg!==null) return $msg;
 992            return "Value cannot be less than $min";
 993        }
 994    }
 995
 996    /**
 997     * Validate Maximum value of a number.
 998     *
 999     * @param string $value Value of data to be validated
1000     * @param int $max Maximum value
1001     * @param string $msg Custom error message
1002     * @return string
1003     */
1004    public function testMax($value, $max, $msg=null){
1005        if( $value > $max){
1006            if($msg!==null) return $msg;
1007            return "Value cannot be more than $max";
1008        }
1009    }
1010
1011    /**
1012     * Validate if a value is Between 2 values (inclusive)
1013     *
1014     * @param string $value Value of data to be validated
1015     * @param int $min Minimum value
1016     * @param int $max Maximum value
1017     * @param string $msg Custom error message
1018     * @return string
1019     */
1020    public function testBetweenInclusive($value, $min, $max, $msg=null){
1021        if( $value < $min || $value > $max ){
1022            if($msg!==null) return $msg;
1023            return "Value must be between $min and $max inclusively.";
1024        }
1025    }
1026
1027    /**
1028     * Validate if a value is Between 2 values
1029     *
1030     * @param string $value Value of data to be validated
1031     * @param int $min Minimum value
1032     * @param int $max Maximum value
1033     * @param string $msg Custom error message
1034     * @return string
1035     */
1036    public function testBetween($value, $min, $max, $msg=null){
1037        if( $value < $min+1 || $value > $max-1 ){
1038            if($msg!==null) return $msg;
1039            return "Value must be between $min and $max.";
1040        }
1041    }
1042
1043    /**
1044     * Validate if a value is greater than a number
1045     *
1046     * @param string $value Value of data to be validated
1047     * @param int $number Number to be compared
1048     * @param string $msg Custom error message
1049     * @return string
1050     */
1051    public function testGreaterThan($value, $number, $msg=null){
1052        if( !($value > $number)){
1053            if($msg!==null) return $msg;
1054            return "Value must be greater than $number.";
1055        }
1056    }
1057
1058    /**
1059     * Validate if a value is greater than or equal to a number
1060     *
1061     * @param string $value Value of data to be validated
1062     * @param int $number Number to be compared
1063     * @param string $msg Custom error message
1064     * @return string
1065     */
1066    public function testGreaterThanOrEqual($value, $number, $msg=null){
1067        if( !($value >= $number)){
1068            if($msg!==null) return $msg;
1069            return "Value must be greater than or equal to $number.";
1070        }
1071    }
1072
1073    /**
1074     * Validate if a value is less than a number
1075     *
1076     * @param string $value Value of data to be validated
1077     * @param int $number Number to be compared
1078     * @param string $msg Custom error message
1079     * @return string
1080     */
1081    public function testLessThan($value, $number, $msg=null){
1082        if( !($value < $number)){
1083            if($msg!==null) return $msg;
1084            return "Value must be less than $number.";
1085        }
1086    }
1087
1088    /**
1089     * Validate if a value is less than or equal to a number
1090     *
1091     * @param string $value Value of data to be validated
1092     * @param int $number Number to be compared
1093     * @param string $msg Custom error message
1094     * @return string
1095     */
1096    public function testLessThanOrEqual($value, $number, $msg=null){
1097        if( !($value <= $number)){
1098            if($msg!==null) return $msg;
1099            return "Value must be less than $number.";
1100        }
1101    }
1102
1103    /**
1104     * Validate if a value is equal to a number
1105     *
1106     * @param string $value Value of data to be validated
1107     * @param int $equalValue Number to be compared
1108     * @param string $msg Custom error message
1109     * @return string
1110     */
1111    public function testEqual($value, $equalValue, $msg=null){
1112        if(!($value==$equalValue && strlen($value)==strlen($equalValue))){
1113            if($msg!==null) return $msg;
1114            return 'Both values must be the same.';
1115        }
1116    }
1117
1118    /**
1119     * Validate if a value is Not equal to a number
1120     *
1121     * @param string $value Value of data to be validated
1122     * @param int $equalValue Number to be compared
1123     * @param string $msg Custom error message
1124     * @return string
1125     */
1126    public function testNotEqual($value, $equalValue, $msg=null){
1127        if( $value==$equalValue && strlen($value)==strlen($equalValue) ){
1128            if($msg!==null) return $msg;
1129            return 'Both values must be different.';
1130        }
1131    }
1132
1133   /**
1134    * Validate if value Exists in database
1135    *
1136    * @param string $value Value of data to be validated
1137    * @param string $table Name of the table in DB
1138    * @param string $field Name of field you want to check
1139    * @return string
1140    */
1141    public function testDbExist($value, $table, $field, $msg=null) {
1142        $result = Doo::db()->fetchRow("SELECT COUNT($field) AS count FROM " . $table . ' WHERE '.$field.' = ? LIMIT 1', array($value));
1143        if ((!isset($result['count'])) || ($result['count'] < 1)) {
1144            if($msg!==null) return $msg;
1145            return 'Value does not exist in database.';
1146        }
1147    }
1148
1149   /**
1150    * Validate if value does Not Exist in database
1151    *
1152    * @param string $value Value of data to be validated
1153    * @param string $table Name of the table in DB
1154    * @param string $field Name of field you want to check
1155    * @return string
1156    */
1157    public function testDbNotExist($value, $table, $field, $msg=null) {
1158        $result = Doo::db()->fetchRow("SELECT COUNT($field) AS count FROM " . $table . ' WHERE '.$field.' = ? LIMIT 1', array($value));
1159        if ((isset($result['count'])) && ($result['count'] > 0)) {
1160            if($msg!==null) return $msg;
1161            return 'Same value exists in database.';
1162        }
1163    }
1164
1165
1166
1167    /**
1168     * Validate if a value is in a list of values
1169     *
1170     * @param string $value Value of data to be validated
1171     * @param int $equalValue List of values to be checked
1172     * @param string $msg Custom error message
1173     * @return string
1174     */
1175    public function testInList($value, $valueList, $msg=null){
1176        if(!(in_array($value, $valueList))){
1177            if($msg!==null) return $msg;
1178            return 'Unmatched value.';
1179        }
1180    }
1181
1182    /**
1183     * Validate if a value is NOT in a list of values
1184     *
1185     * @param string $value Value of data to be validated
1186     * @param int $equalValue List of values to be checked
1187     * @param string $msg Custom error message
1188     * @return string
1189     */
1190    public function testNotInList($value, $valueList, $msg=null){
1191        if(in_array($value, $valueList)){
1192            if($msg!==null) return $msg;
1193            return 'Unmatched value.';
1194        }
1195    }
1196
1197	/**
1198	* Validate field if it is equal with some other field from $_GET or $_POST method
1199	* This method is used for validating form
1200	*
1201	* @param string $value Value of data to be validated
1202	* @param string $method Method (get or post), default $_POST
1203	* @param string $field Name of field that you want to check
1204	* @return string
1205	*/
1206
1207	public function testEqualAs($value, $method, $field, $msg=null) {
1208		if ($method == "get") {
1209		  $method = $_GET;
1210		} else if ($method == "post") {
1211		  $method = $_POST;
1212		} else {
1213		  $method = $_POST;
1214		}
1215		if (!isset($method[$field]) || $value != $method[$field]) {
1216		    if($msg!==null) return $msg;
1217            return 'Value '.$value.' is not equal with "'.$field.'".';
1218		}
1219	}
1220
1221}