diff --git a/app/controllers/admin/cache.php b/app/controllers/admin/cache.php
index 329aab7a968a1ce98ce8c2b55d588233ff4b575e..3e9d970a76d739fdc53be9eb410de27e717f3be7 100644
--- a/app/controllers/admin/cache.php
+++ b/app/controllers/admin/cache.php
@@ -111,7 +111,7 @@ class Admin_CacheController extends AuthenticatedController
         // Store settings to global config.
         if (Config::get()->store('SYSTEMCACHE', $settings)) {
             PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.'));
-            StudipCacheFactory::unconfigure();
+            \Studip\Cache\Factory::unconfigure();
         }
 
         $this->relocate('admin/cache/settings');
@@ -122,7 +122,7 @@ class Admin_CacheController extends AuthenticatedController
      */
     public function flush_action()
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache->flush();
 
         PageLayout::postSuccess(_('Die Inhalte des Caches wurden gelöscht.'));
@@ -135,7 +135,7 @@ class Admin_CacheController extends AuthenticatedController
      */
     public function stats_action()
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
 
         $this->stats = $cache->getStats();
     }
diff --git a/app/controllers/file.php b/app/controllers/file.php
index 65344efaa8918ef5f0fc8a9f70544a75d58722d5..bebfe70ebf8253214244f45aaad40e3c48f11dbe 100644
--- a/app/controllers/file.php
+++ b/app/controllers/file.php
@@ -364,7 +364,7 @@ class FileController extends AuthenticatedController
 
             //The file system object is a folder.
             //Calculate the files and the folder size:
-            list($this->folder_size, $this->folder_file_amount) = $this->getFolderSize($this->folder);
+            [$this->folder_size, $this->folder_file_amount] = $this->getFolderSize($this->folder);
             PageLayout::setTitle($this->folder->name);
             $this->render_action('folder_details');
         }
@@ -1128,7 +1128,7 @@ class FileController extends AuthenticatedController
 
                 $this->search_id = md5(json_encode($search_parameters));
 
-                $cache = StudipCacheFactory::getCache();
+                $cache = \Studip\Cache\Factory::getCache();
 
                 $merged_results = LibrarySearchManager::search(
                     $search_parameters,
@@ -1187,7 +1187,7 @@ class FileController extends AuthenticatedController
             $this->search_id = Request::get('search_id');
             $this->page = Request::get('page');
 
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
             $cache_data = $cache->read($this->search_id);
             $results = $cache_data['results'];
             $this->total_results = count($results);
@@ -1247,7 +1247,7 @@ class FileController extends AuthenticatedController
         }
 
         if ($item_id) {
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
             $documents = $cache->read($search_id);
             $document = $documents['results'][$item_id];
             if (!($document instanceof LibraryDocument)) {
@@ -1255,7 +1255,7 @@ class FileController extends AuthenticatedController
             }
             $file = LibraryFile::createFromLibraryDocument($document, $folder_id);
         } else {
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
             $search = $cache->read($search_id);
             if (!$search) {
                 throw new Exception('Search not found in cache!');
@@ -2036,7 +2036,7 @@ class FileController extends AuthenticatedController
                 PageLayout::postMessage($result);
             }
         }
-        list($this->folder_size, $this->folder_file_amount) = $this->getFolderSize($folder);
+        [$this->folder_size, $this->folder_file_amount] = $this->getFolderSize($folder);
     }
 
     public function delete_folder_action($folder_id)
diff --git a/composer.json b/composer.json
index e4d1902d0a51b672eff7cf9f513fd7091f12cf11..009a7a07122ee2538e5a9217e9cf856f4b4f1a27 100644
--- a/composer.json
+++ b/composer.json
@@ -62,7 +62,8 @@
         "psy/psysh": "^0.12.2",
         "okvpn/clock-lts": "^1.0",
         "vlucas/phpdotenv": "^5.6",
-        "edu-sharing/auth-plugin": "8.0.x-dev"
+        "edu-sharing/auth-plugin": "8.0.x-dev",
+        "psr/cache": "^1.0"
     },
     "replace": {
         "symfony/polyfill-php73": "*",
diff --git a/composer.lock b/composer.lock
index ffde9bab8885566d3daae2a25cedeb1bbf637c71..b93431af4c4150d2f296607585d018f687c227f5 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "535218b1c9cca9acd1f2c554c59e5113",
+    "content-hash": "30db4609d0f74dac659c0fcd53db3d0a",
     "packages": [
         {
             "name": "algo26-matthias/idna-convert",
@@ -2889,6 +2889,55 @@
             },
             "time": "2023-02-11T17:10:30+00:00"
         },
+        {
+            "name": "psr/cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/cache.git",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for caching libraries",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr-6"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/cache/tree/master"
+            },
+            "time": "2016-08-06T20:24:11+00:00"
+        },
         {
             "name": "psr/clock",
             "version": "1.0.0",
diff --git a/db/migrations/1.154_recalculate_score.php b/db/migrations/1.154_recalculate_score.php
index 158582ee2918fd214f70e52800e6894a17b9f7ca..5568363ea30e9a6283001dcccab7f241e58aa908 100644
--- a/db/migrations/1.154_recalculate_score.php
+++ b/db/migrations/1.154_recalculate_score.php
@@ -38,7 +38,7 @@ class RecalculateScore extends Migration {
     private static function getScore($user_id)
     {
         $user_id || $user_id = $GLOBALS['user']->id;
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         if ($cache->read("user_score_of_".$user_id)) {
             return $cache->read("user_score_of_".$user_id);
         }
@@ -142,4 +142,3 @@ class RecalculateScore extends Migration {
     }
 
 }
-
diff --git a/db/migrations/1.224_db_cache_table.php b/db/migrations/1.224_db_cache_table.php
index 66227d0b9e2b59bd4944068d641ce6a8204291b9..9abe7e3dc6fe525afa7909d4f14ce84bab95155c 100644
--- a/db/migrations/1.224_db_cache_table.php
+++ b/db/migrations/1.224_db_cache_table.php
@@ -17,7 +17,7 @@ class DbCacheTable extends Migration
                    PRIMARY KEY (cache_key)
                    ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC');
 
-        StudipCacheFactory::getCache()->flush();
+        \Studip\Cache\Factory::getCache()->flush();
     }
 
     public function down()
diff --git a/db/migrations/1.318_step_00353_cache.php b/db/migrations/1.318_step_00353_cache.php
index 901db6045234293e8f608bb3a777b5d1a1fcef50..5959945db68aa14742a113ddcd5b200ac066c5f2 100644
--- a/db/migrations/1.318_step_00353_cache.php
+++ b/db/migrations/1.318_step_00353_cache.php
@@ -1,5 +1,10 @@
 <?php
 
+use Studip\Cache\DbCache;
+use Studip\Cache\FileCache;
+use Studip\Cache\MemcachedCache;
+use Studip\Cache\RedisCache;
+
 class Step00353Cache extends Migration
 {
     public function description()
@@ -20,10 +25,10 @@ class Step00353Cache extends Migration
             ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC");
 
         $types = [
-            'StudipDbCache',
-            'StudipFileCache',
-            'StudipMemcachedCache',
-            'StudipRedisCache'
+            DbCache::class,
+            FileCache::class,
+            MemcachedCache::class,
+            RedisCache::class,
         ];
 
         // Insert pre-defined cache types in to database
diff --git a/db/migrations/5.5.26_tic3193_no_unlimited_courses.php b/db/migrations/5.5.26_tic3193_no_unlimited_courses.php
index ef45bced3abcbbfa41e3a18bf52f8dabccde46af..21a80ca39b4d014548f676de9bec60d8ecfa423c 100644
--- a/db/migrations/5.5.26_tic3193_no_unlimited_courses.php
+++ b/db/migrations/5.5.26_tic3193_no_unlimited_courses.php
@@ -12,15 +12,14 @@ final class Tic3193NoUnlimitedCourses extends Migration
         DBManager::get()->exec("
             ALTER TABLE `sem_classes` ADD `unlimited_forbidden` TINYINT UNSIGNED NOT NULL DEFAULT 0 AFTER `is_group`
         ");
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache->expire('DB_SEM_CLASSES_ARRAY');
     }
 
     public function down()
     {
         DBManager::get()->exec("ALTER TABLE `sem_classes` DROP `unlimited_forbidden`");
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache->expire('DB_SEM_CLASSES_ARRAY');
     }
 }
-
diff --git a/db/migrations/6.0.3_adjust_cache_configuration.php b/db/migrations/6.0.3_adjust_cache_configuration.php
new file mode 100644
index 0000000000000000000000000000000000000000..2432e8474ff4a438cdabb777e004440dfece76d7
--- /dev/null
+++ b/db/migrations/6.0.3_adjust_cache_configuration.php
@@ -0,0 +1,38 @@
+<?php
+return new class extends Migration
+{
+    private const MAPPING = [
+        StudipDbCache::class        => Studip\Cache\DbCache::class,
+        StudipFileCache::class      => Studip\Cache\FileCache::class,
+        StudipMemcachedCache::class => Studip\Cache\MemcachedCache::class,
+        StudipRedisCache::class     => Studip\Cache\RedisCache::class,
+    ];
+
+    public function description()
+    {
+        return 'Replaces the renamed cache classes in system configuration';
+    }
+
+    protected function up()
+    {
+        foreach (self::MAPPING as $old => $new) {
+            self::replaceCache($old, $new);
+        }
+    }
+
+    protected function down()
+    {
+        foreach (self::MAPPING as $old => $new) {
+            self::replaceCache($new, $old);
+        }
+    }
+
+    private function replaceCache(string $old, string $new): void
+    {
+        $query = "UPDATE `config_values`
+                  SET `value` = JSON_REPLACE(`value`, '$.type', ?)
+                  WHERE `field` = 'SYSTEMCACHE'
+                    AND JSON_CONTAINS(`value`, JSON_QUOTE(?), '$.type')";
+        DBManager::get()->execute($query, [$new, $old]);
+    }
+};
diff --git a/lib/activities/Activity.php b/lib/activities/Activity.php
index 3508e5a2170a37f88d0b7f7b9c31e5fb74f225df..e1c918d0aa7401837000f9093a969cb8d24b0b45 100644
--- a/lib/activities/Activity.php
+++ b/lib/activities/Activity.php
@@ -163,7 +163,7 @@ class Activity extends \SimpleORMap
         );
 
         //Expire Cache
-        \StudipCacheFactory::getCache()->expire('activity/oldest_activity');
+        \Studip\Cache\Factory::getCache()->expire('activity/oldest_activity');
     }
 
     /**
@@ -173,7 +173,7 @@ class Activity extends \SimpleORMap
      */
     public static function getOldestActivity()
     {
-        $cache = \StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache_key = 'activity/oldest-activity';
 
         if (!$activity = unserialize($cache->read($cache_key))) {
diff --git a/lib/bootstrap-autoload.php b/lib/bootstrap-autoload.php
index 4e63115f5eb6ccb3a413e017d2ce4798042c2f70..21219fd325eb4d704f66fdc0c9412b520a44a7b9 100644
--- a/lib/bootstrap-autoload.php
+++ b/lib/bootstrap-autoload.php
@@ -20,6 +20,11 @@ StudipAutoloader::addAutoloadPath('lib/classes/admission');
 StudipAutoloader::addAutoloadPath('lib/classes/admission/userfilter');
 StudipAutoloader::addAutoloadPath('lib/classes/auth_plugins');
 StudipAutoloader::addAutoloadPath('lib/classes/calendar');
+
+StudipAutoloader::addAutoloadPath('lib/classes/cache', 'Studip\\Cache');
+class_alias(\Studip\Cache\Factory::class, 'StudipCacheFactory');
+class_alias(\Studip\Cache\Cache::class, 'StudipCache');
+
 StudipAutoloader::addAutoloadPath('lib/classes/exportdocument');
 StudipAutoloader::addAutoloadPath('lib/classes/forms');
 StudipAutoloader::addAutoloadPath('lib/classes/globalsearch');
diff --git a/lib/bootstrap-definitions.php b/lib/bootstrap-definitions.php
index 17f35d16d6478b302201cedd06c7d08eebbc0241..ee80135be09cabd42e8dded7cfb1d982fe53b4d7 100644
--- a/lib/bootstrap-definitions.php
+++ b/lib/bootstrap-definitions.php
@@ -14,8 +14,8 @@ return [
             ),
         ]);
     }),
-    StudipCache::class => DI\factory(function () {
-        return StudipCacheFactory::getCache();
+    \Studip\Cache\Cache::class => DI\factory(function () {
+        return \Studip\Cache\Factory::getCache();
     }),
     StudipPDO::class => DI\factory(function () {
         return DBManager::get();
diff --git a/lib/bootstrap.php b/lib/bootstrap.php
index 364f9d9f69fa47504ab5d323fb6544eebf5beb53..9afc16a7a6843eea0dfeeb1d6afb220512c1cc99 100644
--- a/lib/bootstrap.php
+++ b/lib/bootstrap.php
@@ -170,7 +170,7 @@ if (isset($_SERVER['REQUEST_METHOD'])) {
 // bootstrap because the stud.ip cache needs to have a db conenction)
 if ($GLOBALS['CACHING_ENABLE']) {
     $lookup_hash = null;
-    $cached = StudipCacheFactory::getCache()->read('STUDIP#autoloader-classes');
+    $cached = \Studip\Cache\Factory::getCache()->read('STUDIP#autoloader-classes');
     if ($cached) {
         $class_lookup = json_decode($cached, true);
         if (is_array($class_lookup)) {
@@ -182,7 +182,7 @@ if ($GLOBALS['CACHING_ENABLE']) {
     register_shutdown_function(function () use ($lookup_hash) {
         $cached = json_encode(StudipAutoloader::$class_lookup, JSON_UNESCAPED_UNICODE);
         if (md5($cached) !== $lookup_hash) {
-            StudipCacheFactory::getCache()->write(
+            \Studip\Cache\Factory::getCache()->write(
                 'STUDIP#autoloader-classes',
                 $cached,
                 7 * 24 * 60 * 60
diff --git a/lib/classes/MvvPerm.php b/lib/classes/MvvPerm.php
index 93f9f4404201e1c9e0e10cda3cd7ce265accc0d2..91e1e35b1a56617cbceff1461888e406536ae5fa 100644
--- a/lib/classes/MvvPerm.php
+++ b/lib/classes/MvvPerm.php
@@ -563,7 +563,7 @@ class MvvPerm {
     private static function getPrivileges($mvv_table)
     {
         if (self::$privileges === null) {
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
             self::$privileges = unserialize($cache->read(MVV::CACHE_KEY . '/privileges'));
         }
 
@@ -576,7 +576,7 @@ class MvvPerm {
                     include $config_file; // Defines $privileges
                     self::$privileges[$mvv_table] = $privileges ?? [];
                 }
-                $cache = StudipCacheFactory::getCache();
+                $cache = \Studip\Cache\Factory::getCache();
                 $cache->write(MVV::CACHE_KEY . '/privileges', serialize(self::$privileges));
             }
         }
diff --git a/lib/classes/Score.class.php b/lib/classes/Score.class.php
index 8f038f9fb94c3b5219aff84dcfa96a4042174278..f7d8ea34271e927f88a8b61497ce5ef90484ee05 100644
--- a/lib/classes/Score.class.php
+++ b/lib/classes/Score.class.php
@@ -121,7 +121,7 @@ class Score
     public static function GetMyScore($user_or_id = null)
     {
         $user = $user_or_id ? User::toObject($user_or_id) : User::findCurrent();
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         if ($cache->read("user_score_of_".$user->id)) {
             return $cache->read("user_score_of_".$user->id);
         }
diff --git a/lib/classes/SemClass.class.php b/lib/classes/SemClass.class.php
index 63be92091559a94ac64aacbd1a030c0b191e4d27..2a038e207874a15fee33049dd68bff4106bdb0ee 100644
--- a/lib/classes/SemClass.class.php
+++ b/lib/classes/SemClass.class.php
@@ -389,7 +389,7 @@ class SemClass implements ArrayAccess
                 "chdate = UNIX_TIMESTAMP() " .
             "WHERE id = :id ".
         "");
-        StudipCacheFactory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
+        \Studip\Cache\Factory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
         return $statement->execute([
             'id' => $this->data['id'],
             'name' => $this->data['name'],
@@ -453,7 +453,7 @@ class SemClass implements ArrayAccess
                 DELETE FROM sem_classes
                 WHERE id = :id
             ");
-            StudipCacheFactory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
+            \Studip\Cache\Factory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
             return $statement->execute([
                 'id' => $this->data['id']
             ]);
@@ -552,7 +552,7 @@ class SemClass implements ArrayAccess
             $db = DBManager::get();
             self::$sem_classes = [];
 
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
             $class_array = unserialize($cache->read('DB_SEM_CLASSES_ARRAY'));
             if (!$class_array) {
 
@@ -564,7 +564,7 @@ class SemClass implements ArrayAccess
                     $class_array = $statement->fetchAll(PDO::FETCH_ASSOC);
 
                     if ($class_array) {
-                        $cache = StudipCacheFactory::getCache();
+                        $cache = \Studip\Cache\Factory::getCache();
                         $cache->write('DB_SEM_CLASSES_ARRAY', serialize($class_array));
                     }
                 } catch (PDOException $e) {
@@ -593,7 +593,7 @@ class SemClass implements ArrayAccess
      */
     static public function refreshClasses()
     {
-        StudipCacheFactory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
+        \Studip\Cache\Factory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
         self::$sem_classes = null;
         return self::getClasses();
     }
diff --git a/lib/classes/SemType.class.php b/lib/classes/SemType.class.php
index 2365c4e8e937a8fb4ccb09205406709a476ccba5..5be1f19cd071bfe907ff3cf5e7a634df029f4e9c 100644
--- a/lib/classes/SemType.class.php
+++ b/lib/classes/SemType.class.php
@@ -68,7 +68,7 @@ class SemType implements ArrayAccess
                 "chdate = UNIX_TIMESTAMP() " .
             "WHERE id = :id ".
         "");
-        StudipCacheFactory::getCache()->expire('DB_SEM_TYPES_ARRAY');
+        \Studip\Cache\Factory::getCache()->expire('DB_SEM_TYPES_ARRAY');
         return $statement->execute([
             'id' => $this->data['id'],
             'name' => $this->data['name'],
@@ -89,7 +89,7 @@ class SemType implements ArrayAccess
                 DELETE FROM sem_types
                 WHERE id = :id
             ");
-            StudipCacheFactory::getCache()->expire('DB_SEM_TYPES_ARRAY');
+            \Studip\Cache\Factory::getCache()->expire('DB_SEM_TYPES_ARRAY');
             return $statement->execute([
                 'id' => $this->data['id']
             ]);
@@ -175,7 +175,7 @@ class SemType implements ArrayAccess
             $db = DBManager::get();
             self::$sem_types = [];
 
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
             $types_array = unserialize($cache->read('DB_SEM_TYPES_ARRAY'));
             if (!$types_array) {
                 try {
@@ -185,7 +185,7 @@ class SemType implements ArrayAccess
                     $statement->execute();
                     $types_array = $statement->fetchAll(PDO::FETCH_ASSOC);
                     if ($types_array) {
-                        $cache = StudipCacheFactory::getCache();
+                        $cache = \Studip\Cache\Factory::getCache();
                         $cache->write('DB_SEM_TYPES_ARRAY', serialize($types_array));
                     }
                 } catch (PDOException $e) {
@@ -210,7 +210,7 @@ class SemType implements ArrayAccess
 
     static public function refreshTypes() {
         self::$sem_types = null;
-        StudipCacheFactory::getCache()->expire('DB_SEM_TYPES_ARRAY');
+        \Studip\Cache\Factory::getCache()->expire('DB_SEM_TYPES_ARRAY');
         return self::getTypes();
     }
 
diff --git a/lib/classes/Seminar.class.php b/lib/classes/Seminar.class.php
index 054c337d4a1323f87bed7789511531b3f4202e0c..dda25ee0ffd59a515756b4f9f404e69d4566e66c 100644
--- a/lib/classes/Seminar.class.php
+++ b/lib/classes/Seminar.class.php
@@ -303,7 +303,7 @@ class Seminar
     {
 
         // Caching
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache_key = 'course/undecorated_data/'. $this->id;
 
         if ($filter) {
@@ -745,7 +745,7 @@ class Seminar
         StudipLog::log("SEM_ADD_SINGLEDATE", $this->getId(), $singledate->toString(), 'SingleDateID: '.$singledate->getTerminID());
         // logging <<<<<<
 
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache->expire('course/undecorated_data/'. $this->getId());
 
         $this->readSingleDates();
diff --git a/lib/classes/SimpleORMap.class.php b/lib/classes/SimpleORMap.class.php
index 26060860f639231616f72a3308058ed4eab2c4bc..499149c29b465c6c10c3619d8bfd43b98781c15c 100644
--- a/lib/classes/SimpleORMap.class.php
+++ b/lib/classes/SimpleORMap.class.php
@@ -277,7 +277,7 @@ class SimpleORMap implements ArrayAccess, Countable, IteratorAggregate
                     $relation = $a_config[0] ?? '';
                     $relation_field = $a_config[1] ?? '';
                     if (!$relation) {
-                        list($relation, $relation_field) = explode('_', $a_field);
+                        [$relation, $relation_field] = explode('_', $a_field);
                     }
                     if (!$relation_field || !$relation) {
                         throw new UnexpectedValueException('no relation found for autoget/set additional field: ' . $a_field);
@@ -415,7 +415,7 @@ class SimpleORMap implements ArrayAccess, Countable, IteratorAggregate
     public static function tableScheme($db_table)
     {
         if (self::$schemes === null) {
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
             self::$schemes = unserialize($cache->read('DB_TABLE_SCHEMES'));
         }
         if (!isset(self::$schemes[$db_table])) {
@@ -434,7 +434,7 @@ class SimpleORMap implements ArrayAccess, Countable, IteratorAggregate
             }
             self::$schemes[$db_table]['db_fields'] = $db_fields;
             self::$schemes[$db_table]['pk'] = $pk;
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
             $cache->write('DB_TABLE_SCHEMES', serialize(self::$schemes));
         }
         return isset(self::$schemes[$db_table]);
@@ -446,7 +446,7 @@ class SimpleORMap implements ArrayAccess, Countable, IteratorAggregate
      */
     public static function expireTableScheme()
     {
-        StudipCacheFactory::getCache()->expire('DB_TABLE_SCHEMES');
+        \Studip\Cache\Factory::getCache()->expire('DB_TABLE_SCHEMES');
         self::$schemes = null;
         self::$config = [];
     }
@@ -993,7 +993,7 @@ class SimpleORMap implements ArrayAccess, Countable, IteratorAggregate
      */
     protected function _getAdditionalValueFromRelation($field)
     {
-        list($relation, $relation_field) = [$this->additional_fields()[$field]['relation'],
+        [$relation, $relation_field] = [$this->additional_fields()[$field]['relation'],
                                                 $this->additional_fields()[$field]['relation_field']];
         if (!array_key_exists($field, $this->additional_data)) {
             $this->_setAdditionalValue($field, $this->getRelationValue($relation, $relation_field));
@@ -1010,7 +1010,7 @@ class SimpleORMap implements ArrayAccess, Countable, IteratorAggregate
      */
     protected function _setAdditionalValueFromRelation($field, $value)
     {
-        list($relation, $relation_field) = [$this->additional_fields()[$field]['relation'],
+        [$relation, $relation_field] = [$this->additional_fields()[$field]['relation'],
                 $this->additional_fields()[$field]['relation_field']];
         $this->$relation->$field = $value;
         unset($this->additional_data[$field]);
diff --git a/lib/classes/Siteinfo.php b/lib/classes/Siteinfo.php
index ba7386b8b61817b74951be61bd3a59111afd0408..247b836867dad7bb172dc0d0e2604278949de57b 100644
--- a/lib/classes/Siteinfo.php
+++ b/lib/classes/Siteinfo.php
@@ -419,7 +419,7 @@ class SiteinfoMarkupEngine {
     }
 
     function coregroup() {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         if (!($remotefile = $cache->read('coregroup'))) {
             $remotefile = file_get_contents('https://develop.studip.de/studip/extern.php?module=Persons&config_id=8d1dafc3afca2bce6125d57d4119b631&range_id=4498a5bc62d7974d0a0ac3e97aca5296', false, get_default_http_stream_context('https://develop.studip.de'));
             $cache->write('coregroup', $remotefile);
@@ -428,7 +428,7 @@ class SiteinfoMarkupEngine {
     }
 
     function toplist($item) {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         if ($found_in_cache = $cache->read(__METHOD__ . $item)) {
             return $found_in_cache;
         }
@@ -531,7 +531,7 @@ class SiteinfoMarkupEngine {
     }
 
     function indicator($key) {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         if ($found_in_cache = $cache->read(__METHOD__ . $key)) {
             return $found_in_cache;
         }
diff --git a/lib/classes/StudipCache.class.php b/lib/classes/StudipCache.class.php
deleted file mode 100644
index ba929f9bcffe9d764ebd80a3b651673fe2dc2a44..0000000000000000000000000000000000000000
--- a/lib/classes/StudipCache.class.php
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-/**
- * An interface which has to be implemented by instances returned from
- * StudipCacheFactory#getCache
- *
- * @package    studip
- * @subpackage lib
- *
- * @author     Marco Diedrich (mdiedric@uos)
- * @author     Marcus Lunzenauer (mlunzena@uos.de)
- * @copyright  (c) Authors
- * @since      1.6
- * @license    GPL2 or any later version
- */
-
-interface StudipCache
-{
-    const DEFAULT_EXPIRATION = 12 * 60 * 60; // 12 hours
-
-    /**
-     * Expire item from the cache.
-     *
-     * Example:
-     *
-     *   # expires foo
-     *   $cache->expire('foo');
-     *
-     * @param string $arg a single key
-     */
-    public function expire($arg);
-
-    /**1
-     * Expire all items from the cache.
-     */
-    public function flush();
-
-    /**
-     * Retrieve item from the server.
-     *
-     * Example:
-     *
-     *   # reads foo
-     *   $foo = $cache->reads('foo');
-     *
-     * @param string $arg a single key
-     *
-     * @return mixed    the previously stored data if an item with such a key
-     *                  exists on the server or FALSE on failure.
-     */
-    public function read($arg);
-
-    /**
-     * Store data at the server.
-     *
-     * @param string $name     the item's key.
-     * @param mixed  $content  the item's content (will be serialized if necessary).
-     * @param int    $expires  the item's expiry time in seconds. Optional, defaults to 12h.
-     *
-     * @return bool     returns TRUE on success or FALSE on failure.
-     */
-    public function write($name, $content, $expires = self::DEFAULT_EXPIRATION);
-
-    /**
-     * @return string A translateable display name for this cache class.
-     */
-    public static function getDisplayName(): string;
-
-    /**
-     * Get some statistics from cache, like number of entries, hit rate or
-     * whatever the underlying cache provides.
-     * Results are returned in form of an array like
-     *      "[
-     *          [
-     *              'name' => <displayable name>
-     *              'value' => <value of the current stat>
-     *          ]
-     *      ]"
-     *
-     * @return array
-     */
-    public function getStats(): array;
-
-    /**
-     * Return the Vue component name and props that handle configuration.
-     * The associative array is of the form
-     *  [
-     *      'component' => <Vue component name>,
-     *      'props' => <Properties for component>
-     *  ]
-     *
-     * @return array
-     */
-    public static function getConfig(): array;
-}
diff --git a/lib/classes/StudipCachedArray.php b/lib/classes/StudipCachedArray.php
index 1232d00950d499eb43c450df82835db42b313ebf..46723f447fc465345fdc4316420d2942d071d842 100644
--- a/lib/classes/StudipCachedArray.php
+++ b/lib/classes/StudipCachedArray.php
@@ -27,10 +27,10 @@ class StudipCachedArray implements ArrayAccess
      * @param int    $duration Duration in seconds for which the item shall be
      *                         stored
      */
-    public function __construct(string $key, int $duration = StudipCache::DEFAULT_EXPIRATION)
+    public function __construct(string $key, int $duration = \Studip\Cache\Cache::DEFAULT_EXPIRATION)
     {
         $this->key = self::class . "/{$key}";
-        $this->cache = StudipCacheFactory::getCache();
+        $this->cache = \Studip\Cache\Factory::getCache();
         $this->duration = $duration;
         $this->hash = $this->getHash();
 
@@ -116,11 +116,14 @@ class StudipCachedArray implements ArrayAccess
     protected function loadData(string $offset)
     {
         if (!array_key_exists($offset, $this->data)) {
-            $cached = $this->cache->read($this->getCacheKey($offset));
-            $this->data[$offset] = $this->swapNullAndFalse($cached);
+            // Get the cache item from the cache:
+            $item = $this->cache->getItem($this->getCacheKey($offset));
+            if ($item->isHit()) {
+                $this->data[$offset] = $this->swapNullAndFalse($item->get());
+            }
         }
 
-        return $this->data[$offset];
+        return $this->data[$offset] ?? null;
     }
 
     /**
@@ -132,13 +135,12 @@ class StudipCachedArray implements ArrayAccess
      */
     protected function storeData(string $offset): void
     {
-        $data = $this->swapNullAndFalse($this->data[$offset]);
-
-        $this->cache->write(
+        $item = new \Studip\Cache\Item(
             $this->getCacheKey($offset),
-            $data,
+            $this->swapNullAndFalse($this->data[$offset]),
             $this->duration
         );
+        $this->cache->save($item);
     }
 
     /**
diff --git a/lib/classes/StudipDbCache.class.php b/lib/classes/StudipDbCache.class.php
deleted file mode 100644
index 865825e25880fb1fa72fe9ef0584c609a52add4c..0000000000000000000000000000000000000000
--- a/lib/classes/StudipDbCache.class.php
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-/**
- * StudipCache implementation using database table
- *
- * @package     studip
- * @subpackage  cache
- *
- * @author    Elmar Ludwig <elmar.ludwig@uos.de>
- */
-class StudipDbCache implements StudipCache
-{
-
-    /**
-     * @return string A translateable display name for this cache class.
-     */
-    public static function getDisplayName(): string
-    {
-        return _('Datenbank');
-    }
-
-    /**
-     * Expire item from the cache.
-     *
-     * @param string $arg a single key
-     */
-    public function expire($arg)
-    {
-        $db = DBManager::get();
-
-        $stmt = $db->prepare('DELETE FROM cache WHERE cache_key = ?');
-        $stmt->execute([$arg]);
-    }
-
-    /**
-     * Expire all items from the cache.
-     */
-    public function flush()
-    {
-        $db = DBManager::get();
-
-        $db->exec('TRUNCATE TABLE cache');
-    }
-
-    /**
-     * Delete all expired items from the cache.
-     */
-    public function purge()
-    {
-        $db = DBManager::get();
-
-        $stmt = $db->prepare('DELETE FROM cache WHERE expires < ?');
-        $stmt->execute([time()]);
-    }
-
-    /**
-     * Retrieve item from the server.
-     *
-     * @param string $arg a single key
-     *
-     * @return mixed    the previously stored data if an item with such a key
-     *                  exists on the server or FALSE on failure.
-     */
-    public function read($arg)
-    {
-        $db = DBManager::get();
-
-        $stmt = $db->prepare('SELECT content FROM cache WHERE cache_key = ? AND expires > ?');
-        $stmt->execute([$arg, time()]);
-        $result = $stmt->fetchColumn();
-
-        return $result !== false ? unserialize($result) : false;
-    }
-
-    /**
-     * Store data at the server.
-     *
-     * @param string $name     the item's key.
-     * @param mixed  $content  the item's content (will be serialized if necessary).
-     * @param int    $expired  the item's expiry time in seconds. Optional, defaults to 12h.
-     *
-     * @return bool     returns TRUE on success or FALSE on failure.
-     */
-    public function write($name, $content, $expires = self::DEFAULT_EXPIRATION)
-    {
-        $db = DBManager::get();
-
-        $stmt = $db->prepare('REPLACE INTO cache VALUES(?, ?, ?)');
-        return $stmt->execute([$name, serialize($content), time() + $expires]);
-    }
-
-    /**
-     * Return statistics.
-     *
-     * @see StudipCache::getStats()
-     *
-     * @return array|array[]
-     */
-    public function getStats(): array
-    {
-        return [
-            __CLASS__ => [
-                'name' => _('Anzahl Einträge'),
-                'value' => DBManager::get()->fetchColumn("SELECT COUNT(*) FROM `cache`")
-            ]
-        ];
-    }
-
-    /**
-     * Return the Vue component name and props that handle configuration.
-     *
-     * @see StudipCache::getConfig()
-     *
-     * @return array
-     */
-    public static function getConfig(): array
-    {
-        return [
-            'component' => null,
-            'props' => []
-        ];
-    }
-
-}
diff --git a/lib/classes/StudipKing.class.php b/lib/classes/StudipKing.class.php
index 2d1f15ce883faada0e5e1033262ea116c2833103..3a61c577f987ddd01dd9aa2542f9a258444e5b71 100644
--- a/lib/classes/StudipKing.class.php
+++ b/lib/classes/StudipKing.class.php
@@ -63,7 +63,7 @@ class StudipKing {
     private static function get_kings()
     {
         if (self::$kings === null) {
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
 
             # read cache (unserializing a cache miss - FALSE - does not matter)
             $kings = unserialize($cache->read(self::CACHE_KEY));
diff --git a/lib/classes/StudipMemoryCache.class.php b/lib/classes/StudipMemoryCache.class.php
deleted file mode 100644
index d38385a8ec40fbae2e97a505f3b3a85036a06cd7..0000000000000000000000000000000000000000
--- a/lib/classes/StudipMemoryCache.class.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
- * The php memory implementation of the StudipCache interface.
- *
- * @author  Jan-Hendrik Willms <tleilax+studip@gmail.com>
- * @license GPL2 or any later version
- * @since   Stud.IP 5.0
- */
-class StudipMemoryCache implements StudipCache
-{
-    protected $memory_cache = [];
-
-    /**
-     * Expires just a single key.
-     *
-     * @param  string  the key
-     */
-    public function expire($key)
-    {
-        unset($this->memory_cache[$key]);
-    }
-
-    /**
-     * Expire all items from the cache.
-     */
-    public function flush()
-    {
-        $this->memory_cache = [];
-    }
-
-    /**
-     * Reads just a single key from the cache.
-     *
-     * @param  string  the key
-     *
-     * @return mixed   the corresponding value
-     */
-    public function read($key)
-    {
-        if (!isset($this->memory_cache[$key])) {
-            return false;
-        }
-        if ($this->memory_cache[$key]['expires'] < time()) {
-            $this->expire($key);
-            return false;
-        }
-        return $this->memory_cache[$key]['data'];
-    }
-
-    /**
-     * Store data at the server.
-     *
-     * @param string   the item's key.
-     * @param mixed    the item's content (will be serialized if necessary).
-     * @param int      the item's expiry time in seconds. Defaults to 12h.
-     *
-     * @returns mixed  returns TRUE on success or FALSE on failure.
-     *
-     */
-    public function write($name, $content, $expires = self::DEFAULT_EXPIRATION)
-    {
-        $this->memory_cache[$name] = [
-            'expires' => time() + $expires,
-            'data'    => $content,
-        ];
-
-        return true;
-    }
-
-    public static function getDisplayName(): string
-    {
-        return 'Memory cache';
-    }
-
-    public function getStats(): array
-    {
-        return [];
-    }
-
-    public static function getConfig(): array
-    {
-        return [];
-    }
-}
diff --git a/lib/classes/UserLookup.class.php b/lib/classes/UserLookup.class.php
index ddf9276bc0a7f5064ac4451397f00a0c35a9af7e..bdd9fc368c0ad6281122e9e7300df16560775df4 100644
--- a/lib/classes/UserLookup.class.php
+++ b/lib/classes/UserLookup.class.php
@@ -239,7 +239,7 @@ class UserLookup
             return call_user_func(self::$types[$type]['values']);
         }
 
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache_key = "UserLookup/{$type}/values";
         $cached_values = $cache->read($cache_key);
         if ($cached_values) {
diff --git a/lib/classes/assets/SASSCompiler.php b/lib/classes/assets/SASSCompiler.php
index 2dcda2dc50bb541d72de5630a7a27de22771fc5e..0b03a8cd2fa24b55a8dbfb55475ec535cd0f4c60 100644
--- a/lib/classes/assets/SASSCompiler.php
+++ b/lib/classes/assets/SASSCompiler.php
@@ -2,7 +2,7 @@
 namespace Assets;
 
 use Assets;
-use StudipCacheFactory;
+use Studip\Cache\Factory;
 use Studip;
 
 use ScssPhp\ScssPhp\Compiler as ScssCompiler;
@@ -82,7 +82,7 @@ class SASSCompiler implements Compiler
      */
     private function getPrefix()
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = Studip\Cache\Factory::getCache();
 
         $prefix = $cache->read(self::CACHE_KEY);
 
diff --git a/lib/classes/cache/Cache.class.php b/lib/classes/cache/Cache.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..366945dbcf6e4209c081a846f35dbf4494097de6
--- /dev/null
+++ b/lib/classes/cache/Cache.class.php
@@ -0,0 +1,208 @@
+<?php
+
+namespace Studip\Cache;
+
+use Psr\Cache\CacheItemInterface;
+use Psr\Cache\CacheItemPoolInterface;
+
+/**
+ * An abstract class which has to be extended by instances returned from
+ * \Studip\Cache\Factory#getCache
+ *
+ * @author     Marco Diedrich (mdiedric@uos)
+ * @author     Marcus Lunzenauer (mlunzena@uos.de)
+ * @author     Moritz Strohm <strohm@data-quest.de>
+ * @copyright  (c) Authors
+ * @since      1.6
+ * @license    GPL2 or any later version
+ */
+abstract class Cache implements CacheItemPoolInterface
+{
+    const DEFAULT_EXPIRATION = 12 * 60 * 60; // 12 hours
+
+    /**
+     * @return string A translateable display name for this cache class.
+     */
+    abstract public static function getDisplayName(): string;
+
+    /**
+     * Get some statistics from cache, like number of entries, hit rate or
+     * whatever the underlying cache provides.
+     * Results are returned in form of an array like
+     *      "[
+     *          [
+     *              'name' => <displayable name>
+     *              'value' => <value of the current stat>
+     *          ]
+     *      ]"
+     *
+     * @return array
+     */
+    abstract public function getStats(): array;
+
+    /**
+     * Return the Vue component name and props that handle configuration.
+     * The associative array is of the form
+     *  [
+     *      'component' => <Vue component name>,
+     *      'props' => <Properties for component>
+     *  ]
+     *
+     * @return array
+     */
+    abstract public static function getConfig(): array;
+
+    /**
+     * Expire item from the cache.
+     *
+     * Example:
+     *
+     *   # expires foo
+     *   $cache->expire('foo');
+     *
+     * @param string $arg a single key
+     */
+    abstract public function expire($arg);
+
+    /**
+     * Expire all items from the cache.
+     */
+    abstract public function flush();
+
+    /**
+     * @see CacheItemPoolInterface::getItem
+     */
+    abstract public function getItem($key);
+
+    /**
+     * @see CacheItemPoolInterface::hasItem
+     */
+    abstract public function hasItem($key);
+
+    /**
+     * @var array An array of deferred items that shall be saved only
+     * when commit() is called. This is only used in PSR-6 cache methods.
+     */
+    protected array $deferred_items = [];
+
+    /**
+     * Retrieve item from the server.
+     *
+     * Example:
+     *
+     *   # reads foo
+     *   $foo = $cache->reads('foo');
+     *
+     * @param string $arg a single key
+     *
+     * @return mixed    the previously stored data if an item with such a key
+     *                  exists on the server or FALSE on failure.
+     *
+     * @deprecated To be removed with Stud.IP 7.0.
+     */
+    public function read($arg)
+    {
+        $item = $this->getItem($arg);
+        if ($item->isHit()) {
+            return $item->get();
+        }
+        return false;
+    }
+
+    /**
+     * Store data at the server.
+     *
+     * @param string $name     the item's key.
+     * @param mixed  $content  the item's content (will be serialized if necessary).
+     * @param int    $expires  the item's expiry time in seconds. Optional, defaults to 12h.
+     *
+     * @return bool     returns TRUE on success or FALSE on failure.
+
+     * @deprecated To be removed with Stud.IP 7.0.
+     */
+    public function write($name, $content, $expires = self::DEFAULT_EXPIRATION)
+    {
+        $item = new Item($name, $content, $expires);
+
+        return $this->save($item);
+    }
+
+    /**
+     * Calculates the expiration by a cache item. If that cannot be determined,
+     * the default expiration period is returned.
+     *
+     * @param Item $item The item from which to get the expiration time.
+     *
+     * @return int The time from now until the expiration in seconds.
+     */
+    public function getExpiration(CacheItemInterface $item) : int
+    {
+        $expiration = self::DEFAULT_EXPIRATION;
+        if ($item instanceof Item) {
+            $expiration = $item->getExpirationInSeconds();
+        }
+        return $expiration;
+    }
+
+    // PSR-6 CacheItemPoolInterface:
+
+    /**
+     * @see CacheItemPoolInterface::getItems
+     */
+    public function getItems(array $keys = [])
+    {
+        $items = [];
+        foreach ($keys as $key) {
+            $item = $this->getItem($key);
+            if ($item instanceof Item) {
+                $items[] = $item;
+            }
+        }
+        return $items;
+    }
+
+    /**
+     * @see CacheItemPoolInterface::clear
+     */
+    public function clear()
+    {
+        $this->deferred_items = [];
+        $this->flush();
+    }
+
+    /**
+     * @see CacheItemPoolInterface::deleteItem
+     */
+    public function deleteItem($key)
+    {
+        $this->expire($key);
+    }
+
+    /**
+     * @see CacheItemPoolInterface::deleteItems
+     */
+    public function deleteItems(array $keys)
+    {
+        foreach ($keys as $key) {
+            $this->expire($key);
+        }
+    }
+
+    /**
+     * @see CacheItemPoolInterface::saveDeferred
+     */
+    public function saveDeferred(CacheItemInterface $item)
+    {
+        $this->deferred_items[] = $item;
+    }
+
+    /**
+     * @see CacheItemPoolInterface::commit
+     */
+    public function commit()
+    {
+        foreach ($this->deferred_items as $item) {
+            $this->save($item);
+        }
+    }
+}
diff --git a/lib/classes/cache/DbCache.class.php b/lib/classes/cache/DbCache.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..3361af05a86fbba1127eaa79de8678e810cedbb8
--- /dev/null
+++ b/lib/classes/cache/DbCache.class.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Studip\Cache;
+
+use DBManager;
+
+/**
+ * StudipCache implementation using database table
+ *
+ * @author    Elmar Ludwig <elmar.ludwig@uos.de>
+ */
+class DbCache extends Cache
+{
+    /**
+     * @return string A display name (that can be translated) for this cache class.
+     */
+    public static function getDisplayName(): string
+    {
+        return _('Datenbank');
+    }
+
+    /**
+     * Expire item from the cache.
+     *
+     * @param string $arg a single key
+     */
+    public function expire($arg)
+    {
+        $db = DBManager::get();
+
+        $stmt = $db->prepare('DELETE FROM cache WHERE cache_key = ?');
+        $stmt->execute([$arg]);
+    }
+
+    /**
+     * Expire all items from the cache.
+     */
+    public function flush()
+    {
+        $db = DBManager::get();
+
+        $db->exec('TRUNCATE TABLE cache');
+    }
+
+    /**
+     * Delete all expired items from the cache.
+     */
+    public function purge()
+    {
+        $db = DBManager::get();
+
+        $stmt = $db->prepare('DELETE FROM cache WHERE expires < ?');
+        $stmt->execute([time()]);
+    }
+
+    /**
+     * Return statistics.
+     *
+     * @return array|array[]
+     *@see Cache::getStats()
+     *
+     */
+    public function getStats(): array
+    {
+        return [
+            __CLASS__ => [
+                'name' => _('Anzahl Einträge'),
+                'value' => DBManager::get()->fetchColumn("SELECT COUNT(*) FROM `cache`")
+            ]
+        ];
+    }
+
+    /**
+     * Return the Vue component name and props that handle configuration.
+     *
+     * @return array
+     *@see Cache::getConfig()
+     *
+     */
+    public static function getConfig(): array
+    {
+        return [
+            'component' => null,
+            'props' => []
+        ];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getItem($key)
+    {
+        $query = "SELECT `content`, `expires`
+                  FROM `cache`
+                  WHERE `cache_key` = :key
+                    AND `expires` > UNIX_TIMESTAMP()";
+        $result = DBManager::get()->fetchOne($query, [':key' => $key]);
+
+        $item = new Item($key);
+        if (!empty($result)) {
+            $item->setHit();
+            if ($result['content']) {
+                $item->set(unserialize($result['content']));
+            }
+            if ($result['expires']) {
+                $expiration = new \DateTime();
+                $expiration->setTimestamp($result['expires']);
+                $item->expiresAt($expiration);
+            }
+        }
+        return $item;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function hasItem($key)
+    {
+        $query = "SELECT 1
+                  FROM `cache`
+                  WHERE `cache_key` = :key
+                    AND `expires` > UNIX_TIMESTAMP()";
+        return (bool) DBManager::get()->fetchColumn($query, [':key' => $key]);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function save(\Psr\Cache\CacheItemInterface $item)
+    {
+        $expiration = $this->getExpiration($item);
+        if ($expiration < 1) {
+            // The item would expire immediately.
+            return false;
+        }
+
+        return DBManager::get()->execute(
+            'REPLACE INTO `cache` VALUES (?, ?, ?)',
+            [$item->getKey(), serialize($item->get()), $expiration]
+        );
+    }
+}
diff --git a/lib/classes/cache/Exception.php b/lib/classes/cache/Exception.php
new file mode 100644
index 0000000000000000000000000000000000000000..64ee364087391da65ebf2f78450d6ba76abf4ddd
--- /dev/null
+++ b/lib/classes/cache/Exception.php
@@ -0,0 +1,27 @@
+<?php
+/*
+ * CacheException.class.php
+ * This file is part of Stud.IP.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @author      Moritz Strohm <strohm@data-quest.de>
+ * @copyright   2024
+ * @license     http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category    Stud.IP
+ * @since       6.0
+ */
+
+namespace Studip\Cache;
+
+/**
+ * The CacheException class is an implementation of the CacheException interface
+ * of PSR-6 that behaves like a StudipException.
+ */
+class Exception extends \StudipException implements \Psr\Cache\CacheException
+{
+    //Nothing here, since there is nothing to implement.
+}
diff --git a/lib/classes/StudipCacheFactory.class.php b/lib/classes/cache/Factory.class.php
similarity index 81%
rename from lib/classes/StudipCacheFactory.class.php
rename to lib/classes/cache/Factory.class.php
index 77c5973ac1a7474627846fa1fca7c5f97c1156aa..b5c835951a6e7add5544f565265bf29fdd30bd9f 100644
--- a/lib/classes/StudipCacheFactory.class.php
+++ b/lib/classes/cache/Factory.class.php
@@ -1,4 +1,15 @@
 <?php
+
+namespace Studip\Cache;
+
+use Config;
+use DBSchemaVersion;
+use MessageBox;
+use PageLayout;
+use ReflectionClass;
+use StudipCacheOperation;
+use UnexpectedValueException;
+
 /**
  * This factory retrieves the instance of StudipCache configured for use in
  * this Stud.IP installation.
@@ -12,30 +23,29 @@
  * @since     1.6
  * @license   GPL2 or any later version
  */
-
-class StudipCacheFactory
+class Factory
 {
     /**
      * the default cache class
      *
      * @var string
      */
-    const DEFAULT_CACHE_CLASS = StudipDbCache::class;
+    const DEFAULT_CACHE_CLASS = DbCache::class;
 
     /**
      * singleton instance
      *
-     * @var StudipCache
+     * @var Cache|null
      */
-    private static $cache;
+    private static ?Cache $cache = null;
 
 
     /**
      * config instance
      *
-     * @var Config
+     * @var Config|null
      */
-    private static $config = null;
+    private static ?Config $config = null;
 
 
     /**
@@ -49,7 +59,7 @@ class StudipCacheFactory
      */
     public static function getConfig()
     {
-        return is_null(self::$config) ? Config::getInstance() : self::$config;
+        return self::$config ?? Config::getInstance();
     }
 
 
@@ -58,7 +68,7 @@ class StudipCacheFactory
      *                       determine the class of the implementation of interface
      *                       StudipCache
      */
-    public static function setConfig($config)
+    public static function setConfig(Config $config)
     {
         self::$config = $config;
         self::$cache = NULL;
@@ -77,15 +87,15 @@ class StudipCacheFactory
      *
      * @param bool $apply_proxied_operations Whether or not to apply any
      *                                       proxied (disable this in tests!)
-     * @return StudipCache the cache instance
+     * @return Cache the cache instance
      */
-    public static function getCache($apply_proxied_operations = true)
+    public static function getCache(bool $apply_proxied_operations = true): Cache
     {
-        if (is_null(self::$cache)) {
+        if (self::$cache === null) {
             $proxied = false;
 
             if (!$GLOBALS['CACHING_ENABLE']) {
-                self::$cache = new StudipMemoryCache();
+                self::$cache = new MemoryCache();
 
                 // Proxy cache operations if CACHING_ENABLE is different from the globally set
                 // caching value. This should only be the case in cli mode.
@@ -98,7 +108,7 @@ class StudipCacheFactory
                     $args = self::retrieveConstructorArguments();
 
                     self::$cache = self::instantiateCache($class, $args);
-                } catch (Exception $e) {
+                } catch (\Exception $e) {
                     error_log(__METHOD__ . ': ' . $e->getMessage());
                     PageLayout::addBodyElements(MessageBox::error(__METHOD__ . ': ' . $e->getMessage()));
                     $class = self::DEFAULT_CACHE_CLASS;
@@ -109,7 +119,7 @@ class StudipCacheFactory
             // If proxy should be used, inject it. Otherwise apply pending
             // operations, if any.
             if ($proxied) {
-                self::$cache = new StudipCacheProxy(self::$cache);
+                self::$cache = new Proxy(self::$cache);
             } elseif ($GLOBALS['CACHING_ENABLE'] && $apply_proxied_operations) {
                 // Even if the above condition will try to eliminate most
                 // failures, the following operation still needs to be wrapped
@@ -118,7 +128,7 @@ class StudipCacheFactory
                 // for said operation.
                 try {
                     StudipCacheOperation::apply(self::$cache);
-                } catch (Exception $e) {
+                } catch (\Exception $e) {
                 }
             }
         }
@@ -174,10 +184,11 @@ class StudipCacheFactory
      * 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 $class     the name of the class
-     * @param  array  $arguments 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
+     * @return Cache  an instance of the specified class
+     * @throws \ReflectionException
      */
     public static function instantiateCache($class, $arguments)
     {
@@ -186,8 +197,8 @@ class StudipCacheFactory
                ? $reflection_class->newInstanceArgs($arguments['config'])
                : $reflection_class->newInstance();
 
-        if ($class !== StudipMemoryCache::class) {
-            return new StudipCacheWrapper($cache);
+        if ($class !== MemoryCache::class) {
+            return new Wrapper($cache);
         }
 
         return $cache;
diff --git a/lib/classes/StudipFileCache.class.php b/lib/classes/cache/FileCache.class.php
similarity index 55%
rename from lib/classes/StudipFileCache.class.php
rename to lib/classes/cache/FileCache.class.php
index 9eae66c1fba9967f099a6e56310791895c08d9eb..e94395ac46166c1993285de8301966c4a2b6794c 100644
--- a/lib/classes/StudipFileCache.class.php
+++ b/lib/classes/cache/FileCache.class.php
@@ -1,46 +1,27 @@
 <?php
-# Lifter010: TODO
-// +--------------------------------------------------------------------------+
-// This file is part of Stud.IP
-// StudipFileCache.class.php
-//
-//
-//
-// Copyright (c) 2007 André Noack <noack@data-quest.de>
-// +--------------------------------------------------------------------------+
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or any later version.
-// +--------------------------------------------------------------------------+
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-// +--------------------------------------------------------------------------+
+
+namespace Studip\Cache;
+
+use Config;
+use Exception;
 
 /**
- * StudipCache implementation using files
- *
- * @package     studip
- * @subpackage  cache
+ * Cache implementation using files
  *
  * @author    André Noack <noack@data-quest.de>
- * @version   2
+ * @copyright 2007 André Noack <noack@data-quest.de>
+ * @license GPL2 or any later version
  */
-class StudipFileCache implements StudipCache
+class FileCache extends Cache
 {
-    use StudipCacheKeyTrait;
+    use KeyTrait;
 
     /**
      * full path to cache directory
      *
      * @var string
      */
-    private $dir;
+    private string $dir;
 
     /**
      * @return string A translateable display name for this cache class.
@@ -55,26 +36,28 @@ class StudipFileCache implements StudipCache
      * $CACHING_FILECACHE_PATH or is set to
      * $TMP_PATH/studip_cache
      *
-     * @param string the path to use
-     * @return void
-     * @throws exception if the directory does not exist or could not be
+     * @param string $path the path to use
+     * @throws Exception if the directory does not exist or could not be
      *         created
      */
-    public function __construct($path = '')
+    public function __construct(string $path = '')
     {
         $this->dir = $path
-                  ?: (Config::get()->SYSTEMCACHE['type'] == 'StudipFileCache' ?
-                        Config::get()->SYSTEMCACHE['config']['path'] : '')
+                  ?: (
+                      Config::get()->SYSTEMCACHE['type'] === self::class
+                          ? Config::get()->SYSTEMCACHE['config']['path']
+                          : ''
+                  )
                   ?: $GLOBALS['CACHING_FILECACHE_PATH']
                   ?: ($GLOBALS['TMP_PATH'] . '/' . 'studip_cache');
         $this->dir = rtrim($this->dir, '\\/') . '/';
 
         if (!is_dir($this->dir) && !@mkdir($this->dir, 0700)) {
-            throw new Exception('Could not create directory: ' . $this->dir);
+            throw new \Exception('Could not create directory: ' . $this->dir);
         }
 
         if (!is_writable($this->dir)) {
-            throw new Exception('Can not write to directory: ' . $this->dir);
+            throw new \Exception('Can not write to directory: ' . $this->dir);
         }
     }
 
@@ -91,9 +74,11 @@ class StudipFileCache implements StudipCache
     /**
      * expire cache item
      *
-     * @see StudipCache::expire()
      * @param string $arg
+     *
      * @return void
+     * @throws Exception
+     * @see Cache::expire()
      */
     public function expire($arg)
     {
@@ -112,62 +97,21 @@ class StudipFileCache implements StudipCache
         rmdirr($this->dir);
     }
 
-    /**
-     * retrieve cache item from filesystem
-     * tests first if item is expired
-     *
-     * @see StudipCache::read()
-     * @param string $arg a cache key
-     * @return string|bool
-     */
-    public function read($arg)
-    {
-        $key = $this->getCacheKey($arg);
-
-        if ($file = $this->check($key)){
-            $f = @fopen($file, 'rb');
-            if ($f) {
-                @flock($f, LOCK_SH);
-                $result = stream_get_contents($f);
-                @fclose($f);
-            }
-            return unserialize($result);
-        }
-        return false;
-    }
-
-    /**
-     * store data as cache item in filesystem
-     *
-     * @see StudipCache::write()
-     * @param string $arg a cache key
-     * @param mixed $content data to store
-     * @param int $expire expiry time in seconds, default 12h
-     * @return int|bool the number of bytes that were written to the file,
-     *         or false on failure
-     */
-    public function write($arg, $content, $expire = self::DEFAULT_EXPIRATION)
-    {
-        $key = $this->getCacheKey($arg);
-
-        $this->expire($key);
-        $file = $this->getPathAndFile($key, $expire);
-        return @file_put_contents($file, serialize($content), LOCK_EX);
-    }
-
     /**
      * checks if specified cache item is expired
      * if expired the cache file is deleted
      *
      * @param string $key a cache key to check
-     * @return string|bool the path to the cache file or false if expired
+     *
+     * @return array|bool the path to the cache file or false if expired
+     * @throws Exception
      */
     private function check($key)
     {
         if ($file = $this->getPathAndFile($key)){
-            list($id, $expire) = explode('-', basename($file));
+            [$id, $expire] = explode('-', basename($file));
             if (time() < $expire) {
-                return $file;
+                return [$file, $expire];
             } else {
                 @unlink($file);
             }
@@ -183,16 +127,18 @@ class StudipFileCache implements StudipCache
      * the filename is constructed from the hashed cache key
      * and the timestamp of expiration
      *
-     * @param string $key a cache key
-     * @param int $expire expiry time in seconds
+     * @param string   $key    a cache key
+     * @param int|null $expire expiry time in seconds
+     *
      * @return string|bool full path to cache item or false on failure
+     * @throws Exception
      */
-    private function getPathAndFile($key, $expire = null)
+    private function getPathAndFile(string $key, ?int $expire = null): bool|string
     {
         $id = hash('md5', $key);
         $path = $this->dir . mb_substr($id, 0, 2);
         if (!is_dir($path) && !@mkdir($path, 0700)) {
-            throw new Exception('Could not create directory: ' . $path);
+            throw new \Exception('Could not create directory: ' . $path);
         }
         if (!is_null($expire)){
             return $path . '/' . $id . '-' . (time() + $expire);
@@ -208,16 +154,17 @@ class StudipFileCache implements StudipCache
     /**
      * purges expired entries from the cache directory
      *
-     * @param bool echo messages if set to false
+     * @param bool $be_quiet echo messages if set to false
+     *
      * @return int the number of deleted files
      */
-    public function purge($be_quiet = true)
+    public function purge(bool $be_quiet = true): int
     {
         $now = time();
         $deleted = 0;
         foreach (@glob($this->dir . '*', GLOB_ONLYDIR) as $current_dir){
             foreach (@glob("{$current_dir}/*") as $file){
-                list($id, $expire) = explode('-', basename($file));
+                [$id, $expire] = explode('-', basename($file));
                 if ($expire < $now) {
                     if (@unlink($file)) {
                         ++$deleted;
@@ -225,8 +172,8 @@ class StudipFileCache implements StudipCache
                             echo "File: {$file} deleted.\n";
                         }
                     }
-                } else if (!$be_quiet){
-                    echo "File: {$file} expires on " . strftime('%x %X', $expire) . "\n";
+                } else if (!$be_quiet) {
+                    echo "File: {$file} expires on " . date('Y-m-d H:i:s', $expire) . "\n";
                 }
             }
         }
@@ -243,7 +190,7 @@ class StudipFileCache implements StudipCache
         return [
             __CLASS__ => [
                 'name' => _('Anzahl Einträge'),
-                'value' => DBManager::get()->fetchColumn("SELECT COUNT(*) FROM `cache`")
+                'value' => \DBManager::get()->fetchColumn("SELECT COUNT(*) FROM `cache`")
             ]
         ];
     }
@@ -273,4 +220,58 @@ class StudipFileCache implements StudipCache
         ];
     }
 
+    /**
+     * @inheritDoc
+     */
+    public function getItem($key)
+    {
+        $real_key = $this->getCacheKey($key);
+
+        $item = new \Studip\Cache\Item($key);
+
+        $file_data = $this->check($real_key);
+        if ($file_data) {
+            $file = $file_data[0];
+            $expire = $file_data[1];
+            $f = @fopen($file, 'rb');
+            if ($f) {
+                @flock($f, LOCK_SH);
+                $result = stream_get_contents($f);
+                @fclose($f);
+            }
+            $item->setHit();
+            $item->set(unserialize($result));
+            $expiration = new \DateTime();
+            $expiration->setTimestamp($expire);
+            $item->expiresAt($expiration);
+        }
+        return $item;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function hasItem($key)
+    {
+        $real_key = $this->getCacheKey($key);
+        $file_data = $this->check($real_key);
+        return $file_data !== false;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function save(\Psr\Cache\CacheItemInterface $item)
+    {
+        $expiration = $this->getExpiration($item);
+        if ($expiration < 1) {
+            //The item would expire immediately.
+            return false;
+        }
+
+        $real_key = $this->getCacheKey($item->getKey());
+        $this->expire($real_key);
+        $file = $this->getPathAndFile($real_key, $expiration);
+        return @file_put_contents($file, serialize($item->get()), LOCK_EX);
+    }
 }
diff --git a/lib/classes/cache/InvalidCacheArgumentException.php b/lib/classes/cache/InvalidCacheArgumentException.php
new file mode 100644
index 0000000000000000000000000000000000000000..e85c6b2c1380e6633aeaa2a599a29e35d0e834bb
--- /dev/null
+++ b/lib/classes/cache/InvalidCacheArgumentException.php
@@ -0,0 +1,28 @@
+<?php
+/*
+ * CacheException.class.php
+ * This file is part of Stud.IP.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @author      Moritz Strohm <strohm@data-quest.de>
+ * @copyright   2024
+ * @license     http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category    Stud.IP
+ * @since       6.0
+ */
+
+namespace Studip\Cache;
+
+
+/**
+ * The InvalidCacheArgumentException is an implementation of the InvalidArgumentException interface
+ *  of PSR-6 that behaves like a StudipException.
+ */
+class InvalidCacheArgumentException extends \StudipException implements \Psr\Cache\InvalidArgumentException
+{
+    //Nothing here, since there is nothing to implement.
+}
diff --git a/lib/classes/cache/Item.class.php b/lib/classes/cache/Item.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..1a09f1ddd471a5005b9d8cd4eb8a9df43145ae09
--- /dev/null
+++ b/lib/classes/cache/Item.class.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * Item.class.php
+ * This file is part of Stud.IP.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @author      Moritz Strohm <strohm@data-quest.de>
+ * @copyright   2024
+ * @license     http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category    Stud.IP
+ * @since       6.0
+ */
+
+namespace Studip\Cache;
+
+use DateInterval;
+use DateTime;
+use Psr\Cache\CacheItemInterface;
+
+/**
+ * \Studip\Cache\CacheItem implements the CacheItemInterface of PSR-6. It holds the value and the
+ * key of a cache item and also provides additional methods to get the expiration of the item.
+ */
+class Item implements CacheItemInterface
+{
+    /**
+     * @var string The key of the item in the cache.
+     */
+    protected string $key;
+
+    /**
+     * @var mixed The value of the item.
+     */
+    protected mixed $value;
+
+    /**
+     * @var DateTime|null The expiration as DateTime object or null if the expiration is not defined.
+     */
+    protected ?DateTime $expiration = null;
+
+    /**
+     * @var bool An indicator whether the item has been found in the cache (true) or not (false).
+     */
+    protected bool $cache_hit = false;
+
+    /**
+     * The constructor of \Studip\Cache\CacheItem.
+     *
+     * @param string $key The key of the item in the cache.
+     * @param mixed $value The value of the item.
+     * @param int|null $expiration The expiration of the item in seconds, if applicable.
+     * @param bool $cache_hit Whether the item shall be constructed as cache hit (true) or not (false).
+     *
+     */
+    public function __construct(
+        string $key,
+        mixed $value = null,
+        ?int $expiration = null,
+        bool $cache_hit = false
+    ) {
+        $this->key         = $key;
+        $this->value       = $value;
+        $this->cache_hit   = $cache_hit;
+        $this->expiresAfter($expiration);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getKey()
+    {
+        return $this->key;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function get()
+    {
+        return $this->value;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function isHit()
+    {
+        return $this->cache_hit;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function set($value)
+    {
+        $this->value = $value;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function expiresAt($expiration)
+    {
+        $this->expiration = $expiration;
+        return $this;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function expiresAfter($time)
+    {
+        $this->expiration = new DateTime();
+        if ($time instanceof DateInterval) {
+            $this->expiration = $this->expiration->add($time);
+        } elseif (is_integer($time)) {
+            $this->expiration->setTimestamp(time() + $time);
+        } else {
+            $this->expiration->setTimestamp(time() + Cache::DEFAULT_EXPIRATION);
+        }
+        return $this;
+    }
+
+    // \Studip\Cache\CacheItem specific methods:
+
+    /**
+     * Sets the item to be a cache hit.
+     *
+     * @return void
+     */
+    public function setHit() : void
+    {
+        $this->cache_hit = true;
+    }
+
+    /**
+     * Returns the expiration, if set.
+     *
+     * @return DateTime|null A DateTime object with the expiration date and time
+     *     or null if the expiration is not defined.
+     */
+    public function getExpiration() : ?DateTime
+    {
+        return $this->expiration;
+    }
+
+    /**
+     * Returns the seconds from the current timestamp until the expiration of the item.
+     *
+     * @return int The seconds until the item expires
+     */
+    public function getExpirationInSeconds() : int
+    {
+        if ($this->expiration) {
+            return $this->expiration->getTimestamp() - time();
+        }
+        return 0;
+    }
+}
diff --git a/lib/classes/StudipCacheKeyTrait.php b/lib/classes/cache/KeyTrait.php
similarity index 77%
rename from lib/classes/StudipCacheKeyTrait.php
rename to lib/classes/cache/KeyTrait.php
index 62eb142f3b02c533eee84c0eeff228a0f48d093f..021aaba3966574f13fad4428e6cf71a68fcd0efc 100644
--- a/lib/classes/StudipCacheKeyTrait.php
+++ b/lib/classes/cache/KeyTrait.php
@@ -1,4 +1,6 @@
 <?php
+namespace Studip\Cache;
+
 /**
  * Trait for unique cache hashes per key for each system based on db configuration which should
  * be sufficient to eliminate cache mishaps.
@@ -9,9 +11,9 @@
  * @subpackage  cache
  * @since       Stud.IP 5.0
  */
-trait StudipCacheKeyTrait
+trait KeyTrait
 {
-    protected $cache_prefix = null;
+    protected ?string $cache_prefix = null;
 
     /**
      * Returns a prefix cache key based on db configuration.
@@ -19,11 +21,11 @@ trait StudipCacheKeyTrait
      * @param  string $offset
      * @return string
      */
-    protected function getCacheKey($offset)
+    protected function getCacheKey(string $offset): string
     {
         if ($this->cache_prefix === null) {
             $this->cache_prefix = md5("{$GLOBALS['DB_STUDIP_HOST']}|{$GLOBALS['DB_STUDIP_DATABASE']}");
         }
-        return "{$this->cache_prefix}/{$offset}";
+        return "$this->cache_prefix/$offset";
     }
 }
diff --git a/lib/classes/StudipMemcachedCache.php b/lib/classes/cache/MemcachedCache.class.php
similarity index 51%
rename from lib/classes/StudipMemcachedCache.php
rename to lib/classes/cache/MemcachedCache.class.php
index 0e44dd57e1c5176468afc0db610ff47cd2099bea..5d9033be9b8c71f947cea88260e26f1fe8a8d09c 100644
--- a/lib/classes/StudipMemcachedCache.php
+++ b/lib/classes/cache/MemcachedCache.class.php
@@ -1,31 +1,23 @@
 <?php
 
-/**
- * Copyright (C) 2007 - Marcus Lunzenauer <mlunzena@uos.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
+namespace Studip\Cache;
 
+use Memcached;
+use Psr\Cache\CacheItemInterface;
 
 /**
  * Cache implementation using memcached.
  *
- * @package     studip
- * @subpackage  cache
- *
- * @author    mlunzena
+ * @author    Marcus Lunzenauer <mlunzena@uos.de>
  * @copyright (c) Authors
+ * @license GPL2 or any later version
  * @since   5.0
  */
-
-class StudipMemcachedCache implements StudipCache
+class MemcachedCache extends Cache
 {
-    use StudipCacheKeyTrait;
+    use KeyTrait;
 
-    private $memcache;
+    private Memcached $memcache;
 
     /**
      * @return string A translateable display name for this cache class.
@@ -38,18 +30,18 @@ class StudipMemcachedCache implements StudipCache
     public function __construct($servers)
     {
         if (!extension_loaded('memcached')) {
-            throw new Exception('Memcache extension missing.');
+            throw new \Exception('Memcache extension missing.');
         }
 
-        $prefix = Config::get()->STUDIP_INSTALLATION_ID;
-        $this->memcache = new Memcached('studip' . $prefix ? '-' . $prefix : '');
+        $prefix = \Config::get()->STUDIP_INSTALLATION_ID;
+        $this->memcache = new Memcached('studip' . ($prefix ? '-' . $prefix : ''));
 
         if (count($this->memcache->getServerList()) === 0) {
             foreach ($servers as $server) {
                 $status = $this->memcache->addServer($server['hostname'], (int) $server['port']);
 
                 if (!$status) {
-                    throw new Exception("Could not add server: {$server['hostname']} @ port {$server['port']}");
+                    throw new \Exception("Could not add server: {$server['hostname']} @ port {$server['port']}");
                 }
             }
         }
@@ -80,40 +72,6 @@ class StudipMemcachedCache implements StudipCache
         $this->memcache->flush();
     }
 
-    /**
-     * Retrieve item from the server.
-     *
-     * Example:
-     *
-     *   # reads foo
-     *   $foo = $cache->reads('foo');
-     *
-     * @param   string $arg a single key
-     * @returns mixed  the previously stored data if an item with such a key
-     *                 exists on the server or FALSE on failure.
-     */
-    public function read($arg)
-    {
-        $key = $this->getCacheKey($arg);
-        return $this->memcache->get($key);
-    }
-
-    /**
-     * Store data at the server.
-     *
-     * @param string $arg the item's key.
-     * @param string $content the item's content.
-     * @param int $expire the item's expiry time in seconds. Defaults to 12h.
-     *
-     * @returns mixed  returns TRUE on success or FALSE on failure.
-     *
-     */
-    public function write($arg, $content, $expire = self::DEFAULT_EXPIRATION)
-    {
-        $key = $this->getCacheKey($arg);
-        return $this->memcache->set($key, $content, $expire);
-    }
-
     /**
      * Return statistics.
      *
@@ -123,20 +81,19 @@ class StudipMemcachedCache implements StudipCache
      */
     public function getStats(): array
     {
-        $stats = $this->memcache->getStats();
-        return $stats;
+        return $this->memcache->getStats();
     }
 
     /**
      * Return the Vue component name and props that handle configuration.
      *
-     * @see StudipCache::getConfig()
+     * @see Cache::getConfig()
      *
      * @return array
      */
     public static function getConfig(): array
     {
-        $currentCache = Config::get()->SYSTEMCACHE;
+        $currentCache = \Config::get()->SYSTEMCACHE;
 
         // Set default config for this cache
         $currentConfig = [
@@ -154,4 +111,41 @@ class StudipMemcachedCache implements StudipCache
         ];
     }
 
+    /**
+     * @inheritDoc
+     */
+    public function getItem($key)
+    {
+        $item = new Item($key);
+        $value = $this->memcache->get($this->getCacheKey($key));
+        if ($this->memcache->getResultCode() !== Memcached::RES_NOTFOUND) {
+            // Set the value, even if it is the boolean value false:
+            $item->setHit();
+            $item->set($value);
+        }
+        return $item;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function hasItem($key)
+    {
+        return $this->memcache->checkKey($this->getCacheKey($key));
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function save(CacheItemInterface $item)
+    {
+        $expiration = $this->getExpiration($item);
+        if ($expiration < 1) {
+            // The item would expire immediately.
+            return false;
+        }
+
+        $real_key = $this->getCacheKey($item->getKey());
+        return $this->memcache->set($real_key, $item->get(), $expiration);
+    }
 }
diff --git a/lib/classes/cache/MemoryCache.class.php b/lib/classes/cache/MemoryCache.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..93f6bbd66aa9429e62a8fc48d755b95fab2f24eb
--- /dev/null
+++ b/lib/classes/cache/MemoryCache.class.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Studip\Cache;
+
+use DateTime;
+use Psr\Cache\CacheItemInterface;
+
+/**
+ * The php memory implementation of the StudipCache interface.
+ *
+ * @author  Jan-Hendrik Willms <tleilax+studip@gmail.com>
+ * @license GPL2 or any later version
+ * @since   Stud.IP 5.0
+ */
+class MemoryCache extends Cache
+{
+    protected array $memory_cache = [];
+
+    /**
+     * Expires just a single key.
+     *
+     * @param  string  the key
+     */
+    public function expire($key)
+    {
+        unset($this->memory_cache[$key]);
+    }
+
+    /**
+     * Expire all items from the cache.
+     */
+    public function flush()
+    {
+        $this->memory_cache = [];
+    }
+
+    public static function getDisplayName(): string
+    {
+        return 'Memory cache';
+    }
+
+    public function getStats(): array
+    {
+        return [];
+    }
+
+    public static function getConfig(): array
+    {
+        return [];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getItem($key)
+    {
+        $item = new Item($key);
+        if (!isset($this->memory_cache[$key])) {
+            return $item;
+        }
+        if ($this->memory_cache[$key]['expires'] < time()) {
+            $this->expire($key);
+            return $item;
+        }
+        $item->setHit();
+        $item->set($this->memory_cache[$key]['data']);
+        if (!empty($this->memory_cache[$key]['expires'])) {
+            $expiration = new DateTime();
+            $expiration->setTimestamp($this->memory_cache[$key]['expires']);
+            $item->expiresAt($expiration);
+        }
+        return $item;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function hasItem($key)
+    {
+        return isset($this->memory_cache[$key])
+            && $this->memory_cache[$key]['expires'] < time();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function save(CacheItemInterface $item)
+    {
+        $expiration = $this->getExpiration($item);
+
+        $this->memory_cache[$item->getKey()] = [
+            'expires' => $expiration + time(),
+            'data'    => $item->get(),
+        ];
+
+        return true;
+    }
+}
diff --git a/lib/classes/StudipCacheProxy.php b/lib/classes/cache/Proxy.class.php
similarity index 57%
rename from lib/classes/StudipCacheProxy.php
rename to lib/classes/cache/Proxy.class.php
index 686f8129d687e130f474167b32ff4f14a2096532..6791fb72c93a245ed7c6338848163c3a358c4af4 100644
--- a/lib/classes/StudipCacheProxy.php
+++ b/lib/classes/cache/Proxy.class.php
@@ -1,4 +1,9 @@
 <?php
+
+namespace Studip\Cache;
+
+use StudipCacheOperation;
+
 /**
  * Proxies a StudipCache and stores the expire operation in the database.
  * These operations are lateron applied to the cache they should have
@@ -8,18 +13,18 @@
  * @license GPL2 or any later version
  * @since   Stud.IP 3.3
  */
-class StudipCacheProxy implements StudipCache
+class Proxy extends Cache
 {
-    protected $actual_cache;
-    protected $proxy_these;
+    protected Cache $actual_cache;
+    protected array $proxy_these;
 
     /**
-     * @param StudipCache $cache       The actual cache object
-     * @param mixed       $proxy_these List of operations to proxy (should be
-     *                                 an array but a space seperated string
-     *                                 is also valid)
+     * @param Cache $cache       The actual cache object
+     * @param mixed $proxy_these List of operations to proxy (should be an
+     *                           array but a space seperated string is also
+     *                           valid)
      */
-    public function __construct(StudipCache $cache, $proxy_these = ['expire'])
+    public function __construct(Cache $cache, $proxy_these = ['expire'])
     {
         if (!is_array($proxy_these)) {
             $proxy_these = words($proxy_these);
@@ -43,7 +48,7 @@ class StudipCacheProxy implements StudipCache
                 $operation = new StudipCacheOperation([$key, 'expire']);
                 $operation->parameters = serialize([]);
                 $operation->store();
-            } catch (Exception $e) {
+            } catch (\Exception $e) {
             }
         }
 
@@ -60,58 +65,58 @@ class StudipCacheProxy implements StudipCache
                 $operation = new StudipCacheOperation(['', 'flush']);
                 $operation->parameters = serialize([]);
                 $operation->store();
-            } catch (Exception $e) {
+            } catch (\Exception $e) {
             }
         }
 
         return $this->actual_cache->flush();
     }
 
-    /**
-     * Reads just a single key from the cache.
-     *
-     * @param  string $key The item's key
-     * @return mixed The corresponding value
-     */
-    public function read($key)
+    public static function getDisplayName(): string
     {
-        return $this->actual_cache->read($key);
+        return static::class;
     }
 
-    /**
-     * Store data at the server.
-     *
-     * @param string $key     The item's key
-     * @param string $content The item's conten
-     * @param int    $expires The item's expiry time in seconds, defaults to 12h
-     * @return bool  Returns TRUE on success or FALSE on failure
-     */
-    public function write($key, $content, $expires = self::DEFAULT_EXPIRATION)
+    public function getStats(): array
     {
-        if (in_array('write', $this->proxy_these)) {
-            try {
-                $operation = new StudipCacheOperation([$key, 'write']);
-                $operation->parameters = serialize([$content, $expires]);
-                $operation->store();
-            } catch (Exception $e) {
-            }
-        }
+        return $this->actual_cache->getStats();
+    }
 
-        return $this->actual_cache->write($key, $content, $expires);
+    public static function getConfig(): array
+    {
+        return [];
     }
 
-    public static function getDisplayName(): string
+    /**
+     * @inheritDoc
+     */
+    public function getItem($key)
     {
-        return static::class;
+        return $this->actual_cache->getItem($key);
     }
 
-    public function getStats(): array
+    /**
+     * @inheritDoc
+     */
+    public function hasItem($key)
     {
-        return $this->actual_cache->getStats();
+        return $this->actual_cache->hasItem($key);
     }
 
-    public static function getConfig(): array
+    /**
+     * @inheritDoc
+     */
+    public function save(\Psr\Cache\CacheItemInterface $item)
     {
-        return [];
+        if (in_array('save', $this->proxy_these)) {
+            try {
+                $operation = new StudipCacheOperation([$item->getKey(), 'save']);
+                $operation->parameters = serialize([$item]);
+                $operation->store();
+            } catch (\Exception $e) {
+            }
+        }
+
+        return $this->actual_cache->save($item);
     }
 }
diff --git a/lib/classes/StudipRedisCache.class.php b/lib/classes/cache/RedisCache.class.php
similarity index 73%
rename from lib/classes/StudipRedisCache.class.php
rename to lib/classes/cache/RedisCache.class.php
index 7b9570bdd0c76605dbb000f59f2298648440be89..9ea871146222b71444b0d1c48248323c46df83d3 100644
--- a/lib/classes/StudipRedisCache.class.php
+++ b/lib/classes/cache/RedisCache.class.php
@@ -1,4 +1,15 @@
 <?php
+
+namespace Studip\Cache;
+
+use BadMethodCallException;
+use Config;
+use DateTime;
+use Exception;
+use Psr\Cache\CacheItemInterface;
+use Redis;
+use RedisException;
+
 /**
  * Cache implementation using redis.
  *
@@ -8,9 +19,9 @@
  * @subpackage  cache
  * @since       Stud.IP 5.0
  */
-class StudipRedisCache implements StudipCache
+class RedisCache extends Cache
 {
-    use StudipCacheKeyTrait;
+    use KeyTrait;
 
     private $redis;
 
@@ -28,6 +39,8 @@ class StudipRedisCache implements StudipCache
      * @param string $hostname Hostname of redis server
      * @param int    $port     Port of redis server
      * @param string $auth     Optional auth token/password
+     *
+     * @throws RedisException
      */
     public function __construct($hostname, $port, string $auth = '')
     {
@@ -73,41 +86,6 @@ class StudipRedisCache implements StudipCache
         $this->redis->unlink($key);
     }
 
-    /**
-     * Retrieve item from the server.
-     *
-     * Example:
-     *
-     *   # reads foo
-     *   $foo = $cache->reads('foo');
-     *
-     * @param  string $arg a single key
-     * @return mixed  the previously stored data if an item with such a key
-     *                exists on the server or FALSE on failure.
-     */
-    public function read($arg)
-    {
-        $key = $this->getCacheKey($arg);
-
-        $result = $this->redis->get($key);
-
-        return ($result === null) ? null : unserialize($result);
-    }
-
-    /**
-     * Store data at the server.
-     *
-     * @param string   the item's key.
-     * @param string   the item's content.
-     * @param int      the item's expiry time in seconds. Defaults to 12h.
-     * @return mixed  returns TRUE on success or FALSE on failure.
-     */
-    public function write($name, $content, $expire = self::DEFAULT_EXPIRATION)
-    {
-        $key = $this->getCacheKey($name);
-        return $this->redis->setEx($key, $expire, serialize($content));
-    }
-
     /**
      * Expire all items from the cache.
      */
@@ -174,4 +152,47 @@ class StudipRedisCache implements StudipCache
             'props' => $currentConfig
         ];
     }
+
+    /**
+     * @inheritDoc
+     */
+    public function getItem($key)
+    {
+        $item = new Item($key);
+        $real_key = $this->getCacheKey($key);
+        $result = $this->redis->get($real_key);
+        if ($result === null) {
+            return $item;
+        }
+        $item->setHit();
+        $item->set(unserialize($result));
+        $expiration = new DateTime();
+        $expiration->setTimestamp($this->redis->expiretime($real_key));
+        $item->expiresAt($expiration);
+        return $item;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function hasItem($key)
+    {
+        $real_key = $this->getCacheKey($key);
+        return $this->redis->get($real_key) !== null;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function save(CacheItemInterface $item)
+    {
+        $expiration = $this->getExpiration($item);
+        if ($expiration < 1) {
+            // The item would expire immediately.
+            return false;
+        }
+
+        $real_key = $this->getCacheKey($item->getKey());
+        return $this->redis->setEx($real_key, $expiration, serialize($item->get()));
+    }
 }
diff --git a/lib/classes/StudipCacheWrapper.php b/lib/classes/cache/Wrapper.class.php
similarity index 56%
rename from lib/classes/StudipCacheWrapper.php
rename to lib/classes/cache/Wrapper.class.php
index 6c75c01a1203bff23c2c5715c1eb8a7ff9f477d7..744893270b1ccb2e05987b87992921686282356c 100644
--- a/lib/classes/StudipCacheWrapper.php
+++ b/lib/classes/cache/Wrapper.class.php
@@ -1,5 +1,9 @@
 <?php
 
+namespace Studip\Cache;
+
+use Psr\Cache\CacheItemInterface;
+
 /**
  * The cache wrapper wraps a memory cache around another cache. This should
  * reduce the accesses to the actual cache.
@@ -8,17 +12,15 @@
  * @license GPL2 or any later version
  * @since Stud.IP 5.4
  */
-class StudipCacheWrapper implements StudipCache
+class Wrapper extends Cache
 {
-    const DEFAULT_MEMORY_EXPIRATION = 60;
-
-    protected $actual_cache;
-    protected $memory_cache;
+    protected Cache $actual_cache;
+    protected MemoryCache $memory_cache;
 
-    public function __construct(StudipCache $actual_cache)
+    public function __construct(Cache $actual_cache)
     {
         $this->actual_cache = $actual_cache;
-        $this->memory_cache = new StudipMemoryCache();
+        $this->memory_cache = new MemoryCache();
     }
 
     /**
@@ -39,47 +41,55 @@ class StudipCacheWrapper implements StudipCache
         $this->actual_cache->flush();
     }
 
+    public static function getDisplayName(): string
+    {
+        return static::class;
+    }
+
+    public function getStats(): array
+    {
+        return $this->actual_cache->getStats();
+    }
+
+    public static function getConfig(): array
+    {
+        return [];
+    }
+
     /**
-     * @inheritdoc
+     * @inheritDoc
      */
-    public function read($arg)
+    public function getItem($key)
     {
-        $cached = $this->memory_cache->read($arg);
-        if ($cached !== false) {
+        $cached = $this->memory_cache->getItem($key);
+        if ($cached->isHit()) {
             return $cached;
         }
 
-        $cached = $this->actual_cache->read($arg);
-        if ($cached !== false) {
-            $this->memory_cache->write($arg, $cached, self::DEFAULT_MEMORY_EXPIRATION);
+        $cached = $this->actual_cache->getItem($key);
+        if ($cached->isHit()) {
+            $this->memory_cache->save($cached);
         }
         return $cached;
     }
 
     /**
-     * @inheritdoc
+     * @inheritDoc
      */
-    public function write($name, $content, $expires = self::DEFAULT_EXPIRATION)
+    public function hasItem($key)
     {
-        if ($this->actual_cache->write($name, $content, $expires)) {
-            return $this->memory_cache->write($name, $content, $expires);
-        } else {
-            return false;
-        }
-    }
-
-    public static function getDisplayName(): string
-    {
-        return static::class;
-    }
-
-    public function getStats(): array
-    {
-        return $this->actual_cache->getStats();
+        return $this->actual_cache->hasItem($key);
     }
 
-    public static function getConfig(): array
+    /**
+     * @inheritDoc
+     */
+    public function save(CacheItemInterface $item)
     {
-        return [];
+        if ($this->actual_cache->save($item)) {
+            return $this->memory_cache->save($item);
+        } else {
+            return false;
+        }
     }
 }
diff --git a/lib/classes/cas/CAS_PGTStorage_Cache.php b/lib/classes/cas/CAS_PGTStorage_Cache.php
index 284b59134f3f6e11430c5b1bd1a013d0e218bb7a..61ce9fa6ad0f7066c868eb936bf115509698acfb 100644
--- a/lib/classes/cas/CAS_PGTStorage_Cache.php
+++ b/lib/classes/cas/CAS_PGTStorage_Cache.php
@@ -43,7 +43,7 @@ class CAS_PGTStorage_Cache extends CAS_PGTStorage_AbstractStorage
      */
     public function write($pgt, $pgt_iou)
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache_key = 'pgtiou/' . $pgt_iou;
         return $cache->write($cache_key, $pgt);
     }
@@ -58,7 +58,7 @@ class CAS_PGTStorage_Cache extends CAS_PGTStorage_AbstractStorage
      */
     public function read($pgt_iou)
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache_key = 'pgtiou/' . $pgt_iou;
         $pgt = $cache->read($cache_key);
         $cache->expire($cache_key);
diff --git a/lib/cronjobs/purge_cache.class.php b/lib/cronjobs/purge_cache.class.php
index 4e5682f2b26d19f02d383f119dec4a2abee22262..d2e3697db8d464cb362fc5c786e866c90e45f1e8 100644
--- a/lib/cronjobs/purge_cache.class.php
+++ b/lib/cronjobs/purge_cache.class.php
@@ -70,7 +70,7 @@ class PurgeCacheJob extends CronJob
      */
     public function setUp()
     {
-        require_once 'lib/classes/StudipFileCache.class.php';
+        require_once 'lib/classes/cache/FileCache.class.php';
     }
 
     /**
@@ -86,7 +86,7 @@ class PurgeCacheJob extends CronJob
      */
     public function execute($last_result, $parameters = [])
     {
-        $cache = new StudipFileCache();
+        $cache = new \Studip\Cache\FileCache();
         $cache->purge(empty($parameters['verbose']));
     }
 }
diff --git a/lib/functions.php b/lib/functions.php
index 0e806aa7f47dff028d3ad9323351d9bec87bf58d..7d9f4f2defba00312b50ba76e729a0bf19d1a881 100644
--- a/lib/functions.php
+++ b/lib/functions.php
@@ -636,7 +636,7 @@ function get_users_online($active_time = 5, $name_format = 'full_rev')
  */
 function get_users_online_count($active_time = 10)
 {
-    $cache = StudipCacheFactory::getCache();
+    $cache = \Studip\Cache\Factory::getCache();
     $online_count = $cache->read("online_count/{$active_time}");
     if ($online_count === false) {
         $query = "SELECT COUNT(*) FROM user_online
diff --git a/lib/models/CourseDate.class.php b/lib/models/CourseDate.class.php
index 59fbb9c3a2584db0e0a3164c33f92504a9dfb7a9..eadeb0a020e3b0006bc74fc668aefd2236c0198c 100644
--- a/lib/models/CourseDate.class.php
+++ b/lib/models/CourseDate.class.php
@@ -364,7 +364,7 @@ class CourseDate extends SimpleORMap implements PrivacyObject, Event
         // load room-booking, if any
         $this->room_booking;
 
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache->expire('course/undecorated_data/'. $this->range_id);
         return parent::store();
     }
@@ -376,7 +376,7 @@ class CourseDate extends SimpleORMap implements PrivacyObject, Event
      */
     public function delete()
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache->expire('course/undecorated_data/'. $this->range_id);
         return parent::delete();
     }
@@ -433,7 +433,7 @@ class CourseDate extends SimpleORMap implements PrivacyObject, Event
             $folders = array_merge($folders, $topic->folders->getArrayCopy());
         }
         foreach ($folders as $folder) {
-            list($files, $typed_folders) = array_values(FileManager::getFolderFilesRecursive($folder->getTypedFolder(), $user_id));
+            [$files, $typed_folders] = array_values(FileManager::getFolderFilesRecursive($folder->getTypedFolder(), $user_id));
             foreach ($files as $file) {
                 $all_files[$file->id] = $file;
             }
diff --git a/lib/models/OERMaterial.php b/lib/models/OERMaterial.php
index 6ec7de84485cf07bc2a44f5c8e8c4be14f2deef9..8411d149d84a2bee9884f6729704fe8e085ba69a 100644
--- a/lib/models/OERMaterial.php
+++ b/lib/models/OERMaterial.php
@@ -164,7 +164,7 @@ class OERMaterial extends SimpleORMap
     public static function fetchRemoteSearch($text, $tag = false)
     {
         $cache_name = "oer_remote_searched_for_".md5($text)."_".($tag ? 1 : 0);
-        $already_searched = (bool) StudipCacheFactory::getCache()->read($cache_name);
+        $already_searched = (bool) \Studip\Cache\Factory::getCache()->read($cache_name);
         if (!$already_searched) {
             $hosts = OERHost::findBySQL("index_server = '1' AND allowed_as_index_server = '1' ORDER BY RAND()");
             foreach ($hosts as $host) {
@@ -172,7 +172,7 @@ class OERMaterial extends SimpleORMap
                     $host->fetchRemoteSearch($text, $tag);
                 }
             }
-            StudipCacheFactory::getCache()->write($cache_name, "1", 60);
+            \Studip\Cache\Factory::getCache()->write($cache_name, "1", 60);
         }
     }
 
diff --git a/lib/models/PersonalNotifications.class.php b/lib/models/PersonalNotifications.class.php
index 721038cd63d0d31e377bcfbc46cf3e196548eb51..12d9c1b14c6f2bdac20a2de51d7f4d507aba6632 100644
--- a/lib/models/PersonalNotifications.class.php
+++ b/lib/models/PersonalNotifications.class.php
@@ -262,7 +262,7 @@ class PersonalNotifications extends SimpleORMap
      */
     protected static function getCache($user_id)
     {
-        $cache  = StudipCacheFactory::getCache();
+        $cache  = \Studip\Cache\Factory::getCache();
         $hash   = self::getCacheHash($user_id);
         $cached = $cache->read($hash);
 
@@ -281,7 +281,7 @@ class PersonalNotifications extends SimpleORMap
      */
     protected static function setCache($user_id, $items)
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $hash  = self::getCacheHash($user_id);
         $cache->write($hash, serialize($items), self::CACHE_DURATION);
     }
@@ -293,7 +293,7 @@ class PersonalNotifications extends SimpleORMap
      */
     protected static function expireCache($user_id)
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $hash  = self::getCacheHash($user_id);
         $cache->expire($hash);
     }
diff --git a/lib/models/Semester.class.php b/lib/models/Semester.class.php
index 4eefa3b886cd9424bd3ac8c7a4258cc9060e8dde..03243a57d04049020951bd5d376a1b4153179a70 100644
--- a/lib/models/Semester.class.php
+++ b/lib/models/Semester.class.php
@@ -181,7 +181,7 @@ class Semester extends SimpleORMap
         if (!is_array(self::$semester_cache) || $force_reload) {
             self::$semester_cache = [];
             if (!$force_reload) {
-                $cache = StudipCacheFactory::getCache();
+                $cache = \Studip\Cache\Factory::getCache();
                 $semester_data_array = unserialize($cache->read('DB_SEMESTER_DATA'));
                 if ($semester_data_array) {
                     foreach ($semester_data_array as $semester_data) {
@@ -202,7 +202,7 @@ class Semester extends SimpleORMap
                     }
                     $semester_data[] = $semester->toRawArray();
                 }
-                $cache = StudipCacheFactory::getCache();
+                $cache = \Studip\Cache\Factory::getCache();
                 $cache->write('DB_SEMESTER_DATA', serialize($semester_data));
             }
         }
@@ -471,7 +471,7 @@ class Semester extends SimpleORMap
      */
     public function refreshCache()
     {
-        StudipCacheFactory::getCache()->expire('DB_SEMESTER_DATA');
+        \Studip\Cache\Factory::getCache()->expire('DB_SEMESTER_DATA');
     }
 
     /*
diff --git a/lib/models/StudipCacheOperation.php b/lib/models/StudipCacheOperation.php
index e9c8738371e9d9cb2f583fd6f4085ddbb18d0140..d2287c32a5f662e5c98af4cf764d334f51e549ec 100644
--- a/lib/models/StudipCacheOperation.php
+++ b/lib/models/StudipCacheOperation.php
@@ -42,7 +42,7 @@ class StudipCacheOperation extends SimpleORMap
      */
     public static function apply(StudipCache $cache)
     {
-        self::findEachBySQL(function ($item) use ($cache) {
+        self::findEachBySQL(function (StudipCacheOperation $item) use ($cache): void {
             $parameters = unserialize($item->parameters);
             array_unshift($parameters, $item->cache_key);
             call_user_func_array([$cache, $item->operation], $parameters);
diff --git a/lib/phplib/CT_Cache.class.php b/lib/phplib/CT_Cache.class.php
index 311b27cba5526c1ea3203b1ae973f29806f0d6f0..d4eccfa0a2702b7055e5c7197010a1c8b9e95d1c 100644
--- a/lib/phplib/CT_Cache.class.php
+++ b/lib/phplib/CT_Cache.class.php
@@ -14,7 +14,7 @@ class CT_Cache
 
     public function ac_start()
     {
-        $this->cache = StudipCacheFactory::getCache();
+        $this->cache = \Studip\Cache\Factory::getCache();
     }
 
     public function ac_get_lock()
diff --git a/lib/plugins/db/RolePersistence.class.php b/lib/plugins/db/RolePersistence.class.php
index b11fa4ad8fd0d066700f4945e77d5d1a72c0abe4..df63a773debc8e975e553354270ecef6c2b8e304 100644
--- a/lib/plugins/db/RolePersistence.class.php
+++ b/lib/plugins/db/RolePersistence.class.php
@@ -29,7 +29,7 @@ class RolePersistence
     {
         if (self::$all_roles === null) {
             // read cache
-            $cache = StudipCacheFactory::getCache();
+            $cache = \Studip\Cache\Factory::getCache();
 
             // cache miss, retrieve from database
             self::$all_roles = $cache->read(self::ROLES_CACHE_KEY);
@@ -675,7 +675,7 @@ class RolePersistence
     public static function expireRolesCache()
     {
         self::$all_roles = null;
-        StudipCacheFactory::getCache()->expire(self::ROLES_CACHE_KEY);
+        \Studip\Cache\Factory::getCache()->expire(self::ROLES_CACHE_KEY);
     }
 
     /**
diff --git a/lib/plugins/engine/PluginRepository.class.php b/lib/plugins/engine/PluginRepository.class.php
index 14415f42bd097602608bcb936048b7f8f90b6886..235e9bd97b36087e2e666ef3eef6ae653488a437 100644
--- a/lib/plugins/engine/PluginRepository.class.php
+++ b/lib/plugins/engine/PluginRepository.class.php
@@ -55,7 +55,7 @@ class PluginRepository
      */
     public function readMetadata($url)
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache_key = 'plugin_metadata/'.$url;
         $metadata = $cache->read($cache_key);
 
diff --git a/lib/raumzeit/SingleDate.class.php b/lib/raumzeit/SingleDate.class.php
index 82a89db6852cda2e10533efa4d99e01716aa412c..1a58695bd28b55c7167e84096c75bae740b08e25 100644
--- a/lib/raumzeit/SingleDate.class.php
+++ b/lib/raumzeit/SingleDate.class.php
@@ -292,7 +292,7 @@ class SingleDate
 
     function delete()
     {
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache->expire('course/undecorated_data/' . $this->range_id);
 
         $this->chdate = time();
@@ -304,7 +304,7 @@ class SingleDate
     function store()
     {
 
-        $cache = StudipCacheFactory::getCache();
+        $cache = \Studip\Cache\Factory::getCache();
         $cache->expire('course/undecorated_data/' . $this->range_id);
 
         $this->chdate = time();
diff --git a/tests/functional/_bootstrap.php b/tests/functional/_bootstrap.php
index 49f332495c8e160e62cd58f0f031d07fdaecd3b1..8a9125b45a43e57c2d0c47054354a470a347a174 100644
--- a/tests/functional/_bootstrap.php
+++ b/tests/functional/_bootstrap.php
@@ -30,6 +30,8 @@ StudipAutoloader::register();
 StudipAutoloader::addAutoloadPath($STUDIP_BASE_PATH . '/lib/calendar', 'Studip\\Calendar');
 StudipAutoloader::addAutoloadPath($STUDIP_BASE_PATH . '/lib/classes');
 StudipAutoloader::addAutoloadPath($STUDIP_BASE_PATH . '/lib/classes', 'Studip');
+StudipAutoloader::addAutoloadPath($STUDIP_BASE_PATH . '/lib/classes/cache');
+StudipAutoloader::addAutoloadPath($STUDIP_BASE_PATH . '/lib/classes/cache', 'Studip');
 StudipAutoloader::addAutoloadPath($STUDIP_BASE_PATH . '/lib/exceptions');
 StudipAutoloader::addAutoloadPath($STUDIP_BASE_PATH . '/lib/exceptions/resources');
 StudipAutoloader::addAutoloadPath($STUDIP_BASE_PATH . '/lib/filesystem');
@@ -74,7 +76,7 @@ if (!class_exists('StudipTestHelper')) {
     {
         static function set_up_tables($tables)
         {
-            $cache = StudipCacheFactory::getCache(false);
+            $cache = \Studip\Cache\Factory::getCache(false);
 
             // second step, expire table scheme
             SimpleORMap::expireTableScheme();
diff --git a/tests/jsonapi/_bootstrap.php b/tests/jsonapi/_bootstrap.php
index 82ae54e514aa29dc2c319848c0d9865d855a7b4d..baf37409845a3eb27f27e6349fafa4e31c3042c5 100644
--- a/tests/jsonapi/_bootstrap.php
+++ b/tests/jsonapi/_bootstrap.php
@@ -53,6 +53,8 @@ StudipAutoloader::addAutoloadPath($GLOBALS['STUDIP_BASE_PATH'].'/lib/plugins/eng
 
 StudipAutoloader::addAutoloadPath($GLOBALS['STUDIP_BASE_PATH'].'/lib/calendar');
 StudipAutoloader::addAutoloadPath($GLOBALS['STUDIP_BASE_PATH'].'/lib/calendar', 'Studip\\Calendar');
+StudipAutoloader::addAutoloadPath($GLOBALS['STUDIP_BASE_PATH'].'lib/classes/cache');
+StudipAutoloader::addAutoloadPath($GLOBALS['STUDIP_BASE_PATH'].'lib/classes/cache', 'Studip');
 StudipAutoloader::addAutoloadPath($GLOBALS['STUDIP_BASE_PATH'].'/lib/calendar/lib');
 StudipAutoloader::addAutoloadPath($GLOBALS['STUDIP_BASE_PATH'].'/lib/exceptions');
 
diff --git a/tests/unit/_bootstrap.php b/tests/unit/_bootstrap.php
index 3aa1144f66872c84576d8bea631eee01ee697c72..af956074d8511d123290bd9e95e47f802231b67b 100644
--- a/tests/unit/_bootstrap.php
+++ b/tests/unit/_bootstrap.php
@@ -54,6 +54,8 @@ StudipAutoloader::addAutoloadPath('lib/models');
 StudipAutoloader::addAutoloadPath('lib/classes');
 StudipAutoloader::addAutoloadPath('lib/classes', 'Studip');
 StudipAutoloader::addAutoloadPath('lib/exTpl', 'exTpl');
+StudipAutoloader::addAutoloadPath('lib/classes/cache');
+StudipAutoloader::addAutoloadPath('lib/classes/cache', 'Studip');
 StudipAutoloader::addAutoloadPath('lib/exceptions');
 StudipAutoloader::addAutoloadPath('lib/classes/sidebar');
 StudipAutoloader::addAutoloadPath('lib/classes/helpbar');
@@ -108,7 +110,7 @@ if (!class_exists('StudipTestHelper')) {
         static function set_up_tables($tables)
         {
             // first step, set fake cache
-            $cache = StudipCacheFactory::getCache(false);
+            $cache = \Studip\Cache\Factory::getCache(false);
 
             // second step, expire table scheme
             SimpleORMap::expireTableScheme();
diff --git a/tests/unit/lib/classes/MigrationTest.php b/tests/unit/lib/classes/MigrationTest.php
index 4a45e71598c2396b1275c8fc3e499b2e1f20dc23..12df1201c7ee64f52ef3f7406acf8434ddc31003 100644
--- a/tests/unit/lib/classes/MigrationTest.php
+++ b/tests/unit/lib/classes/MigrationTest.php
@@ -16,11 +16,6 @@ class MigrationTest extends \Codeception\Test\Unit
         $this->before = $GLOBALS['CACHING_ENABLE'] ?? null;
         $GLOBALS['CACHING_ENABLE'] = false;
 
-        require_once 'lib/classes/SimpleORMap.class.php';
-        require_once 'lib/classes/StudipCache.class.php';
-        require_once 'lib/classes/StudipMemoryCache.class.php';
-        require_once 'lib/classes/StudipCacheFactory.class.php';
-
         require_once 'lib/migrations/Migration.php';
         require_once 'lib/migrations/Migrator.php';
         require_once 'lib/migrations/SchemaVersion.php';
diff --git a/tests/unit/lib/classes/StudipCachedArrayTest.php b/tests/unit/lib/classes/StudipCachedArrayTest.php
index c98c1bd6b5c151bc9f34656047edec3e4b989e74..5fd39f2d7e93ec71672cdad795a9f4add9ae6e0b 100644
--- a/tests/unit/lib/classes/StudipCachedArrayTest.php
+++ b/tests/unit/lib/classes/StudipCachedArrayTest.php
@@ -1,4 +1,7 @@
 <?php
+
+use Studip\Cache\MemoryCache;
+
 /**
  * StudipCachedArrayTest.php - unit tests for the StudipCachedArray class
  *
@@ -6,7 +9,7 @@
  * @license  GPL2 or any later version
  *
  * @covers StudipCachedArray
- * @uses StudipMemoryCache
+ * @uses   MemoryCache
  */
 
 class StudipCachedArrayTest extends \Codeception\Test\Unit