4 private static $sort_mode_list = ['default' => 'score', 'newcome'];
6 public function __construct() {
7 $this->dbh = new PDO('sqlite:db/score.db');
8 $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
10 $this->set_sort_mode(filter_input(INPUT_GET, 'sort'));
16 * @param string $mode 設定する表示モード
17 * 'score' - スコア順に表示(デフォルト)
19 * 存在しない表示モードが指定された場合デフォルトの表示モードが設定される
21 public function set_sort_mode($mode)
23 if ($mode !== NULL && in_array($mode, self::$sort_mode_list)) {
24 $this->sort_mode = $mode;
26 $this->sort_mode = self::$sort_mode_list['default'];
34 * @return string スコア表示モード名
36 public function get_sort_mode_name()
38 switch ($this->sort_mode) {
50 * スコア検索の絞り込み用WHERE句を取得する
52 * @return string スコア検索絞り込み用のWHERE句
54 private function get_where()
56 $last_days = filter_input(INPUT_GET, 'last_days', FILTER_VALIDATE_INT);
58 $wheres[] = "date >= datetime('now', 'localtime', '-{$last_days} days')";
61 foreach (['race_id', 'class_id', 'personality_id'] as $id_str) {
62 $id = filter_input(INPUT_GET, $id_str, FILTER_VALIDATE_INT);
64 $wheres[] = "{$id_str} = {$id}";
68 $where = isset($wheres) ? 'WHERE '.implode(' AND ', $wheres) : '';
75 * スコアソート用のORDER BY句を取得する
77 * @return string スコアソート用のORDER BY句
79 private function get_order_by()
81 switch ($this->sort_mode) {
83 $order_by = "ORDER BY score DESC";
86 $order_by = 'ORDER BY score_id DESC';
97 * @param integer $offset スコア取得開始位置
98 * @param integer $limit スコア取得最大件数
99 * @param string $where スコア検索に用いるWHERE句
100 * @param string $order_by スコアソートに用いるORDER BY句
101 * @return string スコア検索用SQLクエリ
103 private function get_search_query($offset, $limit, $where, $order_by)
105 switch ($this->sort_mode) {
107 $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}";
110 $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}";
119 * 検索でヒットしたスコアの総件数を取得する
121 * @param string $where スコア検索に用いるWHERE句
122 * @return integer スコア総件数
124 private function get_query_data_count($where)
126 $stmt = $this->dbh->query("SELECT count(*) AS data_count from scores {$where}");
127 $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
129 return intval($res[0]['data_count']);
136 * @param integer $start_num 検索するスコアの開始位置
137 * @param integer $count 検索するスコア数
139 * @return array 検索したスコアの配列と、条件に合致するスコアの総件数の連想配列
141 public function search_score($start_num, $count)
143 $where = $this->get_where();
144 $order_by = $this->get_order_by();
145 $query_sql = $this->get_search_query(intval($start_num / $count) * $count, $count, $where, $order_by);
147 $search_start_time = microtime(true);
148 $this->dbh->beginTransaction();
150 $score_stmt = $this->dbh->query($query_sql);
151 $result['scores'] = $score_stmt->fetchAll(PDO::FETCH_ASSOC);
152 $result['total_data_count'] = $this->get_query_data_count($where);
154 $this->dbh->commit();
155 $result['elapsed_time'] = microtime(true) - $search_start_time;
160 public function get_db_handle() {
164 private function update_killers_cache_table()
167 $this->dbh->beginTransaction();
169 $this->dbh->exec("DROP TABLE IF EXISTS killers_cache");
170 $this->dbh->exec(<<<EOM
176 count(*) AS killer_count_total,
177 count(killed_status = 0 OR NULL) AS killer_count_normal,
178 count(killed_status != 0 OR NULL) AS killer_count_freeze
182 WHEN killer LIKE '麻痺状態で%' THEN substr(killer, 6)
183 WHEN killer LIKE '彫像状態で%' THEN substr(killer, 6)
184 WHEN killer = 'ripe' THEN '引退'
185 WHEN killer = 'Seppuku' THEN '切腹'
189 WHEN killer LIKE '麻痺状態で%' THEN 1
190 WHEN killer LIKE '彫像状態で%' THEN 2
199 killer_count_total DESC
202 $this->dbh->commit();
205 } catch (PDOException $e) {
206 $this->dbh->rollBack();
212 public function get_killers_table()
214 //$this->update_cache_status('killers_cache', 0);
215 if (!$this->get_cache_status('killers_cache')) {
216 $this->update_killers_cache_table();
217 $this->update_cache_status('killers_cache', 1);
220 $killers = $this->dbh->query("SELECT * FROM killers_cache ORDER BY killer_count_total DESC")->fetchAll(PDO::FETCH_ASSOC);
225 private function update_statistics_tables() {
226 $statistics_list = ['race', 'class', 'personality'];
229 foreach ($statistics_list as $stat) {
230 $table_name = $stat."_statistics";
231 $this->dbh->exec("DROP TABLE IF EXISTS ".$table_name);
232 $stmt = $this->dbh->query("CREATE TABLE ".$table_name." AS SELECT ".$stat."_id, count(sex=1 or NULL) AS male_count, count(sex=0 or NULL) AS female_count, count(*) AS total_count, count(winner=1 OR NULL) AS winner_count, avg(score) AS average_score, max(score) AS max_score from scores GROUP BY ".$stat."_id");
236 } catch (PDOException $e) {
241 public function get_statistics_tables() {
242 if (!$this->get_cache_status('statistics_cache')) {
243 $this->update_statistics_tables();
244 $this->update_cache_status('statistics_cache', 1);
249 $this->dbh->beginTransaction();
250 $stmt = $this->dbh->query("SELECT race_name AS name, * from race_statistics NATURAL INNER JOIN races ORDER BY total_count DESC");
251 $stat['race'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
252 $stmt = $this->dbh->query("SELECT class_name AS name, * from class_statistics NATURAL INNER JOIN classes ORDER BY total_count DESC");
253 $stat['class'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
254 $stmt = $this->dbh->query("SELECT personality_name AS name, * from personality_statistics NATURAL INNER JOIN personalities ORDER BY total_count DESC");
255 $stat['personality'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
257 $this->dbh->commit();
262 public function table_exists($table_name)
264 $stmt = $this->dbh->prepare("SELECT count(*) AS table_exists FROM sqlite_master WHERE type='table' AND name=?");
265 $stmt->execute([$table_name]);
266 $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
268 return intval($res[0]['table_exists']) === 1;
272 public function get_cache_status($cache_table_name)
274 if (!$this->table_exists('cache_status_table')) {
275 $this->create_cache_status_table();
278 $stmt = $this->dbh->prepare("SELECT cached FROM cache_status_table WHERE table_name=?");
279 $stmt->execute([$cache_table_name]);
280 $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
282 return count($res) === 1 ? intval($res[0]['cached']) : 0;
285 public function update_cache_status($cache_table_name, $status)
287 if (!$this->table_exists('cache_status_table')) {
288 $this->create_cache_status_table();
291 $stmt = $this->dbh->prepare("INSERT OR REPLACE INTO cache_status_table VALUES(?, ?)");
292 $stmt->execute([$cache_table_name, $status]);
294 return $stmt->rowCount();
297 public function create_cache_status_table()
299 $this->dbh->exec(<<<EOM
300 CREATE TABLE cache_status_table
301 (table_name TEXT PRIMARY KEY,
307 private static function killer_normalize($killer)
309 if ($killer === "ripe") {
311 } else if ($killer === "Seppuku") {
315 $n = str_replace("麻痺状態で", "", $killer);
316 $n = str_replace("彫像状態で", "", $n);