<?php
class ScoreDB
{
+ private static $sort_mode_list = ['default' => 'score', 'newcome'];
+
public function __construct() {
$this->dbh = new PDO('sqlite:db/score.db');
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+ $this->set_sort_mode(filter_input(INPUT_GET, 'sort'));
+ }
+
+ /**
+ * スコア表示モードを設定する
+ *
+ * @param string $mode 設定する表示モード
+ * 'score' - スコア順に表示(デフォルト)
+ * 'newcome' - 新着順に表示
+ * 存在しない表示モードが指定された場合デフォルトの表示モードが設定される
+ */
+ public function set_sort_mode($mode)
+ {
+ if ($mode !== NULL && in_array($mode, self::$sort_mode_list)) {
+ $this->sort_mode = $mode;
+ } else {
+ $this->sort_mode = self::$sort_mode_list['default'];
+ }
+ }
+
+
+ /**
+ * スコア表示モード名を取得する
+ *
+ * @return string スコア表示モード名
+ */
+ public function get_sort_mode_name()
+ {
+ switch ($this->sort_mode) {
+ case 'score':
+ return "スコア順";
+ case 'newcome':
+ return "新着順";
+ default:
+ return "不明";
+ }
+ }
+
+
+ /**
+ * スコア検索の絞り込み用WHERE句を取得する
+ *
+ * @return string スコア検索絞り込み用のWHERE句
+ */
+ private function get_where()
+ {
+ $last_days = filter_input(INPUT_GET, 'last_days', FILTER_VALIDATE_INT);
+ if ($last_days > 0) {
+ $wheres[] = "date >= datetime('now', 'localtime', '-{$last_days} days')";
+ }
+
+ foreach (['race_id', 'class_id', 'personality_id'] as $id_str) {
+ $id = filter_input(INPUT_GET, $id_str, FILTER_VALIDATE_INT);
+ if ($id > 0) {
+ $wheres[] = "{$id_str} = {$id}";
+ }
+ }
+
+ $where = isset($wheres) ? 'WHERE '.implode(' AND ', $wheres) : '';
+
+ return $where;
+ }
+
+
+ /**
+ * スコアソート用のORDER BY句を取得する
+ *
+ * @return string スコアソート用のORDER BY句
+ */
+ private function get_order_by()
+ {
+ switch ($this->sort_mode) {
+ case "score":
+ $order_by = "ORDER BY score DESC";
+ break;
+ case "newcome":
+ $order_by = 'ORDER BY score_id DESC';
+ break;
+ }
+
+ return $order_by;
+ }
+
+
+ /**
+ * スコア検索用のSQLクエリを取得する
+ *
+ * @param integer $offset スコア取得開始位置
+ * @param integer $limit スコア取得最大件数
+ * @param string $where スコア検索に用いるWHERE句
+ * @param string $order_by スコアソートに用いるORDER BY句
+ * @return string スコア検索用SQLクエリ
+ */
+ private function get_search_query($offset, $limit, $where, $order_by)
+ {
+ switch ($this->sort_mode) {
+ case "score":
+ $query = "SELECT *, group_concat(realm_name) AS realms_name from (select * from scores ${where} {$order_by} LIMIT ${offset}, {$limit}) NATURAL JOIN races NATURAL JOIN classes NATURAL JOIN personalities NATURAL LEFT JOIN score_realms NATURAL LEFT JOIN realms GROUP BY score_id {$order_by}";
+ break;
+ case "newcome":
+ $query = "SELECT *, group_concat(realm_name) AS realms_name from (select * from scores ${where} {$order_by} LIMIT ${offset}, {$limit}) NATURAL JOIN races NATURAL JOIN classes NATURAL JOIN personalities NATURAL LEFT JOIN score_realms NATURAL LEFT JOIN realms GROUP BY score_id {$order_by}";
+ break;
+ }
+
+ return $query;
+ }
+
+
+ /**
+ * 検索でヒットしたスコアの総件数を取得する
+ *
+ * @param string $where スコア検索に用いるWHERE句
+ * @return integer スコア総件数
+ */
+ private function get_query_data_count($where)
+ {
+ $stmt = $this->dbh->query("SELECT count(*) AS data_count from scores {$where}");
+ $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ return intval($res[0]['data_count']);
+ }
+
+
+ /**
+ * スコアを検索する
+ *
+ * @param integer $start_num 検索するスコアの開始位置
+ * @param integer $count 検索するスコア数
+ *
+ * @return array 検索したスコアの配列と、条件に合致するスコアの総件数の連想配列
+ */
+ public function search_score($start_num, $count)
+ {
+ $where = $this->get_where();
+ $order_by = $this->get_order_by();
+ $query_sql = $this->get_search_query(intval($start_num / $count) * $count, $count, $where, $order_by);
+
+ $search_start_time = microtime(true);
+ $this->dbh->beginTransaction();
+
+ $score_stmt = $this->dbh->query($query_sql);
+ $result['scores'] = $score_stmt->fetchAll(PDO::FETCH_ASSOC);
+ $result['total_data_count'] = $this->get_query_data_count($where);
+
+ $this->dbh->commit();
+ $result['elapsed_time'] = microtime(true) - $search_start_time;
+
+ return $result;
}
public function get_db_handle() {
--- /dev/null
+<?php
+//ini_set('display_errors', 'On');
+
+ini_set('log_errors', 'On');
+ini_set('error_log', 'errors/'.pathinfo(__FILE__, PATHINFO_FILENAME).'.log');
+
+include "db_common.inc";
+include "dump_file.inc";
+
+ini_set('zlib.output_compression', 'On');
+
+function get_death_reason($killer)
+{
+ switch ($killer) {
+ case "ripe":
+ return "勝利の後引退";
+ case "Seppuku":
+ return "勝利の後切腹";
+ default:
+ return $killer."に殺された";
+ }
+}
+
+
+/**
+ * ページ情報を計算する
+ *
+ * @param integer $total_data_count 全データ件数
+ * @param integer $start_num GETパラメータで渡された開始データ番号
+ * @param integer $data_count_per_page 1ページあたりのデータ件数
+ *
+ * @return array 計算したページ情報を保持する連想配列
+ */
+function calc_page_info($total_data_count, $start_num, $data_count_per_page)
+{
+ $current_page = intval($start_num / $data_count_per_page);
+ $last_page = intval(($total_data_count - 1) / $data_count_per_page);
+ $navi_page_range_start = ($current_page - 5) > 0 ? $current_page - 5: 0;
+ $navi_page_range_count = max(0, min(9, $last_page - $navi_page_range_start));
+ $navi_page_list = range($navi_page_range_start, $navi_page_range_start + $navi_page_range_count);
+
+ $pageinfo['current'] = $current_page;
+ $pageinfo['last'] = $last_page;
+ $pageinfo['navi_list'] = $navi_page_list;
+
+ $pageinfo['total_data_count'] = $total_data_count;
+ $pageinfo['data_count_per_page'] = $data_count_per_page;
+
+ return $pageinfo;
+}
+
+
+/**
+ * ページナビゲーションテーブルを表示する
+ *
+ * @param array $pageinfo calc_page_info()関数で取得したページ情報を保持する連想配列
+ */
+function print_navi_page_table($pageinfo)
+{
+ if (count($pageinfo['navi_list']) <= 1) return;
+
+ $href_base = preg_replace('/(&?start=\w+)/', '', filter_input(INPUT_SERVER, 'REQUEST_URI'));
+ if (strpos($href_base, "?") === FALSE) {
+ $href_base .= "?";
+ }
+
+ echo "<table align='center'>\n"
+ ."<tr>";
+
+ if ($pageinfo['current'] > 0) {
+ $href = $href_base . "&start=". ($pageinfo['current'] - 1) * $pageinfo['data_count_per_page'];
+ echo "<td><a href={$href}>< 前へ</a></td>";
+ }
+
+ foreach ($pageinfo['navi_list'] as $page) {
+ $page_num = $page + 1;
+ $href = $href_base . "&start=". $page * $pageinfo['data_count_per_page'];
+ if ($page === $pageinfo['current']) {
+ echo "<td>$page_num</td>";
+ } else {
+ echo "<td><a href={$href}>$page_num</a></td>";
+ }
+ }
+
+ if ($pageinfo['current'] < $pageinfo['last']) {
+ $href = $href_base . "&start=". ($pageinfo['current'] + 1) * $pageinfo['data_count_per_page'];
+ echo "<td><a href={$href}>次へ ></a></td>";
+ }
+
+ echo "</tr>\n"
+ ."</table>\n";
+}
+
+
+/**
+ * スコアランキングテーブルを表示する
+ *
+ * @param array $scores スコア
+ * @param integer $rank_start 順位の開始番号(0オリジン)
+ */
+function print_score_table($scores, $rank_start)
+{
+ echo <<<EOM
+<table align='center' border=1>
+<tr>
+<th>順位</th>
+<th>スコア</th>
+<th>日付</th>
+<th>名前</th>
+<th>種族</th>
+<th>職業</th>
+<th>性別</th>
+<th>死因</th>
+</tr>
+
+EOM;
+
+ foreach($scores as $idx => $score) {
+ //$rank = $pageinfo['current'] * $pageinfo['data_count_per_page'] + $idx + 1;
+ $rank = $rank_start + $idx + 1;
+ $score_id = $score['score_id'];
+ $date = substr($score['date'], 0, 10);
+ $sex_str = $score['sex'] ? "男" : "女";
+ $death_reason = get_death_reason($score['killer']);
+ $depth = !$score['winner'] ? $score['depth']."階, " : "";
+ $realms = isset($score['realms_name']) ? "(".$score['realms_name'].")" : "";
+ $dumpfile = new DumpFile($score_id);
+
+ echo "<tr>\n";
+ if ($dumpfile->exists('dumps', 'txt')) {
+ echo "<td><a href=\"show_dump.php?score_id={$score_id}\">{$rank}</a></td>\n";
+ } else {
+ echo "<td>{$rank}</td>\n";
+ }
+ echo <<<EOM
+<td align="right">{$score['score']}</td>
+<td><nobr>$date</nobr></td>
+<td>{$score['personality_name']}{$score['name']}</td>
+<td>{$score['race_name']}</td>
+<td>{$score['class_name']}$realms</td>
+<td>$sex_str</td>
+
+EOM;
+ if ($dumpfile->exists('screens', 'html')) {
+ echo "<td><a href=\"show_screen.php?score_id={$score_id}\">{$death_reason}</a>";
+ } else {
+ echo "<td>{$death_reason}";
+ }
+ echo "<br>({$depth}{$score['version']})</td>\n";
+ echo "</tr>\n";
+ }
+ echo "</table>\n";
+}
+
+$db = new ScoreDB();
+
+$start_num = filter_input(INPUT_GET, 'start', FILTER_VALIDATE_INT) ?: 0;
+$search_result = $db->search_score($start_num, 50);
+
+$pageinfo = calc_page_info($search_result['total_data_count'], $start_num, 50);
+?>
+
+<!DOCTYPE html>
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
+<title>変愚蛮怒 スコア ランキング</title>
+<link rel="alternate" title="変愚蛮怒 新着スコア" href="html/newcome-rss.xml" type="application/rss+xml">
+</head>
+
+<body>
+<h1>変愚蛮怒 歴代スコア (<?php echo $db->get_sort_mode_name(); ?>)</h1>
+<!--3日以内のスコアは<font color=red>赤</font>、10日以内のスコアは<font color=blue>青</font>で表示されます。<br>-->
+<!--10日以内のスコアは<strong>強調表示</strong>されます。-->
+<!--<br><a href ="html/newcome-rss.xml">新着チェック用RSS</a><small>…スコア受信時に自動生成します。URLをRSSリーダー等に登録すると新着スコアをチェックできます。</small>-->
+<small>
+<?php
+echo sprintf("件数 %d 件 (%.2f 秒)", $search_result['total_data_count'], $search_result['elapsed_time']);
+?>
+</small>
+
+<hr>
+
+<?php
+print_navi_page_table($pageinfo);
+print_score_table($search_result['scores'], $pageinfo['current'] * $pageinfo['data_count_per_page']);
+print_navi_page_table($pageinfo);
+?>