* @copyright Copyright (c) 2022, Robert Strutts. * @license https://mit-license.org/ */ namespace tts\traits\database; trait validation { /** * Validate current class members * @retval bool true valid, false failed tests */ public function validate_mysql(): bool { $tbl = (\bs_tts\common::is_string_found($this->table, "`")) ? $this->table : "`{$this->table}`"; foreach ($this->members as $field => $value) { if ($field == $this->primary_key) { continue; } $validation_field = $this->get_vaildation_member($field); if (isset($validation_field['native_type']) && isset($validation_field['len'])) { $type = strtoupper($validation_field['native_type']); $len = intval($validation_field['len']); $meta = $validation_field; } else { $meta = $this->get_MySQL_meta_field($field, $this->table); $type = (isset($meta['native_type']) ? $meta['native_type'] : ''); $len = $meta['len']; } switch ($type) { //This should be all uppercase input. case 'SHORT': //Small INT case 'INT24': //MED INT case 'LONGLONG': //BIG INT or SERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE. case 'LONG': // Integers if (!preg_match('/^[0-9]*$/', $value)) { $this->do_verr("Failed Validation: NOT a digit {$type} {$field}"); return false; } // Does not allow decimal numbers!! if (strlen($value) > $len) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } break; case 'FLOAT': if (strlen($value) > $len) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } if (!is_float($value)) { $this->do_verr("Failed Validation: NOT a float {$type} {$field}"); return false; } break; case 'NEWDECIMAL': $prec = intval($meta['precision']); $maxlen = $len - $prec; if (!\bs_tts\common::is_string_found($value, '.')) { $this->do_verr("Failed Validation: No Decimal Found in field {$field}"); return false; } $x = explode('.', $value); if (strlen($x[0]) >= ($maxlen - 1) || strlen($x[1]) > $prec) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } break; case 'DOUBLE': if (strlen($value) > $len) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } if (!is_double($value)) { $this->do_verr("Failed Validation: NOT a double {$type} {$field}"); return false; } break; case 'BLOB': // Text if ($len == '4294967295' || $len == '16777215') continue 2; //Too Big to process, 16777215 MEDIUMTEXT if (strlen($value) > $len) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } break; case 'VAR_STRING': // VARCHAR or VARBINARY case 'STRING': //CHAR or BINARY if (strlen($value) > $len) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } break; case 'DATE': if (!$this->is_valid_mysql_date($value)) { $this->do_verr("Failed Validation: invalid date in {$field}"); return false; } break; case 'TIME': if (!$this->is_valid_mysql_time($value)) { $this->do_verr("Failed Validation: invalid time in {$field}"); return false; } break; case 'TIMESTAMP': case 'DATETIME': if (strlen($value) > $len) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } if (!$this->is_valid_mysql_timestamp($value)) { $this->do_verr("Failed Validation: invalid timestamp in {$field}"); return false; } break; default: //TINYINT, Bit, Bool, or Year is the default for no meta data //if (!is_Digits($value)) return false; //This fails so its commented out. if ($len == 3) { // Tiny INT if (intval($value) > 255) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } if (intval($value) < -127) { $this->do_verr("Failed Validation: too short {$type} {$field}"); return false; } } elseif ($len == 1) { // Bit or Bool if (intval($value) > 9) { $this->do_verr("Failed Validation: too long {$type} {$field}"); return false; } if (intval($value) < 0) { $this->do_verr("Failed Validation: too short {$type} {$field}"); return false; } } break; } } return true; } public function get_MySQL_meta_field(string $field, string $table): array { $query = "SELECT `{$field}` FROM {$table} LIMIT 1"; $pdo_stmt = $this->pdo->prepare($query); $pdo_stmt->execute(); return $pdo_stmt->getColumnMeta(0); } /** * Check if valid timestamp/datetime * @param type $Str * @return type */ public function is_valid_mysql_timestamp(string $Str): bool { $Stamp = strtotime($Str); $Month = date('m', $Stamp); $Day = date('d', $Stamp); $Year = date('Y', $Stamp); return checkdate($Month, $Day, $Year); } public function is_valid_mysql_date(string $str): bool { $date_parts = explode('-', $str); if (\main_tts\common::count($date_parts) != 3) return false; if ((strlen($date_parts[0]) != 4) || (!is_numeric($date_parts[0]))) return false; if ((strlen($date_parts[1]) != 2) || (!is_numeric($date_parts[1]))) return false; if ((strlen($date_parts[2]) != 2) || (!is_numeric($date_parts[2]))) return false; if (!checkdate($date_parts[1], $date_parts[2], $date_parts[0])) return false; return true; } public function is_valid_mysql_time(string $str): bool { return (strtotime($str) === false) ? false : true; } /** * Helper function for validate mysql * Will echo if not live error message * If enabled will also log the error. * @param string $msg error message */ private function do_verr(string $msg): void { $this->error_message = $msg; $exists = \main_tts\registry::get('di')->exists('log'); if ($exists && \main_tts\configure::get('database', 'log_validation_errors') === true) { $log = \main_tts\registry::get('di')->get_service('log', ['validation_errors']); $log->write($msg); } } }