From af4ec2ac45d9483490ae2a2afd32c378af6a9ccf Mon Sep 17 00:00:00 2001 From: flashwave Date: Mon, 16 Jul 2018 00:39:39 +0200 Subject: [PATCH] Caching stuff. --- misuzu.php | 1 + public/index.php | 85 +++++++++++++------------- src/Application.php | 20 +++++- src/Cache.php | 127 +++++++++++++++++++++++++++++++++++++++ src/Database.php | 28 ++++++++- src/TemplateEngine.php | 4 ++ views/manage/master.twig | 3 + views/mio/master.twig | 3 + 8 files changed, 226 insertions(+), 45 deletions(-) create mode 100644 src/Cache.php diff --git a/misuzu.php b/misuzu.php index 21705e38..15df144f 100644 --- a/misuzu.php +++ b/misuzu.php @@ -135,6 +135,7 @@ if (PHP_SAPI === 'cli') { exit; } + $app->startCache(); $app->startTemplating(); $tpl = $app->getTemplating(); diff --git a/public/index.php b/public/index.php index c8ddb12b..f6029acf 100644 --- a/public/index.php +++ b/public/index.php @@ -1,11 +1,11 @@ getConfig(); $tpl = $app->getTemplating(); -$db = Database::connection(); if ($config->get('Site', 'embed_linked_data', 'bool', false)) { $tpl->vars([ @@ -17,7 +17,7 @@ if ($config->get('Site', 'embed_linked_data', 'bool', false)) { ]); } -$featuredNews = $db->query(' +$news = Database::query(' SELECT p.`post_id`, p.`post_title`, p.`post_text`, p.`created_at`, u.`user_id`, u.`username`, @@ -32,46 +32,49 @@ $featuredNews = $db->query(' LIMIT 3 ')->fetchAll(PDO::FETCH_ASSOC); -$usersCount = (int)$db->query(' - SELECT COUNT(`user_id`) - FROM `msz_users` -')->fetchColumn(); +$statistics = Cache::instance()->get('index:stats:v1', function () { + return [ + 'users' => (int)Database::query(' + SELECT COUNT(`user_id`) + FROM `msz_users` + ')->fetchColumn(), + 'lastUser' => Database::query(' + SELECT + u.`user_id`, u.`username`, u.`created_at`, + COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour` + FROM `msz_users` as u + LEFT JOIN `msz_roles` as r + ON r.`role_id` = u.`display_role` + ORDER BY u.`user_id` DESC + LIMIT 1 + ')->fetch(PDO::FETCH_ASSOC), + ]; +}, 10800); -$lastUser = $db->query(' - SELECT - u.`user_id`, u.`username`, u.`created_at`, - COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `user_colour` - FROM `msz_users` as u - LEFT JOIN `msz_roles` as r - ON r.`role_id` = u.`display_role` - ORDER BY u.`user_id` DESC - LIMIT 1 -')->fetch(PDO::FETCH_ASSOC); - -$featuredChangelog = $db->query(' - SELECT - c.`change_id`, c.`change_log`, - a.`action_name`, a.`action_colour`, a.`action_class`, - DATE(`change_created`) as `change_date`, - !ISNULL(c.`change_text`) as `change_has_text` - FROM `msz_changelog_changes` as c - LEFT JOIN `msz_changelog_actions` as a - ON a.`action_id` = c.`action_id` - WHERE DATE(c.`change_created`) >= ( - SELECT DATE(`change_created`) - FROM `msz_changelog_changes` - GROUP BY DATE(`change_created`) - ORDER BY `change_created` DESC - LIMIT 2, 1 - ) - ORDER BY c.`change_created` DESC -')->fetchAll(PDO::FETCH_ASSOC); - -//var_dump(Database::connection()->query('SHOW SESSION STATUS LIKE "Questions"')->fetch()['Value']); +$changelog = Cache::instance()->get('index:changelog:v1', function () { + return Database::query(' + SELECT + c.`change_id`, c.`change_log`, + a.`action_name`, a.`action_colour`, a.`action_class`, + DATE(`change_created`) as `change_date`, + !ISNULL(c.`change_text`) as `change_has_text` + FROM `msz_changelog_changes` as c + LEFT JOIN `msz_changelog_actions` as a + ON a.`action_id` = c.`action_id` + WHERE DATE(c.`change_created`) >= ( + SELECT DATE(`change_created`) + FROM `msz_changelog_changes` + GROUP BY DATE(`change_created`) + ORDER BY `change_created` DESC + LIMIT 2, 1 + ) + ORDER BY c.`change_created` DESC + ')->fetchAll(PDO::FETCH_ASSOC); +}, 1800); echo $tpl->render('home.index', [ - 'users_count' => $usersCount, - 'last_user' => $lastUser, - 'featured_changelog' => $featuredChangelog, - 'featured_news' => $featuredNews, + 'users_count' => $statistics['users'], + 'last_user' => $statistics['lastUser'], + 'featured_changelog' => $changelog, + 'featured_news' => $news, ]); diff --git a/src/Application.php b/src/Application.php index 42f50009..c4e13661 100644 --- a/src/Application.php +++ b/src/Application.php @@ -217,12 +217,30 @@ class Application extends ApplicationBase public function startDatabase(): void { if (Database::hasInstance()) { - throw new UnexpectedValueException('Database module has already been started.'); + throw new UnexpectedValueException('Database has already been started.'); } new Database($this->configInstance, self::DATABASE_CONNECTIONS[0]); } + /** + * Sets up the caching stuff. + */ + public function startCache(): void + { + if (Cache::hasInstance()) { + throw new UnexpectedValueException('Cache has already been started.'); + } + + new Cache( + $this->configInstance->get('Cache', 'host', 'string', null), + $this->configInstance->get('Cache', 'port', 'int', null), + $this->configInstance->get('Cache', 'database', 'int', null), + $this->configInstance->get('Cache', 'password', 'string', null), + $this->configInstance->get('Cache', 'prefix', 'string', '') + ); + } + /** * Sets up the templating engine module. */ diff --git a/src/Cache.php b/src/Cache.php new file mode 100644 index 00000000..4f19aaf5 --- /dev/null +++ b/src/Cache.php @@ -0,0 +1,127 @@ +redis; + } + + public static function hasInstance(): bool + { + return self::$instance instanceof static; + } + + public function __construct( + string $host, + ?int $port = null, + ?int $database = null, + ?string $password = null, + string $prefix = '' + ) { + if (self::hasInstance()) { + throw new UnexpectedValueException('Only one instance of Cache may exist.'); + } + + self::$instance = $this; + $this->redis = new Redis; + $this->redis->connect($host, $port); + + if ($password !== null && !$this->redis->auth($password)) { + throw new InvalidArgumentException('Redis auth failed.'); + } + + if ($database !== null && !$this->redis->select($database)) { + throw new UnexpectedValueException('Redis select failed.'); + } + + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + $this->redis->setOption(Redis::OPT_PREFIX, $prefix); + } + + public function __destruct() + { + $this->redis->close(); + self::$instance = null; + } + + public function set(string $key, $value, int $ttl = 0) + { + if (is_callable($value)) { + $value = $value(); + } + + if ($ttl < 0) { + return $value; + } elseif ($ttl < 1) { + $this->redis->set($key, $value); + } else { + $this->redis->setEx($key, $ttl, $value); + } + + return $value; + } + + public function exists(string $key): bool + { + return $this->redis->exists($key); + } + + public function delete($keys): int + { + return $this->redis->delete($keys); + } + + public function increment(string $key, int $amount = 1): int + { + if ($amount <= 1) { + return $this->redis->incr($key); + } + + return $this->redis->incrBy($key, $amount); + } + + public function decrement(string $key, int $amount = 1): int + { + if ($amount <= 1) { + return $this->redis->decr($key); + } + + return $this->redis->decrBy($key, $amount); + } + + public function get(string $key, $fallback, int $ttl = 0) + { + if ($ttl < 0) { + return is_callable($fallback) ? $fallback() : $fallback; + } + + if (!$this->exists($key)) { + return $this->set($key, $fallback, $ttl); + } + + return $this->redis->get($key); + } +} diff --git a/src/Database.php b/src/Database.php index 702ee4dc..4a06eb7f 100644 --- a/src/Database.php +++ b/src/Database.php @@ -3,7 +3,9 @@ namespace Misuzu; use Misuzu\Config\ConfigManager; use PDO; +use PDOStatement; use InvalidArgumentException; +use UnexpectedValueException; final class Database { @@ -48,7 +50,7 @@ final class Database public static function getInstance(): Database { if (!self::hasInstance()) { - throw new \UnexpectedValueException('No instance of Database exists yet.'); + throw new UnexpectedValueException('No instance of Database exists yet.'); } return self::$instance; @@ -63,8 +65,8 @@ final class Database ConfigManager $config, string $default = 'default' ) { - if (self::$instance instanceof static) { - throw new \UnexpectedValueException('Only one instance of Database may exist.'); + if (self::hasInstance()) { + throw new UnexpectedValueException('Only one instance of Database may exist.'); } self::$instance = $this; @@ -77,6 +79,26 @@ final class Database return self::getInstance()->getConnection($name); } + public static function prepare(string $statement, ?string $connection = null, $options = []): PDOStatement + { + return self::connection($connection)->prepare($statement, $options); + } + + public static function query(string $statement, ?string $connection = null): PDOStatement + { + return self::connection($connection)->query($statement); + } + + public static function exec(string $statement, ?string $connection = null) + { + return self::connection($connection)->exec($statement); + } + + public static function queryCount(?string $connection = null): int + { + return (int)Database::query('SHOW SESSION STATUS LIKE "Questions"', $connection)->fetch()['Value']; + } + public function getConnection(?string $name = null): PDO { $name = $name ?? $this->default; diff --git a/src/TemplateEngine.php b/src/TemplateEngine.php index 32abba11..4d4c1c26 100644 --- a/src/TemplateEngine.php +++ b/src/TemplateEngine.php @@ -167,6 +167,10 @@ class TemplateEngine */ public function render(string $path, ?array $vars = null): string { + if ($this->twig->isDebug()) { + $this->var('query_count', Database::queryCount()); + } + $path = self::fixPath($path); if ($vars !== null) { diff --git a/views/manage/master.twig b/views/manage/master.twig index de1694e4..f3f5f644 100644 --- a/views/manage/master.twig +++ b/views/manage/master.twig @@ -67,6 +67,9 @@ ''|date('Y') }} / {{ ('https://github.com/flashwave/misuzu/tree/' ~ git_branch())|html_link(git_branch(), 'footer__link')|raw }} # {{ ('https://github.com/flashwave/misuzu/commit/' ~ git_commit_hash(true))|html_link(git_commit_hash(), 'footer__link')|raw }} + {% if query_count is defined %} + / SQL Queries: {{ query_count|number_format }} + {% endif %} diff --git a/views/mio/master.twig b/views/mio/master.twig index 2f467cd9..c52d21b6 100644 --- a/views/mio/master.twig +++ b/views/mio/master.twig @@ -70,6 +70,9 @@ ''|date('Y') }} / {{ ('https://github.com/flashwave/misuzu/tree/' ~ git_branch())|html_link(git_branch(), 'footer__copyright__link') }} # {{ ('https://github.com/flashwave/misuzu/commit/' ~ git_commit_hash(true))|html_link(git_commit_hash(), 'footer__copyright__link') }} + {% if query_count is defined %} + / SQL Queries: {{ query_count|number_format }} + {% endif %}