**/ class twIf_Validator extends twIf_Object_Access { // Validation Conditions const REQUIRE_VALUE = 1; const EMAIL_ADDRESS = 2; const GERMAN_POSTCODE = 4; const IS_NUMBER = 8; const IS_FLOAT = 16; // Dates and times const IS_DATE_TIMESTAMP = 32; const IS_DATE_STRING = 64; const IS_DATE_PAST18 = 128; const IS_DATE_FUTURE = 256; const CONVERT_MYSQL_TIME = 512; const CONVERT_MYSQL_DATE = 1024; const CONVERT_MYSQL_DATETIME = 2048; // Internal storage for validation-errors private $Errors = array (); // {{{ toTimestamp /** * Make sure that we have a timestamp here * * @param mixed $Value * * @access public * @return int **/ public static function toTimestamp ($Value) { // Check if this might already be a timestamp if (is_numeric ($Value)) return $Value; // Try to convert return strtotime ($Value); } // }}} // {{{ isUsername /** * Check wheter a given username is well formed * * @param string $Username * * @access public * @return bool */ public static function isUsername ($Username) { if (empty ($Username) || !is_string ($Username)) return false; // Allowed chars: a-z, A-Z, '-', '_' and '.' // Allowed length: 1 till 64 if (!preg_match ('/^([a-zA-Z0-9]|-|_|\.){1,64}$/', $Username)) return false; // Not allowed: '-', '_' and '.' at the beginning if (preg_match ('/(^-|^_|^\.)/', $Username)) return false; return true; } // }}} // {{{ isDomainname /** * Check wheter a domainname is well formed * * @param string $Domain * @param bool $checkNS (optional) * @param string $Type (optional) * @param bool $AllowWildcard (optional) Allow wildcard-records * * @access public * @return bool */ public static function isDomainname ($Domain, $checkNS = false, $Type = 'A', $AllowWildcard = false) { if (empty ($Domain) || !is_string ($Domain)) return false; // Is a mx record for this domain existing? if ($checkDNS && !checkdnsrr ($Domain, $Type)) return false; // Split domain $Blocks = explode ('.', $Domain); if (!is_array ($Blocks) || count ($Blocks) < 2) return false; if ($Blocks [0] == '*') { array_shift ($Blocks); if (!$AllowWildcard) return false; } $Blocks = array_reverse ($Blocks); // Check length according to registry setting $defaultLength = 3; $overrides = array ( 'am' => 2, 'cz' => 1, 'cm' => 2, 'de' => 1, 'gd' => 1, 'kr' => 2, 'la' => 2, 'lc' => 1, 'ms' => 1, 'pk' => 4, 'tc' => 1, 'tv' => 4, 'vc' => 2, 'vg' => 1, 'ws' => 4, ); if (isset ($overrides [$Blocks [0]])) $length = $overrides [$Blocks [0]]; else $length = $defaultLength; // Hack for me.com if (($Blocks [0] == 'com') && ($Blocks [1] == 'me')) $length = 2; if (strlen ($Blocks [1]) < $length) return false; // Check each domain block: $count = 0; foreach ($Blocks as $Block) { // Check top level domain (tld): if ($count == 0) { // Allowed chars: a-z and A-Z // Allowed length: 2 till 12 if (!preg_match ('/^[a-zA-Z]{2,12}$/', $Block)) return false; // Check other domains: } else { // Allowed chars: a-z, A-Z and '-' // Allowed length: 1 till 63 if (!preg_match ('/^([a-zA-Z0-9]|-|_){1,63}$/', $Block)) return false; // Not allowed: '-' at the beginning or at the end if (preg_match ('/(^-|-$)/', $Block)) return false; } $count++; } // A domain has a maximum length of 255 chars: if (strlen ($Domain) > 255) return false; return true; } // }}} // {{{ isEmailAddress /** * Check whetere a given mail-address is well formed * * @param string $Alias * @param bool $checkNS (optional) * @param bool $AllowCatchAll (optional) * * @access public * @return book */ public static function isEmailAddress ($Alias, $checkNS = false, $AllowCatchall = false) { if (empty ($Alias) || !is_string ($Alias)) return false; // Split this address $Parts = explode ('@', $Alias); if (!is_array ($Parts) || count ($Parts) != 2) return false; // Check local part if ($Parts [0] != '') { if (!self::isUsername ($Parts [0])) return false; } elseif (!$AllowCatchall) return false; // Check domain if (!self::isDomainname ($Parts [1], $checkNS, 'MX')) return false; return true; } // }}} // {{{ validateObject /** * Validate the fields on an object * * @param object $Handle * @param array $Fields * @param bool $Exceptions (optional) * * @access public * @return mixed Array with errors or true if no errors happened **/ public static function validateObject ($Handle, $Fields, $Exceptions = false) { $Errors = array (); $Updates = array (); foreach ($Fields as $Field=>$Validators) { // Make sure validators are an array if (!is_array ($Validators)) $Validators = array ($Validators); // Retrive the value $Definition = self::parseFieldDef ($Field, array ('Read' => $Field, 'Write' => $Field), $Handle, 0, false); $Value = self::getFieldValue ($Handle, $Definition); // Run all validators $converters = array (); foreach ($Validators as $k=>$Validator) { switch ($Validator) { case self::REQUIRE_VALUE: $newError = (empty ($Value) || (strlen (trim ($Value)) < 1)); break; case self::EMAIL_ADDRESS: $newError = !self::isEmailAddress ($Value); break; case self::GERMAN_POSTCODE: $newError = ((strlen ($Value) != 5) || !is_numeric ($Value)); break; case self::IS_NUMBER: $newError = !is_numeric ($Value); $converters [] = self::IS_NUMBER; break; case self::IS_FLOAT: $newError = !is_float ($Value); $converters [] = self::IS_FLOAT; break; case self::IS_DATE_TIMESTAMP: $newError = (self::toTimestamp ($Value) === false); $converters [] = self::IS_DATE_TIMESTAMP; break; case self::IS_DATE_PAST18: $newError = (self::toTimestamp ($Value) > time () - 553651200); break; case self::IS_DATE_FUTURE: $newError = (self::toTimestamp ($Value) <= time ()); break; // Handle all converters later on case self::CONVERT_MYSQL_TIME: case self::CONVERT_MYSQL_DATE: case self::CONVERT_MYSQL_DATETIME: $converters [] = $Validator; } if ($newError) { $Errors [] = array ($Field, $Validator, $Value); break; } } // Only convert if there are no errors if ((count ($Errors) == 0) && (count ($converters) > 0)) { foreach ($converters as $converter) switch ($converter) { case self::IS_NUMBER: $Value = intval ($Value); break; case self::IS_FLOAT: $Value = floatval ($Value); break; case self::IS_DATE_TIMESTAMP: $Value = self::toTimestamp ($Value); break; case self::CONVERT_MYSQL_TIME: $Value = date ('H:i:s', self::toTimestamp ($Value)); break; case self::CONVERT_MYSQL_DATE: $Value = date ('Y-m-d', self::toTimestamp ($Value)); break; case self::CONVERT_MYSQL_DATETIME: $Value = date ('Y-m-d H:i:s', self::toTimestamp ($Value)); break; } // Save the new value here $Updates [] = array ($Definition, $Value); } } if (count ($Errors) > 0) { if ($Exceptions) throw new twIf_Validator_Error (twIf_i18n::getText ('Validation failed'), $Errors, $Handle); return $Errors; } // Forward the changes to our object # REMARK: We don't save anything here, the developer has to commit() himself if (count ($Updates) > 0) foreach ($Updates as $Data) self::setFieldValue ($Handle, $Data [0], $Data [1]); return true; } // }}} } ?>