From 8d9e894af816a08729d9caefb79f7e19534017b1 Mon Sep 17 00:00:00 2001 From: Belle Aerni Date: Mon, 19 Jun 2023 16:18:49 -0700 Subject: [PATCH] ~ 50% reduction in the time to render a page (#49) --- src/AntCMS/AntCMS.php | 14 +++++++----- src/AntCMS/AntCache.php | 48 +++++++++++++++++++++++++--------------- src/AntCMS/AntConfig.php | 8 +++---- src/AntCMS/AntPages.php | 10 ++++----- src/AntCMS/AntTwig.php | 2 +- src/AntCMS/AntYaml.php | 25 ++++++++++++++++----- 6 files changed, 65 insertions(+), 42 deletions(-) diff --git a/src/AntCMS/AntCMS.php b/src/AntCMS/AntCMS.php index f60dab1..e3e4cf4 100644 --- a/src/AntCMS/AntCMS.php +++ b/src/AntCMS/AntCMS.php @@ -126,25 +126,27 @@ class AntCMS */ public static function getThemeTemplate(string $layout = 'default', string $theme = null) { - $theme = $theme ?? AntConfig::currentConfig('activeTheme'); + $theme ??= AntConfig::currentConfig('activeTheme'); - if (!is_dir(antThemePath . '/' . $theme)) { + if (!is_dir(antThemePath . DIRECTORY_SEPARATOR . $theme)) { $theme = 'Default'; } + $basePath = AntTools::repairFilePath(antThemePath . DIRECTORY_SEPARATOR . $theme); + if (strpos($layout, '_') !== false) { $layoutPrefix = explode('_', $layout)[0]; - $templatePath = AntTools::repairFilePath(antThemePath . '/' . $theme . '/' . 'Templates' . '/' . $layoutPrefix); + $templatePath = $basePath . DIRECTORY_SEPARATOR . 'Templates' . DIRECTORY_SEPARATOR . $layoutPrefix; $defaultTemplates = AntTools::repairFilePath(antThemePath . '/Default/Templates' . '/' . $layoutPrefix); } else { - $templatePath = AntTools::repairFilePath(antThemePath . '/' . $theme . '/' . 'Templates'); + $templatePath = $basePath . DIRECTORY_SEPARATOR . 'Templates'; $defaultTemplates = AntTools::repairFilePath(antThemePath . '/Default/Templates'); } try { - $template = @file_get_contents(AntTools::repairFilePath($templatePath . '/' . $layout . '.html.twig')); + $template = @file_get_contents($templatePath . DIRECTORY_SEPARATOR . $layout . '.html.twig'); if (empty($template)) { - $template = file_get_contents(AntTools::repairFilePath($defaultTemplates . '/' . $layout . '.html.twig')); + $template = file_get_contents($defaultTemplates . DIRECTORY_SEPARATOR . $layout . '.html.twig'); } } catch (\Exception) { } diff --git a/src/AntCMS/AntCache.php b/src/AntCMS/AntCache.php index 6ab33ce..ed51dfa 100644 --- a/src/AntCMS/AntCache.php +++ b/src/AntCMS/AntCache.php @@ -17,10 +17,10 @@ class AntCache /** * Creates a new cache object, sets the correct caching type. ('auto', 'filesystem', 'apcu', or 'none') */ - public function __construct() + public function __construct(null|string $mode = null) { - $config = AntConfig::currentConfig(); - $mode = $config['cacheMode'] ?? 'auto'; + $mode = $mode ?? AntConfig::currentConfig('cacheMode') ?? 'auto'; + switch ($mode) { case 'none': $this->cacheType = self::noCache; @@ -86,11 +86,9 @@ class AntCache return file_get_contents($cachePath); case self::apcuCache: $apcuKey = $this->cacheKeyApcu . $key; - if (apcu_exists($apcuKey)) { - return apcu_fetch($apcuKey); - } else { - return false; - } + $success = false; + $result = apcu_fetch($apcuKey, $success); + return $success ? $result : false; default: return false; } @@ -129,16 +127,20 @@ class AntCache */ public function createCacheKey(string $content, string $salt = 'cache') { - /** - * If the server is modern enough to have xxh128, use that. It is really fast and still produces long hashes - * If not, use MD4 since it's still quite fast. - * Source: https://php.watch/articles/php-hash-benchmark - */ - if (defined('HAS_XXH128')) { - return hash('xxh128', $content . $salt); - } else { - return hash('md4', $content . $salt); - } + return hash(self::getHashAlgo(), $content . $salt); + } + + /** + * Generates a unique cache key for a file and a salt value. + * The salt is used to ensure that each cache key is unique to each component, even if multiple components are using the same source content but caching different results. + * + * @param string $filePath The file path to create a cache key for. + * @param string $salt An optional salt value to use in the cache key generation. Default is 'cache'. + * @return string The generated cache key. + */ + public function createCacheKeyFile(string $filePath, string $salt = 'cache') + { + return hash_file(self::getHashAlgo(), $filePath) . $salt; } public static function clearCache(): void @@ -162,4 +164,14 @@ class AntCache } } } + + public static function getHashAlgo(): string + { + /** + * If the server is modern enough to have xxh128, use that. It is really fast and still produces long hashes + * If not, use MD4 since it's still quite fast. + * Source: https://php.watch/articles/php-hash-benchmark + */ + return defined('HAS_XXH128') ? 'xxh128' : 'md4'; + } } diff --git a/src/AntCMS/AntConfig.php b/src/AntCMS/AntConfig.php index fa05bcd..602ef26 100644 --- a/src/AntCMS/AntConfig.php +++ b/src/AntCMS/AntConfig.php @@ -48,7 +48,8 @@ class AntConfig */ public static function currentConfig(?string $key = null) { - $config = AntYaml::parseFile(antConfigFile); + // FS cache enabled to save ~10% of the time to deliver the file page. + $config = AntYaml::parseFile(antConfigFile, true); if (is_null($key)) { return $config; } else { @@ -57,7 +58,6 @@ class AntConfig } } - /** * @param array $array * @param array $keys @@ -67,13 +67,11 @@ class AntConfig { foreach ($keys as $key) { if (isset($array[$key])) { - $array = $array[$key]; + return $array[$key]; } else { return null; } } - - return $array; } /** diff --git a/src/AntCMS/AntPages.php b/src/AntCMS/AntPages.php index 58d655b..8d82e4a 100644 --- a/src/AntCMS/AntPages.php +++ b/src/AntCMS/AntPages.php @@ -52,21 +52,18 @@ class AntPages AntYaml::saveFile(antPagesList, $pageList); } - /** @return array */ - public static function getPages() + public static function getPages():array { return AntYaml::parseFile(antPagesList); } /** * @param string $currentPage optional - What page is the active page. Used for highlighting the active page in the navbar - * @return string */ - public static function generateNavigation(string $navTemplate = '', string $currentPage = '') + public static function generateNavigation(string $navTemplate = '', string $currentPage = ''): string { $pages = AntPages::getPages(); $antCache = new AntCache; - $antTwig = new AntTwig(); $theme = AntConfig::currentConfig('activeTheme'); $cacheKey = $antCache->createCacheKey(json_encode($pages), $theme . $currentPage); @@ -74,7 +71,7 @@ class AntPages if ($antCache->isCached($cacheKey)) { $cachedContent = $antCache->getCache($cacheKey); - if ($cachedContent !== false && !empty($cachedContent)) { + if (!empty($cachedContent)) { return $cachedContent; } } @@ -91,6 +88,7 @@ class AntPages } } + $antTwig = new AntTwig(); $navHTML = $antTwig->renderWithTiwg($navTemplate, array('pages' => $pages)); $antCache->setCache($cacheKey, $navHTML); diff --git a/src/AntCMS/AntTwig.php b/src/AntCMS/AntTwig.php index a9b2795..0dcc9cc 100644 --- a/src/AntCMS/AntTwig.php +++ b/src/AntCMS/AntTwig.php @@ -14,7 +14,7 @@ class AntTwig $twigCache = (AntConfig::currentConfig('enableCache') !== 'none') ? AntCachePath : false; $this->theme = $theme ?? AntConfig::currentConfig('activeTheme'); - if (!is_dir(antThemePath . '/' . $this->theme)) { + if (!is_dir(antThemePath . DIRECTORY_SEPARATOR . $this->theme)) { $this->theme = 'Default'; } diff --git a/src/AntCMS/AntYaml.php b/src/AntCMS/AntYaml.php index 4a06d01..586df14 100644 --- a/src/AntCMS/AntYaml.php +++ b/src/AntCMS/AntYaml.php @@ -7,12 +7,25 @@ use Symfony\Component\Yaml\Yaml; class AntYaml { - /** - * @return array - */ - public static function parseFile(string $file) + public static function parseFile(string $file, bool $fileCache = false): array { - return Yaml::parseFile($file); + if ($fileCache) { + $antCache = new AntCache('filesystem'); + } else { + $antCache = new AntCache(); + } + + $cacheKey = $antCache->createCacheKeyFile($file); + if ($antCache->isCached($cacheKey)) { + $parsed = json_decode($antCache->getCache($cacheKey), true); + } + + if (empty($parsed)) { + $parsed = Yaml::parseFile($file); + $antCache->setCache($cacheKey, json_encode($parsed)); + } + + return $parsed; } /** @@ -27,7 +40,7 @@ class AntYaml /** * @return array|null */ - public static function parseYaml(string $yaml) + public static function parseYaml(string $yaml): ?array { try { return Yaml::parse($yaml);