From 18e1427ed3d0cd70083d979a4b883a4ec2007bb4 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Mon, 2 Dec 2024 21:33:47 -0600 Subject: [PATCH] refactor database access --- public/assets/css/forms.css | 13 +++ public/assets/css/game.css | 35 ++++---- public/assets/img/icons/earth.png | Bin 5148 -> 0 bytes public/assets/img/icons/home.png | Bin 4628 -> 0 bytes public/assets/img/icons/map.png | Bin 4976 -> 0 bytes public/assets/img/icons/settings.png | Bin 5126 -> 0 bytes public/assets/img/icons/shop.png | Bin 4642 -> 0 bytes public/assets/img/icons/user1.png | Bin 3868 -> 0 bytes public/assets/img/icons/world.png | Bin 6794 -> 0 bytes public/index.php | 41 ++++----- src/bootstrap.php | 24 ++---- src/database.php | 81 ++++++++++++++++++ src/helpers.php | 99 ++++++++++++++++++++-- src/models/character.php | 102 +++------------------- src/models/session.php | 84 +++++++++---------- src/models/token.php | 6 +- src/models/user.php | 17 ++-- src/models/wallet.php | 9 +- src/{util => }/router.php | 65 ++++++++++++++ src/util/auth.php | 9 +- src/util/database.php | 121 --------------------------- src/util/env.php | 47 ----------- src/util/render.php | 20 ----- templates/layouts/game.php | 71 +++++++--------- templates/pages/hello.php | 4 + 25 files changed, 401 insertions(+), 447 deletions(-) delete mode 100644 public/assets/img/icons/earth.png delete mode 100644 public/assets/img/icons/home.png delete mode 100644 public/assets/img/icons/map.png delete mode 100644 public/assets/img/icons/settings.png delete mode 100644 public/assets/img/icons/shop.png delete mode 100644 public/assets/img/icons/user1.png delete mode 100644 public/assets/img/icons/world.png create mode 100644 src/database.php rename src/{util => }/router.php (65%) delete mode 100644 src/util/database.php delete mode 100644 src/util/env.php delete mode 100644 src/util/render.php create mode 100644 templates/pages/hello.php diff --git a/public/assets/css/forms.css b/public/assets/css/forms.css index 23d733d..7f10d71 100644 --- a/public/assets/css/forms.css +++ b/public/assets/css/forms.css @@ -126,3 +126,16 @@ .character-select:not(:has(input[type="radio"]:checked)) > .buttons { display: none; } + +form.logout-form { + display: inline-block; + + & > button { + display: inline-block; + appearance: none; + background: none; + outline: none; + border: none; + cursor: pointer; + } +} diff --git a/public/assets/css/game.css b/public/assets/css/game.css index 9266295..b530515 100644 --- a/public/assets/css/game.css +++ b/public/assets/css/game.css @@ -1,15 +1,6 @@ -@import 'src/buttons.css'; - -:root { - font-size: 16px; - --main-font: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} +@import 'utilities.css'; +@import 'buttons.css'; +@import 'forms.css'; body { background-color: #bcc6cf; @@ -18,16 +9,13 @@ body { background-position: center top; background-repeat: no-repeat; font-family: var(--main-font); -} - -main#game-container { width: 100vw; height: 100vh; overflow: hidden; position: relative; } -div#game-ui, div#game-windows { +div#ui, div#windows { position: absolute; top: 0; left: 0; @@ -37,7 +25,18 @@ div#game-ui, div#game-windows { height: 100%; } -div#game-windows { +div#ui { + section#menu { + display: flex; + gap: 0.5rem; + + & > a { + cursor: pointer; + } + } +} + +div#windows { display: flex; align-items: center; justify-content: center; @@ -99,7 +98,7 @@ div#game-windows { } } -canvas#game-canvas { +canvas#canvas { width: 100%; height: 100%; position: absolute; diff --git a/public/assets/img/icons/earth.png b/public/assets/img/icons/earth.png deleted file mode 100644 index ac09f832ad70d4ad761fcbc9807158361dc87bbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5148 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 diff --git a/public/assets/img/icons/settings.png b/public/assets/img/icons/settings.png deleted file mode 100644 index 83ce6c1863302fb5c8bef5f7d109f1c09ea5638a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5126 zcmV+h6#46kP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000RwNkl+XN4UcT0U9@PM zCMDWbwJFxNL>-lub#a?6Sx3`Mn^e(FY11}pNTR0TMx~@_!ppo0A&?jx@DnHo8(;jo z`(rj@jHK=MM~`%*`#9%!zH{z5_k0A$arh>M#5XL|Mh|s5T?>G%0Bit01Mse1ukYW; zwzakO{{ld4^a*r2T>^j)9((LD4NcP+8XAJdVtEsQPOsP7B6i1)9oy^k`L?hun<*3u zeZ?xVE;|^*;j$gP;L?Tvk@5(Q36Y7!3IE!w*OOe*gDRojPR)fXT_p zA%Y+nzuzA>GBU!Sn79YC*$g1$Q>j#M#KpxOYiMY2KCI6k2%xaA5M^a$$pDN;j~2kS#&T)j;?wCb;dmG$K3$qNv>}zUj`WOH< zibXysH~<(71}A_w`}+D23CmSxlG>gog=WiJ+stHfe)^Zfig_1tsMB1M`Ex5tZ3X{mVW zWHUxbM2z&1H8tfWB_*)gY;ZUn$j{G5cXv0eRxA4Z`_bLq z4YgVgtJMmt)q0nvY0Z1@y*D2To|u@Z&B(}jLo61jWMpKZtE&rAsT3zq{s~PlyokGZ z@8Da?%@9jsF+VrE5(2u$pnkVg5lv| z+`M@cb#-;9t*u3Nb~Zd7Pr}^XT%B62K0h=x42yjK78dp#Ja{nf@ZrNSo6R_W{J588+4_!-j=uu%06xB$Ig51~*9tyYWX=4K2J597#@BVLwe zf7IF8c?Cddt$%fO^=tBstV1@N9h0W<8}ae+fh=|R$x_Kd$N|OnK zxU5tvX^BJvo6QEZ*$jz90)ikQlgW^gk-@un@7{~?@$s=vr}Oy-92@*nLb6_@QYj7K zvQnwck;~HXtJ}jVX;_XGMOL{2;lX4L%VkEBK3MbrlzLgbUGoCNRXG8 zM@&yo|K!OhpZvB=CVK@ym%(6|_}ZLMDwQSxXj3Q@rMbDeuv)F~`FvPfT7uK*Y|qWj z{ocaD!tC2`zYT>#0UnPBpU;P2Fo>+IER>g*Z&j<+|4c|o7%wd?y}Dz^j+9Ov+poCY?ii_53X{o%ZQHh? zva%AvU=S-SD_~g`*RNmS`O!xob!s%4EdX3!R)Jjrx^r@JvXn|CtX3;rE*Cr=4;&81 z8L!t{1i;5}9Nu~7oljVn{Z&UtM_^=R1S*vZ6%`d|Yiq;KojXxpUXHV8&w{3D$Ye6P z-|s*5AC!z?7})K0006@<=>SeK3{xYQ%aNFv2&>hKiHQja1Ok{$rcYRwEd$_RPqFsw z*>hYbll}J4p+k|G$MW*>T8@dRs3;5$4mw&|TA1|}Ad-@j8aR%-os*MOqtR#(8ykz6 znHl)~ek?65!EU#|8wtLaimjpNNBMldlcFeec6MTUc{!3b($dm!_wHSgB#A&EkhD<` zBn5-PmsBbhotl~oo6QD~#{;w342q(#xVUH!1OkU5nLk9jCnhGUBoYY(0s&G|Qb3ab z&T-SGO$Y=62!%o`|1||X9*^QU&M`ebjggTNI2;bR-EJ%{F2ZWHdP$P}K_C#A-6-?5 z!m{jzo}M0%B#DNG28cu=5Cnn3!a~U9a#$=D5Ck!`VGl${N5@bU^{GfC$`^~pAP538 zGczDb^4Bhx>%@a-^?EsQ;J~MeiHY0d42OcKfP704gghOI%P; z@E4Bbo}eg-^!xo_Sr#NodPO49;q&LuzZLNZz!3n10HVVhLl8tpKRnGsQ|f7W5XzY0JyM* o*6W%ItoJ9@{bM%t2f+Pn0J_U5=6uU7ssI2007*qoM6N<$f-Pl~0RR91 diff --git a/public/assets/img/icons/shop.png b/public/assets/img/icons/shop.png deleted file mode 100644 index 865433ea5b2df04af43b2b41171cba9d29b69ba3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4642 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 s^;ZAadB3wcKFbaEXFPsgum2Zd0NQWuG-get('/', function() { - if (!user()) redirect('/login'); - redirect('/world'); + if (user()) redirect('/world'); + echo render('layouts/basic', ['view' => 'pages/hello']); }); /* @@ -78,7 +79,7 @@ $r->post('/register', function() { // If there are errors at this point, send them to the page with errors flashed. if (!empty($errors)) { $GLOBALS['form-errors'] = $errors; - echo page('auth/register'); + echo render('layouts/basic', ['view' => 'pages/auth/register']); exit; } @@ -124,20 +125,14 @@ $r->post('/login', function() { $_SESSION['user'] = serialize($user); if ($_POST['remember'] ?? false) { - $token = token(); - $expires = strtotime('+30 days'); - $result = db_query( - db_auth(), - "INSERT INTO sessions (token, user_id, expires) VALUES (:t, :u, :e)", - [':t' => $token, ':u' => user()->id, ':e' => $expires] - ); - if (!$result) error_response(400); - set_cookie('remember_me', $token, $expires); + $session = Session::create($user->id, strtotime('+30 days')); + if ($session === false) error_response(400); + set_cookie('remember_me', $session->token, $session->expires); } - if (user()->char_count() === 0) { + if ($user->char_count() === 0) { redirect('/character/create-first'); - } elseif (!change_user_character(user()->char_id)) { + } elseif (!change_user_character($user->char_id)) { echo "failed to change user character (aclp)"; error_response(999); } @@ -147,14 +142,14 @@ $r->post('/login', function() { $r->post('/logout', function() { csrf_ensure(); - session_delete(user()->id); + Session::delete(user()->id); unset($_SESSION['user']); set_cookie('remember_me', '', 1); redirect('/'); }); $r->get('/debug/logout', function() { - session_delete(user()->id); + Session::delete(user()->id); unset($_SESSION['user']); set_cookie('remember_me', '', 1); redirect('/'); @@ -167,7 +162,7 @@ $r->get('/characters', function() { auth_only_and_must_have_character(); $GLOBALS['active_nav_tab'] = 'chars'; - echo page('chars/list', ['chars' => user()->char_list()]); + //echo page('chars/list', ['chars' => user()->char_list()]); }); $r->post('/characters', function() { @@ -210,7 +205,7 @@ $r->post('/characters', function() { if ($action === 'delete') { if (!Character::belongs_to($char_id, user()->id)) error_response(999); - echo page('chars/delete', ['char' => Character::find($char_id)]); + //echo page('chars/delete', ['char' => Character::find($char_id)]); exit; } @@ -225,7 +220,7 @@ $r->get('/character/create-first', function() { // If the user already has a character, redirect them to the main page. if (user()->char_count() > 0) redirect('/'); - echo page('chars/first'); + //echo page('chars/first'); }); $r->post('/character/create', function() { @@ -257,11 +252,11 @@ $r->post('/character/create', function() { if (isset($_POST['first']) && $_POST['first'] === 'true') { // If this is the first character, return to the first character creation page. - echo page('chars/first'); + //echo page('chars/first'); exit; } else { // If this is not the first character, return to the character list page. - echo page('chars/list', ['chars' => user()->char_list()]); + //echo page('chars/list', ['chars' => user()->char_list()]); exit; } } @@ -357,7 +352,7 @@ $r->post('/move', function() { error_response(999); } - $r = db_query(db_live(), 'UPDATE char_locations SET x = :x, y = :y WHERE char_id = :c', [ + $r = live_db()->query('UPDATE char_locations SET x = :x, y = :y WHERE char_id = :c', [ ':x' => $x, ':y' => $y, ':c' => user()->char_id @@ -371,7 +366,7 @@ $r->post('/move', function() { /* UI */ -$r->post('/ui/stats', function() { +$r->get('/ui/stats', function() { ui_guard(); echo c_profile_stats(char()); }); diff --git a/src/bootstrap.php b/src/bootstrap.php index 70bd483..e6c1ed2 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -5,24 +5,19 @@ session_start(); // SRC is defined as the path to the src/ directory from public/ define('CLASS_MAP', [ - 'User' => '/models/user.php', + 'Database' => '/database.php', + 'Router' => '/router.php', + 'User' => '/models/user.php', 'Character' => '/models/character.php', - 'Wallet' => '/models/wallet.php' + 'Wallet' => '/models/wallet.php', + 'Session' => '/models/session.php', ]); require_once SRC . '/helpers.php'; - -stopwatch_start('bootstrap'); // Start the bootstrap stopwatch - -require_once SRC . '/util/env.php'; -require_once SRC . '/util/database.php'; require_once SRC . '/util/auth.php'; -require_once SRC . '/util/router.php'; require_once SRC . '/util/components.php'; -require_once SRC . '/util/render.php'; require_once SRC . '/util/enums.php'; -require_once SRC . '/models/session.php'; require_once SRC . '/models/token.php'; spl_autoload_register(function (string $class) { @@ -43,14 +38,11 @@ if (env('debug') === 'true') { error_reporting(E_ALL); } +// Create an array in GLOBALS to hold database connections. +$GLOBALS['databases'] = []; + // Generate a new CSRF token. (if one doesn't exist, that is) csrf(); -// Have global counters for queries -$GLOBALS['queries'] = 0; -$GLOBALS['query_time'] = 0; - // Run auth_check to see if we're logged in, since it populates the user data in SESSION auth_check(); - -stopwatch_stop('bootstrap'); // Stop the bootstrap stopwatch diff --git a/src/database.php b/src/database.php new file mode 100644 index 0000000..05078de --- /dev/null +++ b/src/database.php @@ -0,0 +1,81 @@ +exec('PRAGMA cache_size = 32000'); + // Enable WAL mode + $db->exec('PRAGMA journal_mode = WAL'); + // Move temp store to memory + $db->exec('PRAGMA temp_store = MEMORY'); + + $this->db = $db; + } + + public function query(string $query, array $params = []): SQLite3Result|false + { + $p = strpos($query, '?') !== false; // generic placeholders? + $stmt = $this->db->prepare($query); + if (!empty($params)) { + foreach ($params as $k => $v) $stmt->bindValue($p ? $k + 1 : $k, $v, $this->getSQLiteType($v)); + } + $start = microtime(true); + $r = $stmt->execute(); + $this->log($query, microtime(true) - $start); + return $r; + } + + public function exec(string $query): bool + { + $start = microtime(true); + $r = $this->db->exec($query); + $this->log($query, microtime(true) - $start); + return $r; + } + + public function exists(string $table, string $column, mixed $value, bool $case_insensitive = true): bool + { + if ($case_insensitive) { + $query = "SELECT 1 FROM $table WHERE $column = :v COLLATE NOCASE LIMIT 1"; + } else { + $query = "SELECT 1 FROM $table WHERE $column = :v LIMIT 1"; + } + + $result = $this->query($query, [':v' => $value]); + return $result->fetchArray(SQLITE3_NUM) !== false; + } + + private function log(string $query, float $time_taken): void + { + $this->count++; + $this->query_time += $time_taken; + if (env('debug', false)) $this->log[] = [$query, $time_taken]; + } + + private function getSQLiteType(mixed $value): int + { + return match (true) { + is_int($value) => SQLITE3_INTEGER, + is_float($value) => SQLITE3_FLOAT, + is_null($value) => SQLITE3_NULL, + default => SQLITE3_TEXT + }; + } + + public function lastInsertRowID(): int + { + return $this->db->lastInsertRowID(); + } +} diff --git a/src/helpers.php b/src/helpers.php index 1be12eb..69e6332 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -1,5 +1,86 @@ true, + $v === 'false' => false, + is_numeric($v) => (int) $v, + is_float($v) => (float) $v, + default => $v + }; +} + +/** + * Return the path to a view file. + */ +function template(string $name): string +{ + return SRC . "/../templates/$name.php"; +} + +/** + * Render a view with the given data. Looks for `$view` through `template()`. + */ +function render(string $path_to_base_view, array $data = []): string|false +{ + ob_start(); + extract($data); + require template($path_to_base_view); + return ob_get_clean(); +} + +/** + * Get the auth database connection from GLOBALS['databases'], or create it if it doesn't exist. + */ +function auth_db(): Database +{ + return $GLOBALS['databases']['auth'] ??= new Database(DATABASE_PATH . '/auth.db'); +} + +/** + * Get the live database connection from GLOBALS['databases'], or create it if it doesn't exist. + */ +function live_db(): Database +{ + return $GLOBALS['databases']['live'] ??= new Database(DATABASE_PATH . '/live.db'); +} + /** * Generate a pretty dope token. */ @@ -75,7 +156,8 @@ function csrf_field() */ function csrf_ensure() { - if (!csrf_verify($_POST['csrf'] ?? '')) error_response(418); + $csrf = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? ''; + if (!csrf_verify($csrf)) error_response(418); } /** @@ -138,7 +220,7 @@ function change_user_character(int $char_id): bool // If the character ID is different, update the session and database if (user()->char_id !== $char_id) { modify_user_session('char_id', $char_id); - db_query(db_auth(), "UPDATE users SET char_id = :c WHERE id = :u", [':c' => $char_id, ':u' => user()->id]); + auth_db()->query("UPDATE users SET char_id = :c WHERE id = :u", [':c' => $char_id, ':u' => user()->id]); } return true; @@ -175,8 +257,7 @@ function wallet(): Wallet|false function location($field = '') { if (empty($GLOBALS['location'])) { - $GLOBALS['location'] = db_query( - db_live(), + $GLOBALS['location'] = live_db()->query( "SELECT * FROM char_locations WHERE char_id = :c", [':c' => user()->char_id] )->fetchArray(SQLITE3_ASSOC); @@ -289,7 +370,7 @@ function error_response(int $code): void */ function title(int $title_id): array|false { - return db_query(db_live(), 'SELECT * FROM titles WHERE id = ?', [$title_id])->fetchArray(); + return live_db()->query('SELECT * FROM titles WHERE id = ?', [$title_id])->fetchArray(); } /** @@ -364,3 +445,11 @@ function ui_guard() ajax_only(); csrf_ensure(); } + +/** + * Shorthand to call fetchArray() on a SQLite3Result. Defaults to SQLITE3_ASSOC but can pass any constant to $mode. + */ +function db_fetch_array(SQLite3Result $result, int $mode = SQLITE3_ASSOC): array|false +{ + return $result->fetchArray($mode); +} diff --git a/src/models/character.php b/src/models/character.php index ae4fc63..3bfbaf1 100644 --- a/src/models/character.php +++ b/src/models/character.php @@ -70,8 +70,7 @@ class Character */ public static function find(int|string $id): Character|false { - $q = db_query( - db_live(), + $q = live_db()->query( "SELECT * FROM characters WHERE id = :id OR name = :id COLLATE NOCASE", [':id' => $id] ); @@ -98,13 +97,13 @@ class Character $v = implode(', ', array_map(fn($x) => ":$x", $k)); // Create the character! - if (db_query(db_live(), "INSERT INTO characters ($f) VALUES ($v)", $data) === false) { + if (live_db()->query("INSERT INTO characters ($f) VALUES ($v)", $data) === false) { // @TODO: Log this error throw new Exception('Failed to create character. (cc)'); } // Get the character ID - return Character::find(db_live()->lastInsertRowID()); + return Character::find(live_db()->lastInsertRowID()); } /** @@ -112,8 +111,7 @@ class Character */ public function create_location(int $x = 0, int $y = 0, int $currently = 0): bool { - $l = db_query( - db_live(), + $l = live_db()->query( "INSERT INTO char_locations (char_id, x, y, currently) VALUES (:i, :x, :y, :c)", [':i' => $this->id, ':x' => $x, ':y' => $y, ':c' => $currently] ); @@ -126,7 +124,7 @@ class Character public function create_gear(array $initialGear = []): bool { // @TODO implement initial gear - $g = db_query(db_live(), "INSERT INTO char_gear (char_id) VALUES (:i)", [':i' => $this->id]); + $g = live_db()->query("INSERT INTO char_gear (char_id) VALUES (:i)", [':i' => $this->id]); return $g !== false; } @@ -135,7 +133,7 @@ class Character */ public static function name_exists(string $name): bool { - return db_exists(db_live(), 'characters', 'name', $name); + return live_db()->exists('characters', 'name', $name); } /** @@ -143,7 +141,7 @@ class Character */ public static function exists(int $id): bool { - return db_exists(db_live(), 'characters', 'id', $id); + return live_db()->exists('characters', 'id', $id); } /** @@ -151,8 +149,7 @@ class Character */ public static function belongs_to(int $id, int $user_id): bool { - $q = db_query( - db_live(), + $q = live_db()->query( "SELECT 1 FROM characters WHERE id = :i AND user_id = :u LIMIT 1", [':i' => $id, ':u' => $user_id] ); @@ -175,8 +172,7 @@ class Character */ public function award_title(int $title_id): bool { - $r = db_query( - db_live(), + $r = live_db()->query( 'INSERT INTO owned_titles (`title_id`, `char_id`) VALUES (:t, :i)', [':t' => $title_id, ':i' => $this->id] ); @@ -189,84 +185,9 @@ class Character public static function delete(int $id) { // Delete the character - if (db_query(db_live(), "DELETE FROM characters WHERE id = :p", [':p' => $id]) === false) { + if (live_db()->query("DELETE FROM characters WHERE id = :p", [':p' => $id]) === false) { throw new Exception('Failed to delete character. (C::d)'); } - - // Get item IDs from the character's inventory - $items = db_query(db_live(), "SELECT item_id FROM char_inventory WHERE char_id = :p", [':p' => $id]); - // delete the character's inventory and items - while ($row = $items->fetchArray(SQLITE3_ASSOC)) { - if (db_query(db_live(), "DELETE FROM char_inventory WHERE char_id = :c", [':c' => $id]) === false) { - throw new Exception('Failed to delete character inventory. (C::d)'); - } - - if (db_query(db_live(), "DELETE FROM items WHERE id = :p", [':p' => $row['id']]) === false) { - throw new Exception('Failed to delete character item slots. (C::d)'); - } - } - - // Delete the character's location - if (db_query(db_live(), "DELETE FROM char_locations WHERE char_id = :p", [':p' => $id]) === false) { - throw new Exception('Failed to delete character location. (C::d)'); - } - - // Delete the character's gear - if (db_query(db_live(), "DELETE FROM char_gear WHERE char_id = :p", [':p' => $id]) === false) { - throw new Exception('Failed to delete character gear. (C::d)'); - } - - // Delete the character's bank - if (db_query(db_live(), "DELETE FROM char_bank WHERE char_id = :p", [':p' => $id]) === false) { - throw new Exception('Failed to delete character bank. (C::d)'); - } - - // Delete character's banked items - if (db_query(db_live(), "DELETE FROM char_banked_items WHERE char_id = :p", [':p' => $id]) === false) { - throw new Exception('Failed to delete character bank items. (C::d)'); - } - - // Delete the user's guild membership - if (db_query(db_live(), "DELETE FROM guild_members WHERE char_id = :p", [':p' => $id]) === false) { - throw new Exception('Failed to delete character guild membership. (C::d)'); - } - - // if the character was a guild leader, hand leadership to the next highest ranking member - $guild = db_query(db_live(), "SELECT id FROM guilds WHERE leader_id = :p", [':p' => $id])->fetchArray(SQLITE3_ASSOC); - if ($guild !== false) { - $members = db_query(db_live(), "SELECT char_id FROM guild_members WHERE guild_id = :p ORDER BY rank DESC", [':p' => $guild['id']]); - $newLeader = $members->fetchArray(SQLITE3_ASSOC); - if ($newLeader !== false) { - db_query(db_live(), "UPDATE guilds SET leader_id = :p WHERE id = :g", [':p' => $newLeader['char_id'], ':g' => $guild['id']]); - } - } - - // Get a list of all pve fight IDs. - $pve = db_query(db_fights(), "SELECT id FROM pve WHERE char_id = :p", [':p' => $id]); - // Get a list of all pvp fight IDs. - $pvp = db_query(db_fights(), "SELECT id FROM pvp WHERE char1_id = :p OR char2_id = :p", [':p' => $id]); - - // Delete all pve fights - while ($row = $pve->fetchArray(SQLITE3_ASSOC)) { - if (db_query(db_fights(), "DELETE FROM pve WHERE id = :p", [':p' => $row['id']]) === false) { - throw new Exception('Failed to delete pve fight. (C::d)'); - } - - if (db_query(db_fights(), "DELETE FROM pve_logs WHERE fight_id = :p", [':p' => $row['id']]) === false) { - throw new Exception('Failed to delete pve fight logs. (C::d)'); - } - } - - // Delete all pvp fights - while ($row = $pvp->fetchArray(SQLITE3_ASSOC)) { - if (db_query(db_fights(), "DELETE FROM pvp WHERE id = :p", [':p' => $row['id']]) === false) { - throw new Exception('Failed to delete pvp fight. (C::d)'); - } - - if (db_query(db_fights(), "DELETE FROM pvp_logs WHERE fight_id = :p", [':p' => $row['id']]) === false) { - throw new Exception('Failed to delete pvp fight logs. (C::d)'); - } - } } /** @@ -278,8 +199,7 @@ class Character $t = title($this->title_id); - $q = db_query( - db_live(), + $q = live_db()->query( 'SELECT awarded FROM owned_titles WHERE char_id = ? AND title_id = ? LIMIT 1', [$this->id, $this->title_id] ); diff --git a/src/models/session.php b/src/models/session.php index 687e3a6..e7a55b5 100644 --- a/src/models/session.php +++ b/src/models/session.php @@ -1,50 +1,46 @@ $token, - ':u' => $userId, - ':e' => $expires - ]); - if (!$result) return false; - return $token; -} + public function __construct( + public int $user_id, + public string $token, + public int $expires + ) {} -/** - * Find a session by token. - */ -function session_find($token) -{ - $result = db_query(db_auth(), "SELECT * FROM sessions WHERE token = :t", [':t' => $token]); - $session = $result->fetchArray(SQLITE3_ASSOC); - if (!$session) return false; - $result->finalize(); - return $session; -} - -/** - * Delete sessions by user id. - */ -function session_delete($userId) -{ - return db_query(db_auth(), "DELETE FROM sessions WHERE user_id = :u", [':u' => $userId]); -} - -/** - * Validate a session by token and expiration date. If expired, the session is deleted and false is returned. - */ -function session_validate($token) -{ - $session = session_find($token); - if (!$session) return false; - if ($session['expires'] < time()) { - session_delete($session['user_id']); - return false; + public static function create(int $user_id, int $expires): Session|false + { + $token = token(); + $result = auth_db()->query("INSERT INTO sessions (token, user_id, expires) VALUES (:t, :u, :e)", [ + ':t' => $token, + ':u' => $user_id, + ':e' => $expires + ]); + if ($result === false) return false; + return new Session($user_id, $token, $expires); + } + + public static function find(string $token): Session|false + { + $result = auth_db()->query("SELECT * FROM sessions WHERE token = :t", [':t' => $token]); + $session = db_fetch_array($result); + if ($session === false) return false; + $result->finalize(); + return new Session($session['user_id'], $session['token'], $session['expires']); + } + + public static function delete(int $user_id): SQLite3Result|false + { + return auth_db()->query("DELETE FROM sessions WHERE user_id = :u", [':u' => $user_id]); + } + + public function validate(): bool + { + if (empty($this->user_id) || empty($this->token)) return false; + if ($this->expires < time()) { + self::delete($this->user_id); + return false; + } + return true; } - return true; } diff --git a/src/models/token.php b/src/models/token.php index 403bd50..c5cfb37 100644 --- a/src/models/token.php +++ b/src/models/token.php @@ -6,7 +6,7 @@ function token_create($userId) { $token = token(); - $result = db_query(db_auth(), "INSERT INTO tokens (token, user_id) VALUES (:t, :u)", [ + $result = auth_db()->query("INSERT INTO tokens (token, user_id) VALUES (:t, :u)", [ ':t' => $token, ':u' => $userId ]); @@ -19,7 +19,7 @@ function token_create($userId) */ function token_find($token) { - $result = db_query(db_auth(), "SELECT * FROM tokens WHERE token = :t", [':t' => $token]); + $result = auth_db()->query("SELECT * FROM tokens WHERE token = :t", [':t' => $token]); $token = $result->fetchArray(SQLITE3_ASSOC); if (!$token) return false; $result->finalize(); @@ -31,7 +31,7 @@ function token_find($token) */ function token_delete($token) { - return db_query(db_auth(), "DELETE FROM tokens WHERE token = :t", [':t' => $token]); + return auth_db()->query("DELETE FROM tokens WHERE token = :t", [':t' => $token]); } /** diff --git a/src/models/user.php b/src/models/user.php index b660cc7..7e45bc1 100644 --- a/src/models/user.php +++ b/src/models/user.php @@ -68,8 +68,7 @@ class User */ public static function find(string|int $identifier): User|false { - $r = db_query( - db_auth(), + $r = auth_db()->query( "SELECT * FROM users WHERE username = :i COLLATE NOCASE OR email = :i COLLATE NOCASE OR id = :i LIMIT 1", [':i' => $identifier] ); @@ -86,7 +85,7 @@ class User */ public static function create(string $username, string $email, string $password, int $auth = 0): SQLite3Result|false { - return db_query(db_auth(), "INSERT INTO users (username, email, password, auth) VALUES (:u, :e, :p, :a)", [ + return auth_db()->query("INSERT INTO users (username, email, password, auth) VALUES (:u, :e, :p, :a)", [ ':u' => $username, ':e' => $email, ':p' => password_hash($password, PASSWORD_ARGON2ID), @@ -107,7 +106,7 @@ class User */ public static function username_exists(string $username): bool { - return db_exists(db_auth(), 'users', 'username', $username); + return auth_db()->exists('users', 'username', $username); } /** @@ -115,7 +114,7 @@ class User */ public static function email_exists(string $email): bool { - return db_exists(db_auth(), 'users', 'email', $email); + return auth_db()->exists('users', 'email', $email); } /** @@ -123,8 +122,7 @@ class User */ public static function delete(string|int $identifier): SQLite3Result|false { - return db_query( - db_auth(), + return auth_db()->query( "DELETE FROM users WHERE username = :i OR email = :i OR id = :i", [':i' => $identifier] ); @@ -135,8 +133,7 @@ class User */ public function char_count(): int { - $c = db_query( - db_live(), + $c = live_db()->query( "SELECT COUNT(*) FROM characters WHERE user_id = :u", [':u' => $this->id] )->fetchArray(SQLITE3_NUM); @@ -150,7 +147,7 @@ class User */ public function char_list(): array|false { - $q = db_query(db_live(), "SELECT id, name, level FROM characters WHERE user_id = ?", [$this->id]); + $q = live_db()->query("SELECT id, name, level FROM characters WHERE user_id = ?", [$this->id]); if ($q === false) throw new Exception('Failed to list characters. (U->cl)'); $c = []; diff --git a/src/models/wallet.php b/src/models/wallet.php index b501ce2..5609f5e 100644 --- a/src/models/wallet.php +++ b/src/models/wallet.php @@ -10,7 +10,7 @@ class Wallet public static function find(int $user_id): Wallet|false { - $r = db_query(db_live(), 'SELECT * FROM wallets WHERE user_id = ?', [$user_id]); + $r = live_db()->query('SELECT * FROM wallets WHERE user_id = ?', [$user_id]); if ($r === false) throw new Exception('Failed to query wallet. (W::f)'); // badly formed query $w = $r->fetchArray(SQLITE3_ASSOC); if ($w === false) return false; // no wallet found @@ -19,8 +19,7 @@ class Wallet public static function create(int $user_id, int $silver = -1, int $starGems = -1): SQLite3Result|false { - return db_query( - db_live(), + return live_db()->query( "INSERT INTO wallets (user_id, silver, stargem) VALUES (:u, :s, :sg)", [ ':u' => $user_id, @@ -37,7 +36,7 @@ class Wallet { $cs = $c->string(true); $new = $this->{$cs} + $amt; - return db_query(db_live(), "UPDATE wallets SET $cs = ? WHERE user_id = ?", [$new, $this->user_id]); + return live_db()->query("UPDATE wallets SET $cs = ? WHERE user_id = ?", [$new, $this->user_id]); } /** @@ -47,6 +46,6 @@ class Wallet { $cs = $c->string(true); $new = $this->{$cs} - $amt; - return db_query(db_live(), "UPDATE wallets SET $cs = ? WHERE user_id = ?", [$new < 0 ? 0 : $new, $this->user_id]); + return live_db()->query("UPDATE wallets SET $cs = ? WHERE user_id = ?", [$new < 0 ? 0 : $new, $this->user_id]); } } diff --git a/src/util/router.php b/src/router.php similarity index 65% rename from src/util/router.php rename to src/router.php index d77bea7..0c8e8be 100644 --- a/src/util/router.php +++ b/src/router.php @@ -6,6 +6,14 @@ */ class Router { + /** + * List of valid HTTP verbs. + */ + private const VALID_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']; + + /** + * The tree of currently registered routes. + */ private array $routes = []; /** @@ -17,6 +25,9 @@ class Router */ public function add(string $method, string $route, callable $handler): Router { + $this->validateMethod($method); + $this->validateRoute($route); + // Expand the route into segments and make dynamic segments into a common placeholder $segments = array_map(function($segment) { return str_starts_with($segment, ':') ? ':x' : $segment; @@ -98,4 +109,58 @@ class Router { return $this->add('POST', $route, $handler); } + + /** + * Shorthand to register a PUT route. + */ + public function put(string $route, callable $handler): Router + { + return $this->add('PUT', $route, $handler); + } + + /** + * Shorthand to register a DELETE route. + */ + public function delete(string $route, callable $handler): Router + { + return $this->add('DELETE', $route, $handler); + } + + /** + * Shorthand to register a PATCH route. + */ + public function patch(string $route, callable $handler): Router + { + return $this->add('PATCH', $route, $handler); + } + + /** + * Validate the given method against valid HTTP verbs. + */ + private function validateMethod(string $method): void + { + if (!in_array($method, self::VALID_METHODS)) { + throw new InvalidArgumentException("Invalid HTTP method: $method"); + } + } + + /** + * Validate that a new route follows expected formatting. + */ + private function validateRoute(string $route): void + { + if ($route === '') { + throw new InvalidArgumentException("Route cannot be empty"); + } + + // Ensure route starts with a slash + if (!str_starts_with($route, '/')) { + throw new InvalidArgumentException("Route must start with a '/'"); + } + + // Optional: Check for consecutive dynamic segments or invalid characters + if (preg_match('/(:x.*){2,}/', $route)) { + throw new InvalidArgumentException("Invalid route pattern: consecutive dynamic segments"); + } + } } diff --git a/src/util/auth.php b/src/util/auth.php index f90f2d7..2de78c2 100644 --- a/src/util/auth.php +++ b/src/util/auth.php @@ -9,9 +9,9 @@ function auth_check() if (isset($_SESSION['user'])) return true; if (isset($_COOKIE['remember_me'])) { - $session = session_validate($_COOKIE['remember_me']); - if ($session === true) { - $user = User::find($session['user_id']); + $session = Session::find($_COOKIE['remember_me']); + if ($session->validate()) { + $user = User::find($session->user_id); $_SESSION['user'] = serialize($user); return true; } @@ -54,8 +54,7 @@ function must_have_character() // if no character selected, select the first one if (user()->char_id === 0) { - $char = db_query( - db_live(), + $char = live_db()->query( 'SELECT * FROM characters WHERE user_id = :u ORDER BY id ASC LIMIT 1', [':u' => user()->id] )->fetchArray(SQLITE3_ASSOC); diff --git a/src/util/database.php b/src/util/database.php deleted file mode 100644 index f80875e..0000000 --- a/src/util/database.php +++ /dev/null @@ -1,121 +0,0 @@ -exec('PRAGMA cache_size = 32000'); - // Enable WAL mode - $db->exec('PRAGMA journal_mode = WAL'); - // Move temp store to memory - $db->exec('PRAGMA temp_store = MEMORY'); - - return $db; -} - -/** - * Return a connection to the auth database. - */ -function db_auth() -{ - return $GLOBALS['db_auth'] ??= db_open(DBP . '/auth.db'); -} - -/** - * Return a connection to the live database. - */ -function db_live() -{ - return $GLOBALS['db_live'] ??= db_open(DBP . '/live.db'); -} - - -/** - * Return a connection to the fights database. - */ -function db_fights() -{ - return $GLOBALS['db_fights'] ??= db_open(DBP . '/fights.db'); -} - - -/** - * Return a connection to the blueprints database. - */ -function db_blueprints() -{ - return $GLOBALS['db_blueprints'] ??= db_open(DBP . '/blueprints.db'); -} - -/** - * Take a SQLite3 database connection, a query string, and an array of parameters. Prepare the query and - * bind the parameters with proper type casting. Then execute the query and return the result. - */ -function db_query(SQLite3 $db, string $query, array $params = []): SQLite3Result|false -{ - $p = strpos($query, '?') !== false; // are generic placeholders? - $stmt = $db->prepare($query); - if (!empty($params)) { - foreach ($params as $k => $v) $stmt->bindValue($p ? $k + 1 : $k, $v, getSQLiteType($v)); - } - $start = microtime(true); - $r = $stmt->execute(); - db_log($query, microtime(true) - $start); - return $r; -} - -/** - * Take a SQLite3 database connection and a query string. Execute the query and return the result. - */ -function db_exec($db, $query) -{ - $start = microtime(true); - $r = $db->exec($query); - db_log($query, microtime(true) - $start); - return $r; -} - -/** - * Take a SQLite3 database connection, a column name, and a value. Execute a SELECT query to see if the value - * exists in the column. Return true if the value exists, false otherwise. - */ -function db_exists(SQLite3 $db, string $table, string $column, mixed $value, bool $caseInsensitive = true): bool -{ - if ($caseInsensitive) { - $query = "SELECT 1 FROM $table WHERE $column = :v COLLATE NOCASE LIMIT 1"; - } else { - $query = "SELECT 1 FROM $table WHERE $column = :v LIMIT 1"; - } - - $result = db_query($db, $query, [':v' => $value]); - return $result->fetchArray(SQLITE3_NUM) !== false; -} - -/** - * Return the appropriate SQLite type casting for the value. - */ -function getSQLiteType($value): int -{ - return match (true) { - is_int($value) => SQLITE3_INTEGER, - is_float($value) => SQLITE3_FLOAT, - is_null($value) => SQLITE3_NULL, - default => SQLITE3_TEXT - }; -} - -/** - * Log the given query string to the db debug log. - */ -function db_log($query, $timeTaken = 0) -{ - $GLOBALS['queries']++; - $GLOBALS['query_time'] += $timeTaken; - if (env('debug', false)) $GLOBALS['query_log'][] = [$query, $timeTaken]; -} diff --git a/src/util/env.php b/src/util/env.php deleted file mode 100644 index 3fc1ab1..0000000 --- a/src/util/env.php +++ /dev/null @@ -1,47 +0,0 @@ - true, - $v === 'false' => false, - is_numeric($v) => (int) $v, - is_float($v) => (float) $v, - default => $v - }; -} diff --git a/src/util/render.php b/src/util/render.php deleted file mode 100644 index 9072796..0000000 --- a/src/util/render.php +++ /dev/null @@ -1,20 +0,0 @@ - -

-
-
- name ?> - Llevel ?> title()['name'] ?> + -
-
- -
+
+ -
-
- -
-
+
+ (level ?>) name ?>, title()['name'] ?> - -
+
+
+ +
-
+
+
+ +
+ + - -
+