From 7c83d5c78d8683f08dceb90a7fd0f37a05f4ebc7 Mon Sep 17 00:00:00 2001 From: flashwave Date: Fri, 19 Feb 2016 00:28:44 +0100 Subject: [PATCH] r20160219 --- cron.php | 20 +- libraries/ActionCode.php | 26 +- libraries/BBcode.php | 8 +- libraries/Bans.php | 25 +- libraries/Comments.php | 92 ++--- libraries/Config.php | 40 ++- libraries/Controllers/Forums.php | 14 +- libraries/Controllers/Meta.php | 57 ++- libraries/Controllers/Premium.php | 1 + libraries/Controllers/User.php | 13 +- libraries/DB.php | 112 ++++++ libraries/DBWrapper/mysql.php | 478 ------------------------- libraries/Database.php | 151 -------- libraries/File.php | 42 ++- libraries/Forum/Forum.php | 72 ++-- libraries/Forum/Post.php | 79 ++-- libraries/Forum/Thread.php | 199 +++++----- libraries/News.php | 6 +- libraries/Perms.php | 50 ++- libraries/Rank.php | 34 +- libraries/Session.php | 56 +-- libraries/User.php | 393 ++++++++++++-------- libraries/Users.php | 249 ++++++------- libraries/Utils.php | 54 +-- public/authenticate.php | 11 +- public/posting.php | 15 +- public/settings.php | 126 ++++--- sakura.php | 18 +- templates/yuuno/elements/newsPost.twig | 2 +- 29 files changed, 1103 insertions(+), 1340 deletions(-) create mode 100644 libraries/DB.php delete mode 100644 libraries/DBWrapper/mysql.php delete mode 100644 libraries/Database.php diff --git a/cron.php b/cron.php index 7ad6ab2..9084d43 100644 --- a/cron.php +++ b/cron.php @@ -24,23 +24,25 @@ define('SAKURA_CRON', true); require_once 'sakura.php'; // Clean expired sessions -Database::delete('sessions', [ - 'session_expire' => [time(), '<'], - 'session_remember' => ['1', '!='], +$cleanSessions = DB::prepare('DELETE FROM `{prefix}sessions` WHERE `session_expire` < :time AND `session_remember` != 1'); +$cleanSessions->execute([ + 'time' => time(), ]); // Delete notifications that are older than a month but not unread -Database::delete('notifications', [ - 'alert_timestamp' => [(time() - 109500), '<'], - 'alert_read' => ['1', '='], +$cleanAlerts = DB::prepare('DELETE FROM `{prefix}notifications` WHERE `alert_timestamp` < :time AND `alert_read` = 1'); +$cleanAlerts->execute([ + 'time' => (time() - 109500), ]); // Get expired premium accounts -$expiredPremium = Database::fetch('premium', true, [ - 'premium_expire' => [time(), '<'], +$expiredPremium = DB::prepare('SELECT * FROM `{prefix}premium` WHERE `premium_expire` < :time'); +$expiredPremium->execute([ + 'time' => time(), ]); +$expiredPremium = $expiredPremium->fetchAll(); // Process expired premium accounts, make this not stupid in the future foreach ($expiredPremium as $expired) { - Users::updatePremiumMeta($expired['user_id']); + Users::updatePremiumMeta($expired->user_id); } diff --git a/libraries/ActionCode.php b/libraries/ActionCode.php index de4d430..49c874c 100644 --- a/libraries/ActionCode.php +++ b/libraries/ActionCode.php @@ -29,10 +29,11 @@ class ActionCode $code = uniqid(); // Insert it - Database::insert('actioncodes', [ - 'code_action' => $action, - 'user_id' => $user, - 'action_code' => $code, + DB::prepare('INSERT INTO `{prefix}actioncodes` (`code_action`, `user_id`, `action_code`) VALUES (:action, :id, :code)') + ->execute([ + 'action' => $action, + 'id' => $user, + 'code' => $code, ]); // Return the code @@ -52,11 +53,13 @@ class ActionCode public static function validate($action, $code, $user = 0, $invalidate = true) { // Fetch the code from the db - $get = Database::count('actioncodes', [ - 'code_action' => [$action, '='], - 'action_code' => [$code, '='], - 'user_id' => [$user, '='], + $get = DB::prepare('SELECT * FROM `{prefix}actioncodes` WHERE `code_action` = :code AND `action_code` = :action AND `user_id` = :id'); + $get->execute([ + 'code' => $action, + 'action' => $code, + 'id' => $user, ]); + $get = $get->rowCount(); // Invalidate the code if requested if ($invalidate) { @@ -64,7 +67,7 @@ class ActionCode } // Return the result - return $get[0] > 0; + return $get > 0; } /** @@ -74,8 +77,9 @@ class ActionCode */ public static function invalidate($code) { - Database::delete('actioncodes', [ - 'code_action' => [$code, '='], + DB::prepare('DELETE FROM `{prefix}actioncodes` WHERE `code_action` = :code') + ->execute([ + 'code' => $code, ]); } } diff --git a/libraries/BBcode.php b/libraries/BBcode.php index 8287957..a4d3a6c 100644 --- a/libraries/BBcode.php +++ b/libraries/BBcode.php @@ -48,12 +48,14 @@ class BBcode public static function parseEmoticons($text) { // Get emoticons from the database - $emotes = Database::fetch('emoticons'); + $emotes = DB::prepare('SELECT * FROM `{prefix}emoticons`'); + $emotes->execute(); + $emotes = $emotes->fetchAll(); // Parse all emoticons foreach ($emotes as $emote) { - $image = "{$emote["; - $icon = preg_quote($emote['emote_string'], '#'); + $image = "{$emote->emote_string}"; + $icon = preg_quote($emote->emote_string, '#'); $text = preg_replace("#$icon#", $image, $text); } diff --git a/libraries/Bans.php b/libraries/Bans.php index f47fba6..1dca80c 100644 --- a/libraries/Bans.php +++ b/libraries/Bans.php @@ -26,7 +26,11 @@ class Bans { // Attempt to get a ban from this user - $bans = Database::fetch('bans', true, ['user_id' => [$uid, '=']]); + $bans = DB::prepare('SELECT * FROM `{prefix}bans` WHERE `user_id` = :id'); + $bans->execute([ + 'id' => $uid, + ]); + $bans = $bans->fetchAll(); // Reverse the array so new bans are listed first $bans = array_reverse($bans); @@ -34,21 +38,22 @@ class Bans // Go over each ban foreach ($bans as $ban) { // Check if it hasn't expired - if ($ban['ban_end'] != 0 && $ban['ban_end'] < time()) { + if ($ban->ban_end != 0 && $ban->ban_end < time()) { // If it has delete the entry and continue - Database::delete('bans', ['id' => [$ban['user_id'], '=']]); + DB::prepare('DELETE FROM `{prefix}bans` WHERE `ban_id` = :id') + ->execute([ + 'id' => $ban->user_id, + ]); continue; } // Return the ban if all checks were passed return [ - - 'user' => $ban['user_id'], - 'issuer' => $ban['ban_moderator'], - 'issued' => $ban['ban_begin'], - 'expires' => $ban['ban_end'], - 'reason' => $ban['ban_reason'], - + 'user' => $ban->user_id, + 'issuer' => $ban->ban_moderator, + 'issued' => $ban->ban_begin, + 'expires' => $ban->ban_end, + 'reason' => $ban->ban_reason, ]; } diff --git a/libraries/Comments.php b/libraries/Comments.php index 4100aa4..458d37a 100644 --- a/libraries/Comments.php +++ b/libraries/Comments.php @@ -9,6 +9,7 @@ namespace Sakura; /** * Handles and serves comments on pages. + * Needs a reimplementation. * * @package Sakura * @author Julian van de Groep @@ -46,15 +47,11 @@ class Comments $this->category = $category; // Get the comments and assign them to $comments - $comments = Database::fetch( - 'comments', - true, - [ - 'comment_category' => [$this->category, '='], - 'comment_reply_to' => ['0', '='], - ], - ['comment_id', true] - ); + $comments = DB::prepare('SELECT * FROM `{prefix}comments` WHERE `comment_category` = :category AND `comment_reply_to` = 0 ORDER BY `comment_id` DESC'); + $comments->execute([ + 'category' => $this->category, + ]); + $comments = $comments->fetchAll(\PDO::FETCH_ASSOC); // Feed them into the sorter $this->comments = $this->sortComments($comments); @@ -99,10 +96,12 @@ class Comments $this->count += 1; // Attempt to get replies from the database - $replies = Database::fetch('comments', true, [ - 'comment_category' => [$this->category, '='], - 'comment_reply_to' => [$comment['comment_id'], '='], + $replies = DB::prepare('SELECT * FROM `{prefix}comments` WHERE `comment_category` = :category AND `comment_reply_to` = :thread'); + $replies->execute([ + 'category' => $this->category, + 'thread' => $comment['comment_id'], ]); + $replies = $replies->fetchAll(\PDO::FETCH_ASSOC); // Check if this was a reply to something if ($replies) { @@ -124,9 +123,11 @@ class Comments public function getComment($cid) { // Get from database - return Database::fetch('comments', false, [ - 'comment_id' => [$cid, '='], + $comment = DB::prepare('SELECT * FROM `{prefix}comments` WHERE `comment_id` = :id'); + $comment->execute([ + 'id' => $cid, ]); + return $comment->fetch(\PDO::FETCH_ASSOC); } /** @@ -139,9 +140,11 @@ class Comments public function getVotes($cid) { // Get from database - return Database::fetch('comment_votes', true, [ - 'vote_comment' => [$cid, '='], + $comment = DB::prepare('SELECT * FROM `{prefix}comment_votes` WHERE `vote_comment` = :id'); + $comment->execute([ + 'id' => $cid, ]); + return $comment->fetchAll(\PDO::FETCH_ASSOC); } /** @@ -166,12 +169,13 @@ class Comments } // Insert into database - Database::insert('comments', [ - 'comment_category' => $this->category, - 'comment_timestamp' => time(), - 'comment_poster' => $uid, - 'comment_reply_to' => (int) $reply, - 'comment_text' => $content, + DB::prepare('INSERT INTO `{prefix}comments` (`comment_category`, `comment_timestamp`, `comment_poster`, `comment_reply_to`, `comment_text`) VALUES (:cat, :time, :user, :thread, :text)') + ->execute([ + 'cat' => $this->category, + 'time' => time(), + 'user' => $uid, + 'thread' => (int) $reply, + 'text' => $content, ]); // Return success @@ -190,38 +194,39 @@ class Comments public function makeVote($uid, $cid, $mode) { // Attempt to get previous vote - $vote = Database::fetch('comment_votes', false, [ - 'vote_user' => [$uid, '='], - 'vote_comment' => [$cid, '='], + $vote = DB::prepare('SELECT * FROM `{prefix}comment_votes` WHERE `vote_user` = :user AND `vote_comment` = :comment'); + $vote->execute([ + 'user' => $uid, + 'comment' => $cid, ]); + $vote = $vote->fetch(\PDO::FETCH_ASSOC); // Check if anything was returned if ($vote) { // Check if the vote that's being casted is the same if ($vote['vote_state'] == $mode) { // Delete the vote - Database::delete('comment_votes', [ - 'vote_user' => [$uid, '='], - 'vote_comment' => [$cid, '='], + DB::prepare('DELETE FROM `{prefix}comment_votes` WHERE `vote_user` = :user AND `vote_comment` = :comment') + ->execute([ + 'user' => $uid, + 'comment' => $cid, ]); } else { // Otherwise update the vote - Database::update('comment_votes', [ - [ - 'vote_state' => $mode, - ], - [ - 'vote_user' => [$uid, '='], - 'vote_comment' => [$cid, '='], - ], + DB::prepare('UPDATE `{prefix}comment_votes` SET `vote_state` = :state WHERE `vote_user` = :user AND `vote_comment` = :comment') + ->execute([ + 'state' => $mode, + 'user' => $uid, + 'comment' => $cid, ]); } } else { // Create a vote - Database::insert('comment_votes', [ - 'vote_user' => $uid, - 'vote_comment' => $cid, - 'vote_state' => $mode, + DB::prepare('INSERT INTO `{prefix}comment_votes` (`vote_user`, `vote_comment`, `vote_state`) VALUES (:user, :comment, :state)') + ->execute([ + 'user' => $uid, + 'comment' => $cid, + 'state' => $mode, ]); } @@ -232,14 +237,13 @@ class Comments * Remove a comment * * @param int $cid ID of the comment to remove. - * - * @return mixed No idea what this returns but it doesn't really matter anyway, the comment is dead. */ public function removeComment($cid) { // Remove from database - return Database::delete('comments', [ - 'comment_id' => [$cid, '='], + DB::prepare('DELETE FROM `{prefix}comments` WHERE `comment_id` = :id') + ->execute([ + 'id' => $cid, ]); } } diff --git a/libraries/Config.php b/libraries/Config.php index e035377..355251b 100644 --- a/libraries/Config.php +++ b/libraries/Config.php @@ -63,7 +63,7 @@ class Config * @param string $key Configuration section. * @param string $subkey Configuration key. * - * @return string Configuration value. + * @return array|string Configuration value. */ public static function local($key, $subkey = null) { @@ -101,9 +101,13 @@ class Config // Then return the value return self::$database[$key]; } else { - $value = Database::fetch('config', false, ['config_name' => [$key, '=']]); + $value = DB::prepare('SELECT * FROM `{prefix}config` WHERE `config_name` = :name'); + $value->execute([ + 'name' => $key, + ]); + $value = $value->fetch(); if ($value) { - self::$database[$key] = $value['config_value']; + self::$database[$key] = $value->config_value; return self::$database[$key]; } } @@ -115,4 +119,34 @@ class Config ); return null; } + + public static function set($key, $value) + { + // Unset the cached copy + if (array_key_exists($key, self::$database)) { + unset(self::$database[$key]); + } + + // Check if the value already exists + $exists = DB::prepare('SELECT * FROM `{prefix}config` WHERE `config_name` = :name'); + $exists->execute([ + 'name' => $key, + ]); + + // If it exists run an update + if ($exists->rowCount()) { + $set = DB::prepare('UPDATE `{prefix}config` SET `config_value` = :value WHERE `config_name` = :name'); + } else { + $set = DB::prepare('INSERT INTO `{prefix}config` (`config_name`, `config_value`) VALUES (:name, :value)'); + } + + // Run the setter + $set->execute([ + 'name' => $key, + 'value' => $value, + ]); + + // Return the value + return $value; + } } diff --git a/libraries/Controllers/Forums.php b/libraries/Controllers/Forums.php index 5791014..f0af6b7 100644 --- a/libraries/Controllers/Forums.php +++ b/libraries/Controllers/Forums.php @@ -9,6 +9,7 @@ namespace Sakura\Controllers; use Sakura\Config; use Sakura\Database; +use Sakura\DB; use Sakura\Forum; use Sakura\Perms\Forum as ForumPerms; use Sakura\Template; @@ -31,18 +32,25 @@ class Forums extends Controller */ public function index() { + $userCount = DB::prepare("SELECT * FROM `{prefix}users` WHERE `password_algo` != 'disabled' AND `rank_main` != 1"); + $userCount->execute(); + $threadCount = DB::prepare('SELECT * FROM `{prefix}topics`'); + $threadCount->execute(); + $postCount = DB::prepare('SELECT * FROM `{prefix}posts`'); + $postCount->execute(); + // Merge index specific stuff with the global render data Template::vars([ 'forum' => (new Forum\Forum()), 'stats' => [ - 'userCount' => Database::count('users', ['password_algo' => ['nologin', '!='], 'rank_main' => ['1', '!=']])[0], + 'userCount' => $userCount->rowCount(), 'newestUser' => User::construct(Users::getNewestUserId()), 'lastRegData' => date_diff( date_create(date('Y-m-d', User::construct(Users::getNewestUserId())->registered)), date_create(date('Y-m-d')) )->format('%a'), - 'topicCount' => Database::count('topics')[0], - 'postCount' => Database::count('posts')[0], + 'topicCount' => $threadCount->rowCount(), + 'postCount' => $postCount->rowCount(), 'onlineUsers' => Users::checkAllOnline(), ], ]); diff --git a/libraries/Controllers/Meta.php b/libraries/Controllers/Meta.php index 9f7edb2..47313c9 100644 --- a/libraries/Controllers/Meta.php +++ b/libraries/Controllers/Meta.php @@ -9,6 +9,7 @@ namespace Sakura\Controllers; use Sakura\Config; use Sakura\Database; +use Sakura\DB; use Sakura\News; use Sakura\Template; use Sakura\User; @@ -30,19 +31,26 @@ class Meta extends Controller */ public function index() { + $userCount = DB::prepare("SELECT * FROM `{prefix}users` WHERE `password_algo` != 'disabled' AND `rank_main` != 1"); + $userCount->execute(); + $threadCount = DB::prepare('SELECT * FROM `{prefix}topics`'); + $threadCount->execute(); + $postCount = DB::prepare('SELECT * FROM `{prefix}posts`'); + $postCount->execute(); + // Merge index specific stuff with the global render data Template::vars([ 'news' => new News(Config::get('site_news_category')), 'newsCount' => Config::get('front_page_news_posts'), 'stats' => [ - 'userCount' => Database::count('users', ['password_algo' => ['nologin', '!='], 'rank_main' => ['1', '!=']])[0], + 'userCount' => $userCount->rowCount(), 'newestUser' => User::construct(Users::getNewestUserId()), 'lastRegDate' => date_diff( date_create(date('Y-m-d', User::construct(Users::getNewestUserId())->registered)), date_create(date('Y-m-d')) )->format('%a'), - 'topicCount' => Database::count('topics')[0], - 'postCount' => Database::count('posts')[0], + 'topicCount' => $threadCount->rowCount(), + 'postCount' => $postCount->rowCount(), 'onlineUsers' => Users::checkAllOnline(), ], ]); @@ -87,11 +95,16 @@ class Meta extends Controller */ public function faq() { + // Get faq entries + $faq = DB::prepare('SELECT * FROM `{prefix}faq` ORDER BY `faq_id`'); + $faq->execute(); + $faq = $faq->fetchAll(); + // Set parse variables Template::vars([ 'page' => [ 'title' => 'Frequently Asked Questions', - 'questions' => Database::fetch('faq', true, null, ['faq_id']), + 'questions' => $faq, ], ]); @@ -109,25 +122,33 @@ class Meta extends Controller public function infoPage($id = null) { // Set default variables - $renderData['page'] = [ - 'content' => '

Unable to load the requested info page.

Check the URL and try again.

', - ]; + Template::vars([ + 'page' => [ + 'content' => '

Unable to load the requested info page.

Check the URL and try again.

', + ], + ]); // Set page id $id = strtolower($id); - // Get info page data from the database - if ($ipData = Database::fetch('infopages', false, ['page_shorthand' => [$id, '=']])) { - // Assign new proper variable - $renderData['page'] = [ - 'id' => $id, - 'title' => $ipData['page_title'], - 'content' => $ipData['page_content'], - ]; - } + // Get the page from the database + $ipData = DB::prepare('SELECT * FROM `{prefix}infopages` WHERE `page_shorthand` = :id'); + $ipData->execute([ + 'id' => $id, + ]); + $ipData = $ipData->fetch(); - // Set parse variables - Template::vars($renderData); + // Get info page data from the database + if ($ipData) { + // Assign new proper variable + Template::vars([ + 'page' => [ + 'id' => $id, + 'title' => $ipData->page_title, + 'content' => $ipData->page_content, + ], + ]); + } // Return the compiled page return Template::render('main/infopage'); diff --git a/libraries/Controllers/Premium.php b/libraries/Controllers/Premium.php index 49775f7..37d3245 100644 --- a/libraries/Controllers/Premium.php +++ b/libraries/Controllers/Premium.php @@ -103,6 +103,7 @@ class Premium extends Controller // Attempt to complete the transaction if ($finalise) { // Make the user premium + Users::addUserPremium($currentUser->id, (2628000 * $_SESSION['premiumMonths'])); Users::updatePremiumMeta($currentUser->id); Utils::updatePremiumTracker( $currentUser->id, diff --git a/libraries/Controllers/User.php b/libraries/Controllers/User.php index 89b78da..c0a46a9 100644 --- a/libraries/Controllers/User.php +++ b/libraries/Controllers/User.php @@ -9,6 +9,7 @@ namespace Sakura\Controllers; use Sakura\Config; use Sakura\Database; +use Sakura\DB; use Sakura\Rank; use Sakura\Template; use Sakura\User as UserContext; @@ -39,13 +40,19 @@ class User extends Controller // If the user id is zero check if there was a namechange if ($profile->id == 0) { // Fetch from username_history - $check = Database::fetch('username_history', false, ['username_old_clean' => [Utils::cleanString(isset($_GET['u']) ? $_GET['u'] : 0, true, true), '=']]); + $check = DB::prepare('SELECT `user_id` FROM `{prefix}username_history` WHERE `username_old_clean` = :uname ORDER BY `change_id` DESC'); + $check->execute([ + 'uname' => Utils::cleanString($id, true, true), + ]); + $check = $check->fetch(); // Redirect if so if ($check) { Template::vars([ - 'message' => 'The user this profile belongs to changed their username, you are being redirected.', - 'redirect' => (new \Sakura\Urls)->format('USER_PROFILE', [$check['user_id']]), + 'page' => [ + 'message' => 'The user this profile belongs to changed their username, you are being redirected.', + 'redirect' => (new \Sakura\Urls)->format('USER_PROFILE', [$check->user_id]), + ], ]); // Print page contents diff --git a/libraries/DB.php b/libraries/DB.php new file mode 100644 index 0000000..7ebeeee --- /dev/null +++ b/libraries/DB.php @@ -0,0 +1,112 @@ + + */ +class DB +{ + /** + * The container for the PDO object. + * + * @var PDO + */ + public static $db = null; + + /** + * The table prefix + * + * @var string + */ + public static $prefix = ''; + + /** + * Open the SQL connection and creates a PDO object. + * + * @param string $server A PDO driver. + * @param array $dsnParts An array consisting out of DSN string parts. + * @param string $username The username used to authenticate with the SQL server. + * @param string $password The password for the same purpose. + * @param array $options Additional PDO options. + */ + public static function open($server, $dsnParts, $username = null, $password = null, $prefix = '', $options = []) + { + // Check if the selected driver is available + if (!in_array($server, PDO::getAvailableDrivers())) { + trigger_error('A driver for the selected SQL server wasn\'t found!', E_USER_ERROR); + return; + } + + // Set the table prefix + self::$prefix = $prefix; + + // Create start of the DSN + $dsn = "{$server}:"; + + // Append the parts + foreach ($dsnParts as $name => $value) { + $dsn .= "{$name}={$value};"; + } + + try { + // Connect to SQL server using PDO + self::$db = new PDO($dsn, $username, $password, $options); + } catch (PDOException $e) { + // Catch connection errors + trigger_error($e->getMessage(), E_USER_ERROR); + } + + self::$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + self::$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); + } + + /** + * Closes the PDO object. + */ + public static function close() + { + self::$db = null; + } + + /** + * Get the id of the item that was last inserted into the database. + * + * @param string $name Sequence of which the last id should be returned. + * + * @return string The last inserted id. + */ + public static function lastID($name = null) + { + return self::$db->lastInsertID($name); + } + + /** + * Prepares a statement for execution and returns a statement object. + * + * @param string $stmt The statement to prepare. + * @param array $opts Statement specific driver options. + * + * @return PDOStatement + */ + public static function prepare($stmt, $opts = []) + { + // Replace the table prefix + $stmt = str_replace('{prefix}', self::$prefix, $stmt); + + return self::$db->prepare($stmt, $opts); + } +} diff --git a/libraries/DBWrapper/mysql.php b/libraries/DBWrapper/mysql.php deleted file mode 100644 index a61911b..0000000 --- a/libraries/DBWrapper/mysql.php +++ /dev/null @@ -1,478 +0,0 @@ - - */ -class mysql -{ - /** - * Contains the PDO object. - * - * @var PDO - */ - protected $sql; - - /** - * Constructor. - */ - public function __construct() - { - if (!extension_loaded('PDO')) { - // Return error and die - trigger_error('PDO extension not loaded.', E_USER_ERROR); - } - - // Initialise connection - $this->initConnect( - ( - Config::local('database', 'unixsocket') ? - $this->prepareSock( - Config::local('database', 'host'), - Config::local('database', 'database') - ) : - $this->prepareHost( - Config::local('database', 'host'), - Config::local('database', 'database'), - ( - Config::local('database', 'port') !== null ? - Config::local('database', 'port') : - 3306 - ) - ) - ), - Config::local('database', 'username'), - Config::local('database', 'password') - ); - } - - /** - * Generates a DSN for a regular hostname:port endpoint. - * - * @param string $dbHost Database hostname. - * @param string $dbName Database name. - * @param int $dbPort Database host port. - * - * @return string The PDO DSN. - */ - private function prepareHost($dbHost, $dbName, $dbPort = 3306) - { - $dsn = 'mysql:host=' . $dbHost . ';port=' . $dbPort . ';dbname=' . $dbName; - - return $dsn; - } - - /** - * Generates a DSN for a unix socket endpoint. - * - * @param string $dbHost Path to the Unix Socket. - * @param string $dbName Database name. - * - * @return string The PDO DSN. - */ - private function prepareSock($dbHost, $dbName) - { - $dsn = 'mysql:unix_socket=' . $dbHost . ';dbname=' . $dbName; - - return $dsn; - } - - /** - * Initialise a the database connection. - * - * @param string $dsn The PDO DSN. - * @param string $dbUname The database username. - * @param string $dbPword The database password. - * - * @return bool Returns true if the connection was successful. - */ - private function initConnect($dsn, $dbUname, $dbPword) - { - try { - // Connect to SQL server using PDO - $this->sql = new PDO($dsn, $dbUname, $dbPword, [ - PDO::ATTR_EMULATE_PREPARES => false, - PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING, - ]); - } catch (PDOException $e) { - // Catch connection errors - trigger_error('SQL Driver: ' . $e->getMessage(), E_USER_ERROR); - } - - return true; - } - - /** - * Select table row(s). - * - * @param string $table The table to select data from. - * @param array $data The WHERE selectors. - * @param array $order The order in which the data is returned. - * @param array $limit The limit of what should be returned. - * @param array $group The way MySQL will group the data. - * @param bool $distinct Only return distinct values. - * @param string $column Only select from this column. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return PDOStatement The PDOStatement object for this action. - */ - public function select($table, $data = null, $order = null, $limit = null, $group = null, $distinct = false, $column = '*', $prefix = null) - { - - // Begin preparation of the statement - $prepare = 'SELECT ' . ($distinct ? 'DISTINCT ' : '') . ($column == '*' ? '' : '`') . $column . ($column == '*' ? '' : '`') . ' FROM `' . ($prefix ? $prefix : Config::local('database', 'prefix')) . $table . '`'; - - // If $data is set and is an array continue - if (is_array($data)) { - $prepare .= ' WHERE'; - - foreach ($data as $key => $value) { - // Check if there's multiple statements - if (!is_array($value[0])) { - $temp = $value; - unset($value); - $value[0] = $temp; - } - - // Go over each data thing - foreach ($value as $sub => $val) { - $prepare .= ' `' . $key . '` ' . $val[1] . ' :' . $key . '_' . $sub . ($key == key(array_slice($data, -1, 1, true)) && $sub == key(array_slice($value, -1, 1, true)) ? '' : ' ' . (isset($val[2]) && $val[2] ? 'OR' : 'AND')); - - unset($sub); - unset($val); - } - - // Unset variables to be safe - unset($key); - unset($value); - } - } - - // If $group is set and is an array continue - if (is_array($group)) { - $prepare .= ' GROUP BY'; - - foreach ($group as $key => $value) { - $prepare .= ' `' . $value . '`' . ($key == key(array_slice($group, -1, 1, true)) ? '' : ','); - - // Unset variables to be safe - unset($key); - unset($value); - } - } - - // If $order is set and is an array continue - if (is_array($order)) { - $prepare .= ' ORDER BY `' . $order[0] . '`' . (!empty($order[1]) && $order[1] ? ' DESC' : ''); - } - - // If $limit is set and is an array continue - if (is_array($limit)) { - $prepare .= ' LIMIT'; - - foreach ($limit as $key => $value) { - $prepare .= ' ' . $value . ($key == key(array_slice($limit, -1, 1, true)) ? '' : ','); - - // Unset variables to be safe - unset($key); - unset($value); - } - } - - // Add the finishing semicolon - $prepare .= ';'; - - // Actually prepare the preration - $query = $this->sql->prepare($prepare); - - // Bind those parameters if $data is an array that is - if (is_array($data)) { - foreach ($data as $key => $value) { - // Check if there's multiple statements - if (!is_array($value[0])) { - $temp = $value; - unset($value); - $value[0] = $temp; - } - - // Go over each data thing - foreach ($value as $sub => $val) { - $query->bindParam(':' . $key . '_' . $sub, $val[0]); - - unset($sub); - unset($val); - } - - // Unset variables to be safe - unset($key); - unset($value); - } - } - - // Execute the prepared statements with parameters bound - $query->execute(); - - // Return the query - return $query; - } - - /** - * Summary of fetch - * - * @param string $table The table to select data from. - * @param bool $fetchAll Whether all result will be returned or just the first one. - * @param array $data The WHERE selectors. - * @param array $order The order in which the data is returned. - * @param array $limit The limit of what should be returned. - * @param array $group The way MySQL will group the data. - * @param bool $distinct Only return distinct values. - * @param string $column Only select from this column. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return array The data the database returned. - */ - public function fetch($table, $fetchAll = true, $data = null, $order = null, $limit = null, $group = null, $distinct = false, $column = '*', $prefix = null) - { - - // Run a select statement - $query = $this->select($table, $data, $order, $limit, $group, $distinct, $column, $prefix); - - // Return the output - return $fetchAll ? $query->fetchAll(PDO::FETCH_ASSOC) : $query->fetch(PDO::FETCH_ASSOC); - } - - /** - * Insert data into the database. - * - * @param string $table The table that the data will be inserted into. - * @param array $data The data that should be stored. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return bool Successfulness. - */ - public function insert($table, $data, $prefix = null) - { - - // Begin preparation of the statement - $prepare = 'INSERT INTO `' . ($prefix ? $prefix : Config::local('database', 'prefix')) . $table . '` '; - - // Run the foreach statement twice for (`stuff`) VALUES (:stuff) - for ($i = 0; $i < 2; $i++) { - $prepare .= '('; - // Do more shit, don't feel like describing this so yeah - foreach ($data as $key => $value) { - if (strlen($value)) { - $prepare .= ($i ? ':' : '`') . $key . ($i ? '' : '`') . ($key == key(array_slice($data, -1, 1, true)) ? '' : ', '); - } - } - - $prepare .= ')' . ($i ? ';' : ' VALUES '); - } - - // Actually prepare the preration - $query = $this->sql->prepare($prepare); - - // Bind those parameters - foreach ($data as $key => $value) { - if (strlen($value)) { - $query->bindParam(':' . $key, $value); - } - - // Unset variables to be safe - unset($key); - unset($value); - } - - // Execute the prepared statements with parameters bound - $result = $query->execute(); - - // Return whatever can be returned - return $result; - } - - /** - * Update existing database rows. - * - * @param string $table The table that the updated data will be inserted into. - * @param array $data The data that should be stored. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return bool Successfulness. - */ - public function update($table, $data, $prefix = null) - { - - // Begin preparation of the statement - $prepare = 'UPDATE `' . ($prefix ? $prefix : Config::local('database', 'prefix')) . $table . '`'; - - // Run a foreach on $data and complete the statement - foreach ($data as $key => $values) { - // Append WHERE or SET depending on where we are - $prepare .= ' ' . ($key ? 'WHERE' : 'SET'); - - // Do this complicated shit, I barely know what's going on anymore but it works - foreach ($values as $column => $column_data) { - $prepare .= ' `' . $column . '` ' . ($key ? $column_data[1] : '=') . ' :' . ($key ? 'w' : 's') . '_' . $column . ($column == key(array_slice($values, -1, 1, true)) ? ($key ? ';' : '') : ($key ? ' ' . (isset($value[2]) && $value[2] ? 'OR' : 'AND') : ',')); - } - } - - // Actually prepare the preration - $query = $this->sql->prepare($prepare); - - // Seperate the foreaches for the SET and WHERE clauses because it's fucking it up for some odd reason - // Bind Set Clauses - foreach ($data[0] as $key => $value) { - // Do the binding - $query->bindParam(':s_' . $key, $value); - - // Unset variables to be safe - unset($key); - unset($value); - } - - // Bind Where Clauses - foreach ($data[1] as $key => $values) { - // Assign the array entry to a variable because fuck strict standards - $value = $values[0]; - - // Binding two electrifying memes - $query->bindParam(':w_' . $key, $value); - - // Unset variables to be safe - unset($key); - unset($value); - unset($values); - } - - // Execute the prepared statements with parameters bound - $result = $query->execute(); - - // Return whatever can be returned - return $result; - } - - /** - * Deleted data from the database. - * - * @param string $table The table that the data will be removed from. - * @param array $data The pointers to what should be deleted. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return bool Successfulness. - */ - public function delete($table, $data, $prefix = null) - { - - // Begin preparation of the statement - $prepare = 'DELETE FROM `' . ($prefix ? $prefix : Config::local('database', 'prefix')) . $table . '`'; - - // If $data is set and is an array continue - if (is_array($data)) { - $prepare .= ' WHERE'; - - foreach ($data as $key => $value) { - $prepare .= ' `' . $key . '` ' . $value[1] . ' :' . $key . ($key == key(array_slice($data, -1, 1, true)) ? '' : ' ' . (isset($value[2]) && $value[2] ? 'OR' : 'AND')); - - // Unset variables to be safe - unset($key); - unset($value); - } - } - - // Actually prepare the preration - $query = $this->sql->prepare($prepare); - - // Bind those parameters - foreach ($data as $key => $value) { - $query->bindParam(':' . $key, $value[0]); - - // Unset variables to be safe - unset($key); - unset($value); - } - - // Execute the prepared statements with parameters bound - $result = $query->execute(); - - // Return whatever can be returned - return $result; - } - - /** - * Return the amount of rows from a table. - * - * @param string $table Table to count in. - * @param array $data Data that should be matched. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return array Array containing the SQL result. - */ - public function count($table, $data = null, $prefix = null) - { - - // Begin preparation of the statement - $prepare = 'SELECT COUNT(*) FROM `' . ($prefix ? $prefix : Config::local('database', 'prefix')) . $table . '`'; - - // If $data is set and is an array continue - if (is_array($data)) { - $prepare .= ' WHERE'; - - foreach ($data as $key => $value) { - $prepare .= ' `' . $key . '` ' . $value[1] . ' :' . $key . ($key == key(array_slice($data, -1, 1, true)) ? '' : ' ' . (isset($value[2]) && $value[2] ? 'OR' : 'AND')); - - // Unset variables to be safe - unset($key); - unset($value); - } - } - - // Add the finishing semicolon - $prepare .= ';'; - - // Actually prepare the preration - $query = $this->sql->prepare($prepare); - - // Bind those parameters if $data is an array that is - if (is_array($data)) { - foreach ($data as $key => $value) { - $query->bindParam(':' . $key, $value[0]); - - // Unset variables to be safe - unset($key); - unset($value); - } - } - - // Execute the prepared statements with parameters bound - $query->execute(); - - // Return the output - return $query->fetch(PDO::FETCH_BOTH); - } - - /** - * Get the id of the item that was last inserted into the database. - * - * @param string $name Sequence of which the last id should be returned. - * - * @return string The last inserted id. - */ - public function lastInsertID($name = null) - { - return $this->sql->lastInsertID($name); - } -} diff --git a/libraries/Database.php b/libraries/Database.php deleted file mode 100644 index 6c00583..0000000 --- a/libraries/Database.php +++ /dev/null @@ -1,151 +0,0 @@ - - */ -class Database -{ - /** - * The container for the wrapper. - * - * @var mixed - */ - public static $database; - - /** - * Initialise the database wrapper. - * - * @param string $wrapper The wrapper to wrap. - */ - public static function init($wrapper) - { - - // Make the wrapper class name lowercase - $wrapper = __NAMESPACE__ . '\DBWrapper\\' . strtolower($wrapper); - - // Check if the class exists - if (!class_exists($wrapper)) { - trigger_error('Failed to load database wrapper', E_USER_ERROR); - } - - // Initialise SQL wrapper - self::$database = new $wrapper; - } - - /** - * Select table row(s). - * - * @param string $table The table to select data from. - * @param array $data The WHERE selectors. - * @param array $order The order in which the data is returned. - * @param array $limit The limit of what should be returned. - * @param array $group The way MySQL will group the data. - * @param bool $distinct Only return distinct values. - * @param string $column Only select from this column. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return \PDOStatement The PDOStatement object for this action. - */ - public static function select($table, $data = null, $order = null, $limit = null, $group = null, $distinct = false, $column = '*', $prefix = null) - { - return self::$database->select($table, $data, $order, $limit, $group, $distinct, $column, $prefix); - } - - /** - * Summary of fetch - * - * @param string $table The table to select data from. - * @param bool $fetchAll Whether all result will be returned or just the first one. - * @param array $data The WHERE selectors. - * @param array $order The order in which the data is returned. - * @param array $limit The limit of what should be returned. - * @param array $group The way MySQL will group the data. - * @param bool $distinct Only return distinct values. - * @param string $column Only select from this column. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return array The data the database returned. - */ - public static function fetch($table, $fetchAll = true, $data = null, $order = null, $limit = null, $group = null, $distinct = false, $column = '*', $prefix = null) - { - return self::$database->fetch($table, $fetchAll, $data, $order, $limit, $group, $distinct, $column, $prefix); - } - - /** - * Insert data into the database. - * - * @param string $table The table that the data will be inserted into. - * @param array $data The data that should be stored. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return bool Successfulness. - */ - public static function insert($table, $data, $prefix = null) - { - return self::$database->insert($table, $data, $prefix); - } - - /** - * Update existing database rows. - * - * @param string $table The table that the updated data will be inserted into. - * @param array $data The data that should be stored. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return bool Successfulness. - */ - public static function update($table, $data, $prefix = null) - { - return self::$database->update($table, $data, $prefix); - } - - /** - * Deleted data from the database. - * - * @param string $table The table that the data will be removed from. - * @param array $data The pointers to what should be deleted. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return bool Successfulness. - */ - public static function delete($table, $data, $prefix = null) - { - return self::$database->delete($table, $data, $prefix); - } - - /** - * Return the amount of rows from a table. - * - * @param string $table Table to count in. - * @param array $data Data that should be matched. - * @param string $prefix Use a different table prefix than the one from the configuration. - * - * @return array Array containing the SQL result. - */ - public static function count($table, $data = null, $prefix = null) - { - return self::$database->count($table, $data, $prefix); - } - - /** - * Get the id of the item that was last inserted into the database. - * - * @param string $name Sequence of which the last id should be returned. - * - * @return string The last inserted id. - */ - public static function lastInsertID($name = null) - { - return self::$database->lastInsertID($name); - } -} diff --git a/libraries/File.php b/libraries/File.php index f144dcf..e20bf3e 100644 --- a/libraries/File.php +++ b/libraries/File.php @@ -81,17 +81,18 @@ class File $mime = (new finfo(FILEINFO_MIME_TYPE))->buffer($data); // Insert it into the database - Database::insert('uploads', [ - 'user_id' => $user->id, - 'file_data' => $data, - 'file_name' => $name, - 'file_mime' => $mime, - 'file_time' => time(), - 'file_expire' => $expire, + DB::prepare('INSERT INTO `{prefix}uploads` (`user_id`, `file_data`, `file_name`, `file_mime`, `file_time`, `file_expire`) VALUES (:id, :data, :name, :mime, :time, :expire)') + ->execute([ + 'id' => $user->id, + 'data' => $data, + 'name' => $name, + 'mime' => $mime, + 'time' => time(), + 'expire' => $expire, ]); // Get the last insert id - $id = Database::lastInsertID(); + $id = (int) DB::lastID(); // Return a new File object return new File($id); @@ -105,17 +106,21 @@ class File public function __construct($fileId) { // Attempt to get the database row - $fileRow = Database::fetch('uploads', false, ['file_id' => [$fileId, '=']]); + $fr = DB::prepare('SELECT * FROM `{prefix}uploads` WHERE `file_id` = :id'); + $fr->execute([ + 'id' => $fileId, + ]); + $fileRow = $fr->fetch(); // If anything was returned populate the variables if ($fileRow) { - $this->id = $fileRow['file_id']; - $this->user = User::construct($fileRow['user_id']); - $this->data = $fileRow['file_data']; - $this->name = $fileRow['file_name']; - $this->mime = $fileRow['file_mime']; - $this->time = $fileRow['file_time']; - $this->expire = $fileRow['file_expire']; + $this->id = $fileRow->file_id; + $this->user = User::construct($fileRow->user_id); + $this->data = $fileRow->file_data; + $this->name = $fileRow->file_name; + $this->mime = $fileRow->file_mime; + $this->time = $fileRow->file_time; + $this->expire = $fileRow->file_expire; } } @@ -124,8 +129,9 @@ class File */ public function delete() { - Database::delete('uploads', [ - 'file_id' => [$this->id, '='], + DB::prepare('DELETE FROM `{prefix}uploads` WHERE `file_id` = :id') + ->execute([ + 'id' => $this->id, ]); } } diff --git a/libraries/Forum/Forum.php b/libraries/Forum/Forum.php index 389961d..106615b 100644 --- a/libraries/Forum/Forum.php +++ b/libraries/Forum/Forum.php @@ -8,6 +8,7 @@ namespace Sakura\Forum; use Sakura\Database; +use Sakura\DB; use Sakura\Users; use Sakura\User; use Sakura\Perms; @@ -118,21 +119,25 @@ class Forum public function __construct($forumId = 0) { // Get the row from the database - $forumRow = Database::fetch('forums', false, ['forum_id' => [$forumId, '=']]); + $forumRow = DB::prepare('SELECT * FROM `{prefix}forums` WHERE `forum_id` = :id'); + $forumRow->execute([ + 'id' => $forumId, + ]); + $forumRow = $forumRow->fetch(); // Create permissions object $this->_permissions = new Perms(Perms::FORUM); // Populate the variables if ($forumRow) { - $this->id = $forumRow['forum_id']; - $this->order = $forumRow['forum_order']; - $this->name = $forumRow['forum_name']; - $this->description = $forumRow['forum_desc']; - $this->link = $forumRow['forum_link']; - $this->category = $forumRow['forum_category']; - $this->type = $forumRow['forum_type']; - $this->icon = $forumRow['forum_icon']; + $this->id = $forumRow->forum_id; + $this->order = $forumRow->forum_order; + $this->name = $forumRow->forum_name; + $this->description = $forumRow->forum_desc; + $this->link = $forumRow->forum_link; + $this->category = $forumRow->forum_category; + $this->type = $forumRow->forum_type; + $this->icon = $forumRow->forum_icon; } elseif ($forumId != 0) { $this->id = -1; } @@ -173,14 +178,18 @@ class Forum // Check if _forums is populated if (!count($this->_forums)) { // Get all rows with the category id set to the forum id - $forumRows = Database::fetch('forums', true, ['forum_category' => [$this->id, '=']], ['forum_order']); + $forumRows = DB::prepare('SELECT `forum_id` FROM `{prefix}forums` WHERE `forum_category` = :cat ORDER BY forum_order'); + $forumRows->execute([ + 'cat' => $this->id, + ]); + $forumRows = $forumRows->fetchAll(); // Create a storage array $forums = []; // Create new objects for each forum foreach ($forumRows as $forum) { - $forums[$forum['forum_id']] = new Forum($forum['forum_id']); + $forums[$forum->forum_id] = new Forum($forum->forum_id); } $this->_forums = $forums; @@ -202,19 +211,18 @@ class Forum // Check if _threads is populated if (!count($this->_threads)) { // Get all rows with the forum id for this forum - $announcements = Database::fetch('topics', true, ['forum_id' => [$this->id, '='], 'topic_type' => ['2', '=']], ['topic_last_reply', true]); - $sticky = Database::fetch('topics', true, ['forum_id' => [$this->id, '='], 'topic_type' => ['1', '=']], ['topic_last_reply', true]); - $regular = Database::fetch('topics', true, ['forum_id' => [$this->id, '='], 'topic_type' => ['0', '=']], ['topic_last_reply', true]); - - // Combine them into one array - $threadRows = array_merge($announcements, $sticky, $regular); + $threadRows = DB::prepare('SELECT * FROM `{prefix}topics` WHERE `forum_id` = :forum ORDER BY `topic_type` DESC, `topic_last_reply` DESC'); + $threadRows->execute([ + 'forum' => $this->id, + ]); + $threadRows = $threadRows->fetchAll(); // Create a storage array $threads = []; // Create new objects for each thread foreach ($threadRows as $thread) { - $threads[$thread['topic_id']] = new Thread($thread['topic_id']); + $threads[$thread->topic_id] = new Thread($thread->topic_id); } $this->_threads = $threads; @@ -236,10 +244,14 @@ class Forum // Check if _firstPost is set if ($this->_firstPost === null) { // Get the row - $firstPost = Database::fetch('posts', false, ['forum_id' => [$this->id, '=']], ['post_id'], [1]); + $firstPost = DB::prepare('SELECT `post_id` FROM `{prefix}posts` WHERE `forum_id` = :id ORDER BY `post_id` LIMIT 1'); + $firstPost->execute([ + 'id' => $this->id, + ]); + $firstPost = $firstPost->fetch(); // Create the post object - $post = new Post(empty($firstPost) ? 0 : $firstPost['post_id']); + $post = new Post(empty($firstPost) ? 0 : $firstPost->post_id); // Assign it to a "cache" variable $this->_firstPost = $post; @@ -261,10 +273,14 @@ class Forum // Check if _lastPost is set if ($this->_lastPost === null) { // Get the row - $lastPost = Database::fetch('posts', false, ['forum_id' => [$this->id, '=']], ['post_id', true], [1]); + $lastPost = DB::prepare('SELECT `post_id` FROM `{prefix}posts` WHERE `forum_id` = :id ORDER BY `post_id` DESC LIMIT 1'); + $lastPost->execute([ + 'id' => $this->id, + ]); + $lastPost = $lastPost->fetch(); // Create the post object - $post = new Post(empty($lastPost) ? 0 : $lastPost['post_id']); + $post = new Post(empty($lastPost) ? 0 : $lastPost->post_id); // Assign it to a "cache" variable $this->_lastPost = $post; @@ -283,7 +299,11 @@ class Forum */ public function threadCount() { - return Database::count('topics', ['forum_id' => [$this->id, '=']])[0]; + $count = DB::prepare('SELECT * FROM `{prefix}topics` WHERE `forum_id` = :id'); + $count->execute([ + 'id' => $this->id, + ]); + return $count->rowCount(); } /** @@ -293,7 +313,11 @@ class Forum */ public function postCount() { - return Database::count('posts', ['forum_id' => [$this->id, '=']])[0]; + $count = DB::prepare('SELECT * FROM `{prefix}posts` WHERE `forum_id` = :id'); + $count->execute([ + 'id' => $this->id, + ]); + return $count->rowCount(); } /** diff --git a/libraries/Forum/Post.php b/libraries/Forum/Post.php index 45dd3f4..7282106 100644 --- a/libraries/Forum/Post.php +++ b/libraries/Forum/Post.php @@ -9,6 +9,7 @@ namespace Sakura\Forum; use Sakura\Utils; use Sakura\Database; +use Sakura\DB; use Sakura\User; use Sakura\BBcode; use Sakura\Config; @@ -114,21 +115,25 @@ class Post public function __construct($postId) { // Attempt to get the database row - $postRow = Database::fetch('posts', false, ['post_id' => [$postId, '=']]); + $postRow = DB::prepare('SELECT * FROM `{prefix}posts` WHERE `post_id` = :id'); + $postRow->execute([ + 'id' => $postId, + ]); + $postRow = $postRow->fetch(); // Assign data if a row was returned if ($postRow) { - $this->id = $postRow['post_id']; - $this->thread = $postRow['topic_id']; - $this->forum = $postRow['forum_id']; - $this->poster = User::construct($postRow['poster_id']); - $this->ip = $postRow['poster_ip']; - $this->time = $postRow['post_time']; - $this->subject = $postRow['post_subject']; - $this->text = $postRow['post_text']; - $this->editTime = $postRow['post_edit_time']; - $this->editReason = $postRow['post_edit_reason']; - $this->editUser = User::construct($postRow['post_edit_user']); + $this->id = $postRow->post_id; + $this->thread = $postRow->topic_id; + $this->forum = $postRow->forum_id; + $this->poster = User::construct($postRow->poster_id); + $this->ip = $postRow->poster_ip; + $this->time = $postRow->post_time; + $this->subject = $postRow->post_subject; + $this->text = $postRow->post_text; + $this->editTime = $postRow->post_edit_time; + $this->editReason = $postRow->post_edit_reason; + $this->editUser = User::construct($postRow->post_edit_user); } // Parse the markup @@ -144,7 +149,7 @@ class Post * @param int $thread The ID of the thread this post is a reply to. * @param mixed $forum The forum this post is a reply in. * - * @return null|Post Either null, indicating a failure, or the Post object. + * @return null|self Either null, indicating a failure, or the Post object. */ public static function create($subject, $text, User $poster, $thread = 0, $forum = 0) { @@ -169,18 +174,19 @@ class Post } // Insert the post - Database::insert('posts', [ - 'topic_id' => $thread->id, - 'forum_id' => $thread->forum, - 'poster_id' => $poster->id, - 'poster_ip' => Net::IP(), - 'post_time' => time(), - 'post_subject' => $subject, - 'post_text' => $text, + DB::prepare('INSERT INTO `{prefix}posts` (`topic_id`, `forum_id`, `poster_id`, `poster_ip`, `post_time`, `post_subject`, `post_text`) VALUES (:thread, :forum, :user, :ip, :time, :subject, :text)') + ->execute([ + 'thread' => $thread->id, + 'forum' => $thread->forum, + 'user' => $poster->id, + 'ip' => Net::IP(), + 'time' => time(), + 'subject' => $subject, + 'text' => $text, ]); // Get post id - $id = Database::lastInsertID(); + $id = (int) DB::lastID(); // Update the last post date $thread->lastUpdate(); @@ -208,22 +214,19 @@ class Post $thread = new Thread($this->thread); // Update the post - Database::update('posts', [ - [ - 'topic_id' => $thread->id, - 'forum_id' => $thread->forum, - 'poster_id' => $this->poster->id, - 'poster_ip' => Net::pton(Net::IP()), - 'post_time' => $this->time, - 'post_subject' => $this->subject, - 'post_text' => $this->text, - 'post_edit_time' => $this->editTime, - 'post_edit_reason' => $this->editReason, - 'post_edit_user' => $this->editUser->id, - ], - [ - 'post_id' => [$this->id, '='], - ] + DB::prepare('UPDATE `{prefix}posts` SET `topic_id` = :thread, `forum_id` = :forum, `poster_id` = :user, `poster_ip` = :ip, `post_time` = :time, `post_subject` = :subject, `post_text` = :text, `post_edit_time` = :edit_time, `post_edit_reason` = :edit_reason, `post_edit_user` = :edit_user WHERE `post_id` = :post') + ->execute([ + 'post' => $this->id, + 'thread' => $thread->id, + 'forum' => $thread->forum, + 'user' => $this->poster->id, + 'ip' => Net::pton(Net::IP()), + 'time' => $this->time, + 'subject' => $this->subject, + 'text' => $this->text, + 'edit_time' => $this->editTime, + 'edit_reason' => $this->editReason, + 'edit_user' => $this->editUser->id, ]); // Return a new post object diff --git a/libraries/Forum/Thread.php b/libraries/Forum/Thread.php index 9a58286..57637f7 100644 --- a/libraries/Forum/Thread.php +++ b/libraries/Forum/Thread.php @@ -8,6 +8,7 @@ namespace Sakura\Forum; use Sakura\Database; +use Sakura\DB; use Sakura\Utils; /** @@ -129,21 +130,25 @@ class Thread public function __construct($threadId) { // Attempt to get the database row - $threadRow = Database::fetch('topics', false, ['topic_id' => [$threadId, '=']]); + $threadRow = DB::prepare('SELECT * FROM `{prefix}topics` WHERE `topic_id` = :id'); + $threadRow->execute([ + 'id' => $threadId, + ]); + $threadRow = $threadRow->fetch(); // Assign data if a row was returned if ($threadRow) { - $this->id = $threadRow['topic_id']; - $this->forum = $threadRow['forum_id']; - $this->hidden = (bool) $threadRow['topic_hidden']; - $this->title = $threadRow['topic_title']; - $this->time = $threadRow['topic_time']; - $this->timeLimit = $threadRow['topic_time_limit']; - $this->views = $threadRow['topic_views']; - $this->status = $threadRow['topic_status']; - $this->statusChange = $threadRow['topic_status_change']; - $this->type = $threadRow['topic_type']; - $this->oldForum = $threadRow['topic_old_forum']; + $this->id = $threadRow->topic_id; + $this->forum = $threadRow->forum_id; + $this->hidden = (bool) $threadRow->topic_hidden; + $this->title = $threadRow->topic_title; + $this->time = $threadRow->topic_time; + $this->timeLimit = $threadRow->topic_time_limit; + $this->views = $threadRow->topic_views; + $this->status = $threadRow->topic_status; + $this->statusChange = $threadRow->topic_status_change; + $this->type = $threadRow->topic_type; + $this->oldForum = $threadRow->topic_old_forum; } } @@ -155,21 +160,22 @@ class Thread * @param mixed $status Status of the thread. * @param mixed $type Type of thread. * - * @return Thread The new thread instance. + * @return self The new thread instance. */ public static function create($forum, $title, $status = 0, $type = 0) { // Create the database entry - Database::insert('topics', [ - 'forum_id' => $forum, - 'topic_title' => $title, - 'topic_time' => time(), - 'topic_status' => $status, - 'topic_type' => $type, + DB::prepare('INSERT INTO `{prefix}topics` (`forum_id`, `topic_title`, `topic_time`, `topic_status`, `topic_type`) VALUES (:forum, :title, :time, :status, :type)') + ->execute([ + 'forum' => $forum, + 'title' => $title, + 'time' => time(), + 'status' => $status, + 'type' => $type, ]); // Return the thread object - return new Thread(Database::lastInsertID()); + return new Thread(DB::lastID()); } /** @@ -178,13 +184,15 @@ class Thread public function delete() { // Delete all posts - Database::delete('posts', [ - 'topic_id' => [$this->id, '='], + DB::prepare('DELETE FROM `{prefix}posts` WHERE `topic_id` = :id') + ->execute([ + 'id' => $this->id, ]); // Delete thread meta - Database::delete('topics', [ - 'topic_id' => [$this->id, '='], + DB::prepare('DELETE FROM `{prefix}topics` WHERE `topic_id` = :id') + ->execute([ + 'id' => $this->id, ]); } @@ -197,48 +205,39 @@ class Thread public function move($forum, $setOld = true) { // Update all posts - Database::update('posts', [ - [ - 'forum_id' => $forum, - ], - [ - 'topic_id' => [$this->id, '='], - ] + DB::prepare('UPDATE `{prefix}posts` SET `forum_id` = :forum WHERE `topic_id` = :thread') + ->execute([ + 'forum' => $forum, + 'thread' => $this->id, ]); // Update thread meta - Database::update('topics', [ - [ - 'forum_id' => $forum, - 'topic_old_forum' => ($setOld ? $this->forum : 0), - ], - [ - 'topic_id' => [$this->id, '='], - ] + DB::prepare('UPDATE `{prefix}topics` SET `forum_id` = :forum, `topic_old_forum` = :old WHERE `topic_id` = :thread') + ->execute([ + 'forum' => $forum, + 'old' => ($setOld ? $this->forum : 0), + 'thread' => $this->id, ]); } /** * Update the thread data. * - * @return Thread The updated thread. + * @return self The updated thread. */ public function update() { // Update row - Database::update('topics', [ - [ - 'topic_hidden' => $this->hidden, - 'topic_title' => $this->title, - 'topic_time_limit' => $this->timeLimit, - 'topic_status' => $this->status, - 'topic_status_change' => $this->statusChange, - 'topic_type' => $this->type, - 'topic_old_forum' => $this->oldForum, - ], - [ - 'topic_id' => [$this->id, '='], - ] + DB::prepare('UPDATE `{prefix}topics` SET `topic_hidden` = :hidden, `topic_title` = :title, `topic_time_limit` = :limit, `topic_status` = :status, `topic_status_change` = :change, `topic_type` = :type, `topic_old_forum` = :old WHERE `topic_id` = :id') + ->execute([ + 'hidden' => $this->hidden, + 'title' => $this->title, + 'limit' => $this->timeLimit, + 'status' => $this->status, + 'change' => $this->statusChange, + 'type' => $this->type, + 'old' => $this->oldForum, + 'id' => $this->id, ]); // Return new object @@ -255,14 +254,18 @@ class Thread // Check if _posts is something if (!count($this->_posts)) { // Get all rows with the thread id - $postRows = Database::fetch('posts', true, ['topic_id' => [$this->id, '=']]); + $postRows = DB::prepare('SELECT `post_id` FROM `{prefix}posts` WHERE `topic_id` = :thread'); + $postRows->execute([ + 'thread' => $this->id, + ]); + $postRows = $postRows->fetchAll(); // Create a storage array $posts = []; // Create new post objects for each post foreach ($postRows as $post) { - $posts[$post['post_id']] = new Post($post['post_id']); + $posts[$post->post_id] = new Post($post->post_id); } $this->_posts = $posts; @@ -287,10 +290,14 @@ class Thread } // Get the row from the database - $post = Database::fetch('posts', false, ['topic_id' => [$this->id, '=']], ['post_id'], [1]); + $post = DB::prepare('SELECT `post_id` FROM `{prefix}posts` WHERE `topic_id` = :thread ORDER BY `post_id` LIMIT 1'); + $post->execute([ + 'thread' => $this->id, + ]); + $post = $post->fetch(); // Create the post class - $post = new Post($post ? $post['post_id'] : 0); + $post = new Post($post ? $post->post_id : 0); // Assign it to the cache var $this->_firstPost = $post; @@ -312,10 +319,14 @@ class Thread } // Get the row from the database - $post = Database::fetch('posts', false, ['topic_id' => [$this->id, '=']], ['post_id', true], [1]); + $post = DB::prepare('SELECT `post_id` FROM `{prefix}posts` WHERE `topic_id` = :thread ORDER BY `post_id` DESC LIMIT 1'); + $post->execute([ + 'thread' => $this->id, + ]); + $post = $post->fetch(); // Create the post class - $post = new Post($post ? $post['post_id'] : 0); + $post = new Post($post ? $post->post_id : 0); // Assign it to the cache var $this->_lastPost = $post; @@ -331,7 +342,11 @@ class Thread */ public function replyCount() { - return Database::count('posts', ['topic_id' => [$this->id, '=']])[0]; + $count = DB::prepare('SELECT * FROM `{prefix}posts` WHERE `topic_id` = :thread'); + $count->execute([ + 'thread' => $this->id, + ]); + return $count->rowCount(); } /** @@ -344,10 +359,15 @@ class Thread public function unread($user) { // Attempt to get track row from the database - $track = Database::fetch('topics_track', false, ['user_id' => [$user, '='], 'topic_id' => [$this->id, '='], 'mark_time' => [$this->lastPost()->time, '>']]); + $track = DB::prepare('SELECT * FROM `{prefix}topics_track` WHERE `user_id` = :user AND `topic_id` = :thread AND `mark_time` > :last'); + $track->execute([ + 'user' => $user, + 'thread' => $this->id, + 'last' => $this->lastPost()->time, + ]); // If nothing was returned it's obvious that the status is unread - if (!$track) { + if (!$track->rowCount()) { return true; } @@ -363,26 +383,29 @@ class Thread public function trackUpdate($user) { // Check if we already have a track record - $track = Database::fetch('topics_track', false, ['user_id' => [$user, '='], 'topic_id' => [$this->id, '='], 'forum_id' => [$this->forum, '=']]); + $track = DB::prepare('SELECT * FROM `{prefix}topics_track` WHERE `user_id` = :user AND `topic_id` = :thread AND `forum_id` = :forum'); + $track->execute([ + 'user' => $user, + 'thread' => $this->id, + 'forum' => $this->forum, + ]); // If so update it - if ($track) { - Database::update('topics_track', [ - [ - 'mark_time' => time(), - ], - [ - 'user_id' => [$user, '='], - 'topic_id' => [$this->id, '='], - ], + if ($track->rowCount()) { + DB::prepare('UPDATE `{prefix}topics_track` SET `mark_time` = :time WHERE `user_id` = :user AND `topic_id` = :thread') + ->execute([ + 'user' => $user, + 'thread' => $this->id, + 'time' => time(), ]); } else { // If not create a new record - Database::insert('topics_track', [ - 'user_id' => $user, - 'topic_id' => $this->id, - 'forum_id' => $this->forum, - 'mark_time' => time(), + DB::prepare('INSERT INTO `{prefix}topics_track` (`user_id`, `topic_id`, `forum_id`, `mark_time`) VALUES (:user, :thread, :forum, :time)') + ->execute([ + 'user' => $user, + 'thread' => $this->id, + 'forum' => $this->forum, + 'time' => time(), ]); } } @@ -392,13 +415,10 @@ class Thread */ public function viewsUpdate() { - Database::update('topics', [ - [ - 'topic_views' => $this->views + 1, - ], - [ - 'topic_id' => [$this->id, '='], - ], + DB::prepare('UPDATE `{prefix}topics` SET `topic_views` = :views WHERE `topic_id` = :thread') + ->execute([ + 'views' => $this->views + 1, + 'thread' => $this->id, ]); } @@ -407,13 +427,10 @@ class Thread */ public function lastUpdate() { - Database::update('topics', [ - [ - 'topic_last_reply' => time(), - ], - [ - 'topic_id' => [$this->id, '='], - ], + DB::prepare('UPDATE `{prefix}topics` SET `topic_last_reply` = :last WHERE `topic_id` = :thread') + ->execute([ + 'last' => time(), + 'thread' => $this->id, ]); } } diff --git a/libraries/News.php b/libraries/News.php index c07e63c..1d1654b 100644 --- a/libraries/News.php +++ b/libraries/News.php @@ -31,7 +31,11 @@ class News { // Get the news posts and assign them to $posts - $posts = Database::fetch('news', true, ['news_category' => [$category, '=']], ['news_id', true]); + $posts = DB::prepare('SELECT * FROM `{prefix}news` WHERE `news_category` = :cat ORDER BY `news_id` DESC'); + $posts->execute([ + 'cat' => $category, + ]); + $posts = $posts->fetchAll(\PDO::FETCH_ASSOC); // Attach poster data foreach ($posts as $post) { diff --git a/libraries/Perms.php b/libraries/Perms.php index 47ac097..ecc8a58 100644 --- a/libraries/Perms.php +++ b/libraries/Perms.php @@ -93,11 +93,30 @@ class Perms */ public function rank($rid, $conditions = [], $perm = 0) { - // Merge rank id and additional conditions - $conditions = array_merge(['rank_id' => [$rid, '='], 'user_id' => [0, '=']], $conditions); + // Build statement + $stmt = "SELECT * FROM `{prefix}{$this->table}` WHERE `rank_id` = :rank AND `user_id` = 0"; + + // Append additional conditionals (DBWrapper v1 format, except OR is ignored) + foreach ($conditions as $column => $value) { + $stmt .= " AND `{$column}` {$value[1]} :_retarded_{$column}"; + } + + // Prepare the statement + $get = DB::prepare($stmt); + + // Bind rank + $get->bindParam('rank', $rid); + + // Bind additionals + foreach ($conditions as $column => $value) { + $get->bindParam("_retarded_{$column}", $value[0]); + } + + // Execute! + $get->execute(); // Fetch from the db - $get = Database::fetch($this->table, false, $conditions); + $get = $get->fetch(\PDO::FETCH_ASSOC); // Check if anything was returned if ($get && array_key_exists($this->column, $get) && $get['rank_id']) { @@ -127,12 +146,31 @@ class Perms foreach (array_keys($user->ranks) as $rank) { $perm = $perm | $this->rank($rank, $conditions, $perm); } + + // Build statement + $stmt = "SELECT * FROM `{prefix}{$this->table}` WHERE `rank_id` = 0 AND `user_id` = :user"; - // Merge user id and additional conditions - $conditions = array_merge(['user_id' => [$uid, '='], 'rank_id' => [0, '=']], $conditions); + // Append additional conditionals (DBWrapper v1 format, except OR is ignored) + foreach ($conditions as $column => $value) { + $stmt .= " AND `{$column}` {$value[1]} :_retarded_{$column}"; + } + + // Prepare the statement + $get = DB::prepare($stmt); + + // Bind rank + $get->bindParam('user', $uid); + + // Bind additionals + foreach ($conditions as $column => $value) { + $get->bindParam("_retarded_{$column}", $value[0]); + } + + // Execute! + $get->execute(); // Fetch from the db - $get = Database::fetch($this->table, false, $conditions); + $get = $get->fetch(\PDO::FETCH_ASSOC); // Check if anything was returned if ($get && array_key_exists($this->column, $get) && $get['user_id']) { diff --git a/libraries/Rank.php b/libraries/Rank.php index 6e07964..4446b10 100644 --- a/libraries/Rank.php +++ b/libraries/Rank.php @@ -117,24 +117,22 @@ class Rank { // Get the rank database row - $rankRow = Database::fetch( - 'ranks', - false, - [ - 'rank_id' => [$rid, '=', true], - ] - ); + $rankRow = DB::prepare('SELECT * FROM `{prefix}ranks` WHERE `rank_id` = :id'); + $rankRow->execute([ + 'id' => $rid, + ]); + $rankRow = $rankRow->fetch(); // Check if the rank actually exists if ($rankRow) { - $this->id = $rankRow['rank_id']; - $this->name = $rankRow['rank_name']; - $this->hierarchy = $rankRow['rank_hierarchy']; - $this->multiple = $rankRow['rank_multiple']; - $this->hidden = (bool) $rankRow['rank_hidden']; - $this->colour = $rankRow['rank_colour']; - $this->description = $rankRow['rank_description']; - $this->title = $rankRow['rank_title']; + $this->id = $rankRow->rank_id; + $this->name = $rankRow->rank_name; + $this->hierarchy = $rankRow->rank_hierarchy; + $this->multiple = $rankRow->rank_multiple; + $this->hidden = (bool) $rankRow->rank_hidden; + $this->colour = $rankRow->rank_colour; + $this->description = $rankRow->rank_description; + $this->title = $rankRow->rank_title; } // Init the permissions @@ -191,7 +189,11 @@ class Rank public function users($justIds = false) { // Fetch all users part of this rank - $userIds = array_column(Database::fetch('user_ranks', true, ['rank_id' => [$this->id, '=']]), 'user_id'); + $fetch = DB::prepare('SELECT `user_id` FROM `{prefix}user_ranks` WHERE `rank_id` = :id'); + $fetch->execute([ + 'id' => $this->id, + ]); + $userIds = array_column($fetch->fetchAll(\PDO::FETCH_ASSOC), 'user_id'); // Just return that if we were asked for just the ids if ($justIds) { diff --git a/libraries/Session.php b/libraries/Session.php index 4633535..8b3b997 100644 --- a/libraries/Session.php +++ b/libraries/Session.php @@ -53,9 +53,10 @@ class Session public function destroy() { // Invalidate the session key - Database::delete('sessions', [ - 'session_key' => [$this->sessionId, '='], - 'user_id' => [$this->userId, '='], + DB::prepare('DELETE FROM `{prefix}sessions` WHERE `session_key` = :key AND `user_id` = :user') + ->execute([ + 'key' => $this->sessionId, + 'user' => $this->userId, ]); // Unset userId and sessionId @@ -74,7 +75,10 @@ class Session public function destroyAll() { // Delete all database entries with this user in it - Database::delete('sessions', ['user_id' => [$this->userId, '=']]); + DB::prepare('DELETE FROM `{prefix}sessions` WHERE `user_id` = :user') + ->execute([ + 'user' => $this->userId, + ]); // Destroy this session to finish it off $this->destroy(); @@ -93,14 +97,15 @@ class Session $session = hash('sha256', $this->userId . base64_encode('sakura' . mt_rand(0, 99999999)) . time()); // Insert the session into the database - Database::insert('sessions', [ - 'user_id' => $this->userId, - 'user_ip' => Net::pton(Net::IP()), - 'user_agent' => Utils::cleanString(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'No user agent header.'), - 'session_key' => $session, - 'session_start' => time(), - 'session_expire' => time() + 604800, - 'session_remember' => $permanent ? '1' : '0', + DB::prepare('INSERT INTO `{prefix}sessions` (`user_id`, `user_ip`, `user_agent`, `session_key`, `session_start`, `session_expire`, `session_remember`) VALUES (:id, :ip, :agent, :key, :start, :end, :remember)') + ->execute([ + 'id' => $this->userId, + 'ip' => Net::pton(Net::IP()), + 'agent' => Utils::cleanString(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'No user agent header.'), + 'key' => $session, + 'start' => time(), + 'end' => time() + 604800, + 'remember' => $permanent ? '1' : '0', ]); // Return the session key @@ -115,10 +120,12 @@ class Session public function validate() { // Get session from database - $session = Database::fetch('sessions', false, [ - 'user_id' => [$this->userId, '='], - 'session_key' => [$this->sessionId, '='], + $session = DB::prepare('SELECT * FROM `{prefix}sessions` WHERE `user_id` = :user AND `session_key` = :key'); + $session->execute([ + 'user' => $this->userId, + 'key' => $this->sessionId, ]); + $session = $session->fetch(); // Check if we actually got something in return if (!$session) { @@ -126,13 +133,13 @@ class Session } // Check if the session expired - if ($session['session_expire'] < time()) { + if ($session->session_expire < time()) { // ...and return false return 0; } // IP Check - $ipCheck = false;// Config::get('session_check'); + $ipCheck = false; // Forced disabled due to incompatibility with the Net class. -- Config::get('session_check'); // Origin checking if ($ipCheck) { @@ -178,18 +185,15 @@ class Session } // If the remember flag is set extend the session time - if ($session['session_remember']) { - Database::update('sessions', [ - [ - 'session_expire' => time() + 604800, - ], - [ - 'session_id' => [$session['session_id'], '='], - ], + if ($session->session_remember) { + DB::prepare('UPDATE `{prefix}sessions` SET `session_expire` = :expire WHERE `session_id` = :id') + ->execute([ + 'expire' => time() + 604800, + 'id' => $session->session_id, ]); } // Return 2 if the remember flag is set and return 1 if not - return $session['session_remember'] ? 2 : 1; + return $session->session_remember ? 2 : 1; } } diff --git a/libraries/User.php b/libraries/User.php index faf359b..13b39c7 100644 --- a/libraries/User.php +++ b/libraries/User.php @@ -258,24 +258,25 @@ class User $password = Hashing::createHash($password); // Insert the user into the database - Database::insert('users', [ - 'username' => $username, - 'username_clean' => $usernameClean, - 'password_hash' => $password[3], - 'password_salt' => $password[2], - 'password_algo' => $password[0], - 'password_iter' => $password[1], + DB::prepare('INSERT INTO `{prefix}users` (`username`, `username_clean`, `password_hash`, `password_salt`, `password_algo`, `password_iter`, `email`, `rank_main`, `register_ip`, `last_ip`, `user_registered`, `user_last_online`, `user_country`) VALUES (:uname, :uname_clean, :pw_hash, :pw_salt, :pw_algo, :pw_iter, :email, :rank, :r_ip, :l_ip, :registered, :l_online, :country)') + ->execute([ + 'uname' => $username, + 'uname_clean' => $usernameClean, + 'pw_hash' => $password[3], + 'pw_salt' => $password[2], + 'pw_algo' => $password[0], + 'pw_iter' => $password[1], 'email' => $emailClean, - 'rank_main' => 0, - 'register_ip' => Net::pton(Net::IP()), - 'last_ip' => Net::pton(Net::IP()), - 'user_registered' => time(), - 'user_last_online' => 0, - 'user_country' => Utils::getCountryCode(), + 'rank' => 0, + 'r_ip' => Net::pton(Net::IP()), + 'l_ip' => Net::pton(Net::IP()), + 'registered' => time(), + 'l_online' => 0, + 'country' => Utils::getCountryCode(), ]); // Get the last id - $userId = Database::lastInsertID(); + $userId = DB::lastID(); // Create a user object $user = self::construct($userId); @@ -298,49 +299,51 @@ class User private function __construct($uid) { // Get the user database row - $userRow = Database::fetch( - 'users', - false, - [ - 'user_id' => [$uid, '=', true], - 'username_clean' => [Utils::cleanString($uid, true), '=', true], - ] - ); + $userRow = DB::prepare('SELECT * FROM `{prefix}users` WHERE `user_id` = :id OR `username_clean` = :clean'); + $userRow->execute([ + 'id' => $uid, + 'clean' => Utils::cleanString($uid, true, true), + ]); + $userRow = $userRow->fetch(); // Populate the variables if ($userRow) { - $this->id = $userRow['user_id']; - $this->username = $userRow['username']; - $this->usernameClean = $userRow['username_clean']; - $this->passwordHash = $userRow['password_hash']; - $this->passwordSalt = $userRow['password_salt']; - $this->passwordAlgo = $userRow['password_algo']; - $this->passwordIter = $userRow['password_iter']; - $this->passwordChan = $userRow['password_chan']; - $this->email = $userRow['email']; - $this->mainRankId = $userRow['rank_main']; - $this->colour = $userRow['user_colour']; - $this->registerIp = $userRow['register_ip']; - $this->lastIp = $userRow['last_ip']; - $this->title = $userRow['user_title']; - $this->registered = $userRow['user_registered']; - $this->lastOnline = $userRow['user_last_online']; - $this->birthday = $userRow['user_birthday']; - $this->country = $userRow['user_country']; - $this->avatar = $userRow['user_avatar']; - $this->background = $userRow['user_background']; - $this->header = $userRow['user_header']; - $this->page = $userRow['user_page']; - $this->signature = $userRow['user_signature']; + $this->id = $userRow->user_id; + $this->username = $userRow->username; + $this->usernameClean = $userRow->username_clean; + $this->passwordHash = $userRow->password_hash; + $this->passwordSalt = $userRow->password_salt; + $this->passwordAlgo = $userRow->password_algo; + $this->passwordIter = $userRow->password_iter; + $this->passwordChan = $userRow->password_chan; + $this->email = $userRow->email; + $this->mainRankId = $userRow->rank_main; + $this->colour = $userRow->user_colour; + $this->registerIp = $userRow->register_ip; + $this->lastIp = $userRow->last_ip; + $this->title = $userRow->user_title; + $this->registered = $userRow->user_registered; + $this->lastOnline = $userRow->user_last_online; + $this->birthday = $userRow->user_birthday; + $this->country = $userRow->user_country; + $this->avatar = $userRow->user_avatar; + $this->background = $userRow->user_background; + $this->header = $userRow->user_header; + $this->page = $userRow->user_page; + $this->signature = $userRow->user_signature; } // Get all ranks - $ranks = Database::fetch('user_ranks', true, ['user_id' => [$this->id, '=']]); + $ranks = DB::prepare('SELECT * FROM `{prefix}user_ranks` WHERE `user_id` = :id'); + $ranks->execute([ + 'id' => $this->id, + ]); + $ranks = $ranks->fetchAll(); // Get the rows for all the ranks foreach ($ranks as $rank) { // Store the database row in the array - $this->ranks[$rank['rank_id']] = Rank::construct($rank['rank_id']); + $this->ranks[$rank->rank_id] = Rank::construct($rank->rank_id); } // Check if ranks were set @@ -423,10 +426,13 @@ class User public function isOnline() { // Get all sessions - $sessions = Database::fetch('sessions', true, ['user_id' => [$this->id, '=']]); + $sessions = DB::prepare('SELECT `user_id` FROM `{prefix}sessions` WHERE `user_id` = :id'); + $sessions->execute([ + 'id' => $this->id, + ]); // If there's no entries just straight up return false - if (!$sessions) { + if (!$sessions->rowCount()) { return false; } @@ -441,19 +447,19 @@ class User */ public function forumStats() { + $posts = DB::prepare('SELECT * FROM `{prefix}posts` WHERE `poster_id` = :id'); + $posts->execute([ + 'id' => $this->id, + ]); + + $threads = DB::prepare('SELECT DISTINCT * FROM `{prefix}posts` WHERE `poster_id` = :id GROUP BY `topic_id` ORDER BY `post_time`'); + $threads->execute([ + 'id' => $this->id, + ]); + return [ - 'posts' => Database::count( - 'posts', - ['poster_id' => [$this->id, '=']] - )[0], - 'topics' => count(Database::fetch( - 'posts', - true, - ['poster_id' => [$this->id, '=']], - ['post_time'], - null, - ['topic_id'] - )), + 'posts' => $posts->rowCount(), + 'topics' => $threads->rowCount(), ]; } @@ -476,9 +482,10 @@ class User // Save to the database foreach ($ranks as $rank) { - Database::insert('user_ranks', [ - 'rank_id' => $rank, - 'user_id' => $this->id, + DB::prepare('INSERT INTO `{prefix}ranks` (`rank_id`, `user_id`) VALUES (:rank, :user)') + ->execute([ + 'rank' => $rank, + 'user' => $this->id, ]); } } @@ -495,7 +502,11 @@ class User // Iterate over the ranks foreach ($remove as $rank) { - Database::delete('user_ranks', ['user_id' => [$this->id, '='], 'rank_id' => [$rank, '=']]); + DB::prepare('DELETE FROM `{prefix}user_ranks` WHERE `user_id` = :user AND `rank_id` = :rank') + ->execute([ + 'user' => $this->id, + 'rank' => $rank, + ]); } } @@ -509,13 +520,10 @@ class User public function setMainRank($rank) { // If it does exist update their row - Database::update('users', [ - [ - 'rank_main' => $rank, - ], - [ - 'user_id' => [$this->id, '='], - ], + DB::prepare('UPDATE `{prefix}users` SET `rank_main` = :rank WHERE `user_id` = :id') + ->execute([ + 'rank' => $rank, + 'id' => $this->id, ]); // Return true if everything was successful @@ -571,10 +579,11 @@ class User } // Add friend - Database::insert('friends', [ - 'user_id' => $this->id, - 'friend_id' => $uid, - 'friend_timestamp' => time(), + DB::prepare('INSERT INTO `{prefix}friends` (`user_id`, `friend_id`, `friend_timestamp`) VALUES (:user, :friend, :time)') + ->execute([ + 'user' => $this->id, + 'friend' => $uid, + 'time' => time(), ]); // Return true because yay @@ -599,17 +608,20 @@ class User return [0, 'USER_NOT_EXIST']; } + // Prepare the statement + $rem = DB::prepare('DELETE FROM `{prefix}friends` WHERE `user_id` = :user AND `friend_id` = :friend'); + // Remove friend - Database::delete('friends', [ - 'user_id' => [$this->id, '='], - 'friend_id' => [$uid, '='], + $rem->execute([ + 'user' => $this->id, + 'friend' => $uid, ]); // Attempt to remove the request if ($deleteRequest) { - Database::delete('friends', [ - 'friend_id' => [$this->id, '='], - 'user_id' => [$uid, '='], + $rem->execute([ + 'user' => $uid, + 'friend' => $this->id, ]); } @@ -627,16 +639,19 @@ class User public function isFriends($with) { // Accepted from this user - $user = Database::count('friends', [ - 'user_id' => [$this->id, '='], - 'friend_id' => [$with, '='], - ])[0]; + $get = DB::prepare('SELECT * FROM `{prefix}friends` WHERE `user_id` = :user AND `friend_id` = :friend'); + $get->execute([ + 'user' => $this->id, + 'friend' => $with, + ]); + $user = $get->rowCount(); // And the other user - $friend = Database::count('friends', [ - 'user_id' => [$with, '='], - 'friend_id' => [$this->id, '='], - ])[0]; + $get->execute([ + 'user' => $with, + 'friend' => $this->id, + ]); + $friend = $get->rowCount(); if ($user && $friend) { return 2; // Mutual friends @@ -663,34 +678,72 @@ class User // Select the correct level switch ($level) { + // Mutual case 2: // Get all the current user's friends - $self = array_column(Database::fetch('friends', true, ['user_id' => [$this->id, '=']]), 'friend_id'); + $self = DB::prepare('SELECT `friend_id` FROM `{prefix}friends` WHERE `user_id` = :user'); + $self->execute([ + 'user' => $this->id, + ]); + $self = array_column($self->fetchAll(\PDO::FETCH_ASSOC), 'friend_id'); + // Get all the people that added this user as a friend - $others = array_column(Database::fetch('friends', true, ['friend_id' => [$this->id, '=']]), 'user_id'); + $others = DB::prepare('SELECT `user_id` FROM `{prefix}friends` WHERE `friend_id` = :user'); + $others->execute([ + 'user' => $this->id, + ]); + $others = array_column($others->fetchAll(\PDO::FETCH_ASSOC), 'user_id'); + // Create a difference map $users = array_intersect($self, $others); break; + // Non-mutual (from user perspective) case 1: - $users = array_column(Database::fetch('friends', true, ['user_id' => [$this->id, '=']]), 'friend_id'); + $users = DB::prepare('SELECT `friend_id` FROM `{prefix}friends` WHERE `user_id` = :user'); + $users->execute([ + 'user' => $this->id, + ]); + $users = array_column($users->fetchAll(\PDO::FETCH_ASSOC), 'friend_id'); break; + // All friend cases case 0: default: // Get all the current user's friends - $self = array_column(Database::fetch('friends', true, ['user_id' => [$this->id, '=']]), 'friend_id'); + $self = DB::prepare('SELECT `friend_id` FROM `{prefix}friends` WHERE `user_id` = :user'); + $self->execute([ + 'user' => $this->id, + ]); + $self = array_column($self->fetchAll(\PDO::FETCH_ASSOC), 'friend_id'); + // Get all the people that added this user as a friend - $others = array_column(Database::fetch('friends', true, ['friend_id' => [$this->id, '=']]), 'user_id'); + $others = DB::prepare('SELECT `user_id` FROM `{prefix}friends` WHERE `friend_id` = :user'); + $others->execute([ + 'user' => $this->id, + ]); + $others = array_column($others->fetchAll(\PDO::FETCH_ASSOC), 'user_id'); + // Create a difference map $users = array_merge($others, $self); break; + // Open requests case -1: // Get all the current user's friends - $self = array_column(Database::fetch('friends', true, ['user_id' => [$this->id, '=']]), 'friend_id'); + $self = DB::prepare('SELECT `friend_id` FROM `{prefix}friends` WHERE `user_id` = :user'); + $self->execute([ + 'user' => $this->id, + ]); + $self = array_column($self->fetchAll(\PDO::FETCH_ASSOC), 'friend_id'); + // Get all the people that added this user as a friend - $others = array_column(Database::fetch('friends', true, ['friend_id' => [$this->id, '=']]), 'user_id'); + $others = DB::prepare('SELECT `user_id` FROM `{prefix}friends` WHERE `friend_id` = :user'); + $others->execute([ + 'user' => $this->id, + ]); + $others = array_column($others->fetchAll(\PDO::FETCH_ASSOC), 'user_id'); + // Create a difference map $users = array_diff($others, $self); break; @@ -770,8 +823,17 @@ class User // Create array and get values $profile = []; - $profileFields = Database::fetch('profilefields'); - $profileValuesRaw = Database::fetch('user_profilefields', true, ['user_id' => [$this->id, '=']]); + + $profileFields = DB::prepare('SELECT * FROM `{prefix}profilefields`'); + $profileFields->execute(); + $profileFields = $profileFields->fetchAll(\PDO::FETCH_ASSOC); + + $profileValuesRaw = DB::prepare('SELECT * FROM `{prefix}user_profilefields` WHERE `user_id` = :user'); + $profileValuesRaw->execute([ + 'user' => $this->id, + ]); + $profileValuesRaw = $profileValuesRaw->fetchAll(\PDO::FETCH_ASSOC); + $profileValueKeys = array_map(function ($a) { return $a['field_name']; }, $profileValuesRaw); @@ -849,8 +911,17 @@ class User // Create array and get values $options = []; - $optionFields = Database::fetch('optionfields'); - $optionValuesRaw = Database::fetch('user_optionfields', true, ['user_id' => [$this->id, '=']]); + + $optionFields = DB::prepare('SELECT * FROM `{prefix}optionfields`'); + $optionFields->execute(); + $optionFields = $optionFields->fetchAll(\PDO::FETCH_ASSOC); + + $optionValuesRaw = DB::prepare('SELECT * FROM `{prefix}user_optionfields` WHERE `user_id` = :user'); + $optionValuesRaw->execute([ + 'user' => $this->id, + ]); + $optionValuesRaw = $optionValuesRaw->fetchAll(\PDO::FETCH_ASSOC); + $optionValueKeys = array_map(function ($a) { return $a['field_name']; }, $optionValuesRaw); @@ -901,9 +972,11 @@ class User } // Attempt to retrieve the premium record from the database - $getRecord = Database::fetch('premium', false, [ - 'user_id' => [$this->id, '='], + $getRecord = DB::prepare('SELECT * FROM `{prefix}premium` WHERE `user_id` = :user'); + $getRecord->execute([ + 'user' => $this->id, ]); + $getRecord = $getRecord->fetch(); // If nothing was returned just return false if (empty($getRecord)) { @@ -911,12 +984,12 @@ class User } // Check if the Tenshi hasn't expired - if ($getRecord['premium_expire'] < time()) { - return [0, $getRecord['premium_start'], $getRecord['premium_expire']]; + if ($getRecord->premium_expire < time()) { + return [0, $getRecord->premium_start, $getRecord->premium_expire]; } // Else return the start and expiration date - return [1, $getRecord['premium_start'], $getRecord['premium_expire']]; + return [1, $getRecord->premium_start, $getRecord->premium_expire]; } /** @@ -927,9 +1000,11 @@ class User public function getWarnings() { // Do the database query - $getWarnings = Database::fetch('warnings', true, [ - 'user_id' => [$this->id, '='], + $getWarnings = DB::prepare('SELECT * FROM `{prefix}warnings` WHERE `user_id` = :user'); + $getWarnings->execute([ + 'user' => $this->id, ]); + $getWarnings = $getWarnings->fetchAll(\PDO::FETCH_ASSOC); // Storage array $warnings = []; @@ -938,7 +1013,10 @@ class User foreach ($getWarnings as $warning) { // Check if it hasn't expired if ($warning['warning_expires'] < time()) { - Database::delete('warnings', ['warning_id' => [$warning['warning_id'], '=']]); + DB::prepare('DELETE FROM `{prefix}warnings` WHERE `warning_id` = :warn') + ->execute([ + 'warn' => $warning['warning_id'], + ]); continue; } @@ -1001,12 +1079,13 @@ class User public function getUsernameHistory() { // Do the database query - $changes = Database::fetch('username_history', true, [ - 'user_id' => [$this->id, '='], - ], ['change_id', true]); + $changes = DB::prepare('SELECT * FROM `{prefix}username_history` WHERE `user_id` = :user ORDER BY `change_id` DESC'); + $changes->execute([ + 'user' => $this->id, + ]); - // Return all the warnings - return $changes; + // Return all the changes + return $changes->fetchAll(\PDO::FETCH_ASSOC); } /** @@ -1032,45 +1111,47 @@ class User } // Check if this username hasn't been used in the last amount of days set in the config - $getOld = Database::fetch('username_history', false, [ - 'username_old_clean' => [$username_clean, '='], - 'change_time' => [(Config::get('old_username_reserve') * 24 * 60 * 60), '>'], - ], ['change_id', true]); + $getOld = DB::prepare('SELECT * FROM `{prefix}username_history` WHERE `username_old_clean` = :clean AND `change_time` > :time ORDER BY `change_id` DESC'); + $getOld->execute([ + 'clean' => $username_clean, + 'time' => (Config::get('old_username_reserve') * 24 * 60 * 60), + ]); + $getOld = $getOld->fetch(); // Check if anything was returned - if ($getOld && $getOld['user_id'] != $this->id) { + if ($getOld && $getOld->user_id != $this->id) { return [0, 'TOO_RECENT', $getOld['change_time']]; } // Check if the username is already in use - $getInUse = Database::fetch('users', false, [ - 'username_clean' => [$username_clean, '='], + $getInUse = DB::prepare('SELECT * FROM `{prefix}users` WHERE `username_clean` = :clean'); + $getInUse->execute([ + 'clean' => $username_clean, ]); + $getInUse = $getInUse->fetch(); // Check if anything was returned if ($getInUse) { - return [0, 'IN_USE', $getInUse['user_id']]; + return [0, 'IN_USE', $getInUse->user_id]; } // Insert into username_history table - Database::insert('username_history', [ - 'change_time' => time(), - 'user_id' => $this->id, - 'username_new' => $username, - 'username_new_clean' => $username_clean, - 'username_old' => $this->username, - 'username_old_clean' => $this->usernameClean, + DB::prepare('INSERT INTO `{prefix}username_history` (`change_time`, `user_id`, `username_new`, `username_new_clean`, `username_old`, `username_old_clean`) VALUES (:time, :user, :new, :new_clean, :old, :old_clean)') + ->execute([ + 'time' => time(), + 'user' => $this->id, + 'new' => $username, + 'new_clean' => $username_clean, + 'old' => $this->username, + 'old_clean' => $this->usernameClean, ]); // Update userrow - Database::update('users', [ - [ - 'username' => $username, - 'username_clean' => $username_clean, - ], - [ - 'user_id' => [$this->id, '='], - ], + DB::prepare('UPDATE `{prefix}users` SET `username` = :username, `username_clean` = :clean WHERE `user_id` = :id') + ->execute([ + 'username' => $username, + 'clean' => $username_clean, + 'id' => $this->id, ]); // Return success @@ -1092,23 +1173,22 @@ class User } // Check if the username is already in use - $getInUse = Database::fetch('users', false, [ - 'email' => [$email, '='], + $getInUse = DB::prepare('SELECT * FROM `{prefix}users` WHERE `email` = :email'); + $getInUse->execute([ + 'email' => $email, ]); + $getInUse = $getInUse->fetch(); // Check if anything was returned if ($getInUse) { - return [0, 'IN_USE', $getInUse['user_id']]; + return [0, 'IN_USE', $getInUse->user_id]; } // Update userrow - Database::update('users', [ - [ - 'email' => $email, - ], - [ - 'user_id' => [$this->id, '='], - ], + DB::prepare('UPDATE `{prefix}users` SET `email` = :email WHERE `user_id` = :id') + ->execute([ + 'email' => $email, + 'id' => $this->id, ]); // Return success @@ -1159,17 +1239,14 @@ class User $password = Hashing::createHash($new); // Update userrow - Database::update('users', [ - [ - 'password_hash' => $password[3], - 'password_salt' => $password[2], - 'password_algo' => $password[0], - 'password_iter' => $password[1], - 'password_chan' => time(), - ], - [ - 'user_id' => [$this->id, '='], - ], + DB::prepare('UPDATE `{prefix}users` SET `password_hash` = :hash, `password_salt` = :salt, `password_algo` = :algo, `password_iter` = :iter, `password_chan` = :chan WHERE `user_id` = :id') + ->execute([ + 'hash' => $password[3], + 'salt' => $password[2], + 'algo' => $password[0], + 'iter' => $password[1], + 'chan' => time(), + 'id' => $this->id, ]); // Return success diff --git a/libraries/Users.php b/libraries/Users.php index ea9525d..44b06a0 100644 --- a/libraries/Users.php +++ b/libraries/Users.php @@ -85,13 +85,10 @@ class Users } // Update last online - Database::update('users', [ - [ - 'user_last_online' => time(), - ], - [ - 'user_id' => [$uid, '='], - ], + DB::prepare('UPDATE `{prefix}users` SET `user_last_online` = :lo WHERE `user_id` = :id') + ->execute([ + 'lo' => time(), + 'id' => $uid, ]); // Update the premium meta @@ -119,11 +116,12 @@ class Users } // Check if we haven't hit the rate limit - $rates = Database::fetch('login_attempts', true, [ - 'attempt_ip' => [Net::pton(Net::IP()), '='], - 'attempt_timestamp' => [time() - 1800, '>'], - 'attempt_success' => [0, '='], + $rates = DB::prepare('SELECT * FROM `{prefix}login_attempts` WHERE `attempt_ip` = :ip AND `attempt_timestamp` > :time AND `attempt_success` = 0'); + $rates->execute([ + 'ip' => Net::pton(Net::IP()), + 'time' => time() - 1800, ]); + $rates = $rates->fetchAll(\PDO::FETCH_ASSOC); if (count($rates) > 4) { return [0, 'RATE_LIMIT']; @@ -287,7 +285,11 @@ class Users } // Check if the e-mail has already been used - if (Database::count('users', ['email' => [$email, '=']])[0] > 0) { + $emailCheck = DB::prepare('SELECT `user_id` FROM `{prefix}users` WHERE `email` = :email'); + $emailCheck->execute([ + 'email' => $email, + ]); + if ($emailCheck->rowCount() > 0) { return [0, 'EMAIL_EXISTS']; } @@ -338,10 +340,12 @@ class Users $emailClean = Utils::cleanString($email, true); // Do database request - $user = Database::fetch('users', false, [ - 'username_clean' => [$usernameClean, '='], - 'email' => [$emailClean, '='], + $user = DB::prepare('SELECT * FROM `{prefix}users` WHERE `username_clean` = :clean AND `email` = :email'); + $user->execute([ + 'clean' => $usernameClean, + 'email' => $emailClean, ]); + $user = $user->fetch(\PDO::FETCH_ASSOC); // Check if user exists if (count($user) < 2) { @@ -417,20 +421,16 @@ class Users // Hash the password $password = Hashing::createHash($newpass); - $time = time(); // Update the user - Database::update('users', [ - [ - 'password_hash' => $password[3], - 'password_salt' => $password[2], - 'password_algo' => $password[0], - 'password_iter' => $password[1], - 'password_chan' => $time, - ], - [ - 'user_id' => [$uid, '='], - ], + DB::prepare('UPDATE `{prefix}users` SET `password_hash` = :hash, `password_salt` = :salt, `password_algo` = :algo, `password_iter` = :iter, `password_chan` = :chan WHERE `user_id` = :id') + ->execute([ + 'hash' => $password[3], + 'salt' => $password[2], + 'algo' => $password[0], + 'iter' => $password[1], + 'chan' => time(), + 'id' => $uid, ]); // Return success @@ -457,10 +457,12 @@ class Users $emailClean = Utils::cleanString($email, true); // Do database request - $user = Database::fetch('users', false, [ - 'username_clean' => [$usernameClean, '='], - 'email' => [$emailClean, '='], + $user = DB::prepare('SELECT * FROM `{prefix}users` WHERE `username_clean` = :clean AND `email` = :email'); + $user->execute([ + 'clean' => $usernameClean, + 'email' => $emailClean, ]); + $user = $user->fetch(\PDO::FETCH_ASSOC); // Check if user exists if (count($user) < 2) { @@ -580,21 +582,23 @@ class Users /** * Check if a user exists. * - * @param mixed $user The Username or ID. - * @param bool $id Use id instead. + * @param mixed $id The Username or ID. + * @param mixed $unused Unused variable. * * @return mixed Returns the ID if it exists, false otherwise. */ - public static function userExists($user, $id = true) + public static function userExists($id, $unused = null) { - // Clean string - $user = Utils::cleanString($user, true); - // Do database request - $user = Database::fetch('users', true, [($id ? 'user_id' : 'username_clean') => [$user, '=']]); + $user = DB::prepare('SELECT * FROM `{prefix}users` WHERE `user_id` = :id OR `username_clean` = :clean'); + $user->execute([ + 'id' => $id, + 'clean' => Utils::cleanString($id, true, true), + ]); + $user = $user->fetch(); // Return count (which would return 0, aka false, if nothing was found) - return count($user) ? $user[0]['user_id'] : false; + return $user ? $user->user_id : false; } /** @@ -605,7 +609,9 @@ class Users public static function getProfileFields() { // Get profile fields - $profileFields = Database::fetch('profilefields'); + $profileFields = DB::prepare('SELECT * FROM `{prefix}profilefields`'); + $profileFields->execute(); + $profileFields = $profileFields->fetchAll(\PDO::FETCH_ASSOC); // If there's nothing just return null if (!count($profileFields)) { @@ -634,7 +640,9 @@ class Users public static function getOptionFields() { // Get option fields - $optionFields = Database::fetch('optionfields'); + $optionFields = DB::prepare('SELECT * FROM `{prefix}optionfields`'); + $optionFields->execute(); + $optionFields = $optionFields->fetchAll(\PDO::FETCH_ASSOC); // If there's nothing just return null if (!count($optionFields)) { @@ -672,10 +680,14 @@ class Users $return = []; // Get all online users in the past 5 minutes - $getAll = Database::fetch('users', true, ['user_last_online' => [$time, '>']]); + $getAll = DB::prepare('SELECT * FROM `{prefix}users` WHERE `user_last_online` > :lo'); + $getAll->execute([ + 'lo' => $time, + ]); + $getAll = $getAll->fetchAll(); foreach ($getAll as $user) { - $return[] = User::construct($user['user_id']); + $return[] = User::construct($user->user_id); } // Return all the online users @@ -693,9 +705,11 @@ class Users public static function addUserPremium($id, $seconds) { // Check if there's already a record of premium for this user in the database - $getUser = Database::fetch('premium', false, [ - 'user_id' => [$id, '='], + $getUser = DB::prepare('SELECT * FROM `{prefix}premium` WHERE `user_id` = :user'); + $getUser->execute([ + 'user' => $id, ]); + $getUser = $getUser->fetch(\PDO::FETCH_ASSOC); // Calculate the (new) start and expiration timestamp $start = isset($getUser['premium_start']) ? $getUser['premium_start'] : time(); @@ -703,19 +717,17 @@ class Users // If the user already exists do an update call, otherwise an insert call if (empty($getUser)) { - Database::insert('premium', [ - 'user_id' => $id, - 'premium_start' => $start, - 'premium_expire' => $expire, + DB::prepare('INSERT INTO `{prefix}premium` (`user_id`, `premium_start`, `premium_expire`) VALUES (:user, :start, :expire)') + ->execute([ + 'user' => $id, + 'start' => $start, + 'expire' => $expire, ]); } else { - Database::update('premium', [ - [ - 'premium_expire' => $expire, - ], - [ - 'user_id' => [$id, '='], - ], + DB::prepare('UPDATE `{prefix}premium` SET `premium_expire` = :expire WHERE `user_id` = :id') + ->execute([ + 'expire' => $expire, + 'user_id' => $id, ]); } @@ -751,8 +763,9 @@ class Users } } elseif (!$check[0]) { // Remove the expired entry - Database::delete('premium', [ - 'user_id' => [$user->id, '='], + DB::prepare('DELETE FROM `{prefix}premium` WHERE `user_id` = :user') + ->execute([ + 'user' => $user->id, ]); // Else remove the rank from them @@ -769,56 +782,18 @@ class Users */ public static function getUsersByIP($ip) { - // Get users by registration IP - $registeredFrom = Database::fetch('users', true, ['register_ip' => [$ip, '=']]); - - // Get users by last IP - $lastFrom = Database::fetch('users', true, ['last_ip' => [$ip, '='], 'register_ip' => [$ip, '!=']]); - - // Merge the arrays - $users = array_merge($registeredFrom, $lastFrom); + // Get the users + $users = DB::prepare('SELECT * FROM `{prefix}users` WHERE `register_ip` = :rip OR `last_ip` = :lip'); + $users->execute([ + 'rip' => $ip, + 'lip' => $ip, + ]); + $users = $users->fetchAll(\PDO::FETCH_ASSOC); // Return the array with users return $users; } - /** - * Get all users. - * - * @param mixed $includeInactive include deactivated users. - * @param mixed $includeRestricted include restricted users. - * - * @return array The users. - */ - public static function getAllUsers($includeInactive = true, $includeRestricted = false) - { - // Execute query - $getUsers = Database::fetch('users', true); - - // Define variable - $users = []; - - // Reorder shit - foreach ($getUsers as $user) { - $user = User::construct($user['user_id']); - - // Skip if inactive and not include deactivated users - if (!$includeInactive && $user->permission(Site::DEACTIVATED)) { - continue; - } - - // Skip if inactive and not include restricted users - if (!$includeRestricted && $user->permission(Site::RESTRICTED)) { - continue; - } - - $users[$user->id] = $user; - } - - // and return an array with the users - return $users; - } - /** * Get all ranks. * @@ -827,14 +802,16 @@ class Users public static function getAllRanks() { // Execute query - $getRanks = Database::fetch('ranks', true); + $getRanks = DB::prepare('SELECT * FROM `{prefix}ranks`'); + $getRanks->execute(); + $getRanks = $getRanks->fetchAll(); // Define variable $ranks = []; // Reorder shit foreach ($getRanks as $rank) { - $ranks[$rank['rank_id']] = Rank::construct($rank['rank_id']); + $ranks[$rank->rank_id] = Rank::construct($rank->rank_id); } // and return an array with the ranks @@ -854,19 +831,18 @@ class Users public static function getNotifications($uid = null, $timediff = 0, $excludeRead = true, $markRead = false) { // Prepare conditions - $conditions = []; - $conditions['user_id'] = [($uid ? $uid : self::checkLogin()[0]), '=']; - - if ($timediff) { - $conditions['alert_timestamp'] = [time() - $timediff, '>']; - } - - if ($excludeRead) { - $conditions['alert_read'] = [0, '=']; - } + $uid = $uid ? $uid : self::checkLogin()[0]; + $time = $timediff ? time() - $timediff : '%'; + $read = $excludeRead ? '0' : '%'; // Get notifications for the database - $notifications = Database::fetch('notifications', true, $conditions); + $notifications = DB::prepare('SELECT * FROM `{prefix}notifications` WHERE `user_id` = :user AND `alert_timestamp` > :time AND `alert_read` = :read'); + $notifications->execute([ + 'user' => $uid, + 'time' => $time, + 'read' => $read, + ]); + $notifications = $notifications->fetchAll(\PDO::FETCH_ASSOC); // Mark the notifications as read if ($markRead) { @@ -895,13 +871,10 @@ class Users public static function markNotificationRead($id, $mode = true) { // Execute an update statement - Database::update('notifications', [ - [ - 'alert_read' => ($mode ? 1 : 0), - ], - [ - 'alert_id' => [$id, '='], - ], + DB::prepare('UPDATE `{prefix}notifications` SET `alert_read` = :read WHERE `alert_id` = :id') + ->execute([ + 'read' => ($mode ? 1 : 0), + 'id' => $id, ]); } @@ -918,20 +891,18 @@ class Users */ public static function createNotification($user, $title, $text, $timeout = 60000, $img = 'FONT:fa-info-circle', $link = '', $sound = 0) { - // Get current timestamp - $time = time(); - // Insert it into the database - Database::insert('notifications', [ - 'user_id' => $user, - 'alert_timestamp' => $time, - 'alert_read' => 0, - 'alert_sound' => ($sound ? 1 : 0), - 'alert_title' => $title, - 'alert_text' => $text, - 'alert_link' => $link, - 'alert_img' => $img, - 'alert_timeout' => $timeout, + DB::prepare('INSERT INTO `{prefix}notifications` (`user_id`, `alert_timestamp`, `alert_read`, `alert_sound`, `alert_title`, `alert_text`, `alert_link`, `alert_img`, `alert_timeout`) VALUES (:user, :time, :read, :sound, :title, :text, :link, :img, :timeout)') + ->execute([ + 'user' => $user, + 'time' => time(), + 'read' => 0, + 'sound' => ($sound ? 1 : 0), + 'title' => $title, + 'text' => $text, + 'link' => $link, + 'img' => $img, + 'timeout' => $timeout, ]); } @@ -942,6 +913,12 @@ class Users */ public static function getNewestUserId() { - return Database::fetch('users', false, ['rank_main' => [Config::get('restricted_rank_id'), '!=']], ['user_id', true], ['1'])['user_id']; + $get = DB::prepare('SELECT `user_id` FROM `{prefix}users` WHERE `rank_main` != :restricted ORDER BY `user_id` DESC LIMIT 1'); + $get->execute([ + 'restricted' => Config::get('restricted_rank_id'), + ]); + $get = $get->fetch(); + + return $get ? $get->user_id : 0; } } diff --git a/libraries/Utils.php b/libraries/Utils.php index 6ef6fdb..d83b0d5 100644 --- a/libraries/Utils.php +++ b/libraries/Utils.php @@ -66,31 +66,32 @@ class Utils $backtrace = base64_encode(json_encode(debug_backtrace())); // Check if this error has already been logged in the past - if ($past = Database::fetch( - 'error_log', - false, - [ - 'error_backtrace' => [$backtrace, '=', true], - 'error_string' => [$errstr, '='], - 'error_line' => [$errline, '='], - ] - )) { + $past = DB::prepare('SELECT * FROM `{prefix}error_log` WHERE `error_backtrace` = :bc OR (`error_string` = :str AND `error_line` = :li)'); + $past->execute([ + 'bc' => $backtrace, + 'str' => $errstr, + 'li' => $errline, + ]); + $past = $past->fetch(); + + if ($past) { // If so assign the errid - $errid = $past['error_id']; + $errid = $past->error_id; } else { // Create an error ID $errid = substr(md5(microtime()), rand(0, 22), 10); // Log the error - Database::insert('error_log', [ - 'error_id' => $errid, - 'error_timestamp' => date("r"), - 'error_revision' => SAKURA_VERSION, - 'error_type' => $errno, - 'error_line' => $errline, - 'error_string' => $errstr, - 'error_file' => $errfile, - 'error_backtrace' => $backtrace, + DB::prepare('INSERT INTO `{prefix}error_log` (`error_id`, `error_timestamp`, `error_revision`, `error_type`, `error_line`, `error_string`, `error_file`, `error_backtrace`) VALUES (:id, :time, :rev, :type, :line, :string, :file, :bc)') + ->execute([ + 'id' => $errid, + 'time' => date("r"), + 'rev' => SAKURA_VERSION, + 'type' => $errno, + 'line' => $errline, + 'string' => $errstr, + 'file' => $errfile, + 'bc' => $backtrace, ]); } } @@ -430,7 +431,9 @@ class Utils $data = []; // Get database stuff - $table = Database::fetch('premium_log', true, null, ['transaction_id', true]); + $table = DB::prepare('SELECT * FROM `{prefix}premium_log` ORDER BY `transaction_id` DESC'); + $table->execute(); + $table = $table->fetchAll(\PDO::FETCH_ASSOC); // Add raw table data to data array $data['table'] = $table; @@ -465,11 +468,12 @@ class Utils */ public static function updatePremiumTracker($id, $amount, $comment) { - Database::insert('premium_log', [ - 'user_id' => $id, - 'transaction_amount' => $amount, - 'transaction_date' => time(), - 'transaction_comment' => $comment, + DB::prepare('INSERT INTO `{prefix}premium_log` (`user_id`, `transaction_amount`, `transaction_date`, `transaction_comment`) VALUES (:user, :amount, :date, :comment)') + ->execute([ + 'user' => $id, + 'amount' => $amount, + 'date' => time(), + 'comment' => $comment, ]); } diff --git a/public/authenticate.php b/public/authenticate.php index 4beaed5..4732d57 100644 --- a/public/authenticate.php +++ b/public/authenticate.php @@ -184,11 +184,12 @@ if (isset($_REQUEST['mode'])) { // Check if we're not RATE_LIMIT if ($login[1] != 'RATE_LIMIT') { // Add to database - Database::insert('login_attempts', [ - 'attempt_success' => $login[0], - 'attempt_timestamp' => time(), - 'attempt_ip' => Net::pton(Net::IP()), - 'user_id' => isset($login[2]) ? $login[2] : 0, + DB::prepare('INSERT INTO `{prefix}login_attempts` (`attempt_success`, `attempt_timestamp`, `attempt_ip`, `user_id`) VALUES (:succ, :time, :ip, :user)') + ->execute([ + 'succ' => $login[0], + 'time' => time(), + 'ip' => Net::pton(Net::IP()), + 'user' => isset($login[2]) ? $login[2] : 0, ]); } diff --git a/public/posting.php b/public/posting.php index 9b4ed15..f194fcd 100644 --- a/public/posting.php +++ b/public/posting.php @@ -67,9 +67,12 @@ if (!isset($thread) && !$forum->permission(ForumPerms::CREATE_THREADS, $currentU $mode = isset($_GET['f']) ? 'f' : (isset($_GET['t']) ? 't' : (isset($_GET['p']) ? 'p' : null)); +$emotes = DB::prepare('SELECT * FROM `{prefix}emoticons`'); +$emotes->execute(); + // Include emotes and bbcodes $posting = [ - 'emoticons' => Database::fetch('emoticons'), + 'emoticons' => $emotes->fetchAll(), ]; // Check if we're in reply mode @@ -198,8 +201,9 @@ if ($mode != 'f') { // Post deletion code if (isset($_POST['yes'])) { // Delete the post - Database::delete('posts', [ - 'post_id' => [$_POST['post_id'], '='], + DB::prepare('DELETE FROM `{prefix}posts` WHERE `post_id` = :post') + ->execute([ + 'post' => $_POST['post_id'], ]); // Reload the topic @@ -207,8 +211,9 @@ if ($mode != 'f') { // If there's no more posts left in the topic delete it as well if (!$thread->replyCount()) { - Database::delete('topics', [ - 'topic_id' => [$thread->id, '='], + DB::prepare('DELETE FROM `{prefix}topics` WHERE `topic_id` = :thread') + ->execute([ + 'thread' => $thread->id, ]); } diff --git a/public/settings.php b/public/settings.php index 9a89b43..8ab6785 100644 --- a/public/settings.php +++ b/public/settings.php @@ -451,14 +451,14 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification // Assign the correct column and title to a variable switch ($mode) { case 'background': - $column = 'user_background'; + $stmt = 'UPDATE `{prefix}users` SET `user_background` = :img WHERE `user_id` = :user'; $msgTitle = 'Background'; $current = $currentUser->background; $permission = $currentUser->permission(Site::CHANGE_BACKGROUND); break; case 'header': - $column = 'user_header'; + $stmt = 'UPDATE `{prefix}users` SET `user_header` = :img WHERE `user_id` = :user'; $msgTitle = 'Header'; $current = $currentUser->header; $permission = $currentUser->permission(Site::CHANGE_HEADER); @@ -466,7 +466,7 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification case 'avatar': default: - $column = 'user_avatar'; + $stmt = 'UPDATE `{prefix}users` SET `user_avatar` = :img WHERE `user_id` = :user'; $msgTitle = 'Avatar'; $current = $currentUser->avatar; $permission = $currentUser->permission(Site::CHANGE_AVATAR); @@ -617,13 +617,10 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification } // Update table - Database::update('users', [ - [ - $column => $fileId, - ], - [ - 'user_id' => [$currentUser->id, '='], - ], + DB::prepare($stmt) + ->execute([ + 'img' => $fileId, + 'user' => $currentUser->id, ]); // Set render data @@ -643,11 +640,16 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification foreach ($fields as $field) { // Add to the store array if (isset($_POST['profile_' . $field['field_identity']]) && !empty($_POST['profile_' . $field['field_identity']])) { - Database::delete('user_profilefields', ['user_id' => [$currentUser->id, '='], 'field_name' => [$field['field_identity'], '=']]); - Database::insert('user_profilefields', [ - 'user_id' => $currentUser->id, - 'field_name' => $field['field_identity'], - 'field_value' => $_POST['profile_' . $field['field_identity']], + DB::prepare('DELETE FROM `{prefix}user_profilefields` WHERE `user_id` = :user AND `field_name` = :id') + ->execute([ + 'user' => $currentUser->id, + 'id' => $field['field_identity'], + ]); + DB::prepare('INSERT INTO `{prefix}user_profilefields` (`user_id`, `field_name`, `field_value`) VALUES (:user, :name, :value)') + ->execute([ + 'user' => $currentUser->id, + 'name' => $field['field_identity'], + 'value' => $_POST['profile_' . $field['field_identity']], ]); } @@ -657,11 +659,16 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification foreach ($field['field_additional'] as $addKey => $addVal) { // Add to the array $store = (isset($_POST['profile_additional_' . $addKey]) || !empty($_POST['profile_additional_' . $addKey])) ? $_POST['profile_additional_' . $addKey] : false; - Database::delete('user_profilefields', ['user_id' => [$currentUser->id, '='], 'field_name' => [$addKey, '=']]); - Database::insert('user_profilefields', [ - 'user_id' => $currentUser->id, - 'field_name' => $addKey, - 'field_value' => $store, + DB::prepare('DELETE FROM `{prefix}user_profilefields` WHERE `user_id` = :user AND `field_name` = :id') + ->execute([ + 'user' => $currentUser->id, + 'id' => $addKey, + ]); + DB::prepare('INSERT INTO `{prefix}user_profilefields` (`user_id`, `field_name`, `field_value`) VALUES (:user, :name, :value)') + ->execute([ + 'user' => $currentUser->id, + 'name' => $addKey, + 'value' => $store, ]); } } @@ -724,13 +731,10 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification [$_POST['birthday_year'], $_POST['birthday_month'], $_POST['birthday_day']] ); - Database::update('users', [ - [ - 'user_birthday' => $birthdate, - ], - [ - 'user_id' => [$currentUser->id, '='], - ], + DB::prepare('UPDATE `{prefix}users` SET `user_birthday` = :bd WHERE `user_id` = :id') + ->execute([ + 'bd' => $birthdate, + 'id' => $currentUser->id, ]); } break; @@ -742,7 +746,11 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification // Go over each field foreach ($fields as $field) { - Database::delete('user_optionfields', ['user_id' => [$currentUser->id, '='], 'field_name' => [$field['option_id'], '=']]); + DB::prepare('DELETE FROM `{prefix}user_optionfields` WHERE `user_id` = :user AND `field_name` = :id') + ->execute([ + 'user' => $currentUser->id, + 'id' => $field['option_id'], + ]); // Make sure the user has sufficient permissions to complete this action if (!$currentUser->permission(constant('Sakura\Perms\Site::' . $field['option_permission']))) { @@ -751,10 +759,11 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification if (isset($_POST['option_' . $field['option_id']]) && !empty($_POST['option_' . $field['option_id']])) { - Database::insert('user_optionfields', [ - 'user_id' => $currentUser->id, - 'field_name' => $field['option_id'], - 'field_value' => $_POST['option_' . $field['option_id']], + DB::prepare('INSERT INTO `{prefix}user_optionfields` (`user_id`, `field_name`, `field_value`) VALUES (:user, :name, :value)') + ->execute([ + 'user' => $currentUser->id, + 'name' => $field['option_id'], + 'value' => $_POST['option_' . $field['option_id']], ]); } } @@ -792,17 +801,11 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification } // Update database - Database::update( - 'users', - [ - [ - 'user_title' => (isset($_POST['usertitle']) ? $_POST['usertitle'] : null), - ], - [ - 'user_id' => [$currentUser->id, '='], - ], - ] - ); + DB::prepare('UPDATE `{prefix}users` SET `user_title` = :title WHERE `user_id` = :id') + ->execute([ + 'title' => (isset($_POST['usertitle']) ? $_POST['usertitle'] : null), + 'id' => $currentUser->id, + ]); // Set render data $renderData['page'] = [ @@ -935,7 +938,11 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification } // Update database - Database::update('users', [['user_page' => $_POST['userpage']], ['user_id' => [$currentUser->id, '=']]]); + DB::prepare('UPDATE `{prefix}users` SET `user_page` = :up WHERE `user_id` = :id') + ->execute([ + 'up' => $_POST['userpage'], + 'id' => $currentUser->id, + ]); // Set render data $renderData['page'] = [ @@ -957,7 +964,11 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification } // Update database - Database::update('users', [['user_signature' => $_POST['signature']], ['user_id' => [$currentUser->id, '=']]]); + DB::prepare('UPDATE `{prefix}users` SET `user_signature` = :us WHERE `user_id` = :id') + ->execute([ + 'us' => $_POST['signature'], + 'id' => $currentUser->id, + ]); // Set render data $renderData['page'] = [ @@ -1038,8 +1049,9 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification // Check if sessionid is set to all if ($_POST['sessionid'] === 'all') { // Delete all sessions assigned to the current user - Database::delete('sessions', [ - 'user_id' => [$currentUser->id, '='], + DB::prepare('DELETE FROM `{prefix}sessions` WHERE `user_id` = :user') + ->execute([ + 'user' => $currentUser->id, ]); // Set render data @@ -1052,7 +1064,12 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification } // Check if the session is owned by the current user - if (!Database::fetch('sessions', false, ['user_id' => [$currentUser->id, '='], 'session_id' => [$_POST['sessionid'], '=']])) { + $us = DB::prepare('SELECT * FROM `{prefix}sessions` WHERE `user_id` = :user AND `session_id` = :key'); + $us->execute([ + 'user' => $currentUser->id, + 'key' => $_POST['sessionid'], + ]); + if (!$us->rowCount()) { $renderData['page'] = [ 'redirect' => $redirect, 'message' => 'The session you tried to kill doesn\'t exist.', @@ -1062,8 +1079,10 @@ if (isset($_REQUEST['request-notifications']) && $_REQUEST['request-notification } // Delete the session - Database::delete('sessions', [ - 'session_id' => [$_POST['sessionid'], '='], + DB::prepare('DELETE FROM `{prefix}sessions` WHERE `user_id` = :user AND `session_id` = :session') + ->execute([ + 'user' => $currentUser->id, + 'session' => $_POST['sessionid'], ]); // Set render data @@ -1500,7 +1519,12 @@ if (Users::checkLogin()) { // Sessions case 'advanced.sessions': - $renderData['sessions'] = Database::fetch('sessions', true, ['user_id' => [$currentUser->id, '=']]); + $sessions = DB::prepare('SELECT * FROM `{prefix}sessions` WHERE `user_id` = :user'); + $sessions->execute([ + 'user' => $currentUser->id, + ]); + + $renderData['sessions'] = $sessions->fetchAll(); break; } diff --git a/sakura.php b/sakura.php index 12b6f6b..22fe444 100644 --- a/sakura.php +++ b/sakura.php @@ -8,7 +8,7 @@ namespace Sakura; // Define Sakura version -define('SAKURA_VERSION', '20160215'); +define('SAKURA_VERSION', '20160219'); define('SAKURA_VLABEL', 'Amethyst'); define('SAKURA_COLOUR', '#9966CC'); @@ -62,7 +62,13 @@ Config::init(ROOT . 'config/config.ini'); error_reporting(Config::local('dev', 'show_errors') ? -1 : 0); // Make the database connection -Database::init(Config::local('database', 'driver')); +DB::open( + Config::local('database', 'driver'), + Config::local('dsn'), + Config::local('database', 'username'), + Config::local('database', 'password'), + Config::local('database', 'prefix') +); // Check if we're using console if (php_sapi_name() === 'cli' && !defined('SAKURA_CRON')) { @@ -83,7 +89,7 @@ if (Config::get('no_cron_service')) { } // Update last execution time - Database::update('config', [['config_value' => time()], ['config_name' => ['no_cron_last', '=']]]); + Config::set('no_cron_last', time()); } } @@ -117,7 +123,7 @@ if (!defined('SAKURA_NO_TPL')) { Template::set($templateName); // Set base page rendering data - $renderData = [ + Template::vars([ 'sakura' => [ 'versionInfo' => [ 'version' => SAKURA_VERSION, @@ -176,10 +182,10 @@ if (!defined('SAKURA_NO_TPL')) { 'get' => $_GET, 'post' => $_POST, - ]; + ]); // Add the default render data - Template::vars($renderData); + $renderData = []; // Site closing if (Config::get('site_closed')) { diff --git a/templates/yuuno/elements/newsPost.twig b/templates/yuuno/elements/newsPost.twig index 19da52f..725a20c 100644 --- a/templates/yuuno/elements/newsPost.twig +++ b/templates/yuuno/elements/newsPost.twig @@ -12,5 +12,5 @@
- Posted on {% if not (viewPost and postExists) %} {{ post.news_comments.count }} comment{% if post.news_comments.count != 1 %}s{% endif %}{% endif %} + Posted {% if not (viewPost and postExists) %} {{ post.news_comments.count }} comment{% if post.news_comments.count != 1 %}s{% endif %}{% endif %}