aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2022-12-20 13:18:31 +0000
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2022-12-20 13:18:31 +0000
commit71724582928190ecd5e0e9169a8eab557d8cd1bc (patch)
treeed26d756ce7aea303bf78ea02b38ff01877464cb
parent181142fd5b5efb5842fb7fe4576accba85f87bee (diff)
allow StudipCachedArray to expire completely and use that in RolePersistence, fixes #1580
Closes #1580 Merge request studip/studip!1009
-rw-r--r--lib/classes/StudipCachedArray.php62
-rw-r--r--lib/plugins/db/RolePersistence.class.php54
-rw-r--r--tests/unit/lib/classes/StudipCachedArrayTest.php15
3 files changed, 113 insertions, 18 deletions
diff --git a/lib/classes/StudipCachedArray.php b/lib/classes/StudipCachedArray.php
index 221b76c..830128d 100644
--- a/lib/classes/StudipCachedArray.php
+++ b/lib/classes/StudipCachedArray.php
@@ -15,21 +15,24 @@ class StudipCachedArray implements ArrayAccess
protected $data = [];
+ protected $hash;
+
/**
* Constructs the cached array
*
- * @param string $key Cache key where the array is/should be stored
- * an int which will be length of the substring
- * of the given chache offset or a callable which
- * will return the partition key.
- * @param int $duration Duration in seconds for which the item shall be
- * stored
+ * @param string $key Cache key where the array is/should be stored
+ * an int which will be length of the substring
+ * of the given chache offset or a callable which
+ * will return the partition key.
+ * @param int $duration Duration in seconds for which the item shall be
+ * stored
*/
public function __construct(string $key, int $duration = StudipCache::DEFAULT_EXPIRATION)
{
- $this->key = self::class . "/{$key}";
- $this->cache = StudipCacheFactory::getCache();
+ $this->key = self::class . "/{$key}";
+ $this->cache = StudipCacheFactory::getCache();
$this->duration = $duration;
+ $this->hash = $this->getHash();
$this->reset();
}
@@ -43,9 +46,19 @@ class StudipCachedArray implements ArrayAccess
}
/**
+ * Removes all values from the cache.
+ */
+ public function expire(): void
+ {
+ $this->hash = $this->getHash(true);
+ $this->reset();
+ }
+
+ /**
* Determines whether an offset exists in the array.
*
* @param string $offset Offset
+ *
* @return bool
*/
public function offsetExists($offset): bool
@@ -58,6 +71,7 @@ class StudipCachedArray implements ArrayAccess
* Returns the value at given offset or null if it doesn't exist.
*
* @param string $offset Offset
+ *
* @return mixed
*/
public function offsetGet($offset)
@@ -75,7 +89,7 @@ class StudipCachedArray implements ArrayAccess
public function offsetSet($offset, $value): void
{
if ($offset === null) {
- throw new Exception('Cannot push to cached array, use StudipCachedArray instead');
+ throw new Exception('Cannot push to cached array, use correct offset instead');
}
if (!isset($this->data[$offset]) || $this->data[$offset] !== $value) {
@@ -120,9 +134,11 @@ class StudipCachedArray implements ArrayAccess
*/
protected function storeData(string $offset): void
{
+ $data = $this->swapNullAndFalse($this->data[$offset]);
+
$this->cache->write(
$this->getCacheKey($offset),
- $this->swapNullAndFalse($this->data[$offset]),
+ $data,
$this->duration
);
}
@@ -131,11 +147,18 @@ class StudipCachedArray implements ArrayAccess
* Returns the cache key for a specific offset.
*
* @param string $offset Offset of the cached item
+ *
* @return string
*/
private function getCacheKey(string $offset): string
{
- return rtrim($this->key, '/') . "/{$offset}";
+ $key = rtrim($this->key, '/');
+ if ($this->hash) {
+ $key .= "/{$this->hash}";
+ }
+ $key .= "/{$offset}";
+
+ return $key;
}
/**
@@ -158,4 +181,21 @@ class StudipCachedArray implements ArrayAccess
return $value;
}
+
+ /**
+ * Loads or creates and stores a hash for this cached array.
+ *
+ * @return string
+ */
+ private function getHash(bool $recreate = false): string
+ {
+ if (!$recreate) {
+ $hash = $this->cache->read($this->key);
+ return $hash === false ? '' : $hash;
+ }
+
+ $hash = md5(uniqid(__CLASS__, true));
+ $this->cache->write($this->key, $hash);
+ return $hash;
+ }
}
diff --git a/lib/plugins/db/RolePersistence.class.php b/lib/plugins/db/RolePersistence.class.php
index 1b03a01..ff17a94 100644
--- a/lib/plugins/db/RolePersistence.class.php
+++ b/lib/plugins/db/RolePersistence.class.php
@@ -129,9 +129,10 @@ class RolePersistence
// sweep roles cache
self::expireRolesCache();
+ self::expireUserCache();
foreach ($statement as $plugin_id) {
- unset(self::getPluginRolesCache()[$plugin_id]);
+ self::expirePluginCache($plugin_id);
}
NotificationCenter::postNotification('RoleDidDelete', $id, $name);
@@ -338,7 +339,7 @@ class RolePersistence
$statement->execute();
}
- unset(self::getPluginRolesCache()[$plugin_id]);
+ self::expirePluginCache($plugin_id);
foreach ($role_ids as $role_id) {
NotificationCenter::postNotification(
@@ -370,7 +371,7 @@ class RolePersistence
$statement->execute();
}
- unset(self::getPluginRolesCache()[$plugin_id]);
+ self::expirePluginCache($plugin_id);
foreach ($role_ids as $role_id) {
NotificationCenter::postNotification(
@@ -488,7 +489,7 @@ class RolePersistence
private static $user_roles_cache = null;
private static $plugin_roles_cache = null;
- private static function getUserRolesCache()
+ private static function getUserRolesCache(): StudipCachedArray
{
if (self::$user_roles_cache === null) {
self::$user_roles_cache = new StudipCachedArray(self::USER_ROLES_CACHE_KEY);
@@ -496,7 +497,7 @@ class RolePersistence
return self::$user_roles_cache;
}
- private static function getPluginRolesCache()
+ private static function getPluginRolesCache(): StudipCachedArray
{
if (self::$plugin_roles_cache === null) {
self::$plugin_roles_cache = new StudipCachedArray(self::PLUGIN_ROLES_CACHE_KEY);
@@ -504,13 +505,52 @@ class RolePersistence
return self::$plugin_roles_cache;
}
+ /**
+ * Expires all cached roles.
+ */
public static function expireRolesCache()
{
StudipCacheFactory::getCache()->expire(self::ROLES_CACHE_KEY);
}
- public static function expireUserCache($user_id)
+ /**
+ * Expires all cached user role assignments.
+ *
+ * @param string|null $user_id Optional user id to expire the cache for.
+ * If none is given, the whole cache is cleared.
+ */
+ public static function expireUserCache($user_id = null)
{
- unset(self::getUserRolesCache()[$user_id]);
+ if ($user_id === null) {
+ self::getUserRolesCache()->expire();
+ } else {
+ unset(self::getUserRolesCache()[$user_id]);
+ }
+ }
+
+ /**
+ * Expires all cached plugin role assignments.
+ *
+ * @param string|int|null $plugin_id Optional plugin id to expire the cache
+ * for. If none is given, the whole cache
+ * is cleared.
+ */
+ public static function expirePluginCache($plugin_id = null)
+ {
+ if ($plugin_id === null) {
+ self::getPluginRolesCache()->expire();
+ } else {
+ unset(self::getPluginRolesCache()[$plugin_id]);
+ }
+ }
+
+ /**
+ * Expires all caches
+ */
+ public static function expireCaches(): void
+ {
+ self::expireRolesCache();
+ self::expireUserCache();
+ self::expirePluginCache();
}
}
diff --git a/tests/unit/lib/classes/StudipCachedArrayTest.php b/tests/unit/lib/classes/StudipCachedArrayTest.php
index e473dc6..c98c1bd 100644
--- a/tests/unit/lib/classes/StudipCachedArrayTest.php
+++ b/tests/unit/lib/classes/StudipCachedArrayTest.php
@@ -48,6 +48,21 @@ class StudipCachedArrayTest extends \Codeception\Test\Unit
$this->assertFalse(isset($cache[$key]));
}
+ /**
+ * @depends testStorage
+ * @dataProvider StorageProvider
+ */
+ public function testExpiration($key, $value)
+ {
+ $cache = $this->getCachedArray();
+
+ $cache[$key] = $value;
+
+ $cache->expire();
+
+ $this->assertFalse(isset($cache[$key]));
+ }
+
public function StorageProvider(): array
{
return [