From 769675071b44cff1f699396270b378d189ada866 Mon Sep 17 00:00:00 2001
From: Jan-Hendrik Willms <tleilax+studip@gmail.com>
Date: Thu, 14 Jul 2022 14:58:26 +0000
Subject: [PATCH] fix most of the errors reported from phpstan in
 `app/controllers`, fixes #1328

Closes #1328

Merge request studip/studip!813
---
 app/controllers/admin/autoinsert.php          |  5 +-
 app/controllers/admin/courseplanning.php      |  9 +--
 app/controllers/admin/courses.php             |  7 +-
 app/controllers/admin/install.php             |  4 ++
 app/controllers/admin/smileys.php             |  2 +-
 app/controllers/admission/courseset.php       | 18 +++--
 app/controllers/calendar/contentbox.php       |  1 -
 app/controllers/course/forum/index.php        |  2 +-
 app/controllers/course/lvgselector.php        | 38 +++--------
 app/controllers/course/members.php            |  6 +-
 app/controllers/course/plus.php               |  3 +-
 app/controllers/course/wizard.php             |  7 +-
 app/controllers/files.php                     | 13 ++--
 app/controllers/help_content.php              | 43 +++---------
 app/controllers/institute/basicdata.php       | 39 +++++++----
 app/controllers/institute/members.php         | 11 ++--
 app/controllers/messages.php                  |  2 +-
 app/controllers/module/module.php             |  7 +-
 app/controllers/my_courses.php                | 20 +++---
 app/controllers/news.php                      | 18 +++--
 app/controllers/questionnaire.php             |  4 +-
 app/controllers/settings/settings.php         |  4 +-
 app/controllers/shared/contacts.php           | 22 +++----
 app/controllers/shared/download.php           | 29 +++-----
 .../studiengaenge/stgteilbezeichnungen.php    |  4 +-
 .../studiengaenge/studiengaenge.php           |  7 +-
 .../studiengaenge/studiengangteile.php        |  7 +-
 app/controllers/studip_controller.php         | 10 ++-
 .../studip_controller_properties_trait.php    | 66 +++++++++++++++++++
 app/controllers/tour.php                      | 16 +++--
 app/views/help_content/edit.php               | 24 +++----
 lib/models/MvvContactRange.php                | 19 ++++++
 lib/models/ToolActivation.php                 |  2 +-
 phpstan.neon.dist                             |  4 +-
 34 files changed, 267 insertions(+), 206 deletions(-)
 create mode 100644 app/controllers/studip_controller_properties_trait.php

diff --git a/app/controllers/admin/autoinsert.php b/app/controllers/admin/autoinsert.php
index 8ae63c1a2e0..6edf3c68af2 100644
--- a/app/controllers/admin/autoinsert.php
+++ b/app/controllers/admin/autoinsert.php
@@ -204,7 +204,7 @@ class Admin_AutoinsertController extends AuthenticatedController
 
         if (Request::get('sem_search') and Request::get('sem_select')) {
             if (Request::get('sem_search')) {
-                $search = new SeminarSearch('number-name');
+                $search = new SeminarSearch();
                 $this->seminar_search = $search->getResults(Request::get('sem_search'), ['search_sem_sem' => $this->sem_select]);
                 if (count($this->seminar_search) == 0) {
                     PageLayout::postInfo(_('Es wurden keine Veranstaltungen gefunden.'));
@@ -249,7 +249,6 @@ class Admin_AutoinsertController extends AuthenticatedController
             }
             $data = ['users' => count($userlookup->execute())];
         }
-        $this->set_content_type('application/json;charset=utf-8');
-        return $this->render_text(json_encode($data));
+        $this->render_json($data);
     }
 }
diff --git a/app/controllers/admin/courseplanning.php b/app/controllers/admin/courseplanning.php
index 9e508a57d4f..47c181d5016 100644
--- a/app/controllers/admin/courseplanning.php
+++ b/app/controllers/admin/courseplanning.php
@@ -652,7 +652,6 @@ class Admin_CourseplanningController extends AuthenticatedController
      * Returns a course type widthet depending on all available courses and theirs types
      * @param string $selected
      * @param array $params
-     * @return ActionsWidget
      */
     private function setCourseTypeWidget($selected = 'all')
     {
@@ -702,7 +701,6 @@ class Admin_CourseplanningController extends AuthenticatedController
     /**
      * Returns a widget to selected a specific teacher
      * @param array $teachers
-     * @return ActionsWidget|null
      */
     private function setTeacherWidget()
     {
@@ -751,7 +749,7 @@ class Admin_CourseplanningController extends AuthenticatedController
      * @param String $course_id Id of the course
      * @return array of user infos [user_id, username, Nachname, fullname]
      */
-    private function getTeacher($course_id)
+    private function getTeacher($course_id): array
     {
         $teachers   = CourseMember::findByCourseAndStatus($course_id, 'dozent');
         $collection = SimpleCollection::createFromArray($teachers);
@@ -769,11 +767,10 @@ class Admin_CourseplanningController extends AuthenticatedController
      * Returns all courses matching set criteria.
      *
      * @param Array $params Additional parameters
-     * @param String $parent_id Fetch only subcourses of this parent
-     * @param display_all : boolean should we show all courses or check for a limit of 500 courses?
+     * @param bool $display_all should we show all courses or check for a limit of 500 courses?
      * @return Array of courses
      */
-    private function getCourses($params = [], $display_all = false)
+    private function getCourses($params = [], $display_all = false): array
     {
         // Init
         if ($GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT === "all") {
diff --git a/app/controllers/admin/courses.php b/app/controllers/admin/courses.php
index 9da12665e61..6cee886bb9d 100644
--- a/app/controllers/admin/courses.php
+++ b/app/controllers/admin/courses.php
@@ -1222,7 +1222,7 @@ class Admin_CoursesController extends AuthenticatedController
             $filter->filterByStgTeil($GLOBALS['user']->cfg->MY_COURSES_SELECTED_STGTEIL);
         }
         if ($params['sortby'] === "status") {
-            $filter->orderBy(sprintf('sem_classes.name %s, sem_types.name %s, VeranstaltungsNummer', $params['sortFlag'], $params['sortFlag'], $params['sortFlag']), $params['sortFlag']);
+            $filter->orderBy(sprintf('sem_classes.name %s, sem_types.name %s, VeranstaltungsNummer %s', $params['sortFlag'], $params['sortFlag'], $params['sortFlag']), $params['sortFlag']);
         } elseif ($params['sortby'] === 'institute') {
             $filter->orderBy('Institute.Name', $params['sortFlag']);
         } elseif ($params['sortby']) {
@@ -1419,7 +1419,6 @@ class Admin_CoursesController extends AuthenticatedController
     /**
      * Adds HTML-Selector to the sidebar
      * @param null $selected_action
-     * @return string
      */
     private function setActionsWidget($selected_action = null)
     {
@@ -1438,7 +1437,6 @@ class Admin_CoursesController extends AuthenticatedController
      * Returns a course type widthet depending on all available courses and theirs types
      * @param string $selected
      * @param array $params
-     * @return ActionsWidget
      */
     private function setCourseTypeWidget($selected = 'all')
     {
@@ -1488,7 +1486,6 @@ class Admin_CoursesController extends AuthenticatedController
     /**
      * Returns a widget to selected a specific teacher
      * @param array $teachers
-     * @return ActionsWidget|null
      */
     private function setTeacherWidget()
     {
@@ -1547,7 +1544,7 @@ class Admin_CoursesController extends AuthenticatedController
      *
      * @return array containing the filter configuration
      */
-    private function getFilterConfig()
+    private function getFilterConfig(): array
     {
         $available_filters = array_keys($this->getViewFilters());
 
diff --git a/app/controllers/admin/install.php b/app/controllers/admin/install.php
index 4d1f63d429f..dba8ac0bc12 100644
--- a/app/controllers/admin/install.php
+++ b/app/controllers/admin/install.php
@@ -1,6 +1,10 @@
 <?php
+require_once __DIR__ . '/studip_controller_properties_trait.php';
+
 class Admin_InstallController extends Trails_Controller
 {
+    use StudipControllerPropertiesTrait;
+
     public function __construct($dispatcher)
     {
         if (basename($dispatcher->trails_uri, '.php') !== 'install') {
diff --git a/app/controllers/admin/smileys.php b/app/controllers/admin/smileys.php
index 40ba1f82d25..7b4a7747970 100644
--- a/app/controllers/admin/smileys.php
+++ b/app/controllers/admin/smileys.php
@@ -77,7 +77,7 @@ class Admin_SmileysController extends AuthenticatedController
             }
 
             $short = Request::get('short', $smiley->short);
-            if (!$message && $smiley->short != $short) { // rename short
+            if ($smiley->short != $short) { // rename short
                 if (Smiley::getByShort($short)->id) {
                     $error = sprintf(_('Es gibt bereits einen Smileys mit dem Kürzel "%s".'), $short);
                     PageLayout::postError($error);
diff --git a/app/controllers/admission/courseset.php b/app/controllers/admission/courseset.php
index 86416309689..a8aab78a959 100644
--- a/app/controllers/admission/courseset.php
+++ b/app/controllers/admission/courseset.php
@@ -52,16 +52,18 @@ class Admission_CoursesetController extends AuthenticatedController
     /**
      * Show all coursesets the current user has access to.
      */
-    public function index_action() {
+    public function index_action()
+    {
         $this->course_set_details = Request::option('course_set_details');
         if ($this->course_set_details && Request::isXhr()) {
             $courseset = new CourseSet($this->course_set_details);
-            return $this->render_text($courseset->toString());
+            $this->render_text($courseset->toString());
+            return;
         }
         $this->ruleTypes = AdmissionRule::getAvailableAdmissionRules(false);
         $this->coursesets = [];
         foreach (words('current_institut_id current_rule_types set_name_prefix current_semester_id current_rule_types') as $param) {
-            $this->$param = $_SESSION[get_class($this)][$param];
+            $this->$param = $_SESSION[self::class][$param] ?? null;
         }
         if (Request::submitted('choose_institut')) {
             $this->current_institut_id = Request::option('choose_institut_id');
@@ -75,7 +77,9 @@ class Admission_CoursesetController extends AuthenticatedController
             $_SESSION['_default_sem'] = $this->current_semester_id;
         }
         if (!isset($this->current_rule_types)) {
-            $this->current_rule_types['ParticipantRestrictedAdmission'] = true;
+            $this->current_rule_types = [
+                'ParticipantRestrictedAdmission' => true,
+            ];
         }
         $filter['course_set_name'] = $this->set_name_prefix;
         $filter['semester_id'] = $this->current_semester_id != 'all' ? $this->current_semester_id : null;
@@ -112,8 +116,12 @@ class Admission_CoursesetController extends AuthenticatedController
         uasort($this->coursesets, function($a,$b) {
             return strnatcasecmp($a->getName(), $b->getName());
         });
+
+        if (!isset($_SESSION[self::class])) {
+            $_SESSION[self::class] = [];
+        }
         foreach (words('current_institut_id current_rule_types set_name_prefix current_semester_id current_rule_types') as $param) {
-            $_SESSION[get_class($this)][$param] = $this->$param;
+            $_SESSION[self::class][$param] = $this->$param;
         }
         $not_distributed_coursesets = array_filter(array_map(function ($cs) {
                 return ($cs->isSeatDistributionEnabled() && $cs->getSeatDistributionTime() < (time() - 1000) && !$cs->hasAlgorithmRun())
diff --git a/app/controllers/calendar/contentbox.php b/app/controllers/calendar/contentbox.php
index 50a241d5ef3..134fe0cb04f 100644
--- a/app/controllers/calendar/contentbox.php
+++ b/app/controllers/calendar/contentbox.php
@@ -18,7 +18,6 @@ class Calendar_ContentboxController extends StudipController {
      * Widget controller to produce the formally known show_dates()
      *
      * @param String $range_id range id (or array of range ids) of the news to get displayed
-     * @return array() Array of votes
      */
      public function display_action($range_id, $timespan = 604800, $start = null)
      {
diff --git a/app/controllers/course/forum/index.php b/app/controllers/course/forum/index.php
index cc2f6b024ab..1162dfc11ef 100644
--- a/app/controllers/course/forum/index.php
+++ b/app/controllers/course/forum/index.php
@@ -845,6 +845,6 @@ class Course_Forum_IndexController extends ForumController
             $GLOBALS['auth']->login_if($GLOBALS['user']->id === 'nobody');
         }
 
-        parent::rescue($exception);
+        return parent::rescue($exception);
     }
 }
diff --git a/app/controllers/course/lvgselector.php b/app/controllers/course/lvgselector.php
index 6fcb6580266..a84d55642ff 100644
--- a/app/controllers/course/lvgselector.php
+++ b/app/controllers/course/lvgselector.php
@@ -33,6 +33,10 @@ class Course_LvgselectorController extends AuthenticatedController
         }
         $this->selection = new StudipLvgruppeSelection($this->course_id);
         $this->semester_id = $this->course->start_semester->id;
+
+        $widget = new HelpbarWidget();
+        $widget->addElement(new WidgetElement(_('Auf dieser Seite kann die Veranstaltung ausgewählten Lehrveranstaltungsgruppen zugeordnet werden.')));
+        Helpbar::get()->addWidget($widget);
     }
 
     /**
@@ -61,7 +65,8 @@ class Course_LvgselectorController extends AuthenticatedController
         $this->lvgruppen_not_allowed = !$this->course->getSemClass()->offsetGet('module');
 
         if ($this->lvgruppen_not_allowed) {
-            return $this->render_text(MessageBox::info(_('Für diesen Veranstaltungstyp ist die Zuordnung zu Lehrveranstaltungsgruppen nicht vorgesehen.')));
+            $this->render_text(MessageBox::info(_('Für diesen Veranstaltungstyp ist die Zuordnung zu Lehrveranstaltungsgruppen nicht vorgesehen.')));
+            return;
         }
         $this->open_lvg_nodes = [];
         if (Request::submitted('open_nodes')) {
@@ -209,7 +214,8 @@ class Course_LvgselectorController extends AuthenticatedController
 
         if ($id === NULL) {
             $this->set_status(400);
-            return $this->render_nothing();
+            $this->render_nothing();
+            return;
         }
 
         $selection = new StudipLvgruppeSelection($this->course_id);
@@ -218,7 +224,8 @@ class Course_LvgselectorController extends AuthenticatedController
 
         if ($selection->size() == 1) {
             $this->set_status(409);
-            return $this->render_nothing();
+            $this->render_nothing();
+            return;
         }
         $selection->remove($id);
         $this->store_selection($this->course_id, $selection);
@@ -294,29 +301,4 @@ class Course_LvgselectorController extends AuthenticatedController
         Lvgruppe::setLvgruppen($course_id, $lv_group_ids);
         PageLayout::postMessage(MessageBox::success(_('Die Zuordnung der LV-Gruppen wurde übernommen.')));
     }
-
-    /**
-     * Creates the sidebar widgets
-     */
-    protected function setSidebar()
-    {
-        $helpbar = Helpbar::get();
-        $widget = new HelpbarWidget();
-        $widget->addElement(new WidgetElement(_('Auf dieser Seite kann die Veranstaltung ausgewählten Lehrveranstaltungsgruppen zugeordnet werden.')));
-        $helpbar->addWidget($widget);
-
-        if ($GLOBALS['perm']->have_perm('admin')) {
-            $admin_list_template = AdminList::getInstance()
-                    ->getSelectTemplate($this->course_id);
-            if ($admin_list_template) {
-                $sidebar = Sidebar::get();
-                $widget  = new SidebarWidget();
-                $widget->setTitle('Veranstaltungliste');
-                $widget->addElement(new WidgetElement($admin_list_template->render()));
-                $sidebar->addWidget($widget, 'Veranstaltungliste');
-
-            }
-        }
-    }
-
 }
diff --git a/app/controllers/course/members.php b/app/controllers/course/members.php
index 8c841ab5223..a3653160da8 100644
--- a/app/controllers/course/members.php
+++ b/app/controllers/course/members.php
@@ -594,7 +594,7 @@ class Course_MembersController extends AuthenticatedController
 
     /**
      * Old version of CSV import (copy and paste from teilnehmer.php
-     * @return type
+     *
      * @throws AccessDeniedException
      */
     public function set_autor_csv_action()
@@ -1000,7 +1000,6 @@ class Course_MembersController extends AuthenticatedController
      * @param String $status
      * @param String $cmd
      * @param String $target_status
-     * @return String
      * @throws AccessDeniedException
      */
     public function insert_admission_action($status, $cmd, $target_status = 'autor')
@@ -1265,14 +1264,13 @@ class Course_MembersController extends AuthenticatedController
 
     /**
      * Displays all members of the course and their aux data
-     * @return int fake return to stop after redirect;
      */
     public function additional_action($format = null)
     {
         // Users get forwarded to aux_input
         if (!($this->is_dozent || $this->is_tutor)) {
             $this->redirect('course/members/additional_input');
-            return 0;
+            return;
         }
 
         Navigation::activateItem('/course/members/additional');
diff --git a/app/controllers/course/plus.php b/app/controllers/course/plus.php
index 1cfe36bf2d8..79e94cffe8f 100644
--- a/app/controllers/course/plus.php
+++ b/app/controllers/course/plus.php
@@ -131,7 +131,8 @@ class Course_PlusController extends AuthenticatedController
         $id = explode('_', $plugin)[1];
         $this->tool = ToolActivation::find([$this->sem->id, $id]);
         if (!$this->tool) {
-            return $this->render_nothing();
+            $this->render_nothing();
+            return;
         }
         if (Request::submitted('save')) {
             CSRFProtection::verifyUnsafeRequest();
diff --git a/app/controllers/course/wizard.php b/app/controllers/course/wizard.php
index c8da5b1ce3e..aaf06e9aea9 100644
--- a/app/controllers/course/wizard.php
+++ b/app/controllers/course/wizard.php
@@ -423,10 +423,11 @@ class Course_WizardController extends AuthenticatedController
     }
 
     /**
-     * @param $stepclass class name of the current step.
-     * @return Array
+     * @param string $stepclass name of the current step.
+     * @param mixed $values
      */
-    private function setStepValues($stepclass, $values) {
+    private function setStepValues($stepclass, $values)
+    {
         $_SESSION['coursewizard'][$this->temp_id][$stepclass] = $values;
     }
 
diff --git a/app/controllers/files.php b/app/controllers/files.php
index 6d3a5511e4c..531463d7d67 100644
--- a/app/controllers/files.php
+++ b/app/controllers/files.php
@@ -833,16 +833,19 @@ class FilesController extends AuthenticatedController
 
         switch ($destination_folder->range_type) {
             case 'course':
-                return $this->redirect(URLHelper::getURL('dispatch.php/course/files/index/' . $destination_folder->getId() . '?cid=' . $dest_range));
+                $this->redirect(URLHelper::getURL('dispatch.php/course/files/index/' . $destination_folder->getId() . '?cid=' . $dest_range));
+                break;
             case 'institute':
-                return $this->redirect(URLHelper::getURL('dispatch.php/institute/files/index/' . $destination_folder->getId() . '?cid=' . $dest_range));
+                $this->redirect(URLHelper::getURL('dispatch.php/institute/files/index/' . $destination_folder->getId() . '?cid=' . $dest_range));
+                break;
             case 'user':
-                return $this->redirect(URLHelper::getURL('dispatch.php/files/index/' . $destination_folder->getId()));
+                $this->redirect(URLHelper::getURL('dispatch.php/files/index/' . $destination_folder->getId()));
+                break;
             default:
                 if ($destination_plugin) {
-                    return $this->redirect(URLHelper::getURL('dispatch.php/files/system/' . $destination_plugin->getPluginId() .'/'. $destination_folder->getId()));
+                    $this->redirect(URLHelper::getURL('dispatch.php/files/system/' . $destination_plugin->getPluginId() .'/'. $destination_folder->getId()));
                 } else {
-                    return $this->redirect(URLHelper::getURL('dispatch.php/course/files/index/' . $destination_folder->getId()));
+                    $this->redirect(URLHelper::getURL('dispatch.php/course/files/index/' . $destination_folder->getId()));
                 }
         }
 
diff --git a/app/controllers/help_content.php b/app/controllers/help_content.php
index c63346b53da..a04c1684f27 100644
--- a/app/controllers/help_content.php
+++ b/app/controllers/help_content.php
@@ -24,11 +24,16 @@ class HelpContentController extends AuthenticatedController
     /**
      * Callback function being called before an action is executed.
      */
-    function before_filter(&$action, &$args)
+    public function before_filter(&$action, &$args)
     {
         parent::before_filter($action, $args);
 
-        $this->help_admin = $GLOBALS['perm']->have_perm('root') || RolePersistence::isAssignedRole($GLOBALS['user']->id, 'Hilfe-Administrator(in)');
+        if (
+            !$GLOBALS['perm']->have_perm('root')
+            && !User::findCurrent()->hasRole('Hilfe-Administrator(in)')
+        ) {
+            throw new AccessDeniedException();
+        }
 
         $this->buildSidebar($action);
     }
@@ -38,11 +43,6 @@ class HelpContentController extends AuthenticatedController
      */
     public function admin_overview_action()
     {
-        // check permission
-        if (!$this->help_admin) {
-            throw new AccessDeniedException();
-        }
-
         // initialize
         PageLayout::setTitle(_('Verwalten von Hilfe-Texten'));
         PageLayout::setHelpKeyword('Basis.HelpContentAdmin');
@@ -77,11 +77,6 @@ class HelpContentController extends AuthenticatedController
      */
     public function admin_conflicts_action()
     {
-        // check permission
-        if (!$this->help_admin) {
-            throw new AccessDeniedException();
-        }
-
         // initialize
         PageLayout::setTitle(_('Versions-Konflikte der Hilfe-Texte'));
         PageLayout::setHelpKeyword('Basis.HelpContentAdmin');
@@ -103,10 +98,6 @@ class HelpContentController extends AuthenticatedController
      */
     public function resolve_conflict_action($id, $mode)
     {
-        // check permission
-        if (!$this->help_admin) {
-            throw new AccessDeniedException();
-        }
         $GLOBALS['perm']->check('root');
 
         $this->help_content = HelpContent::GetContentByID($id);
@@ -125,9 +116,6 @@ class HelpContentController extends AuthenticatedController
      */
     public function add_action()
     {
-        if (!$this->help_admin) {
-            return $this->render_nothing();
-        }
         PageLayout::setTitle(_('Hilfe-Text erstellen'));
 
         $parameters = [];
@@ -147,10 +135,6 @@ class HelpContentController extends AuthenticatedController
      */
     public function edit_action($id)
     {
-        if (!$this->help_admin) {
-            return $this->render_nothing();
-        }
-
         PageLayout::setTitle(_('Hilfe-Text bearbeiten'));
 
         $parameters = [];
@@ -173,9 +157,6 @@ class HelpContentController extends AuthenticatedController
      */
     public function store_action($id = '')
     {
-        if (!$this->help_admin) {
-            return $this->render_nothing();
-        }
         CSRFProtection::verifySecurityToken();
 
         $content_id         = md5(uniqid('help_content', 1));
@@ -233,9 +214,6 @@ class HelpContentController extends AuthenticatedController
      */
     public function store_settings_action()
     {
-        if (!$this->help_admin) {
-            return $this->render_nothing();
-        }
         CSRFProtection::verifyUnsafeRequest();
 
         $this->help_contents = HelpContent::GetContentByFilter(Request::get('help_content_searchterm'));
@@ -265,10 +243,6 @@ class HelpContentController extends AuthenticatedController
      */
     public function delete_action($id)
     {
-        if (!$this->help_admin) {
-            return $this->render_nothing();
-        }
-
         CSRFProtection::verifySecurityToken();
         PageLayout::setTitle(_('Hilfe-Text löschen'));
 
@@ -278,7 +252,8 @@ class HelpContentController extends AuthenticatedController
                 PageLayout::postMessage(MessageBox::success(sprintf(_('Der Hilfe-Text zur Route "%s" wurde gelöscht.'), htmlReady($this->help_content->route))));
                 $this->help_content->delete();
                 $this->response->add_header('X-Dialog-Close', 1);
-                return $this->render_nothing();
+                $this->render_nothing();
+                return;
             }
         }
 
diff --git a/app/controllers/institute/basicdata.php b/app/controllers/institute/basicdata.php
index 13a2674e162..a265cfef9c1 100644
--- a/app/controllers/institute/basicdata.php
+++ b/app/controllers/institute/basicdata.php
@@ -195,7 +195,8 @@ class Institute_BasicdataController extends AuthenticatedController
         // Do we have all necessary data?
         if (!mb_strlen($institute->name)) {
             PageLayout::postError(_('Bitte geben Sie eine Bezeichnung für die Einrichtung ein!'));
-            return $this->redirect('institute/basicdata/index/' . $i_id);
+            $this->redirect('institute/basicdata/index/' . $i_id);
+            return;
         }
 
         if ($create_institute) {
@@ -204,13 +205,15 @@ class Institute_BasicdataController extends AuthenticatedController
             // Is the user allowed to create new faculties
             if (!$institute->fakultaets_id && !$GLOBALS['perm']->have_perm('root')) {
                 PageLayout::postError(_('Sie haben nicht die Berechtigung, neue Fakultäten zu erstellen'));
-                return $this->redirect('institute/basicdata/index/new');
+                $this->redirect('institute/basicdata/index/new');
+                return;
             }
 
             // Is the user allowed to create new institutes
             if (!$GLOBALS['perm']->have_perm('root') && !($GLOBALS['perm']->is_fak_admin() && Config::get()->INST_FAK_ADMIN_PERMS !== 'none'))  {
                 PageLayout::postError(_('Sie haben nicht die Berechtigung, um neue Einrichtungen zu erstellen!'));
-                return $this->redirect('institute/basicdata/index/new');
+                $this->redirect('institute/basicdata/index/new');
+                return;
             }
 
             // Does an institute with the given name already exist in the given faculty?
@@ -219,13 +222,15 @@ class Institute_BasicdataController extends AuthenticatedController
                     _('Die Einrichtung "%s" existiert bereits innerhalb der angegebenen Fakultät!'),
                     htmlReady($institute->name)
                 ));
-                return $this->redirect('institute/basicdata/index/new');
+                $this->redirect('institute/basicdata/index/new');
+                return;
             }
 
             // Does a faculty with the given name already exist
             if (!$institute->fakultaets_id && Institute::findOneBySQL('Name = ? AND fakultaets_id = Institut_id', [$institute->name]) !== null) {
                 PageLayout::postError(sprintf(_('Die Fakultät "%s" existiert bereits!'), htmlReady($institute->name)));
-                return $this->redirect('institute/basicdata/index/new');
+                $this->redirect('institute/basicdata/index/new');
+                return;
             }
 
 
@@ -237,7 +242,8 @@ class Institute_BasicdataController extends AuthenticatedController
             // Is the user allowed to change the institute/faculty?
             if (!$GLOBALS['perm']->have_studip_perm('admin', $institute->id)) {
                 PageLayout::postError(_('Sie haben nicht die Berechtigung diese Einrichtung zu verändern!'));
-                return $this->redirect('institute/basicdata/index/' . $institute->id);
+                $this->redirect('institute/basicdata/index/' . $institute->id);
+                return;
             }
 
             // Save datafields
@@ -273,7 +279,8 @@ class Institute_BasicdataController extends AuthenticatedController
             } else {
                 PageLayout::postError(_('Die Änderungen konnten nicht gespeichert werden.'));
             }
-            return $this->redirect('institute/basicdata/index/' . $i_id);
+            $this->redirect('institute/basicdata/index/' . $i_id);
+            return;
         }
 
         if ($create_institute) {
@@ -306,19 +313,22 @@ class Institute_BasicdataController extends AuthenticatedController
 
         // Missing parameter
         if (!Request::get('i_kill')) {
-            return $this->redirect('institute/basicdata/index/' . $i_id);
+            $this->redirect('institute/basicdata/index/' . $i_id);
+            return;
         }
 
         // Invalid ticket
         if (!check_ticket(Request::option('studipticket'))) {
             PageLayout::postError(_('Ihr Ticket ist abgelaufen. Versuchen Sie die letzte Aktion erneut.'));
-            return $this->redirect('institute/basicdata/index/' . $i_id);
+            $this->redirect('institute/basicdata/index/' . $i_id);
+            return;
         }
 
         // User may not delete this institue
         if (!$GLOBALS['perm']->have_perm('root') && !($GLOBALS['perm']->is_fak_admin() && Config::get()->INST_FAK_ADMIN_PERMS === 'all')) {
             PageLayout::postError(_('Sie haben nicht die Berechtigung Fakultäten zu löschen!'));
-            return $this->redirect('institute/basicdata/index/' . $i_id);
+            $this->redirect('institute/basicdata/index/' . $i_id);
+            return;
         }
 
         $institute = Institute::find($i_id);
@@ -331,7 +341,8 @@ class Institute_BasicdataController extends AuthenticatedController
             PageLayout::postError(
                 _('Diese Einrichtung kann nicht gelöscht werden, da noch Veranstaltungen an dieser Einrichtung existieren!')
             );
-            return $this->redirect('institute/basicdata/index/' . $i_id);
+            $this->redirect('institute/basicdata/index/' . $i_id);
+            return;
         }
 
         // Institute has sub institutes?
@@ -339,13 +350,15 @@ class Institute_BasicdataController extends AuthenticatedController
             PageLayout::postError(
                 _('Diese Einrichtung kann nicht gelöscht werden, da sie den Status Fakultät hat und noch andere Einrichtungen zugeordnet sind!')
             );
-            return $this->redirect('institute/basicdata/index/' . $i_id);
+            $this->redirect('institute/basicdata/index/' . $i_id);
+            return;
         }
 
         // Is the user allowed to delete faculties?
         if ($institute->is_fak && !$GLOBALS['perm']->have_perm('root')) {
             PageLayout::postError(_('Sie haben nicht die Berechtigung Fakultäten zu löschen!'));
-            return $this->redirect('institute/basicdata/index/' . $i_id);
+            $this->redirect('institute/basicdata/index/' . $i_id);
+            return;
         }
 
         // Save users, name and number of courses
diff --git a/app/controllers/institute/members.php b/app/controllers/institute/members.php
index 162c96deb04..29c325a0ef4 100644
--- a/app/controllers/institute/members.php
+++ b/app/controllers/institute/members.php
@@ -335,8 +335,10 @@ class Institute_MembersController extends AuthenticatedController
                             $relevant_users = $this->institute->members->findBy('inst_perms', $in);
                             foreach ($relevant_users as $user) {
                                 $user_language = getUserLanguagePath($user->id);
-                                include("locale/$user_language/LC_MAILS/new_admin_mail.inc.php");
-                                StudipMail::sendMessage($user->email, $subject, $mailbody);
+
+                                // TODO: This should be refactored so that the included file returns an array
+                                include "locale/$user_language/LC_MAILS/new_admin_mail.inc.php"; // Defines $subject and $mailbody
+                                StudipMail::sendMessage($user->email, $subject ?? '', $mailbody ?? '');
                                 $notin[] = $user->id;
 
                                 $mails_sent += 1;
@@ -351,8 +353,9 @@ class Institute_MembersController extends AuthenticatedController
                                     }
 
                                     $user_language = getUserLanguagePath($user->id);
-                                    include("locale/$user_language/LC_MAILS/new_admin_mail.inc.php");
-                                    StudipMail::sendMessage($user->email, $subject, $mailbody);
+                                    // TODO: This should be refactored so that the included file returns an array
+                                    include("locale/$user_language/LC_MAILS/new_admin_mail.inc.php");  // Defines $subject and $mailbody
+                                    StudipMail::sendMessage($user->email, $subject ?? '', $mailbody ?? '');
                                     $notin[] = $user->id;
 
                                     $mails_sent += 1;
diff --git a/app/controllers/messages.php b/app/controllers/messages.php
index 836c4bd27b1..82d42d1cae5 100644
--- a/app/controllers/messages.php
+++ b/app/controllers/messages.php
@@ -696,7 +696,7 @@ class MessagesController extends AuthenticatedController {
             $this->set_layout($GLOBALS['template_factory']->open('layouts/base'));
         } else {
             $this->set_status(400);
-            return $this->render_nothing();
+            $this->render_nothing();
         }
     }
 
diff --git a/app/controllers/module/module.php b/app/controllers/module/module.php
index 29e8a21bfa3..cf7b6ac23d0 100644
--- a/app/controllers/module/module.php
+++ b/app/controllers/module/module.php
@@ -1290,12 +1290,9 @@ class Module_ModuleController extends MVVController
 
     public function reset_filter_action()
     {
-        $this->filter = [];
         $this->reset_page();
-        // current semester is set in index_action()
-        unset($this->filter['start_sem.beginn']);
-        unset($this->filter['end_sem.ende']);
-        $this->sessSet('filter', $this->filter);
+
+        $this->sessSet('filter', []);
         $this->redirect($this->url_for('/index'));
     }
 
diff --git a/app/controllers/my_courses.php b/app/controllers/my_courses.php
index 45ca5de5f26..2a3dd30b73e 100644
--- a/app/controllers/my_courses.php
+++ b/app/controllers/my_courses.php
@@ -768,9 +768,8 @@ class MyCoursesController extends AuthenticatedController
 
     /**
      * Get widget for grouping selected courses (e.g. by colors, ...)
-     * @param      $action
-     * @param bool $selected
-     * @return string
+     *
+     * @param string $group_field
      */
     private function setGroupingSelector($group_field)
     {
@@ -794,9 +793,8 @@ class MyCoursesController extends AuthenticatedController
     /**
      * Returns a widget for semester selection
      * @param $sem
-     * @return OptionsWidget
      */
-    private function setSemesterWidget(&$sem)
+    private function setSemesterWidget($sem)
     {
         $semesters = new SimpleCollection(Semester::getAll());
         $semesters = $semesters->orderBy('beginn desc');
@@ -805,12 +803,12 @@ class MyCoursesController extends AuthenticatedController
 
         $widget = new SelectWidget(_('Semesterfilter'), $this->url_for('my_courses/set_semester'), 'sem_select');
         $widget->setMaxLength(50);
-        $widget->addElement(new SelectElement('current', _('Aktuelles Semester'), $sem == 'current'));
-        $widget->addElement(new SelectElement('future', _('Aktuelles und nächstes Semester'), $sem == 'future'));
-        $widget->addElement(new SelectElement('last', _('Aktuelles und letztes Semester'), $sem == 'last'));
-        $widget->addElement(new SelectElement('lastandnext', _('Letztes, aktuelles, nächstes Semester'), $sem == 'lastandnext'));
+        $widget->addElement(new SelectElement('current', _('Aktuelles Semester'), $sem === 'current'));
+        $widget->addElement(new SelectElement('future', _('Aktuelles und nächstes Semester'), $sem === 'future'));
+        $widget->addElement(new SelectElement('last', _('Aktuelles und letztes Semester'), $sem === 'last'));
+        $widget->addElement(new SelectElement('lastandnext', _('Letztes, aktuelles, nächstes Semester'), $sem === 'lastandnext'));
         if (Config::get()->MY_COURSES_ENABLE_ALL_SEMESTERS) {
-            $widget->addElement(new SelectElement('all', _('Alle Semester'), $sem == 'all'));
+            $widget->addElement(new SelectElement('all', _('Alle Semester'), $sem === 'all'));
         }
 
         $query = "SELECT semester_data.semester_id
@@ -831,7 +829,7 @@ class MyCoursesController extends AuthenticatedController
             $group = new SelectGroupElement(_('Semester auswählen'));
             foreach ($semesters as $semester) {
                 if ($semester->visible || in_array($semester->id,$courses)) {
-                    $group->addElement(new SelectElement($semester->id, $semester->name, $sem == $semester->id));
+                    $group->addElement(new SelectElement($semester->id, $semester->name, $sem === $semester->id));
                 }
             }
             $widget->addElement($group);
diff --git a/app/controllers/news.php b/app/controllers/news.php
index 2571177b047..3621f4d3f1c 100644
--- a/app/controllers/news.php
+++ b/app/controllers/news.php
@@ -81,12 +81,14 @@ class NewsController extends StudipController
     {
         if (!$range_id) {
             $this->set_status(400);
-            return $this->render_nothing();
+            $this->render_nothing();
+            return;
         }
 
         if (!StudipNews::haveRangePermission('view', $range_id, $GLOBALS['user']->id)) {
             $this->set_status(401);
-            return $this->render_nothing();
+            $this->render_nothing();
+            return;
         }
 
         // Store visit if user opened comments
@@ -229,7 +231,8 @@ class NewsController extends StudipController
         // user has to have autor permission at least
         if (!$GLOBALS['perm']->have_perm('autor')) {
             $this->set_status(401);
-            return $this->render_nothing();
+            $this->render_nothing();
+            return;
         }
 
         // load news and comment data and check if user has permission to edit
@@ -254,19 +257,22 @@ class NewsController extends StudipController
             // if news id given check for valid id and load ranges
             if ($news->isNew()) {
                 PageLayout::postError(_('Die Ankündigung existiert nicht!'));
-                return $this->render_nothing();
+                $this->render_nothing();
+                return;
             }
         } elseif ($template_id) {
             // otherwise, load data from template
             $news_template = new StudipNews($template_id);
             if ($news_template->isNew()) {
                 PageLayout::postError(_('Die Ankündigung existiert nicht!'));
-                return $this->render_nothing();
+                $this->render_nothing();
+                return;
             }
             // check for permission
             if (!$news_template->havePermission('edit')) {
                 $this->set_status(401);
-                return $this->render_nothing();
+                $this->render_nothing();
+                return;
             }
             $ranges = $news_template->news_ranges->toArray();
 
diff --git a/app/controllers/questionnaire.php b/app/controllers/questionnaire.php
index ecd48083381..d886be23756 100644
--- a/app/controllers/questionnaire.php
+++ b/app/controllers/questionnaire.php
@@ -723,7 +723,7 @@ class QuestionnaireController extends AuthenticatedController
                 //We can only add those courses where the current user
                 //has at least admin permissions:
                 foreach ($courses as $course) {
-                    if ($GLOBALS['perm']->have_studip_perm('admin', $course->id, $user_id)) {
+                    if ($GLOBALS['perm']->have_studip_perm('admin', $course->id)) {
                         $this->found_courses[] = $course;
                     }
                 }
@@ -742,7 +742,7 @@ class QuestionnaireController extends AuthenticatedController
                 }
                 $courses_without_perms = [];
                 foreach ($this->selected_courses as $course) {
-                    if (!$GLOBALS['perm']->have_studip_perm('admin', $course->id, $user_id)) {
+                    if (!$GLOBALS['perm']->have_studip_perm('admin', $course->id)) {
                         $courses_without_perms[] = $course->getFullName();
                     }
                 }
diff --git a/app/controllers/settings/settings.php b/app/controllers/settings/settings.php
index 6609f9192f7..0346c242f79 100644
--- a/app/controllers/settings/settings.php
+++ b/app/controllers/settings/settings.php
@@ -142,7 +142,9 @@ abstract class Settings_SettingsController extends AuthenticatedController
             $text = $layout->render();
         }
 
-        return parent::render_text($text);
+        parent::render_text($text);
+
+        return $text;
     }
 
     /**
diff --git a/app/controllers/shared/contacts.php b/app/controllers/shared/contacts.php
index d4a95763e73..b1d82c46310 100644
--- a/app/controllers/shared/contacts.php
+++ b/app/controllers/shared/contacts.php
@@ -469,11 +469,10 @@ class Shared_ContactsController extends MVVController
                 }
                 if (!$user_id) {
                     if (Request::isXhr()) {
-                        header('X-Dialog-Close: 1');
-                        exit;
-                    } else {
-                        return;
+                        $this->response->add_header('X-Dialog-Close', 1);
+                        $this->render_nothing();
                     }
+                    return;
                 }
             }
 
@@ -565,7 +564,7 @@ class Shared_ContactsController extends MVVController
             $this->response->add_header('X-Dialog-Close', 1);
         } else {
             $this->response->add_header('X-Dialog-Close', 1);
-            $this->response->add_header('X-Location', $this->url_for('/index', ['contact_id' => $mvv_contact->id]));
+            $this->response->add_header('X-Location', $this->url_for('/index', ['contact_id' => $contact_range->contact_id]));
         }
         $this->render_nothing();
     }
@@ -584,10 +583,9 @@ class Shared_ContactsController extends MVVController
             PageLayout::postSuccess(_('Die Verknüpfung wurde gelöscht.'));
         }
 
-        $this->range_id = $range_id;
         if (Request::isXhr()) {
-            header('X-Dialog-Close: 1');
-            exit;
+            $this->response->add_header('X-Dialog-Close', 1);
+            $this->render_nothing();
         }
     }
 
@@ -607,8 +605,8 @@ class Shared_ContactsController extends MVVController
             ));
         }
         if (Request::isXhr()) {
-            header('X-Dialog-Close: 1');
-            exit;
+            $this->response->add_header('X-Dialog-Close', 1);
+            $this->render_nothing();
         }
     }
 
@@ -627,8 +625,8 @@ class Shared_ContactsController extends MVVController
             ));
         }
         if (Request::isXhr()) {
-            header('X-Dialog-Close: 1');
-            exit;
+            $this->response->add_header('X-Dialog-Close', 1);
+            $this->render_nothing();
         }
     }
 
diff --git a/app/controllers/shared/download.php b/app/controllers/shared/download.php
index 59b17512687..92d15d9102e 100644
--- a/app/controllers/shared/download.php
+++ b/app/controllers/shared/download.php
@@ -7,7 +7,7 @@ class Shared_DownloadController extends AuthenticatedController
 
         parent::before_filter($action, $args);
     }
-    
+
     /**
      * @param type $format only pdf is implememted yet
      * @param type $semester_id
@@ -27,14 +27,14 @@ class Shared_DownloadController extends AuthenticatedController
         }
 
         include  $GLOBALS['STUDIP_BASE_PATH'] . '/config/mvv_config.php';
-        
+
         $this->MHBPdf($semester_id, $version_id, $language);
 
         init_i18n($current_lang);
         $_SESSION['_language'] = $current_lang;
         include  $GLOBALS['STUDIP_BASE_PATH'] . '/config/mvv_config.php';
     }
-    
+
     private function MHBPdf($semester_id, $version_id, $language)
     {
         $semester = Semester::find($semester_id);
@@ -90,13 +90,11 @@ class Shared_DownloadController extends AuthenticatedController
             $language
         ));
     }
-    
+
     /**
      * Renders a template and outputs it as a PDF file.
      * @param string $view_path the path of the template (controller/view...)
      * @param string $title the title, optional. If not set, takes title from pagelayout.
-     * @param bool $force_pdf forces PDF download, oterwise can be overridden by setting the request no_export.
-     * @return type
      */
     protected function exportTcpdf($html, string $title = '')
     {
@@ -129,19 +127,10 @@ class Shared_DownloadController extends AuthenticatedController
             $pdf->writeHTMLCell(0, 0, '', '', $html_block , 0, 1, 0, true, '', true);
         }
 
-        $output = $pdf->Output(null, 'S');
-
         $filename = trim($title ?: PageLayout::getTitle());
-
-        $this->set_content_type('application/pdf');
-        $this->response->add_header('Content-Disposition', sprintf(
-            'attachment;filename="%s.pdf"',
-            preg_replace('/_{2,}/', '_', preg_replace('/\W/', '_', $filename))
-        ));
-        $this->response->add_header('Content-Length', strlen($output));
-        $this->render_text($output);
+        $this->render_pdf($pdf, $filename . '.pdf');
     }
-    
+
     private static function sanitizeFilename($filename)
     {
         $replacements = [
@@ -161,7 +150,7 @@ class Shared_DownloadController extends AuthenticatedController
 
         return $filename;
     }
-    
+
     public function getMVVPluginModulDescription($modul, $display_language = null)
     {
         if ($display_language == null) {
@@ -211,11 +200,11 @@ class Shared_DownloadController extends AuthenticatedController
 
         return $content;
     }
-    
+
     /**
      * Retrieves all modules assigned to the given Studiengangteilversion
      * grouped by Studiengangteilabschnitte
-     * 
+     *
      * @param StgteilVersion $StgteilVersion
      * @param Semester $semester
      * @return type
diff --git a/app/controllers/studiengaenge/stgteilbezeichnungen.php b/app/controllers/studiengaenge/stgteilbezeichnungen.php
index 5d3282ff54c..1f7806ce664 100644
--- a/app/controllers/studiengaenge/stgteilbezeichnungen.php
+++ b/app/controllers/studiengaenge/stgteilbezeichnungen.php
@@ -138,8 +138,7 @@ class Studiengaenge_StgteilbezeichnungenController extends MVVController
 
     /**
      * Display details
-     * @param $bezeichnung_id
-     * @return bool
+     * @param string $bezeichnung_id
      */
     public function details_action($bezeichnung_id)
     {
@@ -148,7 +147,6 @@ class Studiengaenge_StgteilbezeichnungenController extends MVVController
 
         if (!Request::isXhr()) {
             $this->perform_relayed('stgteilbezeichnungen');
-            return true;
         }
     }
 
diff --git a/app/controllers/studiengaenge/studiengaenge.php b/app/controllers/studiengaenge/studiengaenge.php
index f6c0573e3cf..97a9a589c79 100644
--- a/app/controllers/studiengaenge/studiengaenge.php
+++ b/app/controllers/studiengaenge/studiengaenge.php
@@ -994,12 +994,9 @@ class Studiengaenge_StudiengaengeController extends MVVController
 
     public function reset_filter_action()
     {
-        $this->filter = [];
         $this->reset_page();
-        // current semester is set in index_action()
-        unset($this->filter['start_sem.beginn']);
-        unset($this->filter['end_sem.ende']);
-        $this->sessSet('filter', $this->filter);
+
+        $this->sessSet('filter', []);
         $this->redirect($this->url_for('/index'));
     }
 
diff --git a/app/controllers/studiengaenge/studiengangteile.php b/app/controllers/studiengaenge/studiengangteile.php
index 8bd66cd6e4f..ffd71517b53 100644
--- a/app/controllers/studiengaenge/studiengangteile.php
+++ b/app/controllers/studiengaenge/studiengangteile.php
@@ -28,13 +28,14 @@ class Studiengaenge_StudiengangteileController extends SharedVersionController
 
         // Nur Studiengangteile mit zugeordnetem Fach an dessen verantwortlicher
         // Einrichtung der User eine Rolle hat
-        $this->filter['mvv_fach_inst.institut_id'] = MvvPerm::getOwnInstitutes();
+        $filter = [
+            'mvv_fach_inst.institut_id' => MvvPerm::getOwnInstitutes(),
+        ];
 
         $search_result = $this->getSearchResult('StudiengangTeil');
-        $filter = $this->filter;
         $this->sortby = $this->sortby ?: 'fach_name,zusatz,kp';
         $this->order = $this->order ?: 'ASC';
-        unset($filter['start_sem.beginn'], $filter['end_sem.ende']);
+
         //get data
         if (count($search_result)) {
             $filter['stgteil_id'] = $search_result;
diff --git a/app/controllers/studip_controller.php b/app/controllers/studip_controller.php
index e8bb66c5b6b..322faa237d8 100644
--- a/app/controllers/studip_controller.php
+++ b/app/controllers/studip_controller.php
@@ -9,14 +9,20 @@
  * the License, or (at your option) any later version.
  */
 
+require_once __DIR__ . '/studip_controller_properties_trait.php';
 require_once __DIR__ . '/studip_response.php';
 
 abstract class StudipController extends Trails_Controller
 {
+    use StudipControllerPropertiesTrait;
+
     protected $with_session = false; //do we need to have a session for this controller
     protected $allow_nobody = true; //should 'nobody' allowed for this controller or redirected to login?
     protected $_autobind = false;
 
+    /**
+     * @return false|void
+     */
     public function before_filter(&$action, &$args)
     {
         $this->current_action = $action;
@@ -408,7 +414,7 @@ abstract class StudipController extends Trails_Controller
     public function render_json($data)
     {
         $this->set_content_type('application/json;charset=utf-8');
-        return $this->render_text(json_encode($data));
+        $this->render_text(json_encode($data));
     }
 
     /**
@@ -441,7 +447,7 @@ abstract class StudipController extends Trails_Controller
 
         $this->response->add_header('Content-Length', strlen($csv_data));
 
-        return $this->render_text($csv_data);
+        $this->render_text($csv_data);
     }
 
     /**
diff --git a/app/controllers/studip_controller_properties_trait.php b/app/controllers/studip_controller_properties_trait.php
new file mode 100644
index 00000000000..4246da05f98
--- /dev/null
+++ b/app/controllers/studip_controller_properties_trait.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * This trait manages all variable assignments to the controller and templates.
+ *
+ * @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
+ * @license GPL2 or any later version
+ * @since Stud.IP 5.2
+ */
+trait StudipControllerPropertiesTrait
+{
+    /**
+     * Stores the assigned variables.
+     * @var array
+     */
+    protected $_template_variables = [];
+
+    /**
+     * Returns whether a variable is set.
+     *
+     * @param string $offset
+     * @return bool
+     */
+    public function __isset(string $offset): bool
+    {
+        return isset($this->_template_variables[$offset]);
+    }
+
+    /**
+     * Stores a variable.
+     *
+     * @param string $offset
+     * @param mixed $value
+     */
+    public function __set(string $offset, $value): void
+    {
+        $this->_template_variables[$offset] = $value;
+    }
+
+    /**
+     * Returns a previously set variable.
+     *
+     * @param string $offset
+     * @return mixed
+     */
+    public function __get(string $offset)
+    {
+        return $this->_template_variables[$offset] ?? null;
+    }
+
+    /**
+     * Unsets a previously set variable
+     *
+     * @param string $offset
+     */
+    public function __unset(string $offset): void
+    {
+        unset($this->_template_variables[$offset]);
+    }
+
+    public function get_assigned_variables(): array
+    {
+        $variables = $this->_template_variables;
+        $variables['controller'] = $this;
+        return $variables;
+    }
+}
diff --git a/app/controllers/tour.php b/app/controllers/tour.php
index bae6f558042..9b53cadb1b3 100644
--- a/app/controllers/tour.php
+++ b/app/controllers/tour.php
@@ -59,7 +59,8 @@ class TourController extends AuthenticatedController
         $this->route = get_route(Request::get('route'));
         $this->tour  = new HelpTour($tour_id);
         if (!$this->tour->isVisible() || !$this->route) {
-            return $this->render_nothing();
+            $this->render_nothing();
+            return;
         }
 
         $this->user_visit = new HelpTourUser([$tour_id, $GLOBALS['user']->user_id]);
@@ -103,8 +104,10 @@ class TourController extends AuthenticatedController
         }
         if ($this->tour->steps[$step_nr - 1]->route !== $this->route) {
             $data['redirect'] = URLHelper::getURL($this->tour->steps[$step_nr - 1]->route, null, true);
-        } elseif (!count($data['data']))
-            return $this->render_nothing();
+        } elseif (!count($data['data'])) {
+            $this->render_nothing();
+            return;
+        }
         if ($next_first_step <= count($this->tour->steps)) {
             if ($this->tour->type === 'tour') {
                 $data['proceed_link'] = URLHelper::getURL($this->tour->steps[$next_first_step - 1]->route, null, true);
@@ -120,8 +123,8 @@ class TourController extends AuthenticatedController
         $template                  = $GLOBALS['template_factory']->open('tour/tour.php');
         $template->set_layout(null);
         $data['tour_html'] = $template->render();
-        $this->set_content_type('application/json; charset=UTF-8');
-        return $this->render_text(json_encode($data));
+
+        $this->render_json($data);
     }
 
     /**
@@ -137,7 +140,8 @@ class TourController extends AuthenticatedController
         $GLOBALS['perm']->check('user');
         $this->tour = new HelpTour($tour_id);
         if (!$this->tour->isVisible()) {
-            return $this->render_nothing();
+            $this->render_nothing();
+            return;
         }
         $this->user_visit = new HelpTourUser([$tour_id, $GLOBALS['user']->user_id]);
         $this->user_visit->step_nr = $step_nr;
diff --git a/app/views/help_content/edit.php b/app/views/help_content/edit.php
index b7e5d2e5bb0..2197ff1095f 100644
--- a/app/views/help_content/edit.php
+++ b/app/views/help_content/edit.php
@@ -17,18 +17,18 @@
                        placeholder="<?= _('Bitte geben Sie eine Route für den Hilfe-Text an') ?>">
             </label>
         <? endif ?>
-        <? if ($help_admin) : ?>
-            <label for="help_content_language">
-                <span class="required"><?= _('Sprache des Textes') ?>:</span>
-                <select name="help_content_language">
-                    <? foreach ($GLOBALS['INSTALLED_LANGUAGES'] as $key => $language) : ?>
-                        <option value="<?= mb_substr($key, 0, 2) ?>"<?= ($help_content->language == mb_substr($key, 0, 2)) ? ' selected' : '' ?>>
-                            <?= $language['name'] ?>
-                        </option>
-                    <? endforeach ?>
-                </select>
-            </label>
-        <? endif ?>
+
+        <label for="help_content_language">
+            <span class="required"><?= _('Sprache des Textes') ?>:</span>
+            <select name="help_content_language">
+                <? foreach ($GLOBALS['INSTALLED_LANGUAGES'] as $key => $language) : ?>
+                    <option value="<?= mb_substr($key, 0, 2) ?>"<?= ($help_content->language == mb_substr($key, 0, 2)) ? ' selected' : '' ?>>
+                        <?= $language['name'] ?>
+                    </option>
+                <? endforeach ?>
+            </select>
+        </label>
+
         <label for="help_content_content">
             <?= _('Hilfe-Text') ?>:
             <textarea cols="60" rows="5" name="help_content_content"
diff --git a/lib/models/MvvContactRange.php b/lib/models/MvvContactRange.php
index 5d5d501fb41..85bdf6347ae 100644
--- a/lib/models/MvvContactRange.php
+++ b/lib/models/MvvContactRange.php
@@ -11,6 +11,25 @@
  * @license     http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
  * @category    Stud.IP
  * @since       4.5
+ *
+ * @property string $id
+ * @property string $contact_range_id
+ * @property string $contact_id
+ * @property string $range_id
+ * @property string $range_type
+ * @property string $type
+ * @property string $category
+ * @property int|null $position
+ * @property string $author_id
+ * @property string $editor_id
+ * @property int $mkdate
+ * @property int $chdate
+ *
+ * @property MvvContact $contact
+ *
+ * @property int $count_relations
+ * @property string $name
+ * @property string $contact_status
  */
 
 class MvvContactRange extends ModuleManagementModel
diff --git a/lib/models/ToolActivation.php b/lib/models/ToolActivation.php
index 14da02f6cb7..06ec25f4dc8 100644
--- a/lib/models/ToolActivation.php
+++ b/lib/models/ToolActivation.php
@@ -17,7 +17,7 @@
  * @property string range_type database column
  * @property string plugin_id database column
  * @property string position database column
- * @property string metadata database column
+ * @property array metadata database column
  * @property string mkdate database column
  * @property string chdate database column
  */
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index ac4a30c767e..1682aa4262a 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -2,6 +2,7 @@ parameters:
     level: 0
     phpVersion: 70200 # PHP 7.2
     paths:
+        - app/controllers
         - app/routes
         - lib
         - tests/functional
@@ -22,5 +23,4 @@ parameters:
         RESTAPI\RouteMap:
             - error
             - halt
-    ignoreErrors:
-        # - '#Undefined variable#'
+            - notFound
-- 
GitLab