Procházet zdrojové kódy

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

Belle Aerni před 2 roky
rodič
revize
8d9e894af8

+ 8 - 6
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) {
         }

+ 30 - 18
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';
+    }
 }

+ 3 - 5
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<mixed> $array 
      * @param array<mixed> $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;
     }
 
     /**

+ 4 - 6
src/AntCMS/AntPages.php

@@ -52,21 +52,18 @@ class AntPages
         AntYaml::saveFile(antPagesList, $pageList);
     }
 
-    /** @return array<mixed>  */
-    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);

+ 1 - 1
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';
         }
 

+ 19 - 6
src/AntCMS/AntYaml.php

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