From 9b9e88f7f692d7daf6c19deb2428431b15069604 Mon Sep 17 00:00:00 2001
From: Jan-Hendrik Willms <tleilax+studip@gmail.com>
Date: Thu, 22 Jun 2023 11:33:08 +0000
Subject: [PATCH] wrap any cache other than memory cache into a wrapper that
 caches the cache in memory, fixes #2202

Closes #2202

Merge request studip/studip!1782
---
 lib/classes/StudipCacheFactory.class.php | 26 +++++----
 lib/classes/StudipCacheWrapper.php       | 67 ++++++++++++++++++++++++
 2 files changed, 82 insertions(+), 11 deletions(-)
 create mode 100644 lib/classes/StudipCacheWrapper.php

diff --git a/lib/classes/StudipCacheFactory.class.php b/lib/classes/StudipCacheFactory.class.php
index 5332e067881..77c5973ac1a 100644
--- a/lib/classes/StudipCacheFactory.class.php
+++ b/lib/classes/StudipCacheFactory.class.php
@@ -54,11 +54,9 @@ class StudipCacheFactory
 
 
     /**
-     * @param    Config       an instance of class Config which will be used to
-     *                        determine the class of the implementation of interface
-     *                        StudipCache
-     *
-     * @return void
+     * @param Config $config an instance of class Config which will be used to
+     *                       determine the class of the implementation of interface
+     *                       StudipCache
      */
     public static function setConfig($config)
     {
@@ -68,8 +66,6 @@ class StudipCacheFactory
 
     /**
      * Resets the configuration and voids the cache instance.
-     *
-     * @return void
      */
     public static function unconfigure()
     {
@@ -174,18 +170,26 @@ class StudipCacheFactory
     }
 
     /**
-     * Return an instance of a given class using some arguments
+     * Return an instance of a given class using some arguments. Unless the
+     * memory cache is instantiated, the cache will be wrapped in a wrapper
+     * class that uses a memory cache to reduce accesses to the cache.
      *
-     * @param  string  the name of the class
-     * @param  array   an array of arguments to be used by the constructor
+     * @param  string $class     the name of the class
+     * @param  array  $arguments an array of arguments to be used by the constructor
      *
      * @return StudipCache  an instance of the specified class
      */
     public static function instantiateCache($class, $arguments)
     {
         $reflection_class = new ReflectionClass($class);
-        return (is_array($arguments['config']) && count($arguments['config']) > 0)
+        $cache = (is_array($arguments['config']) && count($arguments['config']) > 0)
                ? $reflection_class->newInstanceArgs($arguments['config'])
                : $reflection_class->newInstance();
+
+        if ($class !== StudipMemoryCache::class) {
+            return new StudipCacheWrapper($cache);
+        }
+
+        return $cache;
     }
 }
diff --git a/lib/classes/StudipCacheWrapper.php b/lib/classes/StudipCacheWrapper.php
new file mode 100644
index 00000000000..65c5edb2232
--- /dev/null
+++ b/lib/classes/StudipCacheWrapper.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * The cache wrapper wraps a memory cache around another cache. This should
+ * reduce the accesses to the actual cache.
+ *
+ * @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
+ * @license GPL2 or any later version
+ * @since Stud.IP 5.4
+ */
+class StudipCacheWrapper implements StudipCache
+{
+    const DEFAULT_MEMORY_EXPIRATION = 60;
+
+    protected $actual_cache;
+    protected $memory_cache;
+
+    public function __construct(StudipCache $actual_cache)
+    {
+        $this->actual_cache = $actual_cache;
+        $this->memory_cache = new StudipMemoryCache();
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function expire($arg)
+    {
+        $this->memory_cache->expire($arg);
+        $this->actual_cache->expire($arg);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function flush()
+    {
+        $this->memory_cache->flush();
+        $this->actual_cache->flush();
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function read($arg)
+    {
+        $cached = $this->memory_cache->read($arg);
+        if ($cached !== false) {
+            return $cached;
+        }
+
+        $cached = $this->actual_cache->read($arg);
+        if ($cached !== false) {
+            $this->memory_cache->write($arg, $cached, self::DEFAULT_MEMORY_EXPIRATION);
+        }
+        return $cached;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function write($name, $content, $expires = self::DEFAULT_EXPIRATION)
+    {
+        $this->memory_cache->expire($name);
+        $this->actual_cache->write($name, $content, $expires);
+    }
+}
-- 
GitLab