~ 50% reduction in the time to render a page (#49)

This commit is contained in:
Belle Aerni 2023-06-19 16:18:49 -07:00 committed by GitHub
parent d920b26f52
commit 8d9e894af8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 42 deletions

View file

@ -126,25 +126,27 @@ class AntCMS
*/ */
public static function getThemeTemplate(string $layout = 'default', string $theme = null) 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'; $theme = 'Default';
} }
$basePath = AntTools::repairFilePath(antThemePath . DIRECTORY_SEPARATOR . $theme);
if (strpos($layout, '_') !== false) { if (strpos($layout, '_') !== false) {
$layoutPrefix = explode('_', $layout)[0]; $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); $defaultTemplates = AntTools::repairFilePath(antThemePath . '/Default/Templates' . '/' . $layoutPrefix);
} else { } else {
$templatePath = AntTools::repairFilePath(antThemePath . '/' . $theme . '/' . 'Templates'); $templatePath = $basePath . DIRECTORY_SEPARATOR . 'Templates';
$defaultTemplates = AntTools::repairFilePath(antThemePath . '/Default/Templates'); $defaultTemplates = AntTools::repairFilePath(antThemePath . '/Default/Templates');
} }
try { try {
$template = @file_get_contents(AntTools::repairFilePath($templatePath . '/' . $layout . '.html.twig')); $template = @file_get_contents($templatePath . DIRECTORY_SEPARATOR . $layout . '.html.twig');
if (empty($template)) { 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) { } catch (\Exception) {
} }

View file

@ -17,10 +17,10 @@ class AntCache
/** /**
* Creates a new cache object, sets the correct caching type. ('auto', 'filesystem', 'apcu', or 'none') * 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 = $mode ?? AntConfig::currentConfig('cacheMode') ?? 'auto';
$mode = $config['cacheMode'] ?? 'auto';
switch ($mode) { switch ($mode) {
case 'none': case 'none':
$this->cacheType = self::noCache; $this->cacheType = self::noCache;
@ -86,11 +86,9 @@ class AntCache
return file_get_contents($cachePath); return file_get_contents($cachePath);
case self::apcuCache: case self::apcuCache:
$apcuKey = $this->cacheKeyApcu . $key; $apcuKey = $this->cacheKeyApcu . $key;
if (apcu_exists($apcuKey)) { $success = false;
return apcu_fetch($apcuKey); $result = apcu_fetch($apcuKey, $success);
} else { return $success ? $result : false;
return false;
}
default: default:
return false; return false;
} }
@ -129,16 +127,20 @@ class AntCache
*/ */
public function createCacheKey(string $content, string $salt = 'cache') public function createCacheKey(string $content, string $salt = 'cache')
{ {
/** return hash(self::getHashAlgo(), $content . $salt);
* 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 /**
*/ * Generates a unique cache key for a file and a salt value.
if (defined('HAS_XXH128')) { * 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.
return hash('xxh128', $content . $salt); *
} else { * @param string $filePath The file path to create a cache key for.
return hash('md4', $content . $salt); * @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 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';
}
} }

View file

@ -48,7 +48,8 @@ class AntConfig
*/ */
public static function currentConfig(?string $key = null) 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)) { if (is_null($key)) {
return $config; return $config;
} else { } else {
@ -57,7 +58,6 @@ class AntConfig
} }
} }
/** /**
* @param array<mixed> $array * @param array<mixed> $array
* @param array<mixed> $keys * @param array<mixed> $keys
@ -67,13 +67,11 @@ class AntConfig
{ {
foreach ($keys as $key) { foreach ($keys as $key) {
if (isset($array[$key])) { if (isset($array[$key])) {
$array = $array[$key]; return $array[$key];
} else { } else {
return null; return null;
} }
} }
return $array;
} }
/** /**

View file

@ -52,21 +52,18 @@ class AntPages
AntYaml::saveFile(antPagesList, $pageList); AntYaml::saveFile(antPagesList, $pageList);
} }
/** @return array<mixed> */ public static function getPages():array
public static function getPages()
{ {
return AntYaml::parseFile(antPagesList); return AntYaml::parseFile(antPagesList);
} }
/** /**
* @param string $currentPage optional - What page is the active page. Used for highlighting the active page in the navbar * @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(); $pages = AntPages::getPages();
$antCache = new AntCache; $antCache = new AntCache;
$antTwig = new AntTwig();
$theme = AntConfig::currentConfig('activeTheme'); $theme = AntConfig::currentConfig('activeTheme');
$cacheKey = $antCache->createCacheKey(json_encode($pages), $theme . $currentPage); $cacheKey = $antCache->createCacheKey(json_encode($pages), $theme . $currentPage);
@ -74,7 +71,7 @@ class AntPages
if ($antCache->isCached($cacheKey)) { if ($antCache->isCached($cacheKey)) {
$cachedContent = $antCache->getCache($cacheKey); $cachedContent = $antCache->getCache($cacheKey);
if ($cachedContent !== false && !empty($cachedContent)) { if (!empty($cachedContent)) {
return $cachedContent; return $cachedContent;
} }
} }
@ -91,6 +88,7 @@ class AntPages
} }
} }
$antTwig = new AntTwig();
$navHTML = $antTwig->renderWithTiwg($navTemplate, array('pages' => $pages)); $navHTML = $antTwig->renderWithTiwg($navTemplate, array('pages' => $pages));
$antCache->setCache($cacheKey, $navHTML); $antCache->setCache($cacheKey, $navHTML);

View file

@ -14,7 +14,7 @@ class AntTwig
$twigCache = (AntConfig::currentConfig('enableCache') !== 'none') ? AntCachePath : false; $twigCache = (AntConfig::currentConfig('enableCache') !== 'none') ? AntCachePath : false;
$this->theme = $theme ?? AntConfig::currentConfig('activeTheme'); $this->theme = $theme ?? AntConfig::currentConfig('activeTheme');
if (!is_dir(antThemePath . '/' . $this->theme)) { if (!is_dir(antThemePath . DIRECTORY_SEPARATOR . $this->theme)) {
$this->theme = 'Default'; $this->theme = 'Default';
} }

View file

@ -7,12 +7,25 @@ use Symfony\Component\Yaml\Yaml;
class AntYaml class AntYaml
{ {
/** public static function parseFile(string $file, bool $fileCache = false): array
* @return array<mixed>
*/
public static function parseFile(string $file)
{ {
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<mixed>|null * @return array<mixed>|null
*/ */
public static function parseYaml(string $yaml) public static function parseYaml(string $yaml): ?array
{ {
try { try {
return Yaml::parse($yaml); return Yaml::parse($yaml);