diff --git a/app/controllers/admin/courses.php b/app/controllers/admin/courses.php
index 562a14bcb17bbd648eae87b8caddae16b9b66b98..8a641901e73ce1264caa78420bb28dd84ce0f888 100644
--- a/app/controllers/admin/courses.php
+++ b/app/controllers/admin/courses.php
@@ -490,7 +490,7 @@ class Admin_CoursesController extends AuthenticatedController
                         $multimode = $plugin->useMultimode();
                         if ($multimode) {
                             $data['buttons_top'] = '<label>'._('Alle auswählen').'<input type="checkbox" data-proxyfor=".course-admin td:last-child :checkbox"></label>';
-                            if ($multimode instanceof Flex\Template) {
+                            if ($multimode instanceof Flexi\Template) {
                                 $data['buttons_bottom'] = $multimode->render();
                             } elseif ($multimode instanceof \Studip\Button) {
                                 $data['buttons_bottom'] = (string) $multimode;
diff --git a/app/controllers/admin/tree.php b/app/controllers/admin/tree.php
index 320f10efd9cd884ec567d2f36164ce6df1011f94..5d22775a38504db43bd1796dcf8305d1b31ceb42 100644
--- a/app/controllers/admin/tree.php
+++ b/app/controllers/admin/tree.php
@@ -192,9 +192,9 @@ class Admin_TreeController extends AuthenticatedController
         }
 
         if ($node->store() !== false) {
-            Pagelayout::postSuccess(_('Die Daten wurden gespeichert.'));
+            PageLayout::postSuccess(_('Die Daten wurden gespeichert.'));
         } else {
-            Pagelayout::postError(_('Die Daten konnten nicht gespeichert werden.'));
+            PageLayout::postError(_('Die Daten konnten nicht gespeichert werden.'));
         }
 
         $this->relocate(Request::get('from'));
diff --git a/app/controllers/course/admission.php b/app/controllers/course/admission.php
index 0b3755a1ea095078e9a9ab231f62e13cbfd475ed..a2b7df4e689a86f7b68867d5a48e8fc504c96ac1 100644
--- a/app/controllers/course/admission.php
+++ b/app/controllers/course/admission.php
@@ -169,10 +169,10 @@ class Course_AdmissionController extends AuthenticatedController
                                 continue;
                             }
                             $num_moved++;
-                            setTempLanguage($user_id);
+                            setTempLanguage($user->id);
                             $message_body = sprintf(_('Sie wurden in der Veranstaltung **%s** in den Status **Autor** versetzt, da das Anmeldeverfahren geändert wurde.'), $this->course->name);
                             $message_title = sprintf(_("Statusänderung %s"), $this->course->name);
-                            messaging::sendSystemMessage($user_id, $message_title, $message_body);
+                            messaging::sendSystemMessage($user, $message_title, $message_body);
                             restoreLanguage();
                         }
                         if ($num_moved) {
diff --git a/app/controllers/course/basicdata.php b/app/controllers/course/basicdata.php
index 114bcc8ee9f3ea87f0c72b33017f407945ef3d0f..04bf8fc7eaf8950fea5b3940d1212cd022433c0d 100644
--- a/app/controllers/course/basicdata.php
+++ b/app/controllers/course/basicdata.php
@@ -782,7 +782,7 @@ class Course_BasicdataController extends AuthenticatedController
         } else {
             $this->deleteUserFromCourse(
                 Course::find($course_id),
-                User::find($teacher_id)
+                User::find($tutor_id)
             );
         }
 
diff --git a/app/controllers/course/feedback.php b/app/controllers/course/feedback.php
index df600c7a1f502ad6409f55d7819ef75906ae9eda..903df17300e64b9ebbba1ab6c2d774a7bab72585 100644
--- a/app/controllers/course/feedback.php
+++ b/app/controllers/course/feedback.php
@@ -72,7 +72,6 @@ class Course_FeedbackController extends AuthenticatedController
             'results_visible'   => 1,
             'commentable'       => 1,
             'mode'              => FeedbackElement::MODE_5STAR_RATING,
-            'mode'              => 1,
             'anonymous_entries' => 1,
         ]);
     }
diff --git a/app/controllers/debugbar.php b/app/controllers/debugbar.php
index 59627f27964daf56260823ff12b9f1c63d9161bb..9fc5286c693d189d38a47970c054257accedd5ab 100644
--- a/app/controllers/debugbar.php
+++ b/app/controllers/debugbar.php
@@ -1,5 +1,5 @@
 <?php
-final class DebugbarController extends Trails_Controller
+final class DebugbarController extends Trails\Controller
 {
     public function __construct(
         Trails\Dispatcher $dispatcher,
diff --git a/app/controllers/institute/basicdata.php b/app/controllers/institute/basicdata.php
index 0b7c2960201d627de8965b0c99c0548731c16780..2f211c29ea9da1acf68da2870ae7e6111a86afd9 100644
--- a/app/controllers/institute/basicdata.php
+++ b/app/controllers/institute/basicdata.php
@@ -426,7 +426,7 @@ class Institute_BasicdataController extends AuthenticatedController
 
             // delete all configuration files for the "extern modules"
             if (Config::get()->EXTERN_ENABLE) {
-                $counts = ExternConfig::DeleteAllConfigurations($i_id);
+                $counts = ExternPageConfig::deleteBySQL('range_id = ?', [$i_id]);
                 if ($counts) {
                     $details[] = sprintf(_('%u Konfigurationsdateien für externe Seiten gelöscht.'), $counts);
                 }
diff --git a/app/controllers/resources/ajax.php b/app/controllers/resources/ajax.php
index cffd87837086cb810a2a420f95d8b59d90ad81c8..ebe0e107861728385a2f5191ce84ee97607437b2 100644
--- a/app/controllers/resources/ajax.php
+++ b/app/controllers/resources/ajax.php
@@ -778,7 +778,7 @@ class Resources_AjaxController extends AuthenticatedController
         $semester = \Semester::findByTimestamp($timestamp);
         if (!$semester) {
             $this->notFound('No semester found for given timestamp');
-            throw new RecordNotFoundException();
+            return;
         }
 
         $timestamp = strtotime('today', $timestamp);
diff --git a/lib/classes/AdminCourseFilter.php b/lib/classes/AdminCourseFilter.php
index 0c0ec8deb33f55f86e8558be9cbdcc6015eb97de..bdb84d2871bde185885c4e4c29ff4ea85413a838 100644
--- a/lib/classes/AdminCourseFilter.php
+++ b/lib/classes/AdminCourseFilter.php
@@ -53,7 +53,7 @@ class AdminCourseFilter
     /**
      * Constructor of the singleton-object.
      */
-    public function __construct(bool $reset_settings = false)
+    final public function __construct(bool $reset_settings = false)
     {
         $this->initSettings($reset_settings);
     }
diff --git a/lib/classes/Avatar.php b/lib/classes/Avatar.php
index d2dcd9d65c713646d236a89b2e214e0e772f8f49..bb3a91564e7e9f8bac3c616f73dbbbcb1faeee2d 100644
--- a/lib/classes/Avatar.php
+++ b/lib/classes/Avatar.php
@@ -197,7 +197,7 @@ class Avatar
      * @param string $user_id  the user's id
      * @param string $username the user's username (optional)
      */
-    protected function __construct($user_id, $username = null)
+    final protected function __construct($user_id, $username = null)
     {
         $this->user_id = $user_id;
         $this->username = $username;
diff --git a/lib/classes/JsonApi/Middlewares/DangerousRouteHandler.php b/lib/classes/JsonApi/Middlewares/DangerousRouteHandler.php
index d1112f838b5ac9bf3358ccbc007414fa2d9011e8..54f21d53719eb4617202cde9644e9358ff9b3c59 100644
--- a/lib/classes/JsonApi/Middlewares/DangerousRouteHandler.php
+++ b/lib/classes/JsonApi/Middlewares/DangerousRouteHandler.php
@@ -24,8 +24,7 @@ class DangerousRouteHandler
         if (\Config::get()->getValue('JSONAPI_DANGEROUS_ROUTES_ALLOWED')) {
             return $handler->handle($request);
         }
-        $response = new Response();
 
-        return $response->withStatus(503)->write('Service Unavailable.');
+        throw new \AccessDeniedException('Service unavailable');
     }
 }
diff --git a/lib/classes/JsonApi/RouteMap.php b/lib/classes/JsonApi/RouteMap.php
index e9a0a011a88633b93e00510d523cf7115a4fff0d..4b61b3d27749b801b3e75b7a1ba022ee61b4a4d2 100644
--- a/lib/classes/JsonApi/RouteMap.php
+++ b/lib/classes/JsonApi/RouteMap.php
@@ -325,7 +325,7 @@ class RouteMap
         $group->get('/tree-node/{id}', Routes\Tree\TreeShow::class);
 
         $group->get('/tree-node/{id}/children', Routes\Tree\ChildrenOfTreeNode::class);
-        $group->get('/tree-node/{id}/courseinfo', Routes\Tree\CourseInfoOfTreeNode::class);
+        $group->get('/tree-node/{id}/courseinfo', Routes\Tree\CourseinfoOfTreeNode::class);
         $group->get('/tree-node/{id}/courses', Routes\Tree\CoursesOfTreeNode::class);
         $group->get('/tree-node/course/pathinfo/{classname}/{id}', Routes\Tree\PathinfoOfTreeNodeCourse::class);
         $group->get('/tree-node/course/details/{id}', Routes\Tree\DetailsOfTreeNodeCourse::class);
diff --git a/lib/classes/JsonApi/Routes/Courseware/Rel/SolversOfTaskGroup.php b/lib/classes/JsonApi/Routes/Courseware/Rel/SolversOfTaskGroup.php
index 2ab5ffa6da1513ab849743890c74fb14107cfad5..3d2a325b81050d9db2efe3c166b73329b8902fe9 100644
--- a/lib/classes/JsonApi/Routes/Courseware/Rel/SolversOfTaskGroup.php
+++ b/lib/classes/JsonApi/Routes/Courseware/Rel/SolversOfTaskGroup.php
@@ -13,6 +13,7 @@ use JsonApi\Schemas\Courseware\TaskGroup as TaskGroupSchema;
 use JsonApi\Schemas\StatusGroup as StatusGroupSchema;
 use JsonApi\Schemas\User as UserSchema;
 use Psr\Http\Message\ServerRequestInterface as Request;
+use RuntimeException;
 use Statusgruppen;
 use User;
 
diff --git a/lib/classes/JsonApi/Routes/Feedback/RatingHelper.php b/lib/classes/JsonApi/Routes/Feedback/RatingHelper.php
index 849cba73a7eb33c38b0728a7ab4bcae4e2291a30..c258f7e5dac695cecf39b8438d8fb18d1634c477 100644
--- a/lib/classes/JsonApi/Routes/Feedback/RatingHelper.php
+++ b/lib/classes/JsonApi/Routes/Feedback/RatingHelper.php
@@ -3,6 +3,7 @@
 namespace JsonApi\Routes\Feedback;
 
 use FeedbackElement;
+use InvalidArgumentException;
 
 trait RatingHelper
 {
diff --git a/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php b/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php
index 69f71ec6362a046bbd33c10f93b3d813da925510..6cb6ef3a1318d25e22ab441b383144538b135449 100644
--- a/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php
+++ b/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php
@@ -6,6 +6,7 @@ use JsonApi\Errors\AuthorizationFailedException;
 use JsonApi\Errors\InternalServerError;
 use JsonApi\Errors\RecordNotFoundException;
 use JsonApi\NonJsonApiController;
+use Psr\Container\ContainerInterface;
 use Psr\Http\Message\ResponseInterface as Response;
 use Psr\Http\Message\ServerRequestInterface as Request;
 use Psr\Http\Message\StreamFactoryInterface;
@@ -72,7 +73,7 @@ class FileRefsContentShow extends NonJsonApiController
                 );
             }
 
-            list($done, $response) = $this->handleEtag($request, $response, $fileRef);
+            [$done, $response] = $this->handleEtag($request, $response, $fileRef);
             if ($done) {
                 return $response;
             }
diff --git a/lib/classes/JsonApi/Routes/Holidays/HolidaysShow.php b/lib/classes/JsonApi/Routes/Holidays/HolidaysShow.php
index a587dcae487a0184cd7678e3fc4acbccc6b9897c..eb413d02d95d66ff0025659c9e1b6650c3131373 100644
--- a/lib/classes/JsonApi/Routes/Holidays/HolidaysShow.php
+++ b/lib/classes/JsonApi/Routes/Holidays/HolidaysShow.php
@@ -96,7 +96,7 @@ final class HolidaysShow extends NonJsonApiController
         $errors = new ErrorCollection();
 
         // Get filters
-        $filters = $this->query_parser->getFilteringParameters();
+        $filters = $this->queryParser->getFilteringParameters();
 
         // Validate allowed filters
         foreach ($filters as $key => $value) {
diff --git a/lib/classes/JsonApi/Routes/RangeTree/ChildrenOfRangeTreeNode.php b/lib/classes/JsonApi/Routes/RangeTree/ChildrenOfRangeTreeNode.php
index 8773c0a5b60940fbdff30041ce15ad60de8957e0..58fcbf3601b586c7fcb46715b290a1f1050bc114 100644
--- a/lib/classes/JsonApi/Routes/RangeTree/ChildrenOfRangeTreeNode.php
+++ b/lib/classes/JsonApi/Routes/RangeTree/ChildrenOfRangeTreeNode.php
@@ -7,6 +7,7 @@ use Psr\Http\Message\ResponseInterface as Response;
 use JsonApi\Errors\AuthorizationFailedException;
 use JsonApi\Errors\RecordNotFoundException;
 use JsonApi\JsonApiController;
+use RangeTreeNode;
 
 class ChildrenOfRangeTreeNode extends JsonApiController
 {
@@ -27,7 +28,7 @@ class ChildrenOfRangeTreeNode extends JsonApiController
             throw new RecordNotFoundException();
         }
 
-        list($offset, $limit) = $this->getOffsetAndLimit();
+        [$offset, $limit] = $this->getOffsetAndLimit();
         $total = \RangeTreeNode::countByParent_id($args['id']);
         $children = \RangeTreeNode::findByParent_id(
             $args['id'],
diff --git a/lib/classes/OAuth2/NegotiatesWithPsr7.php b/lib/classes/OAuth2/NegotiatesWithPsr7.php
index 09197f4e6593c50caa14ef353c163e3d030ed985..1f743ad8fbb43c4baedc04095f79bdfaf85bd041 100644
--- a/lib/classes/OAuth2/NegotiatesWithPsr7.php
+++ b/lib/classes/OAuth2/NegotiatesWithPsr7.php
@@ -5,6 +5,7 @@ namespace Studip\OAuth2;
 use Psr\Http\Message\ResponseFactoryInterface;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use Trails\Controller;
 use Trails\Response as TrailsResponse;
 
 trait NegotiatesWithPsr7
@@ -33,6 +34,10 @@ trait NegotiatesWithPsr7
 
     protected function renderPsrResponse(ResponseInterface $response): void
     {
+        if (!($this instanceof Controller)) {
+            throw new \Exception('Can only render responses on trails controllers');
+        }
+
         $this->set_status($response->getStatusCode());
         $this->render_text((string) $response->getBody());
         foreach ($response->getHeaders() as $key => $values) {
diff --git a/lib/classes/SQLQuery.php b/lib/classes/SQLQuery.php
index 6c8f49ec7d3a1ba1577d3324215297fcc61db568..d1e9aca5e6f4ffa8b93422d2f8457c92ad0164ef 100644
--- a/lib/classes/SQLQuery.php
+++ b/lib/classes/SQLQuery.php
@@ -217,7 +217,7 @@ class SQLQuery
      * a sorm_class the result will be an array of the sorm-objects.
      *
      * @template T of SimpleORMap
-     * @param class-string<T>|string|null $sorm_class_or_column : column name, a class of SimpleORMap or null for associative array.
+     * @param T|class-string<T>|string|null $sorm_class_or_column : column name, a class of SimpleORMap or null for associative array.
      * @param int|null $max_results Maximum number of results to return
      * @return array[]|T[]|mixed[] arrays or array of objects or array of values.
      *
diff --git a/lib/classes/admission/CourseSet.php b/lib/classes/admission/CourseSet.php
index beff9acecdd8d74c56173e90b435db324159fdc6..e7f3dee9d39a0d4c70f06bfe0f1955576ae15106 100644
--- a/lib/classes/admission/CourseSet.php
+++ b/lib/classes/admission/CourseSet.php
@@ -559,7 +559,7 @@ class CourseSet
     /**
      * Gets the course sets the given course belongs to.
      *
-     * @param  String courseId
+     * @param  string $courseId
      * @return CourseSet
      */
     public static function getSetForCourse($courseId)
diff --git a/lib/classes/cache/Exception.php b/lib/classes/cache/Exception.php
index 061d09019a226155c2482c38f44d9d377f69d4cb..d4ebe120b83996ff21eb9da3fc028069d30ad07a 100644
--- a/lib/classes/cache/Exception.php
+++ b/lib/classes/cache/Exception.php
@@ -21,7 +21,7 @@ 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
+class Exception extends \Studip\Exception implements \Psr\Cache\CacheException
 {
     //Nothing here, since there is nothing to implement.
 }
diff --git a/lib/classes/cache/InvalidCacheArgumentException.php b/lib/classes/cache/InvalidCacheArgumentException.php
index a201cad983ebc23c776264a6b72765b2e24e22ce..5c4e25a6e3a781d08d583472ff6bf0012b3cac70 100644
--- a/lib/classes/cache/InvalidCacheArgumentException.php
+++ b/lib/classes/cache/InvalidCacheArgumentException.php
@@ -22,7 +22,7 @@ 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
+class InvalidCacheArgumentException extends \Studip\Exception implements \Psr\Cache\InvalidArgumentException
 {
     //Nothing here, since there is nothing to implement.
 }
diff --git a/lib/classes/calendar/ICalendarExport.php b/lib/classes/calendar/ICalendarExport.php
index d8d1af7d8ce9936b32f31dedb19d2cf6d9731eac..21bb47f2078ed1854b719523df9d8b27fd2c685c 100644
--- a/lib/classes/calendar/ICalendarExport.php
+++ b/lib/classes/calendar/ICalendarExport.php
@@ -35,9 +35,10 @@ class ICalendarExport
      */
     private $time = 0;
 
+    public string $format;
+
     public function __construct()
     {
-        $this->default_filename_suffix = "ics";
         $this->format = "iCalendar";
     }
 
diff --git a/lib/classes/calendar/ICalendarImport.php b/lib/classes/calendar/ICalendarImport.php
index 6d4a2607d50f931bd7818585df1ce957d5508aec..a2043181c07d1e4d62b1320cc7342fb09f8aa882 100644
--- a/lib/classes/calendar/ICalendarImport.php
+++ b/lib/classes/calendar/ICalendarImport.php
@@ -2,15 +2,12 @@
 class ICalendarImport
 {
     private $range_id;
-
     private $count = 0;
-
-    private $dates = [];
-
     private $import_time;
-
     private $convert_to_private = false;
 
+    private $client_identifier = '';
+
     public function __construct($range_id)
     {
         $this->range_id = $range_id;
@@ -94,10 +91,11 @@ class ICalendarImport
                     $params = [];
 
                     // skip seminar events
-                    if ((!$this->import_sem) && $tag == 'UID') {
-                        if (mb_strpos($value, 'Stud.IP-SEM') === 0) {
-                            continue 2;
-                        }
+                    if (
+                        $tag == 'UID'
+                        && mb_strpos($value, 'Stud.IP-SEM') === 0
+                    ) {
+                        continue 2;
                     }
 
                     if (!empty($parts[2])) {
@@ -312,8 +310,7 @@ class ICalendarImport
 
                 $this->createDateFromProperties($properties);
             } else {
-                // _("Die Datei ist keine gültige iCalendar-Datei!")
-                throw new InvalidValuesException();
+                throw new InvalidValuesException(_('Die Datei ist keine gültige iCalendar-Datei!'));
             }
             $this->count++;
         }
@@ -471,7 +468,7 @@ class ICalendarImport
             }
             return $time;
         }
-        throw new InvalidValuesException();
+        throw new InvalidValuesException(_('Die Zeitangabe ist ungültig'));
     }
 
     /**
@@ -486,7 +483,7 @@ class ICalendarImport
             $date['mday'] = $matches[3];
             return $date;
         }
-        throw new InvalidValuesException();
+        throw new InvalidValuesException(_('Die Datumsangabe ist ungültig'));
     }
 
     /**
@@ -650,13 +647,12 @@ class ICalendarImport
 
     private function parseClientIdentifier(&$data)
     {
-        global $_calendar_error;
-
         if ($this->client_identifier == '') {
-            if (!preg_match('/PRODID((;[\W\w]*)*):([\W\w]+?)(\r\n|\r|\n)/', $data, $matches)
-                || !trim($matches[3])) {
-                // _("Die Datei ist keine gültige iCalendar-Datei!")
-                throw new InvalidValuesException();
+            if (
+                !preg_match('/PRODID((;[\W\w]*)*):([\W\w]+?)(\r\n|\r|\n)/', $data, $matches)
+                || !trim($matches[3])
+            ) {
+                throw new InvalidValuesException(_('Die Datei ist keine gültige iCalendar-Datei!'));
             } else {
                 $this->client_identifier = trim($matches[3]);
             }
diff --git a/lib/exTpl/IteratorNode.php b/lib/exTpl/IteratorNode.php
index 58dd593599a84e9907f3129f452591f5219229d0..17c15d072ef563793b8a64c8598d393b9c24b193 100644
--- a/lib/exTpl/IteratorNode.php
+++ b/lib/exTpl/IteratorNode.php
@@ -38,6 +38,9 @@ class IteratorNode extends ArrayNode
         $result = '';
 
         if (is_array($values) && is_int(key($values))) {
+            $key = null;
+            $value = null;
+
             $bindings = [$this->key_name => &$key, $this->val_name => &$value];
             $context  = new Context($bindings, $context);
 
diff --git a/lib/exTpl/TemplateParserException.php b/lib/exTpl/TemplateParserException.php
index 2c7abf88f7227ead22c66161d3904c829aa61c89..c38b3ff6105e6aa4381912721523ce1eda98e1de 100644
--- a/lib/exTpl/TemplateParserException.php
+++ b/lib/exTpl/TemplateParserException.php
@@ -14,6 +14,6 @@ class TemplateParserException extends Exception
         $type  = $scanner->tokenType();
         $value = is_int($type) ? $scanner->tokenValue() : $type;
 
-        return parent::__construct("$message at \"$value\"");
+        parent::__construct("$message at \"$value\"");
     }
 }
diff --git a/lib/exceptions/FeatureDisabledException.php b/lib/exceptions/FeatureDisabledException.php
index 16af0bf8f8a35f85c5ef6bc7d87d9bb8e3a395d8..52cbbdf18d3512df48f14abe503d8e264eabb203 100644
--- a/lib/exceptions/FeatureDisabledException.php
+++ b/lib/exceptions/FeatureDisabledException.php
@@ -1,5 +1,5 @@
 <?php
-class FeatureDisabledException extends StudipException
+class FeatureDisabledException extends Studip\Exception
 {
     public function __construct($message = '', $code = 0, Exception $previous = null)
     {
diff --git a/lib/helpers.php b/lib/helpers.php
index a44f10b9b97ebb2d5f5b4a69a83e7562d4b0146c..bffed4a9acb47aff2c794f275c1dc870b506fef2 100644
--- a/lib/helpers.php
+++ b/lib/helpers.php
@@ -15,7 +15,7 @@ use Psr\Container\ContainerInterface;
  * $logger = app(LoggerInterface::class);
  * ```
  * @template T
- * @param class-string<T>|string|null $entryId    entry name or a class name
+ * @param T|class-string<T>|string|null $entryId    entry name or a class name
  * @param array       $parameters Optional parameters to use to build the entry.
  *                                Use this to force specific parameters to specific values.
  *                                Parameters not defined in this array will be resolved using the container.
diff --git a/lib/ilias_interface/StudipSoapClient.php b/lib/ilias_interface/StudipSoapClient.php
index 0aafd4af56778021627ba5407ae45d8884f0dad2..6a22f86243f4aa2417a00deb1cb01c20ecaf04f7 100644
--- a/lib/ilias_interface/StudipSoapClient.php
+++ b/lib/ilias_interface/StudipSoapClient.php
@@ -11,8 +11,9 @@
 */
 class StudipSoapClient
 {
-    var $soap_client;
-    var $error;
+    public $soap_client;
+    public $error;
+    public $faultstring;
 
     function __construct($path)
     {
@@ -30,7 +31,7 @@ class StudipSoapClient
             $this->faultstring = "";
             try {
                 $result = $this->soap_client->__soapCall($method, $params);
-            } catch  (SoapFault $fault) {
+            } catch (SoapFault $fault) {
                 $this->faultstring = $fault->faultstring;
                 if (!in_array(mb_strtolower($this->faultstring), ["session not valid","session invalid", "session idled"])) {
                     unset($params['password']);
diff --git a/lib/models/Course.php b/lib/models/Course.php
index 1ff49f4bb49e76da9d90d50b8d0bf75b17f9036d..d7ffff7a956cb69c62f56dbf1066e5857c4e4c06 100644
--- a/lib/models/Course.php
+++ b/lib/models/Course.php
@@ -306,7 +306,7 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe
 
         $config['registered_callbacks']['before_store'][] = 'logStore';
         $config['registered_callbacks']['after_create'][] = 'setDefaultTools';
-        $config['registered_callbacks']['after_delete'][] = function ($course) {
+        $config['registered_callbacks']['after_delete'][] = function (Course $course) {
             CourseAvatar::getAvatar($course->id)->reset();
             FeedbackElement::deleteBySQL('course_id = ?', [$course->id]);
             // Remove subcourse relations, leaving subcourses intact.
@@ -351,7 +351,7 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe
             AutoInsert::deleteSeminar($course->id);
 
             //Remove assignments to admission sets:
-            $cs = $this->getCourseSet();
+            $cs = $course->getCourseSet();
             if ($cs) {
                 CourseSet::removeCourseFromSet($cs->getId(), $course->id);
                 $cs->load();
@@ -605,7 +605,7 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe
      */
     public function hasCourseSet() : bool
     {
-        return CourseSet::countBySeminar_id($this->id) > 0;
+        return $this->getCourseSet() !== null;
     }
 
     /**
diff --git a/lib/models/Courseware/StructuralElement.php b/lib/models/Courseware/StructuralElement.php
index 9b68ea2fdf5b7e68da96611c994a314df0d5a8c4..e72cc4f34ef203a279b04dd05347d1ff170b4f73 100644
--- a/lib/models/Courseware/StructuralElement.php
+++ b/lib/models/Courseware/StructuralElement.php
@@ -184,7 +184,7 @@ class StructuralElement extends \SimpleORMap implements \PrivacyObject, \Feedbac
             $this->image_id = $image->getId();
             $this->image_type = \StockImage::class;
         } else {
-            throw \BadMethodCallException('Invalid argument to method ' . __METHOD__);
+            throw new \BadMethodCallException('Invalid argument to method ' . __METHOD__);
         }
     }
 
@@ -1219,7 +1219,7 @@ SQL;
         if ($this->range_type === 'user') {
             return 'contents/courseware/courseware/' . $unit->id . '#/structural_element/' . $this->id;
         }
- 
+
         return 'course/courseware/courseware/' . $unit->id . '?cid=' . $this->range_id . '#/structural_element/' . $this->id;
     }
 
diff --git a/lib/models/ModuleManagementModel.php b/lib/models/ModuleManagementModel.php
index 786b1e358f6c5ab204e45321a7b7fc1af10614d1..3bd5eb75b4fde2be9ba1572742d050fa8d90879d 100644
--- a/lib/models/ModuleManagementModel.php
+++ b/lib/models/ModuleManagementModel.php
@@ -253,7 +253,6 @@ abstract class ModuleManagementModel extends SimpleORMap implements ModuleManage
      * Logs all changes of this object.
      *
      * @param string $action new, update or delete
-     * @return boolean Return true if logging was successful.
      */
     protected function logChanges ($action = null) {
 
@@ -367,7 +366,7 @@ abstract class ModuleManagementModel extends SimpleORMap implements ModuleManage
                 $num_index = 1;
                 break;
             default:
-                return false;
+                return;
         }
 
         if ($logging) {
@@ -389,7 +388,7 @@ abstract class ModuleManagementModel extends SimpleORMap implements ModuleManage
                     $debuginfo = $this->getDisplayName();
                     break;
                 default:
-                    return false;
+                    return;
             }
 
             $id_array = $this->getId();
@@ -407,7 +406,7 @@ abstract class ModuleManagementModel extends SimpleORMap implements ModuleManage
                     $debuginfo = $id_array[2];
                     break;
                 default:
-                    return false;
+                    return;
             }
 
             if ($action == 'update') {
@@ -424,9 +423,9 @@ abstract class ModuleManagementModel extends SimpleORMap implements ModuleManage
                 StudipLog::log($logging, $aff, $coaff, $this->db_table(), $debuginfo);
             }
 
-            return true;
+            return;
         }
-        return false;
+        return;
     }
 
     /**
@@ -927,7 +926,7 @@ abstract class ModuleManagementModel extends SimpleORMap implements ModuleManage
      */
     protected static function formatDisplayName(
         string $template,
-        array $placeholders, 
+        array $placeholders,
         array $replacements
     ): string {
         if (mb_strlen($template) === 0) {
diff --git a/lib/models/MvvFileRange.php b/lib/models/MvvFileRange.php
index 9155c1fd909ab4e79954365a55eaefef5447209c..4c0c6956f9487775121f3153772e83cc66a0160e 100644
--- a/lib/models/MvvFileRange.php
+++ b/lib/models/MvvFileRange.php
@@ -48,7 +48,7 @@ class MvvFileRange extends ModuleManagementModel
     /**
      * Returns the rangetype of the document based on its foldertype.
      *
-     * @return bool|string Returns false on failure, otherwise the name of the range.
+     * @return string Returns false on failure, otherwise the name of the range.
      */
     public function getRangeType()
     {
diff --git a/lib/models/StudipCacheOperation.php b/lib/models/StudipCacheOperation.php
index d2287c32a5f662e5c98af4cf764d334f51e549ec..36d81b9fad13fda1c9bedacdab3fa9258b24dd52 100644
--- a/lib/models/StudipCacheOperation.php
+++ b/lib/models/StudipCacheOperation.php
@@ -1,4 +1,7 @@
 <?php
+
+use Studip\Cache\Cache;
+
 /**
  * Model for a stored cache operation.
  *
@@ -38,9 +41,9 @@ class StudipCacheOperation extends SimpleORMap
      * The operations are applied in chronological order and are deleted
      * from the database after they have been applied.
      *
-     * @param StudipCache $cache The cache object to apply the operations to
+     * @param Cache $cache The cache object to apply the operations to
      */
-    public static function apply(StudipCache $cache)
+    public static function apply(Cache $cache)
     {
         self::findEachBySQL(function (StudipCacheOperation $item) use ($cache): void {
             $parameters = unserialize($item->parameters);
diff --git a/lib/plugins/core/CorePlugin.php b/lib/plugins/core/CorePlugin.php
index 056efaf6ac10b112211bf2cc6e28ebd23e7fea37..9e68992047d9066b715b322829f898118e5e3041 100644
--- a/lib/plugins/core/CorePlugin.php
+++ b/lib/plugins/core/CorePlugin.php
@@ -8,6 +8,7 @@
  */
 abstract class CorePlugin
 {
+    abstract public function getMetadata();
 
     /**
      * plugin meta data
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 5b8547f1c3b48373329620fd5645950621db29be..2d66785205cdcca1ddfd1fd2b3b0a6003341f834 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -7,17 +7,17 @@ parameters:
         - tests/functional
         - tests/jsonapi
         - tests/unit
+        - tests/_support
     scanDirectories:
         - app/controllers
         - lib
         - vendor
     excludePaths:
         - lib/classes/ZipArchiveLegacyTrait.php
-        - lib/elearning/studip_referrer.php
-        - lib/soap/StudipSoapClient_PHP5.php
+        - lib/ilias_interface/studip_referrer_7x.php
+        - lib/ilias_interface/studip_referrer_8x.php
     tmpDir: .caches
-    earlyTerminatingMethodCalls:
-        RESTAPI\RouteMap:
-            - error
-            - halt
-            - notFound
+    ignoreErrors:
+        -
+            message: '#Unsafe usage of new static\(\)\.#'
+            path: lib/classes/SimpleORMap.php
diff --git a/tests/unit/lib/flexi/PHPTemplatePartialBugTest.php b/tests/unit/lib/flexi/PHPTemplatePartialBugTest.php
index ef265cf22c43d8f173f7e9642ada7a35051a25f9..fd909cb1c95747b63ce579f5ae1fc3fc3ac891be 100644
--- a/tests/unit/lib/flexi/PHPTemplatePartialBugTest.php
+++ b/tests/unit/lib/flexi/PHPTemplatePartialBugTest.php
@@ -4,6 +4,8 @@ use Flexi\Factory;
 
 final class PhpTemplatePartialBugTestCase extends Codeception\Test\Unit
 {
+    private Factory $factory;
+
     public function setUp(): void
     {
         $this->setUpFS();
diff --git a/tests/unit/lib/flexi/TemplateMagicMethodsTest.php b/tests/unit/lib/flexi/TemplateMagicMethodsTest.php
index ad2690a743064d061c6a4860d31fa659889f577c..fda4695d165b85b075276bd7c0f0901547b9bdc3 100644
--- a/tests/unit/lib/flexi/TemplateMagicMethodsTest.php
+++ b/tests/unit/lib/flexi/TemplateMagicMethodsTest.php
@@ -6,6 +6,7 @@ use Flexi\Template;
 final class TemplateMagicMethodsTestCase extends \Codeception\Test\Unit
 {
     private Factory $factory;
+    private Template $template;
 
     public function setUp(): void
     {