"ALTER TABLE `visitor_logs` ADD COLUMN `user_agent` TEXT NULL COMMENT 'User-Agent 完整值' AFTER `language`", 'http_referer' => "ALTER TABLE `visitor_logs` ADD COLUMN `http_referer` TEXT NULL COMMENT 'HTTP Referer 完整值' AFTER `user_agent`", 'accept_language_raw' => "ALTER TABLE `visitor_logs` ADD COLUMN `accept_language_raw` TEXT NULL COMMENT 'Accept-Language 完整值' AFTER `http_referer`", 'judge_timing' => "ALTER TABLE `visitor_logs` ADD COLUMN `judge_timing` TEXT NULL COMMENT '判定用时 JSON' AFTER `accept_language_raw`", 'fp_hdata' => "ALTER TABLE `visitor_logs` ADD COLUMN `fp_hdata` TEXT NULL COMMENT '指纹 hdata 解码 JSON' AFTER `judge_timing`", ]; foreach ($columns as $name => $ddl) { if (!self::columnExists($conn, 'visitor_logs', $name)) { @$conn->query($ddl); } } self::$ensured = true; } /** * @param mysqli $conn */ public static function ensureIndexes($conn) { if (self::$indexesEnsured) { return; } $indexes = [ 'idx_visit_date' => 'ALTER TABLE `visitor_logs` ADD INDEX `idx_visit_date` (`visit_date`)', 'idx_result' => 'ALTER TABLE `visitor_logs` ADD INDEX `idx_result` (`result`)', 'idx_domain_date' => 'ALTER TABLE `visitor_logs` ADD INDEX `idx_domain_date` (`domain`, `visit_date`)', 'idx_country' => 'ALTER TABLE `visitor_logs` ADD INDEX `idx_country` (`country`)', ]; foreach ($indexes as $name => $ddl) { if (!self::indexExists($conn, 'visitor_logs', $name)) { @$conn->query($ddl); } } self::$indexesEnsured = true; } /** * @param mysqli $conn * @param string $table * @param string $indexName */ private static function indexExists($conn, $table, $indexName) { $tableEsc = cloak_db_escape($conn, $table); $indexEsc = cloak_db_escape($conn, $indexName); $sql = "SHOW INDEX FROM `{$tableEsc}` WHERE Key_name = '{$indexEsc}'"; if ($res = $conn->query($sql)) { $exists = $res->num_rows > 0; $res->free(); return $exists; } return false; } /** * @param mysqli $conn * @param string $table * @param string $column */ private static function columnExists($conn, $table, $column) { $tableEsc = cloak_db_escape($conn, $table); $columnEsc = cloak_db_escape($conn, $column); $sql = "SHOW COLUMNS FROM `{$tableEsc}` LIKE '{$columnEsc}'"; if ($res = $conn->query($sql)) { $exists = $res->num_rows > 0; $res->free(); return $exists; } return false; } }