diff --git a/.appveyor.yml b/.appveyor.yml
index 6bae47a324f13ecb5fa1dffc763fef214121741c..78b8a60ee1236559cb948a7f6eaa248999b12dfd 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -1,12 +1,11 @@
 build: false
 shallow_clone: false
-version: '1.17.1.{build}'
+version: '1.19.0.{build}'
 platform: 'x64'
 clone_folder: C:\projects\simplesamlphp
 
 environment:
   matrix:
-    - PHP_VERSION: "5.6"
     - PHP_VERSION: "7.0"
     - PHP_VERSION: "7.1"
     - PHP_VERSION: "7.2"
diff --git a/.travis.yml b/.travis.yml
index 937598cb6c7fb61c9321573ee6700674900f4d46..3f51b83ca41435f6f00e2dc813044901c11c2879 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,6 @@ stages:
 ################
 
 php:
-  - 5.6
   - 7.0
   - 7.1
   - 7.2
@@ -34,13 +33,13 @@ script:
 jobs:
   fast_finish: true
   allow_failures:
-    - php: 5.6
+    - php: 7.0
       env: Psalm
-    - php: 5.6
+    - php: 7.0
       env: Security check (composer install)
-    - php: 5.6
+    - php: 7.0
       env: Security check (composer update)
-    - php: 5.6
+    - php: 7.0
       env: PHP Codesniffer
 
   include:
@@ -49,14 +48,6 @@ jobs:
     #  Pre-conditions stage  #
     ##########################
 
-    - stage: pre-conditions
-      php: 5.6
-      env: Syntax check PHP
-      before_script:
-        - composer install
-      script:
-        - vendor/bin/check-syntax-php.sh
-
     - stage: pre-conditions
       php: 7.0
       env: Syntax check PHP
@@ -104,6 +95,7 @@ jobs:
     ###################
 
     - stage: quality
+      php: 7.3
       env: Security check (composer install)
       before_script:
         - composer install
@@ -111,6 +103,7 @@ jobs:
         - vendor/bin/security-checker security:check
 
     - stage: quality
+      php: 7.3
       env: Security check (composer update)
       before_script:
         - composer update
@@ -118,7 +111,7 @@ jobs:
         - vendor/bin/security-checker security:check
 
     - stage: quality
-      php: 7.2
+      php: 7.3
       env: Codecov
       before_script:
         - composer update
@@ -127,6 +120,7 @@ jobs:
         - bash <(curl -s https://codecov.io/bash)
 
     - stage: quality
+      php: 7.3
       env: Psalm
       before_script:
         - composer update
@@ -134,6 +128,7 @@ jobs:
         - vendor/bin/psalm
 
     - stage: quality
+      php: 7.3
       env: PHP Codesniffer
       before_script:
         - composer update
diff --git a/bin/memcacheSync.php b/bin/memcacheSync.php
index 910841e1c8e698c646b7f620d0184951054a035d..cc60d614d91e4306db83269c60e1170999e096ad 100755
--- a/bin/memcacheSync.php
+++ b/bin/memcacheSync.php
@@ -91,7 +91,7 @@ if ($warnBigSlab > 0) {
  *
  * @return array An array with all the keys available on the server.
  */
-function getServerKeys($server)
+function getServerKeys(string $server): array
 {
     $server = explode(':', $server);
     $host = $server[0];
diff --git a/composer.json b/composer.json
index 50f0b8a338f4afae284928d15c6bf920fcae1682..5c5ef94f04b33c069d3a30082658916c48b7a510 100644
--- a/composer.json
+++ b/composer.json
@@ -32,7 +32,7 @@
         "files": ["tests/_autoload_modules.php"]
     },
     "require": {
-        "php": ">=5.6",
+        "php": ">=7.0",
         "ext-SPL": "*",
         "ext-zlib": "*",
         "ext-pcre": "*",
@@ -88,11 +88,11 @@
     "require-dev": {
         "ext-curl": "*",
         "mikey179/vfsstream": "~1.6",
-        "phpunit/phpunit": "~5.7",
+        "phpunit/phpunit": "~6.3",
         "sensiolabs/security-checker": "^5.0.3",
-        "simplesamlphp/simplesamlphp-test-framework": "^0.0.12",
+        "simplesamlphp/simplesamlphp-test-framework": "^0.1.0",
         "squizlabs/php_codesniffer": "^3.5",
-        "vimeo/psalm": "~1.1.9"
+        "vimeo/psalm": "~3.2"
     },
     "suggest": {
         "predis/predis": "Needed if a Redis server is used to store session information",
diff --git a/docs/simplesamlphp-upgrade-notes-1.19.md b/docs/simplesamlphp-upgrade-notes-1.19.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e039e97425c0b960fc1c7880fef4d83299303d5
--- /dev/null
+++ b/docs/simplesamlphp-upgrade-notes-1.19.md
@@ -0,0 +1,4 @@
+Upgrade notes for SimpleSAMLphp 1.19
+====================================
+
+The minimum PHP version required is now PHP 7.0.
diff --git a/lib/SimpleSAML/Auth/ProcessingChain.php b/lib/SimpleSAML/Auth/ProcessingChain.php
index d160928a8fbbdb440c49b15316d277c12f8444d8..b5bb4888d79282aa357cf14b7d08f5866433c23f 100644
--- a/lib/SimpleSAML/Auth/ProcessingChain.php
+++ b/lib/SimpleSAML/Auth/ProcessingChain.php
@@ -93,11 +93,8 @@ class ProcessingChain
      * @param array $src  Source filters. May be unsorted.
      * @return void
      */
-    private static function addFilters(&$target, $src)
+    private static function addFilters(array &$target, array $src)
     {
-        assert(is_array($target));
-        assert(is_array($src));
-
         foreach ($src as $filter) {
             $fp = $filter->priority;
 
@@ -120,10 +117,8 @@ class ProcessingChain
      * @param array $filterSrc  Array with filter configuration.
      * @return array  Array of ProcessingFilter objects.
      */
-    private static function parseFilterList($filterSrc)
+    private static function parseFilterList(array $filterSrc): array
     {
-        assert(is_array($filterSrc));
-
         $parsedFilters = [];
 
         foreach ($filterSrc as $priority => $filter) {
@@ -151,10 +146,8 @@ class ProcessingChain
      *                           definition.)
      * @return \SimpleSAML\Auth\ProcessingFilter  The parsed filter.
      */
-    private static function parseFilter($config, $priority)
+    private static function parseFilter(array $config, int $priority): ProcessingFilter
     {
-        assert(is_array($config));
-
         if (!array_key_exists('class', $config)) {
             throw new \Exception('Authentication processing filter without name given.');
         }
@@ -346,9 +339,8 @@ class ProcessingChain
      * @param array &$state
      * @return void
      */
-    private static function addUserID(&$state)
+    private static function addUserID(array &$state)
     {
-        assert(is_array($state));
         assert(array_key_exists('Attributes', $state));
 
         if (isset($state['Destination']['userid.attribute'])) {
diff --git a/lib/SimpleSAML/Auth/Source.php b/lib/SimpleSAML/Auth/Source.php
index 36b7e28c6dae3c7efba60b531e9c3efabaa4bed1..b3dfffc285f70e5d07481aa76bdc068a89585e35 100644
--- a/lib/SimpleSAML/Auth/Source.php
+++ b/lib/SimpleSAML/Auth/Source.php
@@ -307,11 +307,8 @@ abstract class Source
      * @return \SimpleSAML\Auth\Source The parsed authentication source.
      * @throws \Exception If the authentication source is invalid.
      */
-    private static function parseAuthSource($authId, $config)
+    private static function parseAuthSource(string $authId, array $config): Source
     {
-        assert(is_string($authId));
-        assert(is_array($config));
-
         self::validateSource($config, $authId);
 
         $id = $config[0];
diff --git a/lib/SimpleSAML/Auth/State.php b/lib/SimpleSAML/Auth/State.php
index ac915cbd3161194b72a70e8067e7c843703a2897..f938f9d572a7a0612629d34ced583f2d88ac6371 100644
--- a/lib/SimpleSAML/Auth/State.php
+++ b/lib/SimpleSAML/Auth/State.php
@@ -177,7 +177,7 @@ class State
      *
      * @return integer  State timeout.
      */
-    private static function getStateTimeout()
+    private static function getStateTimeout(): int
     {
         if (self::$stateTimeout === null) {
             $globalConfig = Configuration::getInstance();
diff --git a/lib/SimpleSAML/Auth/TimeLimitedToken.php b/lib/SimpleSAML/Auth/TimeLimitedToken.php
index ac5846dec9eb9c308086cd8d5406db8dc4312c6a..e668d73ab25a4d6ea8763037b71d52579456152a 100644
--- a/lib/SimpleSAML/Auth/TimeLimitedToken.php
+++ b/lib/SimpleSAML/Auth/TimeLimitedToken.php
@@ -84,7 +84,7 @@ class TimeLimitedToken
      *
      * @return string The token for the given time and offset.
      */
-    private function calculateTokenValue($offset, $time = null)
+    private function calculateTokenValue(int $offset, int $time = null): string
     {
         if ($time === null) {
             $time = time();
diff --git a/lib/SimpleSAML/Bindings/Shib13/Artifact.php b/lib/SimpleSAML/Bindings/Shib13/Artifact.php
index 09703ec99d15dc3dd0dddfc1645d1de38858f555..dfead878b6cab900a03204f89c786c3d53faa896 100644
--- a/lib/SimpleSAML/Bindings/Shib13/Artifact.php
+++ b/lib/SimpleSAML/Bindings/Shib13/Artifact.php
@@ -24,7 +24,7 @@ class Artifact
      *
      * @return array  The artifacts.
      */
-    private static function getArtifacts()
+    private static function getArtifacts(): array
     {
         assert(array_key_exists('QUERY_STRING', $_SERVER));
 
@@ -53,7 +53,7 @@ class Artifact
      * @param array $artifacts  The artifacts we will request.
      * @return string  The request, as an XML string.
      */
-    private static function buildRequest(array $artifacts)
+    private static function buildRequest(array $artifacts): string
     {
         $msg = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">'.
             '<SOAP-ENV:Body>'.
@@ -82,10 +82,8 @@ class Artifact
      * @return string The <saml1p:Response> element, as a string.
      * @throws Error\Exception
      */
-    private static function extractResponse($soapResponse)
+    private static function extractResponse(string $soapResponse): string
     {
-        assert(is_string($soapResponse));
-
         try {
             $doc = DOMDocumentFactory::fromString($soapResponse);
         } catch (\Exception $e) {
diff --git a/lib/SimpleSAML/Configuration.php b/lib/SimpleSAML/Configuration.php
index afa394e2ce96933c70d7bd67a66226c1be605bdf..8949a10c4f088ed4babdfd906e54ab9ccc2ba59e 100644
--- a/lib/SimpleSAML/Configuration.php
+++ b/lib/SimpleSAML/Configuration.php
@@ -109,11 +109,8 @@ class Configuration implements Utils\ClearableState
      *
      * @throws \Exception If the configuration file is invalid or missing.
      */
-    private static function loadFromFile($filename, $required)
+    private static function loadFromFile(string $filename, bool $required): Configuration
     {
-        assert(is_string($filename));
-        assert(is_bool($required));
-
         if (array_key_exists($filename, self::$loadedConfigs)) {
             return self::$loadedConfigs[$filename];
         }
diff --git a/lib/SimpleSAML/Database.php b/lib/SimpleSAML/Database.php
index 59b108f2e05a4fab75cc43a7e0e6e8c407ccd304..5fb3779a7a5c9008f8c2e874e62e60dd7192bb64 100644
--- a/lib/SimpleSAML/Database.php
+++ b/lib/SimpleSAML/Database.php
@@ -4,6 +4,7 @@ namespace SimpleSAML;
 
 use PDO;
 use PDOException;
+use PDOStatement;
 
 /**
  * This file implements functions to read and write to a group of database servers.
@@ -76,7 +77,7 @@ class Database
      *
      * @param \SimpleSAML\Configuration $config Instance of the \SimpleSAML\Configuration class
      */
-    private function __construct($config)
+    private function __construct(Configuration $config)
     {
         $driverOptions = $config->getArray('database.driver_options', []);
         if ($config->getBoolean('database.persistent', true)) {
@@ -116,7 +117,7 @@ class Database
      *
      * @return string $instanceId
      */
-    private static function generateInstanceId($config)
+    private static function generateInstanceId(Configuration $config): string
     {
         $assembledConfig = [
             'master' => [
@@ -137,14 +138,14 @@ class Database
      * This function connects to a database.
      *
      * @param string $dsn Database connection string
-     * @param string $username SQL user
-     * @param string $password SQL password
+     * @param string|null $username SQL user
+     * @param string|null $password SQL password
      * @param array  $options PDO options
      *
      * @throws \Exception If an error happens while trying to connect to the database.
      * @return \PDO object
      */
-    private function connect($dsn, $username, $password, $options)
+    private function connect(string $dsn, string $username = null, string $password = null, array $options): PDO
     {
         try {
             $db = new PDO($dsn, $username, $password, $options);
@@ -163,7 +164,7 @@ class Database
      *
      * @return \PDO object
      */
-    private function getSlave()
+    private function getSlave(): PDO
     {
         if (count($this->dbSlaves) > 0) {
             $slaveId = rand(0, count($this->dbSlaves) - 1);
@@ -197,12 +198,8 @@ class Database
      * @throws \Exception If an error happens while trying to execute the query.
      * @return \PDOStatement object
      */
-    private function query($db, $stmt, $params)
+    private function query(PDO $db, string $stmt, array $params): PDOStatement
     {
-        assert(is_object($db));
-        assert(is_string($stmt));
-        assert(is_array($params));
-
         try {
             $query = $db->prepare($stmt);
 
@@ -233,11 +230,8 @@ class Database
      * @throws \Exception If an error happens while trying to execute the query.
      * @return int The number of rows affected.
      */
-    private function exec($db, $stmt)
+    private function exec(PDO $db, string $stmt): int
     {
-        assert(is_object($db));
-        assert(is_string($stmt));
-
         try {
             return $db->exec($stmt);
         } catch (PDOException $e) {
@@ -260,8 +254,7 @@ class Database
         $db = $this->dbMaster;
 
         if (is_array($params)) {
-            $obj = $this->query($db, $stmt, $params);
-            return ($obj === false) ? $obj : $obj->rowCount();
+            return $this->query($db, $stmt, $params)->rowCount();
         } else {
             return $this->exec($db, $stmt);
         }
diff --git a/lib/SimpleSAML/Error/Exception.php b/lib/SimpleSAML/Error/Exception.php
index abfc6633ba8d33c37daff814ec6bd0acd15d1af7..3e6690942185ac63945f4e6f5b79a188a5e44f3f 100644
--- a/lib/SimpleSAML/Error/Exception.php
+++ b/lib/SimpleSAML/Error/Exception.php
@@ -22,7 +22,7 @@ class Exception extends \Exception
      * We need to save the backtrace, since we cannot rely on
      * serializing the Exception::trace-variable.
      *
-     * @var array
+     * @var array<int, string>
      */
     private $backtrace = [];
 
diff --git a/lib/SimpleSAML/Error/UnserializableException.php b/lib/SimpleSAML/Error/UnserializableException.php
index 6ac5b39fff3fd5ab76cfe2babe80c25bd81a158e..5634c287443a8fcb76f654d2e5cf6317eba0c253 100644
--- a/lib/SimpleSAML/Error/UnserializableException.php
+++ b/lib/SimpleSAML/Error/UnserializableException.php
@@ -37,7 +37,7 @@ class UnserializableException extends Exception
         $msg = $original->getMessage();
         $code = $original->getCode();
 
-        if ($original instanceof \PDOException) {
+        if (is_string($code)) {
             // PDOException uses a string as the code. Filter it out here.
             $code = -1;
         }
diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php
index e11926a78f02a77f1991617f55ba781eb27e2c0e..b1de1602ac953dc4007db2e2afaf1c46d95fafe1 100644
--- a/lib/SimpleSAML/IdP.php
+++ b/lib/SimpleSAML/IdP.php
@@ -64,10 +64,8 @@ class IdP
      *
      * @throws \SimpleSAML\Error\Exception If the IdP is disabled or no such auth source was found.
      */
-    private function __construct($id)
+    private function __construct(string $id)
     {
-        assert(is_string($id));
-
         $this->id = $id;
         $this->associationGroup = $id;
 
diff --git a/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php b/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php
index 9c18499364c707af8c95ad31a0e99e84e3327495..5e1921ad0c0f2d993a88cfa9dfbbca906652c895 100644
--- a/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php
+++ b/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php
@@ -109,6 +109,7 @@ class TraditionalLogoutHandler implements LogoutHandlerInterface
             throw new Error\Exception('RelayState lost during logout.');
         }
 
+        /** @psalm-var array $state */
         $state = Auth\State::loadState($relayState, 'core:LogoutTraditional');
 
         if ($error === null) {
diff --git a/lib/SimpleSAML/Locale/Language.php b/lib/SimpleSAML/Locale/Language.php
index 0c5b761ade03c86124d956145b214e639cfff362..cf32a274b39c1059baecf4e3c79cac022b755ca8 100644
--- a/lib/SimpleSAML/Locale/Language.php
+++ b/lib/SimpleSAML/Locale/Language.php
@@ -163,7 +163,7 @@ class Language
      *
      * @return array The set of languages both in 'language.available' and self::$language_names.
      */
-    private function getInstalledLanguages()
+    private function getInstalledLanguages(): array
     {
         $configuredAvailableLanguages = $this->configuration->getArray('language.available', ['en']);
         $availableLanguages = [];
diff --git a/lib/SimpleSAML/Locale/Localization.php b/lib/SimpleSAML/Locale/Localization.php
index 7e73ca7805dc0aeb2fd1d1aad05c0c5af964fd64..5121772173eab904d43ad9264f2aca590f05c8ed 100644
--- a/lib/SimpleSAML/Locale/Localization.php
+++ b/lib/SimpleSAML/Locale/Localization.php
@@ -238,7 +238,7 @@ class Localization
      *
      * @throws \Exception If something is wrong with the locale file for the domain and activated language
      */
-    private function loadGettextGettextFromPO($domain = self::DEFAULT_DOMAIN, $catchException = true)
+    private function loadGettextGettextFromPO(string $domain = self::DEFAULT_DOMAIN, bool $catchException = true)
     {
         try {
             $langPath = $this->getLangPath($domain);
diff --git a/lib/SimpleSAML/Locale/Translate.php b/lib/SimpleSAML/Locale/Translate.php
index 0d0475edfe74bdb1ed9fb61eefffc86b64bd0698..6d7f5446ada3b3fc7824eb0205d3cbb271809ca2 100644
--- a/lib/SimpleSAML/Locale/Translate.php
+++ b/lib/SimpleSAML/Locale/Translate.php
@@ -97,10 +97,8 @@ class Translate
      *
      * @return array An associative array with the dictionary.
      */
-    private function getDictionary($name)
+    private function getDictionary(string $name): array
     {
-        assert(is_string($name));
-
         if (!array_key_exists($name, $this->dictionaries)) {
             $sepPos = strpos($name, ':');
             if ($sepPos !== false) {
@@ -261,7 +259,7 @@ class Translate
      * @param bool $striptags
      * @deprecated Not used in twig, gettext
      *
-     * @return string|null  The translated tag, or a placeholder value if the tag wasn't found.
+     * @return string  The translated tag, or a placeholder value if the tag wasn't found.
      */
     public function t(
         $tag,
@@ -282,26 +280,6 @@ class Translate
                 ' identical to the $tag in 2.0.'
             );
         }
-        if (!is_array($replacements)) {
-            // TODO: remove this entire if for 2.0
-
-            // old style call to t(...). Print warning to log
-            Logger::warning(
-                'Deprecated use of SimpleSAML\Locale\Translate::t(...) at ' . $where .
-                '. Please update the code to use the new style of parameters.'
-            );
-
-            // for backwards compatibility
-            /** @psalm-suppress PossiblyInvalidArgument */
-            if (!$replacements && ($this->getTag($tag) === null)) {
-                Logger::warning(
-                    'Code which uses $fallbackdefault === FALSE should be updated to use the getTag() method instead.'
-                );
-                return null;
-            }
-
-            $replacements = $oldreplacements;
-        }
 
         if (is_array($tag)) {
             $tagData = $tag;
@@ -339,7 +317,7 @@ class Translate
      *
      * @return string The string that should be used, or the tag name if $fallbacktag is set to false.
      */
-    private function getStringNotTranslated($tag, $fallbacktag)
+    private function getStringNotTranslated(string $tag, bool $fallbacktag)
     {
         if ($fallbacktag) {
             return 'not translated (' . $tag . ')';
@@ -404,7 +382,7 @@ class Translate
      *
      * @return array An array holding all the translations in the file.
      */
-    private function readDictionaryJSON($filename)
+    private function readDictionaryJSON(string $filename): array
     {
         $definitionFile = $filename . '.definition.json';
         assert(file_exists($definitionFile));
@@ -436,7 +414,7 @@ class Translate
      *
      * @return array An array holding all the translations in the file.
      */
-    private function readDictionaryPHP($filename)
+    private function readDictionaryPHP(string $filename): array
     {
         $phpFile = $filename . '.php';
         assert(file_exists($phpFile));
@@ -457,10 +435,8 @@ class Translate
      *
      * @return array An array holding all the translations in the file.
      */
-    private function readDictionaryFile($filename)
+    private function readDictionaryFile(string $filename): array
     {
-        assert(is_string($filename));
-
         Logger::debug('Translate: Reading dictionary [' . $filename . ']');
 
         $jsonFile = $filename . '.definition.json';
diff --git a/lib/SimpleSAML/Logger.php b/lib/SimpleSAML/Logger.php
index 69d38ca699cf22a10964c26a980c604102a005e9..9cbe9ee693137e2c27f4422bc0a899dd0bcd3d4a 100644
--- a/lib/SimpleSAML/Logger.php
+++ b/lib/SimpleSAML/Logger.php
@@ -389,7 +389,7 @@ class Logger
      * @param boolean $stats Whether this is a stats message or a regular one.
      * @return void
      */
-    private static function defer($level, $message, $stats)
+    private static function defer(int $level, string $message, bool $stats)
     {
         // save the message for later
         self::$earlyLog[] = ['level' => $level, 'string' => $message, 'statsLog' => $stats];
@@ -407,7 +407,7 @@ class Logger
      * @return void
      * @throws \Exception
      */
-    private static function createLoggingHandler($handler = null)
+    private static function createLoggingHandler(string $handler = null)
     {
         self::$initializing = true;
 
@@ -466,7 +466,7 @@ class Logger
      * @param bool $statsLog
      * @return void
      */
-    private static function log($level, $string, $statsLog = false)
+    private static function log(int $level, string $string, bool $statsLog = false)
     {
         if (self::$initializing) {
             // some error occurred while initializing logging
@@ -487,17 +487,17 @@ class Logger
         }
 
         if (self::$captureLog) {
-            $ts = microtime(true);
-            $msecs = (int) (($ts - (int) $ts) * 1000);
-            $ts = gmdate('H:i:s', $ts) . sprintf('.%03d', $msecs) . 'Z';
+            $sample = microtime(false);
+            list($msecs, $mtime) = explode(' ', $sample);
+
+            $time = intval($mtime);
+            $usec = substr($msecs, 2, 3);
+
+            $ts = gmdate('H:i:s', $time) . '.' . $usec . 'Z';
             self::$capturedLog[] = $ts . ' ' . $string;
         }
 
         if (self::$logLevel >= $level || $statsLog) {
-            if (is_array($string)) {
-                $string = implode(",", $string);
-            }
-
             $formats = ['%trackid', '%msg', '%srcip', '%stat'];
             $replacements = [self::$trackid, $string, $_SERVER['REMOTE_ADDR']];
 
diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php
index bca1d8a1dff3d1b1c7bae783bd097487c963814e..d95b6c6c139881c0637f33ef53f6288bd9352cb4 100644
--- a/lib/SimpleSAML/Memcache.php
+++ b/lib/SimpleSAML/Memcache.php
@@ -218,7 +218,7 @@ class Memcache
      *
      * @throws \Exception If any configuration option for the server is invalid.
      */
-    private static function addMemcacheServer($memcache, $server)
+    private static function addMemcacheServer($memcache, array $server)
     {
         // the hostname option is required
         if (!array_key_exists('hostname', $server)) {
@@ -364,7 +364,7 @@ class Memcache
      *
      * @throws \Exception If the servers configuration is invalid.
      */
-    private static function getMemcacheServers()
+    private static function getMemcacheServers(): array
     {
         // check if we have loaded the servers already
         if (self::$serverGroups != null) {
@@ -423,7 +423,7 @@ class Memcache
      *
      * @throws \Exception If the option 'memcache_store.expires' has a negative value.
      */
-    private static function getExpireTime()
+    private static function getExpireTime(): int
     {
         // get the configuration instance
         $config = Configuration::getInstance();
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
index 5e77a43bbbfcf2e1a63244314040b91b5d90f1b5..db0dc2a2418bab7c365f837302ba76a7cae41bb2 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
@@ -287,6 +287,7 @@ class MetaDataStorageHandler implements \SimpleSAML\Utils\ClearableState
                     }
                 }
                 // We found the entity id so remove it from the list that needs resolving
+                /** @psalm-suppress PossiblyInvalidArrayOffset */
                 unset($entityIds[array_search($key, $entityIds)]);
             }
             $result = array_merge($srcList, $result);
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
index 9f15a11f3ac27eab5a61242fc5a377096337ee36..33c3d8ba8a12c2f3575897097b414f5ecc200528 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
@@ -75,7 +75,7 @@ class MetaDataStorageHandlerFlatFile extends MetaDataStorageSource
      *     or null if we are unable to load metadata from the given file.
      * @throws \Exception If the metadata set cannot be loaded.
      */
-    private function load($set)
+    private function load(string $set)
     {
         $metadatasetfile = $this->directory . $set . '.php';
 
@@ -83,6 +83,7 @@ class MetaDataStorageHandlerFlatFile extends MetaDataStorageSource
             return null;
         }
 
+        /** @psalm-var mixed $metadata   We cannot be sure what the include below will do with this var */
         $metadata = [];
 
         include($metadatasetfile);
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
index c6f6c3aed44185f3d7d75ea1906af669f7883abf..ea1d5ee8b2bff7f142954ec178bc7298fbde0787 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
@@ -79,10 +79,8 @@ class MetaDataStorageHandlerPdo extends MetaDataStorageSource
      * @throws \Exception If a database error occurs.
      * @throws \SimpleSAML\Error\Exception If the metadata can be retrieved from the database, but cannot be decoded.
      */
-    private function load($set)
+    private function load(string $set)
     {
-        assert(is_string($set));
-
         $tableName = $this->getTableName($set);
 
         if (!in_array($set, $this->supportedSets, true)) {
@@ -275,10 +273,8 @@ class MetaDataStorageHandlerPdo extends MetaDataStorageSource
      *
      * @return string Replaced table name
      */
-    private function getTableName($table)
+    private function getTableName(string $table): string
     {
-        assert(is_string($table));
-
         return $this->db->applyPrefix(str_replace("-", "_", $this->tablePrefix . $table));
     }
 
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
index 85c2440d152e4014c1b892c2e8f14c33783f841f..f168c04aa5f664febdd48479fc1712727c3e3146 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
@@ -62,11 +62,8 @@ class MetaDataStorageHandlerSerialize extends MetaDataStorageSource
      *
      * @return string The path to the metadata file.
      */
-    private function getMetadataPath($entityId, $set)
+    private function getMetadataPath(string $entityId, string $set): string
     {
-        assert(is_string($entityId));
-        assert(is_string($set));
-
         return $this->directory . '/' . rawurlencode($set) . '/' . rawurlencode($entityId) . self::EXTENSION;
     }
 
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
index 5ba3fd19ffba28a9a71c49f8c5d9c2ffba7254c6..04f188435ed83a9a2103894fe1df483f9c8b7b92 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
@@ -298,7 +298,6 @@ abstract class MetaDataStorageSource
     protected function lookupIndexFromEntityId($entityId, array $metadataSet)
     {
         assert(is_string($entityId));
-        assert(is_array($metadataSet));
 
         // check for hostname
         $currentHost = Utils\HTTP::getSelfHost(); // sp.example.org
@@ -324,10 +323,8 @@ abstract class MetaDataStorageSource
      * @throws \Exception
      * @return string
      */
-    private function getDynamicHostedUrl($set)
+    private function getDynamicHostedUrl(string $set): string
     {
-        assert(is_string($set));
-
         // get the configuration
         $baseUrl = Utils\HTTP::getBaseURL();
 
@@ -362,7 +359,6 @@ abstract class MetaDataStorageSource
     {
         assert(is_string($metadataSet));
         assert(is_string($entityId));
-        assert(is_array($metadataEntry));
 
         $modifiedMetadataEntry = $metadataEntry;
 
diff --git a/lib/SimpleSAML/Metadata/SAMLBuilder.php b/lib/SimpleSAML/Metadata/SAMLBuilder.php
index 0752591bebfa117a5f34a071082e58966696e0e7..84ccdb6fb5b16ee96101aa14b0df90cabc5aa1f7 100644
--- a/lib/SimpleSAML/Metadata/SAMLBuilder.php
+++ b/lib/SimpleSAML/Metadata/SAMLBuilder.php
@@ -86,7 +86,7 @@ class SAMLBuilder
      * @param array $metadata
      * @return void
      */
-    private function setExpiration($metadata)
+    private function setExpiration(array $metadata)
     {
         if (array_key_exists('expire', $metadata)) {
             if ($metadata['expire'] - time() < $this->maxDuration) {
@@ -363,10 +363,8 @@ class SAMLBuilder
      * @return array An array of endpoint objects,
      *     either \SAML2\XML\md\EndpointType or \SAML2\XML\md\IndexedEndpointType.
      */
-    private static function createEndpoints(array $endpoints, $indexed)
+    private static function createEndpoints(array $endpoints, bool $indexed): array
     {
-        assert(is_bool($indexed));
-
         $ret = [];
 
         foreach ($endpoints as &$ep) {
@@ -695,7 +693,6 @@ class SAMLBuilder
      */
     public function addAttributeAuthority(array $metadata)
     {
-        assert(is_array($metadata));
         assert(isset($metadata['entityid']));
         assert(isset($metadata['metadata-set']));
 
@@ -791,10 +788,9 @@ class SAMLBuilder
      * @param string                      $x509data The certificate data.
      * @return void
      */
-    private function addX509KeyDescriptor(RoleDescriptor $rd, $use, $x509data)
+    private function addX509KeyDescriptor(RoleDescriptor $rd, string $use, string $x509data)
     {
         assert(in_array($use, ['encryption', 'signing'], true));
-        assert(is_string($x509data));
 
         $keyDescriptor = \SAML2\Utils::createKeyDescriptor($x509data);
         $keyDescriptor->setUse($use);
diff --git a/lib/SimpleSAML/Metadata/SAMLParser.php b/lib/SimpleSAML/Metadata/SAMLParser.php
index 2c8fb8fb9debf002256cd3e58930faf26f4cbdc7..9e154c438f609d2aa3c4fdc691e4adbfb1b3ae7c 100644
--- a/lib/SimpleSAML/Metadata/SAMLParser.php
+++ b/lib/SimpleSAML/Metadata/SAMLParser.php
@@ -3,10 +3,12 @@
 namespace SimpleSAML\Metadata;
 
 use DOMDocument;
+use DOMElement;
 use RobRichards\XMLSecLibs\XMLSecurityDSig;
 use RobRichards\XMLSecLibs\XMLSecurityKey;
 use SAML2\Constants;
 use SAML2\DOMDocumentFactory;
+use SAML2\SignedElementHelper;
 use SAML2\XML\Chunk;
 use SAML2\XML\ds\X509Certificate;
 use SAML2\XML\ds\X509Data;
@@ -183,12 +185,10 @@ class SAMLParser
      */
     private function __construct(
         EntityDescriptor $entityElement,
-        $maxExpireTime,
+        int $maxExpireTime = null,
         array $validators = [],
         array $parentExtensions = []
     ) {
-        assert($maxExpireTime === null || is_int($maxExpireTime));
-
         $this->spDescriptors = [];
         $this->idpDescriptors = [];
 
@@ -372,7 +372,7 @@ class SAMLParser
      *     be the entity id.
      * @throws \Exception if the document is empty or the root is an unexpected node.
      */
-    public static function parseDescriptorsElement(\DOMElement $element = null)
+    public static function parseDescriptorsElement(DOMElement $element = null)
     {
         if ($element === null) {
             throw new \Exception('Document was empty.');
@@ -398,13 +398,11 @@ class SAMLParser
      * @return SAMLParser[] Array of SAMLParser instances.
      */
     private static function processDescriptorsElement(
-        $element,
-        $maxExpireTime = null,
+        SignedElementHelper $element,
+        int $maxExpireTime = null,
         array $validators = [],
         array $parentExtensions = []
     ) {
-        assert($maxExpireTime === null || is_int($maxExpireTime));
-
         if ($element instanceof EntityDescriptor) {
             $ret = new SAMLParser($element, $maxExpireTime, $validators, $parentExtensions);
             $ret = [$ret->getEntityId() => $ret];
@@ -440,7 +438,7 @@ class SAMLParser
      * @return int|null The unix timestamp for when the element should expire. Will be NULL if no
      *             limit is set for the element.
      */
-    private static function getExpireTime($element, $maxExpireTime)
+    private static function getExpireTime($element, int $maxExpireTime = null)
     {
         // validUntil may be null
         $expire = $element->getValidUntil();
@@ -467,7 +465,7 @@ class SAMLParser
     /**
      * @return array
      */
-    private function getMetadataCommon()
+    private function getMetadataCommon(): array
     {
         $ret = [];
         $ret['entityid'] = $this->entityId;
@@ -865,10 +863,8 @@ class SAMLParser
      *
      * @return array An associative array with metadata we have extracted from this element.
      */
-    private static function parseRoleDescriptorType(RoleDescriptor $element, $expireTime)
+    private static function parseRoleDescriptorType(RoleDescriptor $element, int $expireTime = null)
     {
-        assert($expireTime === null || is_int($expireTime));
-
         $ret = [];
 
         $expireTime = self::getExpireTime($element, $expireTime);
@@ -916,10 +912,8 @@ class SAMLParser
      *
      * @return array An associative array with metadata we have extracted from this element.
      */
-    private static function parseSSODescriptor(SSODescriptorType $element, $expireTime)
+    private static function parseSSODescriptor(SSODescriptorType $element, int $expireTime = null): array
     {
-        assert($expireTime === null || is_int($expireTime));
-
         $sd = self::parseRoleDescriptorType($element, $expireTime);
 
         // find all SingleLogoutService elements
@@ -944,10 +938,8 @@ class SAMLParser
      *                             NULL if unknown.
      * @return void
      */
-    private function processSPSSODescriptor(SPSSODescriptor $element, $expireTime)
+    private function processSPSSODescriptor(SPSSODescriptor $element, int $expireTime = null)
     {
-        assert($expireTime === null || is_int($expireTime));
-
         $sp = self::parseSSODescriptor($element, $expireTime);
 
         // find all AssertionConsumerService elements
@@ -981,10 +973,8 @@ class SAMLParser
      *                             NULL if unknown.
      * @return void
      */
-    private function processIDPSSODescriptor(IDPSSODescriptor $element, $expireTime)
+    private function processIDPSSODescriptor(IDPSSODescriptor $element, int $expireTime = null)
     {
-        assert($expireTime === null || is_int($expireTime));
-
         $idp = self::parseSSODescriptor($element, $expireTime);
 
         // find all SingleSignOnService elements
@@ -1035,7 +1025,7 @@ class SAMLParser
      *
      * @return array An associative array with the extensions parsed.
      */
-    private static function processExtensions($element, $parentExtensions = [])
+    private static function processExtensions($element, array $parentExtensions = []): array
     {
         $ret = [
             'scope'            => [],
@@ -1086,6 +1076,7 @@ class SAMLParser
                         // only saml:Attribute are currently supported here. The specifications also allows
                         // saml:Assertions, which more complex processing
                         if ($attr instanceof Attribute) {
+                            /** @psalm-var string|null $attrName   Remove for SSP 2.0 */
                             $attrName = $attr->getName();
                             $attrNameFormat = $attr->getNameFormat();
                             $attrValue = $attr->getAttributeValue();
@@ -1097,7 +1088,7 @@ class SAMLParser
                             // attribute names that is not URI is prefixed as this: '{nameformat}name'
                             $name = $attrName;
                             if ($attrNameFormat === null) {
-                                $name = '{' . Constants::NAMEFORMAT_UNSPECIFIED . '}' . $attr->getName();
+                                $name = '{' . Constants::NAMEFORMAT_UNSPECIFIED . '}' . $attrName;
                             } elseif ($attrNameFormat !== Constants::NAMEFORMAT_URI) {
                                 $name = '{' . $attrNameFormat . '}' . $attrName;
                             }
@@ -1123,6 +1114,7 @@ class SAMLParser
 
                     foreach ($e->getKeywords() as $uiItem) {
                         $keywords = $uiItem->getKeywords();
+                        /** @psalm-var string|null $language */
                         $language = $uiItem->getLanguage();
                         if (($keywords === []) || ($language === null)) {
                             continue;
@@ -1130,6 +1122,7 @@ class SAMLParser
                         $ret['UIInfo']['Keywords'][$language] = $keywords;
                     }
                     foreach ($e->getLogo() as $uiItem) {
+                        /** @psalm-suppress TypeDoesNotContainNull  Remove in SSP 2.0 */
                         if (
                             !($uiItem instanceof Logo)
                             || ($uiItem->getUrl() === null)
@@ -1169,7 +1162,7 @@ class SAMLParser
 
                 $name = $attribute->getAttribute('Name');
                 $values = array_map(
-                    ['\SimpleSAML\Utils\XML', 'getDOMText'],
+                    '\SimpleSAML\Utils\XML::getDOMText',
                     Utils\XML::getDOMChildren($attribute, 'AttributeValue', '@saml2')
                 );
 
@@ -1240,10 +1233,8 @@ class SAMLParser
      * @param array $sp The array with the SP's metadata.
      * @return void
      */
-    private static function parseAttributeConsumerService(AttributeConsumingService $element, &$sp)
+    private static function parseAttributeConsumerService(AttributeConsumingService $element, array &$sp)
     {
-        assert(is_array($sp));
-
         $sp['name'] = $element->getServiceName();
         $sp['description'] = $element->getServiceDescription();
 
@@ -1299,7 +1290,7 @@ class SAMLParser
      *
      * @return array An associative array with the data we have extracted from the element.
      */
-    private static function parseGenericEndpoint(EndpointType $element)
+    private static function parseGenericEndpoint(EndpointType $element): array
     {
         $ep = [];
 
@@ -1329,9 +1320,9 @@ class SAMLParser
      *
      * @return array Array of parsed endpoints.
      */
-    private static function extractEndpoints(array $endpoints)
+    private static function extractEndpoints(array $endpoints): array
     {
-        return array_map(['self', 'parseGenericEndpoint'], $endpoints);
+        return array_map('self::parseGenericEndpoint', $endpoints);
     }
 
 
@@ -1366,6 +1357,7 @@ class SAMLParser
 
         $keyInfo = $kd->getKeyInfo();
 
+        /** @psalm-suppress PossiblyNullReference  This will be fixed in saml2 5.0 */
         foreach ($keyInfo->getInfo() as $i) {
             if ($i instanceof X509Data) {
                 foreach ($i->getData() as $d) {
@@ -1389,10 +1381,8 @@ class SAMLParser
      *
      * @return array with SP descriptors which supports one of the given protocols.
      */
-    private function getSPDescriptors($protocols)
+    private function getSPDescriptors(array $protocols): array
     {
-        assert(is_array($protocols));
-
         $ret = [];
 
         foreach ($this->spDescriptors as $spd) {
@@ -1413,10 +1403,8 @@ class SAMLParser
      *
      * @return array with IdP descriptors which supports one of the given protocols.
      */
-    private function getIdPDescriptors($protocols)
+    private function getIdPDescriptors(array $protocols): array
     {
-        assert(is_array($protocols));
-
         $ret = [];
 
         foreach ($this->idpDescriptors as $idpd) {
@@ -1441,10 +1429,8 @@ class SAMLParser
      * @return \SAML2\XML\md\EntityDescriptor The \DOMEntity which represents the EntityDescriptor.
      * @throws \Exception If the document is empty or the first element is not an EntityDescriptor element.
      */
-    private static function findEntityDescriptor($doc)
+    private static function findEntityDescriptor(DOMDocument $doc): EntityDescriptor
     {
-        assert($doc instanceof DOMDocument);
-
         // find the EntityDescriptor DOMElement. This should be the first (and only) child of the DOMDocument
         $ed = $doc->documentElement;
 
@@ -1500,7 +1486,7 @@ class SAMLParser
      * @throws \UnexpectedValueException
      * @return string
      */
-    private function computeFingerprint($algorithm, $data)
+    private function computeFingerprint(string $algorithm, string $data): string
     {
         switch ($algorithm) {
             case XMLSecurityDSig::SHA1:
diff --git a/lib/SimpleSAML/Metadata/Signer.php b/lib/SimpleSAML/Metadata/Signer.php
index 7a80bdd139df7abec27e8417c8b377394821a7de..37b0cc699b1ec56ec37ee14f450ffce003eec32e 100644
--- a/lib/SimpleSAML/Metadata/Signer.php
+++ b/lib/SimpleSAML/Metadata/Signer.php
@@ -30,7 +30,7 @@ class Signer
      * @return array An associative array with the keys 'privatekey', 'certificate', and optionally 'privatekey_pass'.
      * @throws \Exception If the key and certificate used to sign is unknown.
      */
-    private static function findKeyCert($config, $entityMetadata, $type)
+    private static function findKeyCert(Configuration $config, array $entityMetadata, string $type): array
     {
         // first we look for metadata.privatekey and metadata.certificate in the metadata
         if (
@@ -130,7 +130,7 @@ class Signer
      * @return boolean True if metadata signing is enabled, false otherwise.
      * @throws \Exception If the value of the 'metadata.sign.enable' option is not a boolean.
      */
-    private static function isMetadataSigningEnabled($config, $entityMetadata, $type)
+    private static function isMetadataSigningEnabled(Configuration $config, array $entityMetadata, string $type): bool
     {
         // first check the metadata for the entity
         if (array_key_exists('metadata.sign.enable', $entityMetadata)) {
@@ -166,7 +166,7 @@ class Signer
      *
      * @throws \SimpleSAML\Error\CriticalConfigurationError
      */
-    private static function getMetadataSigningAlgorithm($config, $entityMetadata, $type)
+    private static function getMetadataSigningAlgorithm(Configuration $config, array $entityMetadata, string $type): array
     {
         // configure the algorithm to use
         if (array_key_exists('metadata.sign.algorithm', $entityMetadata)) {
diff --git a/lib/SimpleSAML/Metadata/Sources/MDQ.php b/lib/SimpleSAML/Metadata/Sources/MDQ.php
index 3b8431a14d82712f36d987048da20bc909e97aa1..9694d102bf0270e05b9154d1233c661824d36e48 100644
--- a/lib/SimpleSAML/Metadata/Sources/MDQ.php
+++ b/lib/SimpleSAML/Metadata/Sources/MDQ.php
@@ -154,11 +154,8 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
      *                     if the entity could not be found.
      * @throws \Exception If an error occurs while loading metadata from cache.
      */
-    private function getFromCache($set, $entityId)
+    private function getFromCache(string $set, string $entityId)
     {
-        assert(is_string($set));
-        assert(is_string($entityId));
-
         if (empty($this->cacheDir)) {
             return null;
         }
@@ -214,12 +211,8 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
      * @throws \Exception If metadata cannot be written to cache.
      * @return void
      */
-    private function writeToCache($set, $entityId, $data)
+    private function writeToCache(string $set, string $entityId, array $data)
     {
-        assert(is_string($set));
-        assert(is_string($entityId));
-        assert(is_array($data));
-
         if (empty($this->cacheDir)) {
             return;
         }
@@ -242,10 +235,8 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
      * @return array|NULL  The associative array with the metadata, or NULL if no metadata for
      *                     the given set was found.
      */
-    private static function getParsedSet(SAMLParser $entity, $set)
+    private static function getParsedSet(SAMLParser $entity, string $set)
     {
-        assert(is_string($set));
-
         switch ($set) {
             case 'saml20-idp-remote':
                 return $entity->getMetadata20IdP();
diff --git a/lib/SimpleSAML/Module.php b/lib/SimpleSAML/Module.php
index 33470a77d27b822dd543f3c8d77ea8d9f4e4e832..10ad2a437facf074a99ef2b63600d4eb71639a0e 100644
--- a/lib/SimpleSAML/Module.php
+++ b/lib/SimpleSAML/Module.php
@@ -305,7 +305,7 @@ class Module
      * @param array $mod_config
      * @return bool
      */
-    private static function isModuleEnabledWithConf($module, $mod_config)
+    private static function isModuleEnabledWithConf(string $module, array $mod_config): bool
     {
         if (isset(self::$module_info[$module]['enabled'])) {
             return self::$module_info[$module]['enabled'];
@@ -539,7 +539,7 @@ class Module
                 self::$module_info[$module]['hooks'] = self::getModuleHooks($module);
             }
 
-            if (!isset(self::$module_info[$module]['hooks'][$hook])) {
+            if (!isset(self::$module_info[$module]['hooks'][$hook]) || !empty(self::$module_info[$module]['hooks'][$hook])) {
                 continue;
             }
 
diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php
index 8b54ae5f298d69b1f184762728de4e4ba3bddffe..7cef0c647ccd74bf693e695dad724b77eb081aa7 100644
--- a/lib/SimpleSAML/Session.php
+++ b/lib/SimpleSAML/Session.php
@@ -145,7 +145,7 @@ class Session implements \Serializable, Utils\ClearableState
      *
      * @param boolean $transient Whether to create a transient session or not.
      */
-    private function __construct($transient = false)
+    private function __construct(bool $transient = false)
     {
         $this->setConfiguration(Configuration::getInstance());
 
@@ -393,7 +393,7 @@ class Session implements \Serializable, Utils\ClearableState
      * @param Session $session The session to load.
      * @return Session The session we just loaded, just for convenience.
      */
-    private static function load(Session $session)
+    private static function load(Session $session): Session
     {
         Logger::setTrackId($session->getTrackID());
         self::$instance = $session;
@@ -636,8 +636,11 @@ class Session implements \Serializable, Utils\ClearableState
                     continue;
                 }
 
+                /** @psalm-var \DOMNode $node   We made sure value has at least 1 item in the check above */
+                $node = $value->item(0);
+
                 // create an AttributeValue object and save it to 'RawAttributes', using same attribute name and index
-                $attrval = new AttributeValue($value->item(0)->parentNode);
+                $attrval = new AttributeValue($node->parentNode);
                 $data['RawAttributes'][$attribute][$idx] = $attrval;
             }
         }
@@ -711,9 +714,8 @@ class Session implements \Serializable, Utils\ClearableState
      *
      * @throws \Exception If the handler is not a valid function or method.
      */
-    private function callLogoutHandlers($authority)
+    private function callLogoutHandlers(string $authority)
     {
-        assert(is_string($authority));
         assert(isset($this->authData[$authority]));
 
         if (empty($this->authData[$authority]['LogoutHandlers'])) {
diff --git a/lib/SimpleSAML/SessionHandlerCookie.php b/lib/SimpleSAML/SessionHandlerCookie.php
index 556105f2e830f2d6986cf8514e934cc113b60bf8..4b90a69f97a6768e020d8e6ac542f8214639d305 100644
--- a/lib/SimpleSAML/SessionHandlerCookie.php
+++ b/lib/SimpleSAML/SessionHandlerCookie.php
@@ -101,7 +101,7 @@ abstract class SessionHandlerCookie extends SessionHandler
      *
      * @return string A random session id.
      */
-    private static function createSessionID()
+    private static function createSessionID(): string
     {
         return bin2hex(openssl_random_pseudo_bytes(16));
     }
@@ -115,11 +115,8 @@ abstract class SessionHandlerCookie extends SessionHandler
      *
      * @return boolean True if this session ID is valid, false otherwise.
      */
-    private static function isValidSessionID($session_id)
+    private static function isValidSessionID(string $session_id): bool
     {
-        if (!is_string($session_id)) {
-            return false;
-        }
 
         if (strlen($session_id) != 32) {
             return false;
diff --git a/lib/SimpleSAML/Stats.php b/lib/SimpleSAML/Stats.php
index 25ff214ac09394780488485936aa0deb1da83e41..7a65c9b1a95bcbcd6e9a94322e5b0bb0f6036011 100644
--- a/lib/SimpleSAML/Stats.php
+++ b/lib/SimpleSAML/Stats.php
@@ -35,7 +35,7 @@ class Stats
      *
      * @return mixed A new instance of the configured class.
      */
-    private static function createOutput(\SimpleSAML\Configuration $config)
+    private static function createOutput(Configuration $config)
     {
         $cls = $config->getString('class');
         $cls = Module::resolveClass($cls, 'Stats\Output', '\SimpleSAML\Stats\Output');
@@ -52,7 +52,6 @@ class Stats
      */
     private static function initOutputs()
     {
-
         $config = Configuration::getInstance();
         $outputCfgs = $config->getConfigList('statistics.out');
 
diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php
index a3f5b23727607d44c735d375adc23dcdc9186701..ba5af37a234e339dce0dfc7ab8c088b5e05a6ec6 100644
--- a/lib/SimpleSAML/Utils/Crypto.php
+++ b/lib/SimpleSAML/Utils/Crypto.php
@@ -25,13 +25,8 @@ class Crypto
      *
      * @see \SimpleSAML\Utils\Crypto::aesDecrypt()
      */
-    private static function aesDecryptInternal($ciphertext, $secret)
+    private static function aesDecryptInternal(string $ciphertext, string $secret): string
     {
-        if (!is_string($ciphertext)) {
-            throw new \InvalidArgumentException(
-                'Input parameter "$ciphertext" must be a string with more than 48 characters.'
-            );
-        }
         /** @var int $len */
         $len = mb_strlen($ciphertext, '8bit');
         if ($len < 48) {
@@ -99,12 +94,8 @@ class Crypto
      *
      * @see \SimpleSAML\Utils\Crypto::aesEncrypt()
      */
-    private static function aesEncryptInternal($data, $secret)
+    private static function aesEncryptInternal(string $data, string $secret): string
     {
-        if (!is_string($data)) {
-            throw new \InvalidArgumentException('Input parameter "$data" must be a string.');
-        }
-
         if (!function_exists("openssl_encrypt")) {
             throw new Error\Exception('The openssl PHP module is not loaded.');
         }
diff --git a/lib/SimpleSAML/Utils/EMail.php b/lib/SimpleSAML/Utils/EMail.php
index 9ed3692794af6358f5683c11508f2bbe258931a8..f1595c972b9c8eca7e7fdb46973f01999210270c 100644
--- a/lib/SimpleSAML/Utils/EMail.php
+++ b/lib/SimpleSAML/Utils/EMail.php
@@ -157,8 +157,6 @@ class EMail
     public function setTransportMethod($transportMethod, array $transportOptions = [])
     {
         assert(is_string($transportMethod));
-        assert(is_array($transportOptions));
-
 
         switch (strtolower($transportMethod)) {
             // smtp transport method
diff --git a/lib/SimpleSAML/Utils/HTTP.php b/lib/SimpleSAML/Utils/HTTP.php
index 016f09e0e0c8eaaf7f411343496af1a76a1d601d..b5ed7d1990a27b36f6d048d174cac0480fadf669 100644
--- a/lib/SimpleSAML/Utils/HTTP.php
+++ b/lib/SimpleSAML/Utils/HTTP.php
@@ -27,7 +27,7 @@ class HTTP
      *
      * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
      */
-    private static function getSecurePOSTRedirectURL($destination, $data)
+    private static function getSecurePOSTRedirectURL(string $destination, array $data): string
     {
         $session = Session::getSessionFromRequest();
         $id = self::savePOSTData($session, $destination, $data);
@@ -56,7 +56,7 @@ class HTTP
      *
      * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
      */
-    private static function getServerHost()
+    private static function getServerHost(): string
     {
         if (array_key_exists('HTTP_HOST', $_SERVER)) {
             $current = $_SERVER['HTTP_HOST'];
@@ -171,9 +171,9 @@ class HTTP
      * @author Mads Freek Petersen
      * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
      */
-    private static function redirect($url, $parameters = [])
+    private static function redirect(string $url, array $parameters = [])
     {
-        if (!is_string($url) || empty($url) || !is_array($parameters)) {
+        if (empty($url)) {
             throw new \InvalidArgumentException('Invalid input parameters.');
         }
         if (!self::isValidURL($url)) {
@@ -246,7 +246,7 @@ class HTTP
      * @author Andjelko Horvat
      * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
      */
-    private static function savePOSTData(Session $session, $destination, $data)
+    private static function savePOSTData(Session $session, string $destination, array $data): string
     {
         // generate a random ID to avoid replay attacks
         $id = Random::generateID();
@@ -1165,7 +1165,7 @@ class HTTP
             'lifetime' => 0,
             'expire'   => null,
             'path'     => '/',
-            'domain'   => null,
+            'domain'   => '',
             'secure'   => false,
             'httponly' => true,
             'raw'      => false,
diff --git a/lib/SimpleSAML/Utils/Net.php b/lib/SimpleSAML/Utils/Net.php
index 351a7cf6e03f5b8bd5b6daa9736addd5046b19c7..efff0756718b7a9a6576f7a8c64a21a98934ade6 100644
--- a/lib/SimpleSAML/Utils/Net.php
+++ b/lib/SimpleSAML/Utils/Net.php
@@ -73,8 +73,8 @@ class Net
 
             $ip_mask = ~((1 << (32 - $iteration_mask)) - 1);
 
-            $ip_net_mask = $ip_net[$i] & $ip_mask;
-            $ip_ip_mask = $ip_ip[$i] & $ip_mask;
+            $ip_net_mask = intval($ip_net[$i]) & $ip_mask;
+            $ip_ip_mask = intval($ip_ip[$i]) & $ip_mask;
 
             if ($ip_ip_mask != $ip_net_mask) {
                 return false;
diff --git a/lib/SimpleSAML/Utils/System.php b/lib/SimpleSAML/Utils/System.php
index 26b0e992f0afc6fcd9d03559571bfc04e850d21b..3864cf1bd3605856addea2559122971e53d3f98f 100644
--- a/lib/SimpleSAML/Utils/System.php
+++ b/lib/SimpleSAML/Utils/System.php
@@ -240,7 +240,7 @@ class System
      *
      * @return bool
      */
-    private static function pathContainsDriveLetter($path)
+    private static function pathContainsDriveLetter(string $path): bool
     {
         $letterAsciiValue = ord(strtoupper(substr($path, 0, 1)));
         return substr($path, 1, 1) === ':'
@@ -252,7 +252,7 @@ class System
      * @param string $path
      * @return bool
      */
-    private static function pathContainsStreamWrapper($path)
+    private static function pathContainsStreamWrapper(string $path): bool
     {
         return preg_match('/^[\w\d]*:\/{2}/', $path) === 1;
     }
diff --git a/lib/SimpleSAML/XHTML/IdPDisco.php b/lib/SimpleSAML/XHTML/IdPDisco.php
index 1b8f56cc6f3e54d6711aee9a4d102e1aa5cb1196..70f2d7adfdad1fe0ae2714eab3fd41613d810eef 100644
--- a/lib/SimpleSAML/XHTML/IdPDisco.php
+++ b/lib/SimpleSAML/XHTML/IdPDisco.php
@@ -651,7 +651,7 @@ class IdPDisco
      * @param string $language
      * @return string|null
      */
-    private function getEntityDisplayName(array $idpData, $language)
+    private function getEntityDisplayName(array $idpData, string $language)
     {
         if (isset($idpData['UIInfo']['DisplayName'][$language])) {
             return $idpData['UIInfo']['DisplayName'][$language];
diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php
index 24f46bbc912332e2036fd474a4a52fa750f0f01e..4b14817b1d1187e180c905e19e2a4203465f6795 100644
--- a/lib/SimpleSAML/XHTML/Template.php
+++ b/lib/SimpleSAML/XHTML/Template.php
@@ -213,7 +213,7 @@ class Template extends Response
      * @param string $templateName The template name to normalize.
      * @return string The filename we need to look for.
      */
-    private function normalizeTemplateName($templateName)
+    private function normalizeTemplateName(string $templateName): string
     {
         if (strripos($templateName, '.twig')) {
             return $templateName;
@@ -240,7 +240,7 @@ class Template extends Response
      * @return TemplateLoader The twig template loader or false if the template does not exist.
      * @throws \Twig\Error\LoaderError In case a failure occurs.
      */
-    private function setupTwigTemplatepaths()
+    private function setupTwigTemplatepaths(): TemplateLoader
     {
         $filename = $this->normalizeTemplateName($this->template);
 
@@ -281,7 +281,7 @@ class Template extends Response
      * @return \Twig\Environment
      * @throws \Exception if the template does not exist
      */
-    private function setupTwig()
+    private function setupTwig(): \Twig\Environment
     {
         $auto_reload = $this->configuration->getBoolean('template.auto_reload', true);
         $cache = $this->configuration->getString('template.cache', false);
@@ -358,7 +358,7 @@ class Template extends Response
      *
      * @return array An array of module => templatedir lookups.
      */
-    private function findThemeTemplateDirs()
+    private function findThemeTemplateDirs(): array
     {
         if (!isset($this->theme['module'])) {
             // no module involved
@@ -398,7 +398,7 @@ class Template extends Response
      *
      * @throws \InvalidArgumentException If the module is not enabled or it has no templates directory.
      */
-    private function getModuleTemplateDir($module)
+    private function getModuleTemplateDir(string $module): string
     {
         if (!Module::isModuleEnabled($module)) {
             throw new \InvalidArgumentException('The module \'' . $module . '\' is not enabled.');
@@ -555,7 +555,7 @@ class Template extends Response
      *
      * @return array An array with the name of the module and template
      */
-    private function findModuleAndTemplateName($template)
+    private function findModuleAndTemplateName(string $template): array
     {
         $tmp = explode(':', $template, 2);
         return (count($tmp) === 2) ? [$tmp[0], $tmp[1]] : [null, $tmp[0]];
@@ -578,9 +578,8 @@ class Template extends Response
      *
      * @throws \Exception If the template file couldn't be found.
      */
-    private function findTemplatePath($template, $throw_exception = true)
+    private function findTemplatePath(string $template, bool $throw_exception = true)
     {
-        assert(is_string($template));
         $extensions = ['.tpl.php', '.php'];
 
         list($templateModule, $templateName) = $this->findModuleAndTemplateName($template);
@@ -791,7 +790,7 @@ class Template extends Response
      * @param string $file
      * @return void
      */
-    private function includeAtTemplateBase($file)
+    private function includeAtTemplateBase(string $file)
     {
         $data = $this->data;
 
@@ -837,7 +836,7 @@ class Template extends Response
      *
      * @return bool
      */
-    private function isLanguageRTL()
+    private function isLanguageRTL(): bool
     {
         return $this->translator->getLanguage()->isLanguageRTL();
     }
diff --git a/lib/SimpleSAML/XML/Shib13/AuthnResponse.php b/lib/SimpleSAML/XML/Shib13/AuthnResponse.php
index f50045d8f6f5a0f678ba63f8f035c49a15f504d3..9492626142a9bd96c1954bbecfc759921e4c1814 100644
--- a/lib/SimpleSAML/XML/Shib13/AuthnResponse.php
+++ b/lib/SimpleSAML/XML/Shib13/AuthnResponse.php
@@ -12,6 +12,7 @@ namespace SimpleSAML\XML\Shib13;
 
 use DOMDocument;
 use DOMNode;
+use DOMNodeList;
 use DOMXpath;
 use SAML2\DOMDocumentFactory;
 use SimpleSAML\Configuration;
@@ -160,7 +161,7 @@ class AuthnResponse
      * @param \DOMElement|\SimpleXMLElement $node Node to be validated.
      * @return bool TRUE if the node is validated or FALSE if not.
      */
-    private function isNodeValidated($node)
+    private function isNodeValidated($node): bool
     {
         if ($this->messageValidated) {
             // This message was validated externally
@@ -190,9 +191,8 @@ class AuthnResponse
      *                        then the query will be relative to the root of the response.
      * @return \DOMNodeList
      */
-    private function doXPathQuery($query, $node = null)
+    private function doXPathQuery(string $query, DOMNode $node = null): DOMNodeList
     {
-        assert(is_string($query));
         assert($this->dom instanceof DOMDocument);
 
         if ($node === null) {
@@ -252,7 +252,7 @@ class AuthnResponse
             }
 
             $conditions = $this->doXPathQuery('shib:Conditions', $assertion);
-            if ($conditions && $conditions->length > 0) {
+            if ($conditions->length > 0) {
                 $condition = $conditions->item(0);
 
                 $start = $condition->getAttribute('NotBefore');
@@ -453,13 +453,8 @@ class AuthnResponse
      * @param array $scopedAttributes  Array of attributes names which are scoped.
      * @return string  The attribute encoded as an XML-string.
      */
-    private function encAttribute($name, $values, $base64, $scopedAttributes)
+    private function encAttribute(string $name, array $values, bool $base64, array $scopedAttributes): string
     {
-        assert(is_string($name));
-        assert(is_array($values));
-        assert(is_bool($base64));
-        assert(is_array($scopedAttributes));
-
         if (in_array($name, $scopedAttributes, true)) {
             $scoped = true;
         } else {
diff --git a/lib/SimpleSAML/XML/Validator.php b/lib/SimpleSAML/XML/Validator.php
index 0e201c88984a74b077aab0a2c0ddc6a42e1e7518..55ddd456aaa02690e1761369a23bb2a0c0f32245 100644
--- a/lib/SimpleSAML/XML/Validator.php
+++ b/lib/SimpleSAML/XML/Validator.php
@@ -159,10 +159,8 @@ class Validator
      * @return string|null  The fingerprint as a 40-character lowercase hexadecimal number. NULL is returned if the
      *                 argument isn't an X509 certificate.
      */
-    private static function calculateX509Fingerprint($x509cert)
+    private static function calculateX509Fingerprint(string $x509cert)
     {
-        assert(is_string($x509cert));
-
         $lines = explode("\n", $x509cert);
 
         $data = '';
@@ -203,11 +201,8 @@ class Validator
      * @throws \Exception
      * @return void
      */
-    private static function validateCertificateFingerprint($certificate, $fingerprints)
+    private static function validateCertificateFingerprint(string $certificate, array $fingerprints)
     {
-        assert(is_string($certificate));
-        assert(is_array($fingerprints));
-
         $certFingerprint = self::calculateX509Fingerprint($certificate);
         if ($certFingerprint === null) {
             // Couldn't calculate fingerprint from X509 certificate. Should not happen.
@@ -323,11 +318,8 @@ class Validator
      * @return boolean|string TRUE on success, or a string with error messages if it failed.
      * @deprecated
      */
-    private static function validateCABuiltIn($certificate, $caFile)
+    private static function validateCABuiltIn(string $certificate, string $caFile)
     {
-        assert(is_string($certificate));
-        assert(is_string($caFile));
-
         // Clear openssl errors
         while (openssl_error_string() !== false) {
         }
@@ -361,11 +353,8 @@ class Validator
      * @throws \Exception
      * @deprecated
      */
-    private static function validateCAExec($certificate, $caFile)
+    private static function validateCAExec(string $certificate, string $caFile)
     {
-        assert(is_string($certificate));
-        assert(is_string($caFile));
-
         $command = [
             'openssl', 'verify',
             '-CAfile', $caFile,
diff --git a/lib/_autoload_modules.php b/lib/_autoload_modules.php
index 5bc55bfdafb64cd929d3c284ee82cb1cd1a96e88..6d43e555b46c2f4df59f277e056953f690a51c54 100644
--- a/lib/_autoload_modules.php
+++ b/lib/_autoload_modules.php
@@ -17,7 +17,7 @@
  * with 'SimpleSAML_'.
  * @deprecated This function will be removed in SSP 2.0.
  */
-function temporaryLoader($class)
+function temporaryLoader(string $class)
 {
     // handle the upgrade to the latest version of XMLSecLibs using namespaces
     if (strstr($class, 'XMLSec') && !strstr($class, '\\RobRichards\\XMLSecLibs\\')) {
@@ -83,7 +83,7 @@ function temporaryLoader($class)
  *
  * TODO: this autoloader should be removed once everything has been migrated to namespaces.
  */
-function sspmodAutoloadPSR0($className)
+function sspmodAutoloadPSR0(string $className)
 {
     $modulePrefixLength = strlen('sspmod_');
     $classPrefix = substr($className, 0, $modulePrefixLength);
@@ -141,7 +141,7 @@ function sspmodAutoloadPSR0($className)
  *
  * @param string $className Name of the class.
  */
-function sspmodAutoloadPSR4($className)
+function sspmodAutoloadPSR4(string $className)
 {
     $elements = explode('\\', $className);
     if ($elements[0] === '') {
diff --git a/modules/admin/lib/ConfigController.php b/modules/admin/lib/ConfigController.php
index d4764b58502c09e98d764bf9546288a7eba0c5e1..4f9ea216a6e6ef40c68b2f99db8824c60e95bfea 100644
--- a/modules/admin/lib/ConfigController.php
+++ b/modules/admin/lib/ConfigController.php
@@ -155,11 +155,11 @@ class ConfigController
                 'descr' => [
                     Translate::noop('PHP %minimum% or newer is needed. You are running: %current%'),
                     [
-                        '%minimum%' => '5.6',
+                        '%minimum%' => '7.0',
                         '%current%' => explode('-', phpversion())[0]
                     ]
                 ],
-                'enabled' => version_compare(phpversion(), '5.6', '>=')
+                'enabled' => version_compare(phpversion(), '7.0', '>=')
             ]
         ];
         $store = $this->config->getString('store.type', '');
@@ -389,7 +389,7 @@ class ConfigController
                     $response = curl_exec($ch);
 
                     if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) === 200) {
-                        /** @psalm-suppress InvalidScalarArgument */
+                        /** @psalm-var string $response */
                         $latest = json_decode($response, true);
                         $this->session->setData(self::LATEST_VERSION_STATE_KEY, 'version', $latest);
                     }
diff --git a/modules/admin/lib/FederationController.php b/modules/admin/lib/FederationController.php
index f167514972a4a4b3161d4fe2533c4a1162040db0..d9ceda7291d25baae3b533c36917a4404630804c 100644
--- a/modules/admin/lib/FederationController.php
+++ b/modules/admin/lib/FederationController.php
@@ -162,7 +162,7 @@ class FederationController
      * @return array
      * @throws \Exception
      */
-    private function getHostedIdP()
+    private function getHostedIdP(): array
     {
         $entities = [];
 
@@ -326,7 +326,7 @@ class FederationController
      * @return array
      * @throws \SimpleSAML\Error\Exception If OrganizationName is set for an SP instance but OrganizationURL is not.
      */
-    private function getHostedSP()
+    private function getHostedSP(): array
     {
         $entities = [];
 
diff --git a/modules/admin/lib/TestController.php b/modules/admin/lib/TestController.php
index b77da54f8f5bc7fc7ac2676b1300422de20738a2..62ecf008c0553f116bfb609910e6a0891fa3299a 100644
--- a/modules/admin/lib/TestController.php
+++ b/modules/admin/lib/TestController.php
@@ -25,7 +25,6 @@ use Webmozart\Assert\Assert;
  */
 class TestController
 {
-
     /** @var \SimpleSAML\Configuration */
     protected $config;
 
@@ -116,10 +115,12 @@ class TestController
      * @param \SAML2\XML\saml\NameID $nameId
      * @return string
      */
-    private function getNameIDHTML(Template $t, NameID $nameId)
+    private function getNameIDHTML(Template $t, NameID $nameId): string
     {
         $translator = $t->getTranslator();
         $result = '';
+
+        /** @psalm-suppress TypeDoesNotContainNull  Remove if-case in 2.0 */
         if ($nameId->getValue() === null) {
             $list = ["NameID" => [$translator->t('{status:subject_notset}')]];
             /** @var string $notset */
@@ -154,7 +155,7 @@ class TestController
      * @param string $nameParent
      * @return string
      */
-    private function getAttributesHTML(Template $t, $attributes, $nameParent)
+    private function getAttributesHTML(Template $t, array $attributes, string $nameParent): string
     {
         $alternate = ['pure-table-odd', 'pure-table-even'];
         $i = 0;
@@ -232,7 +233,7 @@ class TestController
      * @param array|string $attr
      * @return string
      */
-    private function presentList($attr)
+    private function presentList($attr): string
     {
         if (is_array($attr) && count($attr) > 1) {
             $str = '<ul>';
@@ -251,7 +252,7 @@ class TestController
      * @param array|string $attr
      * @return string
      */
-    private function presentAssoc($attr)
+    private function presentAssoc($attr): string
     {
         if (is_array($attr)) {
             $str = '<dl>';
@@ -271,7 +272,7 @@ class TestController
      * @param \SAML2\XML\saml\NameID $nameID
      * @return string
      */
-    private function presentEptid(Translate $t, NameID $nameID)
+    private function presentEptid(Translate $t, NameID $nameID): string
     {
         $eptid = [
             'NameID' => [$nameID->getValue()],
diff --git a/modules/core/hooks/hook_sanitycheck.php b/modules/core/hooks/hook_sanitycheck.php
index 7e401a0dc5e7abdfb0f8157419b51018d1c00fe1..730e91c6727a6d9316ac2ca14c78c5c856777534 100644
--- a/modules/core/hooks/hook_sanitycheck.php
+++ b/modules/core/hooks/hook_sanitycheck.php
@@ -26,7 +26,7 @@ function core_hook_sanitycheck(&$hookinfo)
         $hookinfo['info'][] = '[core] In config.php technicalcontact_email is set properly';
     }
 
-    if (version_compare(phpversion(), '5.6', '>=')) {
+    if (version_compare(phpversion(), '7.0', '>=')) {
         $hookinfo['info'][] = '[core] You are running a PHP version suitable for SimpleSAMLphp.';
     } else {
         $hookinfo['errors'][] = '[core] You are running an old PHP installation. ' .
diff --git a/modules/core/lib/ACL.php b/modules/core/lib/ACL.php
index dac755fe601e98912624f0379b0e5c86590daafd..e69b5ffc99cb102eb1c6262bfc741d6fc705d014 100644
--- a/modules/core/lib/ACL.php
+++ b/modules/core/lib/ACL.php
@@ -57,10 +57,8 @@ class ACL
      * @param string $id  The id of the access control list.
      * @return array  The access control list array.
      */
-    private static function getById($id)
+    private static function getById(string $id): array
     {
-        assert(is_string($id));
-
         $config = Configuration::getOptionalConfig('acl.php');
         if (!$config->hasValue($id)) {
             throw new Error\Exception('No ACL with id ' . var_export($id, true) . ' in config/acl.php.');
@@ -100,7 +98,7 @@ class ACL
      * @param array $rule  The rule we should check.
      * @return boolean  TRUE if the rule matches, FALSE if not.
      */
-    private static function match(array $attributes, array $rule)
+    private static function match(array $attributes, array $rule): bool
     {
         $op = array_shift($rule);
         if ($op === null) {
@@ -135,7 +133,7 @@ class ACL
      * @param array $rule  The rule we should check.
      * @return boolean  TRUE if the rule matches, FALSE if not.
      */
-    private static function opAnd($attributes, $rule)
+    private static function opAnd(array $attributes, array $rule): bool
     {
         foreach ($rule as $subRule) {
             if (!self::match($attributes, $subRule)) {
@@ -154,7 +152,7 @@ class ACL
      * @param array $rule  The rule we should check.
      * @return boolean  TRUE if the rule matches, FALSE if not.
      */
-    private static function opEquals($attributes, $rule)
+    private static function opEquals(array $attributes, array $rule): bool
     {
         $attributeName = array_shift($rule);
 
@@ -194,7 +192,7 @@ class ACL
      * @param array $rule  The rule we should check.
      * @return boolean  TRUE if the rule matches, FALSE if not.
      */
-    private static function opEqualsPreg($attributes, $rule)
+    private static function opEqualsPreg(array $attributes, array $rule): bool
     {
         $attributeName = array_shift($rule);
 
@@ -235,7 +233,7 @@ class ACL
      * @param array $rule  The rule we should check.
      * @return boolean  TRUE if the rule matches, FALSE if not.
      */
-    private static function opHas($attributes, $rule)
+    private static function opHas(array $attributes, array $rule): bool
     {
         $attributeName = array_shift($rule);
 
@@ -262,7 +260,7 @@ class ACL
      * @param array $rule  The rule we should check.
      * @return boolean  TRUE if the rule matches, FALSE if not.
      */
-    private static function opHasPreg($attributes, $rule)
+    private static function opHasPreg(array $attributes, array $rule): bool
     {
         $attributeName = array_shift($rule);
 
@@ -290,7 +288,7 @@ class ACL
      * @param array $rule  The rule we should check.
      * @return boolean  TRUE if the rule matches, FALSE if not.
      */
-    private static function opOr($attributes, $rule)
+    private static function opOr(array $attributes, array $rule): bool
     {
         foreach ($rule as $subRule) {
             if (self::match($attributes, $subRule)) {
diff --git a/modules/core/lib/Auth/Process/AttributeLimit.php b/modules/core/lib/Auth/Process/AttributeLimit.php
index 2767a9555d0ee7ff571ee4f23b44113d49821cd8..87db3155c348111f3babf21b9c8d87a4b5308e8a 100644
--- a/modules/core/lib/Auth/Process/AttributeLimit.php
+++ b/modules/core/lib/Auth/Process/AttributeLimit.php
@@ -139,7 +139,7 @@ class AttributeLimit extends \SimpleSAML\Auth\ProcessingFilter
      * @param array $allowedConfigValues The allowed values, and possibly configuration options.
      * @return array The filtered values
      */
-    private function filterAttributeValues(array $values, array $allowedConfigValues)
+    private function filterAttributeValues(array $values, array $allowedConfigValues): array
     {
         if (array_key_exists('regex', $allowedConfigValues) && $allowedConfigValues['regex'] === true) {
             $matchedValues = [];
diff --git a/modules/core/lib/Auth/Process/AttributeMap.php b/modules/core/lib/Auth/Process/AttributeMap.php
index 7efdd2f73e77c99e831b0d48c6eaed64d84737d0..317a6d4cc2e685fc5082daac10706ec555bc50c2 100644
--- a/modules/core/lib/Auth/Process/AttributeMap.php
+++ b/modules/core/lib/Auth/Process/AttributeMap.php
@@ -79,7 +79,7 @@ class AttributeMap extends \SimpleSAML\Auth\ProcessingFilter
      * @throws \Exception If the filter could not load the requested attribute map file.
      * @return void
      */
-    private function loadMapFile($fileName)
+    private function loadMapFile(string $fileName)
     {
         $config = Configuration::getInstance();
 
diff --git a/modules/core/lib/Auth/Process/GenerateGroups.php b/modules/core/lib/Auth/Process/GenerateGroups.php
index a6e2257e774b5afa20a09805d8127c1cab756569..f2f82bbe38b107e99c846fc6e4c74bed29bee24c 100644
--- a/modules/core/lib/Auth/Process/GenerateGroups.php
+++ b/modules/core/lib/Auth/Process/GenerateGroups.php
@@ -102,10 +102,8 @@ class GenerateGroups extends \SimpleSAML\Auth\ProcessingFilter
      * @param array $attributes  The attributes of the user.
      * @return string|null  The realm of the user, or NULL if we are unable to determine the realm.
      */
-    private static function getRealm($attributes)
+    private static function getRealm(array $attributes)
     {
-        assert(is_array($attributes));
-
         if (!array_key_exists('eduPersonPrincipalName', $attributes)) {
             return null;
         }
@@ -136,10 +134,8 @@ class GenerateGroups extends \SimpleSAML\Auth\ProcessingFilter
      * @param string $string  The string which should be escaped.
      * @return string  The escaped string.
      */
-    private static function escapeIllegalChars($string)
+    private static function escapeIllegalChars(string $string): string
     {
-        assert(is_string($string));
-
         return preg_replace_callback(
             '/([^a-zA-Z0-9_@=.])/',
             /**
diff --git a/modules/core/lib/Auth/Process/StatisticsWithAttribute.php b/modules/core/lib/Auth/Process/StatisticsWithAttribute.php
index 63d0a62d89f4a3af52fdd8a0111c884e11869fed..1e357a1296fb0557e528f4977c87538c2fe57f1d 100644
--- a/modules/core/lib/Auth/Process/StatisticsWithAttribute.php
+++ b/modules/core/lib/Auth/Process/StatisticsWithAttribute.php
@@ -104,7 +104,7 @@ class StatisticsWithAttribute extends \SimpleSAML\Auth\ProcessingFilter
      *
      * @return string
      */
-    private function setIdentifier($direction, $state)
+    private function setIdentifier(string $direction, array $state): string
     {
         if (array_key_exists($direction, $state)) {
             if (isset($state[$direction]['core:statistics-id'])) {
diff --git a/modules/core/lib/Auth/Process/TargetedID.php b/modules/core/lib/Auth/Process/TargetedID.php
index 950f02f9ba3a5c8a3420fb6f215546b98f4fba53..0f8d07890687626abf872dd74a0d76dabaff479f 100644
--- a/modules/core/lib/Auth/Process/TargetedID.php
+++ b/modules/core/lib/Auth/Process/TargetedID.php
@@ -160,10 +160,8 @@ class TargetedID extends \SimpleSAML\Auth\ProcessingFilter
      * @param array $metadata  The metadata of the entity.
      * @return string  The unique identifier for the entity.
      */
-    private static function getEntityId($metadata)
+    private static function getEntityId(array $metadata): string
     {
-        assert(is_array($metadata));
-
         $id = '';
 
         if (array_key_exists('metadata-set', $metadata)) {
diff --git a/modules/core/lib/Auth/UserPassBase.php b/modules/core/lib/Auth/UserPassBase.php
index 90104e5f14d2999a5b0b04f2fccfc3672c62f2fa..a2d4d1c069f9defcebd4ac9597196ab6ae8ec49b 100644
--- a/modules/core/lib/Auth/UserPassBase.php
+++ b/modules/core/lib/Auth/UserPassBase.php
@@ -47,7 +47,7 @@ abstract class UserPassBase extends \SimpleSAML\Auth\Source
      *
      * @var array
      */
-    protected $loginLinks;
+    protected $loginLinks = [];
 
     /**
      * Storage for authsource config option remember.username.enabled
diff --git a/modules/core/lib/Controller/Exception.php b/modules/core/lib/Controller/Exception.php
index 072282e8e3f399cc41a981e31b147a8b12542838..9a8e4f009e1d95fba651a888d37f868300036c0c 100644
--- a/modules/core/lib/Controller/Exception.php
+++ b/modules/core/lib/Controller/Exception.php
@@ -11,6 +11,7 @@ use SimpleSAML\Session;
 use SimpleSAML\Utils;
 use SimpleSAML\XHTML\Template;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
 
 /**
  * Controller class for the core module.
@@ -55,7 +56,7 @@ class Exception
      * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\RedirectResponse
      *   An HTML template or a redirection if we are not authenticated.
      */
-    public function cardinality(Request $request)
+    public function cardinality(Request $request): Response
     {
         $stateId = $request->get('StateId', false);
         if ($stateId === false) {
@@ -91,7 +92,7 @@ class Exception
      * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\RedirectResponse
      *   An HTML template or a redirection if we are not authenticated.
      */
-    public function nocookie(Request $request)
+    public function nocookie(Request $request): Response
     {
         $retryURL = $request->get('retryURL', null);
         if ($retryURL !== null) {
@@ -115,7 +116,7 @@ class Exception
      *
      * @throws \SimpleSAML\Error\BadRequest
      */
-    public function shortSsoInterval(Request $request)
+    public function shortSsoInterval(Request $request): Response
     {
         $stateId = $request->get('StateId', false);
         if ($stateId === false) {
diff --git a/modules/core/lib/Controller/Login.php b/modules/core/lib/Controller/Login.php
index 8a5859ff577e067289e1e45c8086be074bb09a89..8e5285088e7e7ecdd55e4361f0334839e11ac612 100644
--- a/modules/core/lib/Controller/Login.php
+++ b/modules/core/lib/Controller/Login.php
@@ -13,6 +13,7 @@ use SimpleSAML\Utils;
 use SimpleSAML\XHTML\Template;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
 use Webmozart\Assert\Assert;
 
 /**
@@ -70,7 +71,7 @@ class Login
      *
      * @throws \SimpleSAML\Error\Exception An exception in case the auth source specified is invalid.
      */
-    public function account($as)
+    public function account(string $as): Response
     {
         if (!array_key_exists($as, $this->sources)) {
             throw new Error\Exception('Invalid authentication source');
@@ -118,7 +119,7 @@ class Login
      *
      * @throws \SimpleSAML\Error\Exception
      */
-    public function login(Request $request, $as = null)
+    public function login(Request $request, string $as = null): Response
     {
         // delete admin
         if (isset($this->sources['admin'])) {
@@ -180,7 +181,7 @@ class Login
      *
      * @throws \SimpleSAML\Error\CriticalConfigurationError
      */
-    public function logout($as)
+    public function logout(string $as): Response
     {
         $auth = new Auth\Simple($as);
         return new RunnableResponse([$auth, 'logout'], [$this->config->getBasePath() . 'core/logout/' . urlencode($as)]);
diff --git a/modules/core/lib/Controller/Redirection.php b/modules/core/lib/Controller/Redirection.php
index 718fd3d941be465741331f188c623be3c74e2a9d..14b7454e64672e6c50048ed2061409726627b363 100644
--- a/modules/core/lib/Controller/Redirection.php
+++ b/modules/core/lib/Controller/Redirection.php
@@ -11,6 +11,7 @@ use SimpleSAML\Session;
 use SimpleSAML\Utils;
 use SimpleSAML\XHTML\Template;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
 use Webmozart\Assert\Assert;
 
 /**
@@ -56,7 +57,7 @@ class Redirection
      * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\RedirectResponse
      *   An HTML template or a redirection if we are not authenticated.
      */
-    public function postredirect(Request $request)
+    public function postredirect(Request $request): Response
     {
         $redirId = $request->get('RedirId', false);
         $redirInfo = $request->get('RedirInfo', false);
diff --git a/modules/core/lib/Stats/Output/File.php b/modules/core/lib/Stats/Output/File.php
index 30a97e1722cfe45f7d204f8053b00efc7f894f9c..37fb79f21e12c333113b36e2603d92b692dda40a 100644
--- a/modules/core/lib/Stats/Output/File.php
+++ b/modules/core/lib/Stats/Output/File.php
@@ -55,10 +55,8 @@ class File extends \SimpleSAML\Stats\Output
      * @param string $date  The date for the log file.
      * @return void
      */
-    private function openLog($date)
+    private function openLog(string $date)
     {
-        assert(is_string($date));
-
         if ($this->file !== null && $this->file !== false) {
             fclose($this->file);
             $this->file = null;
diff --git a/modules/core/lib/Storage/SQLPermanentStorage.php b/modules/core/lib/Storage/SQLPermanentStorage.php
index 1ff18f8ab344a8ad58879a4b8ebb2e6aa09845d4..ed920dd2558cf0bce649d0a01188c70a0d5e9643 100644
--- a/modules/core/lib/Storage/SQLPermanentStorage.php
+++ b/modules/core/lib/Storage/SQLPermanentStorage.php
@@ -70,9 +70,9 @@ class SQLPermanentStorage
 
     /**
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
-     * @param mixed $value
+     * @param string $key1
+     * @param string $key2
+     * @param string $value
      * @param int|null $duration
      * @return void
      */
@@ -88,13 +88,13 @@ class SQLPermanentStorage
 
     /**
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
-     * @param mixed $value
+     * @param string $key1
+     * @param string $key2
+     * @param string $value
      * @param int|null $duration
      * @return array
      */
-    private function insert($type, $key1, $key2, $value, $duration = null)
+    private function insert(string $type, string $key1, string $key2, string $value, int $duration = null): array
     {
         $expire = is_null($duration) ? null : (time() + $duration);
 
@@ -114,13 +114,13 @@ class SQLPermanentStorage
 
     /**
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
-     * @param mixed $value
+     * @param string $key1
+     * @param string $key2
+     * @param string $value
      * @param int|null $duration
      * @return array
      */
-    private function update($type, $key1, $key2, $value, $duration = null)
+    private function update(string $type, string $key1, string $key2, string $value, int $duration = null): array
     {
         $expire = is_null($duration) ? null : (time() + $duration);
 
@@ -139,8 +139,8 @@ class SQLPermanentStorage
 
     /**
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
+     * @param string $key1
+     * @param string $key2
      * @return array|null
      */
     public function get($type = null, $key1 = null, $key2 = null)
@@ -164,8 +164,8 @@ class SQLPermanentStorage
      * Return the value directly (not in a container)
      *
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
+     * @param string $key1
+     * @param string $key2
      * @return array|null
      */
     public function getValue($type = null, $key1 = null, $key2 = null)
@@ -180,8 +180,8 @@ class SQLPermanentStorage
 
     /**
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
+     * @param string $key1
+     * @param string $key2
      * @return bool
      */
     public function exists($type, $key1, $key2)
@@ -197,8 +197,8 @@ class SQLPermanentStorage
 
     /**
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
+     * @param string $key1
+     * @param string $key2
      * @return array|false
      */
     public function getList($type = null, $key1 = null, $key2 = null)
@@ -222,8 +222,8 @@ class SQLPermanentStorage
 
     /**
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
+     * @param string $key1
+     * @param string $key2
      * @param string $whichKey
      * @throws \Exception
      * @return array|null
@@ -254,8 +254,8 @@ class SQLPermanentStorage
 
     /**
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
+     * @param string $key1
+     * @param string $key2
      * @return bool
      */
     public function remove($type, $key1, $key2)
@@ -285,11 +285,11 @@ class SQLPermanentStorage
      * Create a SQL condition statement based on parameters
      *
      * @param string $type
-     * @param mixed $key1
-     * @param mixed $key2
+     * @param string $key1
+     * @param string $key2
      * @return string
      */
-    private function getCondition($type = null, $key1 = null, $key2 = null)
+    private function getCondition(string $type = null, string $key1 = null, string $key2 = null): string
     {
         $conditions = [];
         if (!is_null($type)) {
diff --git a/modules/core/www/frontpage_config.php b/modules/core/www/frontpage_config.php
index 2ede138cdccdaa4015e786c938c008857b9d489b..6669498cbcec3051e6a6c1bfbc237f7dfd214a3a 100644
--- a/modules/core/www/frontpage_config.php
+++ b/modules/core/www/frontpage_config.php
@@ -123,8 +123,8 @@ if (\SimpleSAML\Module::isModuleEnabled('radius')) {
 $funcmatrix = [];
 $funcmatrix[] = [
     'required' => 'required',
-    'descr' => 'PHP Version >= 5.6. You run: ' . phpversion(),
-    'enabled' => version_compare(phpversion(), '5.6', '>=')
+    'descr' => 'PHP Version >= 7.0. You run: ' . phpversion(),
+    'enabled' => version_compare(phpversion(), '7.0', '>=')
 ];
 foreach ($functionchecks as $func => $descr) {
     $funcmatrix[] = ['descr' => $descr[1], 'required' => $descr[0], 'enabled' => function_exists($func)];
diff --git a/modules/core/www/idp/logout-iframe-done.php b/modules/core/www/idp/logout-iframe-done.php
index 92733d00ad5d692c7b512c674cd57b55506a36fc..ca1618dfcd4e5cef624f9b6e613a7e5424f6cea0 100644
--- a/modules/core/www/idp/logout-iframe-done.php
+++ b/modules/core/www/idp/logout-iframe-done.php
@@ -3,6 +3,8 @@
 if (!isset($_REQUEST['id'])) {
     throw new \SimpleSAML\Error\BadRequest('Missing required parameter: id');
 }
+
+/** @psalm-var array $state */
 $state = \SimpleSAML\Auth\State::loadState($_REQUEST['id'], 'core:Logout-IFrame');
 $idp = \SimpleSAML\IdP::getByState($state);
 
diff --git a/modules/core/www/idp/logout-iframe.php b/modules/core/www/idp/logout-iframe.php
index 8f38c891f3984e3f09fc1879a70467d2cb425358..80f94cca9e12aa2e5e6c5b02e0eb791f36a0e3d3 100644
--- a/modules/core/www/idp/logout-iframe.php
+++ b/modules/core/www/idp/logout-iframe.php
@@ -18,6 +18,7 @@ if ($type !== 'embed') {
     \SimpleSAML\Stats::log('core:idp:logout-iframe:page', ['type' => $type]);
 }
 
+/** @psalm-var array $state */
 $state = \SimpleSAML\Auth\State::loadState($_REQUEST['id'], 'core:Logout-IFrame');
 $idp = \SimpleSAML\IdP::getByState($state);
 $mdh = \SimpleSAML\Metadata\MetaDataStorageHandler::getMetadataHandler();
diff --git a/modules/core/www/idp/resumelogout.php b/modules/core/www/idp/resumelogout.php
index 3e84b3a4e2a8b78c001ef8db9b810dc221e556c5..5f90c0bca8b2f7cff6b2f265b9cb7e40a7ebc2a9 100644
--- a/modules/core/www/idp/resumelogout.php
+++ b/modules/core/www/idp/resumelogout.php
@@ -3,6 +3,8 @@
 if (!isset($_REQUEST['id'])) {
     throw new \SimpleSAML\Error\BadRequest('Missing id-parameter.');
 }
+
+/** @psalm-var array $state */
 $state = \SimpleSAML\Auth\State::loadState($_REQUEST['id'], 'core:Logout:afterbridge');
 $idp = \SimpleSAML\IdP::getByState($state);
 
diff --git a/modules/cron/bin/cron.php b/modules/cron/bin/cron.php
index 436397fd296f60cc0265ad5c36fe6f460d83cedf..cb85cbe764639138582ceceda7e96947941680c2 100755
--- a/modules/cron/bin/cron.php
+++ b/modules/cron/bin/cron.php
@@ -32,6 +32,7 @@ if (!array_key_exists('t', $options)) {
     exit(2);
 }
 
+/** @psalm-var string $tag */
 $tag = $options['t'];
 $cron = new SimpleSAML\Module\cron\Cron();
 if (!$cron->isValidTag($tag)) {
diff --git a/modules/multiauth/lib/Auth/Source/MultiAuth.php b/modules/multiauth/lib/Auth/Source/MultiAuth.php
index 85d723588a1aa4743c15af3386ff81d7379324f4..3e0e453c552dc82d137e87c5d110be40d97192c5 100644
--- a/modules/multiauth/lib/Auth/Source/MultiAuth.php
+++ b/modules/multiauth/lib/Auth/Source/MultiAuth.php
@@ -80,7 +80,10 @@ class MultiAuth extends \SimpleSAML\Auth\Source
         $defaultLanguage = $globalConfiguration->getString('language.default', 'en');
         $authsources = Configuration::getConfig('authsources.php');
         $this->sources = [];
-        foreach ($config['sources'] as $source => $info) {
+
+        /** @psalm-var array $sources */
+        $sources = $config['sources'];
+        foreach ($sources as $source => $info) {
             if (is_int($source)) {
                 // Backwards compatibility
                 $source = $info;
diff --git a/modules/saml/lib/Auth/Process/NameIDAttribute.php b/modules/saml/lib/Auth/Process/NameIDAttribute.php
index 5ed3e9800705202864e65504cb7c11a024b241a5..01c09c22b57af53df0150ee01f8db1cd5f12b569 100644
--- a/modules/saml/lib/Auth/Process/NameIDAttribute.php
+++ b/modules/saml/lib/Auth/Process/NameIDAttribute.php
@@ -64,10 +64,8 @@ class NameIDAttribute extends \SimpleSAML\Auth\ProcessingFilter
      *
      * @throws \SimpleSAML\Error\Exception if the replacement is invalid.
      */
-    private static function parseFormat($format)
+    private static function parseFormat(string $format): array
     {
-        assert(is_string($format));
-
         $ret = [];
         $pos = 0;
         while (($next = strpos($format, '%', $pos)) !== false) {
diff --git a/modules/saml/lib/Auth/Source/SP.php b/modules/saml/lib/Auth/Source/SP.php
index 4dd06c044dd70d429a350fce090ac9a29ae77e40..83090e3ce87fd62789c242d355b9640483ad2507 100644
--- a/modules/saml/lib/Auth/Source/SP.php
+++ b/modules/saml/lib/Auth/Source/SP.php
@@ -345,7 +345,7 @@ class SP extends \SimpleSAML\Auth\Source
      * @return array
      * @throws \Exception
      */
-    private function getACSEndpoints()
+    private function getACSEndpoints(): array
     {
         $endpoints = [];
         $default = [
@@ -427,7 +427,7 @@ class SP extends \SimpleSAML\Auth\Source
      * @return array
      * @throws \SimpleSAML\Error\CriticalConfigurationError
      */
-    private function getSLOEndpoints()
+    private function getSLOEndpoints(): array
     {
         $store = Store::getInstance();
         $bindings = $this->metadata->getArray(
diff --git a/modules/saml/lib/Error.php b/modules/saml/lib/Error.php
index 50604b39ffbe82300be17666ba8c41792ddf2111..042bc6eb65e08e91538cbe9d18554c6991f1a3ad 100644
--- a/modules/saml/lib/Error.php
+++ b/modules/saml/lib/Error.php
@@ -187,10 +187,8 @@ class Error extends \SimpleSAML\Error\Exception
      * @param string $status  The status code.
      * @return string  A shorter version of the status code.
      */
-    private static function shortStatus($status)
+    private static function shortStatus(string $status): string
     {
-        assert(is_string($status));
-
         $t = 'urn:oasis:names:tc:SAML:2.0:status:';
         if (substr($status, 0, strlen($t)) === $t) {
             return substr($status, strlen($t));
diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php
index eca7ad6e8949d38dcb516956829eec04eab87714..14cd0e6e09ab2fc38b53d9a4cc3b8218061909a8 100644
--- a/modules/saml/lib/IdP/SAML2.php
+++ b/modules/saml/lib/IdP/SAML2.php
@@ -13,6 +13,7 @@ use SAML2\EncryptedAssertion;
 use SAML2\HTTPRedirect;
 use SAML2\LogoutRequest;
 use SAML2\LogoutResponse;
+use SAML2\Response;
 use SAML2\SOAP;
 use SAML2\XML\ds\X509Certificate;
 use SAML2\XML\ds\X509Data;
@@ -192,14 +193,10 @@ class SAML2
     private static function getAssertionConsumerService(
         array $supportedBindings,
         Configuration $spMetadata,
-        $AssertionConsumerServiceURL,
-        $ProtocolBinding,
-        $AssertionConsumerServiceIndex
+        string $AssertionConsumerServiceURL = null,
+        string $ProtocolBinding = null,
+        int $AssertionConsumerServiceIndex = null
     ) {
-        assert(is_string($AssertionConsumerServiceURL) || $AssertionConsumerServiceURL === null);
-        assert(is_string($ProtocolBinding) || $ProtocolBinding === null);
-        assert(is_int($AssertionConsumerServiceIndex) || $AssertionConsumerServiceIndex === null);
-
         /* We want to pick the best matching endpoint in the case where for example
          * only the ProtocolBinding is given. We therefore pick endpoints with the
          * following priority:
@@ -363,12 +360,14 @@ class SAML2
                 );
             }
 
+            /** @psalm-var null|string|\SAML2\XML\saml\Issuer $issuer   Remove in SSP 2.0 */
             $issuer = $request->getIssuer();
             if ($issuer === null) {
                 throw new Error\BadRequest(
                     'Received message on authentication request endpoint without issuer.'
                 );
             } elseif ($issuer instanceof Issuer) {
+                /** @psalm-var string|null $spEntityId */
                 $spEntityId = $issuer->getValue();
                 if ($spEntityId === null) {
                     /* Without an issuer we have no way to respond to the message. */
@@ -604,11 +603,13 @@ class SAML2
         $binding = Binding::getCurrentBinding();
         $message = $binding->receive();
 
+        /** @psalm-var null|string|\SAML2\XML\saml\Issuer  Remove in SSP 2.0 */
         $issuer = $message->getIssuer();
         if ($issuer === null) {
             /* Without an issuer we have no way to respond to the message. */
             throw new Error\BadRequest('Received message on logout endpoint without issuer.');
         } elseif ($issuer instanceof Issuer) {
+            /** @psalm-var string|null $spEntityId */
             $spEntityId = $issuer->getValue();
             if ($spEntityId === null) {
                 /* Without an issuer we have no way to respond to the message. */
@@ -948,7 +949,6 @@ class SAML2
         Configuration $spMetadata,
         array &$state
     ) {
-
         $attribute = $spMetadata->getString('simplesaml.nameidattribute', null);
         if ($attribute === null) {
             $attribute = $idpMetadata->getString('simplesaml.nameidattribute', null);
@@ -999,7 +999,7 @@ class SAML2
         Configuration $idpMetadata,
         Configuration $spMetadata,
         array $attributes
-    ) {
+    ): array {
         $base64Attributes = $spMetadata->getBoolean('base64attributes', null);
         if ($base64Attributes === null) {
             $base64Attributes = $idpMetadata->getBoolean('base64attributes', false);
@@ -1038,6 +1038,7 @@ class SAML2
 
                 $attrval = $value;
                 if ($value instanceof DOMNodeList) {
+                    /** @psalm-suppress PossiblyNullPropertyFetch */
                     $attrval = new AttributeValue($value->item(0)->parentNode);
                 }
 
@@ -1078,7 +1079,7 @@ class SAML2
     private static function getAttributeNameFormat(
         Configuration $idpMetadata,
         Configuration $spMetadata
-    ) {
+    ): string {
         // try SP metadata first
         $attributeNameFormat = $spMetadata->getString('attributes.NameFormat', null);
         if ($attributeNameFormat !== null) {
@@ -1119,7 +1120,7 @@ class SAML2
         Configuration $idpMetadata,
         Configuration $spMetadata,
         array &$state
-    ) {
+    ): Assertion {
         assert(isset($state['Attributes']));
         assert(isset($state['saml:ConsumerURL']));
 
@@ -1376,8 +1377,8 @@ class SAML2
         Configuration $idpMetadata,
         Configuration $spMetadata,
         array $association,
-        $relayState
-    ) {
+        string $relayState = null
+    ): LogoutRequest {
         $lr = \SimpleSAML\Module\saml\Message::buildLogoutRequest($idpMetadata, $spMetadata);
         $lr->setRelayState($relayState);
         $lr->setSessionIndex($association['saml:SessionIndex']);
@@ -1413,14 +1414,14 @@ class SAML2
     private static function buildResponse(
         Configuration $idpMetadata,
         Configuration $spMetadata,
-        $consumerURL
-    ) {
+        string $consumerURL
+    ): Response {
         $signResponse = $spMetadata->getBoolean('saml20.sign.response', null);
         if ($signResponse === null) {
             $signResponse = $idpMetadata->getBoolean('saml20.sign.response', true);
         }
 
-        $r = new \SAML2\Response();
+        $r = new Response();
         $issuer = new Issuer();
         $issuer->setValue($idpMetadata->getString('entityid'));
         $issuer->setFormat(Constants::NAMEID_ENTITY);
diff --git a/modules/saml/lib/IdP/SQLNameID.php b/modules/saml/lib/IdP/SQLNameID.php
index d4f91d9d3f3d0528bdbda5b3cdfd4d6162cc3fc2..8777dd02b3c73a53a3a5d9567f1bc64cb5625fe4 100644
--- a/modules/saml/lib/IdP/SQLNameID.php
+++ b/modules/saml/lib/IdP/SQLNameID.php
@@ -3,6 +3,7 @@
 namespace SimpleSAML\Module\saml\IdP;
 
 use PDO;
+use PDOStatement;
 use SimpleSAML\Error;
 use SimpleSAML\Store;
 use SimpleSAML\Database;
@@ -26,7 +27,7 @@ class SQLNameID
      * @param array $config
      * @return \PDOStatement object
      */
-    private static function read($query, array $params = [], array $config = [])
+    private static function read(string $query, array $params = [], array $config = []): PDOStatement
     {
         if (!empty($config)) {
             $database = Database::getInstance(Configuration::loadFromArray($config));
@@ -46,7 +47,7 @@ class SQLNameID
      * @param array $config
      * @return int|false The number of rows affected by the query or false on error.
      */
-    private static function write($query, array $params = [], array $config = [])
+    private static function write(string $query, array $params = [], array $config = [])
     {
         if (!empty($config)) {
             $database = Database::getInstance(Configuration::loadFromArray($config));
@@ -67,7 +68,7 @@ class SQLNameID
      * @param array $config
      * @return string
      */
-    private static function tableName(array $config = [])
+    private static function tableName(array $config = []): string
     {
         $store = empty($config) ? self::getStore() : null;
         $prefix = $store === null ? self::DEFAULT_TABLE_PREFIX : $store->prefix;
@@ -102,7 +103,7 @@ class SQLNameID
      * @param array $config
      * @return \PDOStatement
      */
-    private static function createAndRead($query, array $params = [], array $config = [])
+    private static function createAndRead(string $query, array $params = [], array $config = []): PDOStatement
     {
         self::create($config);
         return self::read($query, $params, $config);
@@ -115,7 +116,7 @@ class SQLNameID
      * @param array $config
      * @return int|false The number of rows affected by the query or false on error.
      */
-    private static function createAndWrite($query, array $params = [], array $config = [])
+    private static function createAndWrite(string $query, array $params = [], array $config = [])
     {
         self::create($config);
         return self::write($query, $params, $config);
@@ -129,7 +130,7 @@ class SQLNameID
      * @param array $config
      * @return void
      */
-    private static function createTable($table, array $config = [])
+    private static function createTable(string $table, array $config = [])
     {
         $query = 'CREATE TABLE ' . $table . ' (
             _idp VARCHAR(256) NOT NULL,
@@ -151,7 +152,7 @@ class SQLNameID
      *
      * @return \SimpleSAML\Store\SQL  SQL datastore.
      */
-    private static function getStore()
+    private static function getStore(): Store\SQL
     {
         $store = Store::getInstance();
         if (!($store instanceof Store\SQL)) {
diff --git a/modules/saml/lib/Message.php b/modules/saml/lib/Message.php
index ca15e50130b8089b1e65c4a5e35cf3e5c0f4bb23..379d24065cfaa09cb59782c2b686353f6deb835b 100644
--- a/modules/saml/lib/Message.php
+++ b/modules/saml/lib/Message.php
@@ -133,7 +133,7 @@ class Message
      *
      * @throws \SimpleSAML\Error\Exception if we cannot find the certificate matching the fingerprint.
      */
-    private static function findCertificate(array $certFingerprints, array $certificates)
+    private static function findCertificate(array $certFingerprints, array $certificates): string
     {
         $candidates = [];
 
@@ -381,7 +381,7 @@ class Message
         Configuration $srcMetadata,
         Configuration $dstMetadata,
         $assertion
-    ) {
+    ): Assertion {
         assert($assertion instanceof Assertion || $assertion instanceof EncryptedAssertion);
 
         if ($assertion instanceof Assertion) {
@@ -665,10 +665,9 @@ class Message
         Configuration $idpMetadata,
         Response $response,
         $assertion,
-        $responseSigned
-    ) {
+        bool $responseSigned
+    ): Assertion {
         assert($assertion instanceof Assertion || $assertion instanceof EncryptedAssertion);
-        assert(is_bool($responseSigned));
 
         $assertion = self::decryptAssertion($idpMetadata, $spMetadata, $assertion);
         self::decryptAttributes($idpMetadata, $spMetadata, $assertion);
@@ -911,7 +910,6 @@ class Message
      */
     public static function getEncryptionKey(Configuration $metadata)
     {
-
         $sharedKey = $metadata->getString('sharedkey', null);
         if ($sharedKey !== null) {
             $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
diff --git a/modules/saml/lib/SP/LogoutStore.php b/modules/saml/lib/SP/LogoutStore.php
index df1bff1c41ade577959bac25110715d17239d942..c78db758a16826bb85710794a12c3630de99c9ee 100644
--- a/modules/saml/lib/SP/LogoutStore.php
+++ b/modules/saml/lib/SP/LogoutStore.php
@@ -223,18 +223,12 @@ class LogoutStore
      */
     private static function addSessionSQL(
         Store\SQL $store,
-        $authId,
-        $nameId,
-        $sessionIndex,
-        $expire,
-        $sessionId
+        string $authId,
+        string $nameId,
+        string $sessionIndex,
+        int $expire,
+        string $sessionId
     ) {
-        assert(is_string($authId));
-        assert(is_string($nameId));
-        assert(is_string($sessionIndex));
-        assert(is_int($expire));
-        assert(is_string($sessionId));
-
         self::createLogoutTable($store);
 
         if (rand(0, 1000) < 10) {
@@ -264,11 +258,8 @@ class LogoutStore
      * @param string $nameId  The hash of the users NameID.
      * @return array  Associative array of SessionIndex =>  SessionId.
      */
-    private static function getSessionsSQL(Store\SQL $store, $authId, $nameId)
+    private static function getSessionsSQL(Store\SQL $store, string $authId, string $nameId): array
     {
-        assert(is_string($authId));
-        assert(is_string($nameId));
-
         self::createLogoutTable($store);
 
         $params = [
@@ -301,11 +292,12 @@ class LogoutStore
      * @param array $sessionIndexes  The session indexes.
      * @return array  Associative array of SessionIndex =>  SessionId.
      */
-    private static function getSessionsStore(Store $store, $authId, $nameId, array $sessionIndexes)
-    {
-        assert(is_string($authId));
-        assert(is_string($nameId));
-
+    private static function getSessionsStore(
+        Store $store,
+        string $authId,
+        string $nameId,
+        array $sessionIndexes
+    ): array {
         $res = [];
         foreach ($sessionIndexes as $sessionIndex) {
             $sessionId = $store->get('saml.LogoutStore', $nameId . ':' . $sessionIndex);
@@ -365,6 +357,7 @@ class LogoutStore
         // serialize and anonymize the NameID
         // TODO: remove this conditional statement
         if (is_array($nameId)) {
+            /** @psalm-suppress UndefinedMethod */
             $nameId = NameID::fromArray($nameId);
         }
         $strNameId = serialize($nameId);
@@ -407,6 +400,7 @@ class LogoutStore
         // serialize and anonymize the NameID
         // TODO: remove this conditional statement
         if (is_array($nameId)) {
+            /** @psalm-suppress UndefinedMethod */
             $nameId = NameID::fromArray($nameId);
         }
         $strNameId = serialize($nameId);
@@ -425,10 +419,11 @@ class LogoutStore
 
         if ($store instanceof Store\SQL) {
             $sessions = self::getSessionsSQL($store, $authId, $strNameId);
-        } elseif (empty($sessionIndexes)) {
-            // We cannot fetch all sessions without a SQL store
-            return false;
         } else {
+            if (empty($sessionIndexes)) {
+                // We cannot fetch all sessions without a SQL store
+                return false;
+            }
             /** @var array $sessions At this point the store cannot be false */
             $sessions = self::getSessionsStore($store, $authId, $strNameId, $sessionIndexes);
         }
diff --git a/modules/saml/www/sp/saml2-acs.php b/modules/saml/www/sp/saml2-acs.php
index 4d05bf2b8ecf0fe4defa5d0fc2a0598c1c126ced..7d5c39a792dc6a2d32e1afd4b1f30a22b54272c9 100644
--- a/modules/saml/www/sp/saml2-acs.php
+++ b/modules/saml/www/sp/saml2-acs.php
@@ -37,6 +37,7 @@ if (!($response instanceof \SAML2\Response)) {
     throw new \SimpleSAML\Error\BadRequest('Invalid message received to AssertionConsumerService endpoint.');
 }
 
+/** @psalm-var null|string|\SAML2\XML\saml\Issuer $issuer   Remove in SSP 2.0 */
 $issuer = $response->getIssuer();
 if ($issuer === null) {
     // no Issuer in the response. Look for an unencrypted assertion with an issuer
@@ -47,6 +48,7 @@ if ($issuer === null) {
             break;
         }
     }
+    /** @psalm-var string|null $issuer  Remove in SSP 2.0 */
     if ($issuer === null) {
         // no issuer found in the assertions
         throw new Exception('Missing <saml:Issuer> in message delivered to AssertionConsumerService.');
@@ -54,6 +56,7 @@ if ($issuer === null) {
 }
 
 if ($issuer instanceof \SAML2\XML\saml\Issuer) {
+    /** @psalm-var string|null $issuer */
     $issuer = $issuer->getValue();
     if ($issuer === null) {
         // no issuer found in the assertions
@@ -63,6 +66,7 @@ if ($issuer instanceof \SAML2\XML\saml\Issuer) {
 
 $session = \SimpleSAML\Session::getSessionFromRequest();
 $prevAuth = $session->getAuthData($sourceId, 'saml:sp:prevAuth');
+/** @psalm-var string $issuer */
 if ($prevAuth !== null && $prevAuth['id'] === $response->getId() && $prevAuth['issuer'] === $issuer) {
     /* OK, it looks like this message has the same issuer
      * and ID as the SP session we already have active. We
diff --git a/modules/saml/www/sp/saml2-logout.php b/modules/saml/www/sp/saml2-logout.php
index 61950a71e17664bee2945037254192de434040ae..9b756d8d61e5fdffa09c6eee42a6880d008d2cc6 100644
--- a/modules/saml/www/sp/saml2-logout.php
+++ b/modules/saml/www/sp/saml2-logout.php
@@ -109,6 +109,7 @@ if ($message instanceof \SAML2\LogoutResponse) {
     $nameId = $message->getNameId();
     $sessionIndexes = $message->getSessionIndexes();
 
+    /** @psalm-suppress PossiblyNullArgument  This will be fixed in saml2 5.0 */
     $numLoggedOut = \SimpleSAML\Module\saml\SP\LogoutStore::logoutSessions($sourceId, $nameId, $sessionIndexes);
     if ($numLoggedOut === false) {
         // This type of logout was unsupported. Use the old method
diff --git a/phpunit.xml b/phpunit.xml
index 4a7750b5f74ba104a4bec91b0571d517d88ebca0..878768a43398e4abb9c03f6b07834182d45c7522 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -10,7 +10,8 @@
          bootstrap="./tests/bootstrap.php">
     <testsuites>
         <testsuite name="Unit tests">
-            <directory>./tests/</directory>
+            <directory>./vendor/simplesamlphp/simplesamlphp-test-framework/src</directory>
+            <directory>./tests</directory>
         </testsuite>
     </testsuites>
     <filter>
diff --git a/psalm.xml b/psalm.xml
index 5a230843ec723656c9ef1dcd912045848ea34d71..98c6fd8fa7580fb30c1c8eddb948a957f55bf592 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -25,9 +25,14 @@
 
         <!-- Ignore deprecated classes -->
         <ignoreFiles>
+            <directory name="lib/SimpleSAML/Bindings/Shib13" />
+            <directory name="lib/SimpleSAML/XML/Shib13" />
+            <directory name="tests/lib/SimpleSAML/XML/Shib13" />
+
             <file name="lib/SimpleSAML/Auth/DefaultAuth.php" />
             <file name="lib/SimpleSAML/Auth/TimeLimitedToken.php" />
             <file name="lib/SimpleSAML/Utilities.php" />
+            <file name="tests/lib/SimpleSAML/Auth/TimeLimitedTokenTest.php" />
 
             <!-- Ignore deprecated PHP-templates - Remove for 2.0 -->
             <file name="modules/**/templates/*.tpl.php" />
diff --git a/tests/SigningTestCase.php b/tests/SigningTestCase.php
index 80baba3c421a8002c73c5ef6bc639a191eb67511..bbbaadfbc3f80d1f5eb49a7f25a5147055a03534 100644
--- a/tests/SigningTestCase.php
+++ b/tests/SigningTestCase.php
@@ -4,6 +4,7 @@ namespace SimpleSAML\Test;
 
 use org\bovigo\vfs\vfsStream;
 use PHPUnit\Framework\TestCase;
+use ReflectionClass;
 use SimpleSAML\Configuration;
 
 /**
@@ -205,7 +206,7 @@ NOWDOC;
         $this->good_private_key_file = $this->certdir . DIRECTORY_SEPARATOR . self::GOOD_PRIVATE_KEY;
         $this->good_certificate_file = $this->certdir . DIRECTORY_SEPARATOR . self::GOOD_CERTIFICATE;
 
-        $this->config = \SimpleSAML\Configuration::loadFromArray([
+        $this->config = Configuration::loadFromArray([
             'certdir' => $this->certdir,
         ], '[ARRAY]', 'simplesaml');
     }
@@ -216,19 +217,19 @@ NOWDOC;
      */
     public function tearDown()
     {
-        $this->clearInstance($this->config, '\SimpleSAML\Configuration', []);
+        $this->clearInstance($this->config, Configuration::class, []);
     }
 
 
     /**
      * @param \SimpleSAML\Configuration $service
-     * @param string $className
+     * @param class-string $className
      * @param mixed|null $value
      * @return void
      */
     protected function clearInstance(Configuration $service, $className, $value = null)
     {
-        $reflectedClass = new \ReflectionClass($className);
+        $reflectedClass = new ReflectionClass($className);
         $reflectedInstance = $reflectedClass->getProperty('instance');
         $reflectedInstance->setAccessible(true);
         $reflectedInstance->setValue($service, $value);
diff --git a/tests/Utils/StateClearer.php b/tests/Utils/StateClearer.php
index ec5258819e840cb533130082d41567e1735541e8..da24f1ba850618061c21f530eaf282b48671eaf1 100644
--- a/tests/Utils/StateClearer.php
+++ b/tests/Utils/StateClearer.php
@@ -44,6 +44,7 @@ class StateClearer
         $this->backups['$_GET'] = $_GET;
         $this->backups['$_POST'] = $_POST;
         $this->backups['$_SERVER'] = $_SERVER;
+        /** @psalm-var array|null $_SESSION */
         $this->backups['$_SESSION'] = isset($_SESSION) ? $_SESSION : [];
         $this->backups['$_REQUEST'] = $_REQUEST;
     }
diff --git a/tests/lib/SimpleSAML/Metadata/SAMLBuilderTest.php b/tests/lib/SimpleSAML/Metadata/SAMLBuilderTest.php
index 34dd59c7defa65ce93eb626b8455425a1a89153d..0e5f2d3480c69e7d47c4e93de65b60b51296bb38 100644
--- a/tests/lib/SimpleSAML/Metadata/SAMLBuilderTest.php
+++ b/tests/lib/SimpleSAML/Metadata/SAMLBuilderTest.php
@@ -37,12 +37,16 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
 
         $spDesc = $samlBuilder->getEntityDescriptor();
-        /** @var \DOMNodeList $acs */
+        /** @psalm-var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $this->assertEquals(1, $acs->length);
-        $attributes = $acs->item(0)->getElementsByTagName("RequestedAttribute");
+
+        /** @psalm-var \DOMElement $first */
+        $first = $acs->item(0);
+        $attributes = $first->getElementsByTagName("RequestedAttribute");
         $this->assertEquals(4, $attributes->length);
         for ($c = 0; $c < $attributes->length; $c++) {
+            /** @psalm-var \DOMElement $curAttribute */
             $curAttribute = $attributes->item($c);
             $this->assertTrue($curAttribute->hasAttribute("Name"));
             $this->assertFalse($curAttribute->hasAttribute("FriendlyName"));
@@ -70,10 +74,14 @@ class SAMLBuilderTest extends TestCase
         /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $this->assertEquals(1, $acs->length);
-        $attributes = $acs->item(0)->getElementsByTagName("RequestedAttribute");
+
+        /** @psalm-var \DOMElement $first */
+        $first = $acs->item(0);
+        $attributes = $first->getElementsByTagName("RequestedAttribute");
         $this->assertEquals(4, $attributes->length);
         $keys = array_keys($metadata['attributes']);
         for ($c = 0; $c < $attributes->length; $c++) {
+            /** @psalm-var \DOMElement $curAttribute */
             $curAttribute = $attributes->item($c);
             $this->assertTrue($curAttribute->hasAttribute("Name"));
             $this->assertTrue($curAttribute->hasAttribute("FriendlyName"));
@@ -102,9 +110,13 @@ class SAMLBuilderTest extends TestCase
         /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $this->assertEquals(1, $acs->length);
-        $attributes = $acs->item(0)->getElementsByTagName("RequestedAttribute");
+
+        /** @psalm-var \DOMElement $first */
+        $first = $acs->item(0);
+        $attributes = $first->getElementsByTagName("RequestedAttribute");
         $this->assertEquals(4, $attributes->length);
         for ($c = 0; $c < $attributes->length; $c++) {
+            /** @psalm-var \DOMElement $curAttribute */
             $curAttribute = $attributes->item($c);
             $this->assertTrue($curAttribute->hasAttribute("Name"));
             $this->assertFalse($curAttribute->hasAttribute("FriendlyName"));
@@ -132,10 +144,14 @@ class SAMLBuilderTest extends TestCase
         /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $this->assertEquals(1, $acs->length);
-        $attributes = $acs->item(0)->getElementsByTagName("RequestedAttribute");
+
+        /** @psalm-var \DOMElement $first */
+        $first = $acs->item(0);
+        $attributes = $first->getElementsByTagName("RequestedAttribute");
         $this->assertEquals(4, $attributes->length);
         $keys = array_keys($metadata['attributes']);
         for ($c = 0; $c < $attributes->length; $c++) {
+            /** @psalm-var \DOMElement $curAttribute */
             $curAttribute = $attributes->item($c);
             $this->assertTrue($curAttribute->hasAttribute("Name"));
             $this->assertTrue($curAttribute->hasAttribute("FriendlyName"));
@@ -170,6 +186,8 @@ class SAMLBuilderTest extends TestCase
         $spDesc = $samlBuilder->getEntityDescriptor();
         /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+
+        /** @psalm-var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertFalse($acs1->hasAttribute("isDefault"));
 
@@ -179,6 +197,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
         $spDesc = $samlBuilder->getEntityDescriptor();
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+
         /** @var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertTrue($acs1->hasAttribute("isDefault"));
@@ -190,6 +209,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
         $spDesc = $samlBuilder->getEntityDescriptor();
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+
         /** @var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertTrue($acs1->hasAttribute("isDefault"));
@@ -221,6 +241,7 @@ class SAMLBuilderTest extends TestCase
 
         $spDesc = $samlBuilder->getEntityDescriptor();
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+
         /** @var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertTrue($acs1->hasAttribute("index"));
@@ -233,6 +254,7 @@ class SAMLBuilderTest extends TestCase
 
         $spDesc = $samlBuilder->getEntityDescriptor();
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+
         /** @var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertTrue($acs1->hasAttribute("index"));
diff --git a/tests/lib/SimpleSAML/Metadata/SAMLParserTest.php b/tests/lib/SimpleSAML/Metadata/SAMLParserTest.php
index 7bcb4ad3b432ec6bf9f1a7e46e8b3128f4353225..3dd13a1c1dffc5f61c0c41f05f5bec7facd5b470 100644
--- a/tests/lib/SimpleSAML/Metadata/SAMLParserTest.php
+++ b/tests/lib/SimpleSAML/Metadata/SAMLParserTest.php
@@ -165,6 +165,7 @@ XML
 XML
         );
 
+        /** @psalm-var \DOMElement $entities_root */
         $entities_root = $doc->getElementsByTagName('EntitiesDescriptor')->item(0);
         $signer = new Signer([]);
         $signer->loadPrivateKey($this->good_private_key_file, null, true);
@@ -180,7 +181,7 @@ XML
      * @param string $expected_fingerprint
      * @return void
      */
-    private function validateFingerprint($algo, $expected_fingerprint)
+    private function validateFingerprint(string $algo, string $expected_fingerprint)
     {
         $doc = $this->makeTestDocument();
         $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($doc->documentElement);
diff --git a/tests/lib/SimpleSAML/Store/RedisTest.php b/tests/lib/SimpleSAML/Store/RedisTest.php
index b855925a1235e4a15954e10ee1a3517196e4acbc..fb133be9c0175f4b58001a42f24e17611ac87913 100644
--- a/tests/lib/SimpleSAML/Store/RedisTest.php
+++ b/tests/lib/SimpleSAML/Store/RedisTest.php
@@ -4,6 +4,7 @@ namespace SimpleSAML\Test\Store;
 
 use PHPUnit\Framework\TestCase;
 use Predis\Client;
+use ReflectionClass;
 use SimpleSAML\Configuration;
 use SimpleSAML\Store;
 
@@ -247,12 +248,12 @@ class RedisTest extends TestCase
 
     /**
      * @param \SimpleSAML\Configuration|\SimpleSAML\Store $service
-     * @param string $className
+     * @param class-string $className
      * @return void
      */
     protected function clearInstance($service, $className)
     {
-        $reflectedClass = new \ReflectionClass($className);
+        $reflectedClass = new ReflectionClass($className);
         $reflectedInstance = $reflectedClass->getProperty('instance');
         $reflectedInstance->setAccessible(true);
         $reflectedInstance->setValue($service, null);
diff --git a/tests/lib/SimpleSAML/Store/SQLTest.php b/tests/lib/SimpleSAML/Store/SQLTest.php
index 46188863323c4a01d92e80b8cbc8818779cd95ee..aac6edf9a6b3526ebcfbd31e53370565c86916ee 100644
--- a/tests/lib/SimpleSAML/Store/SQLTest.php
+++ b/tests/lib/SimpleSAML/Store/SQLTest.php
@@ -3,6 +3,7 @@
 namespace SimpleSAML\Test\Store;
 
 use PHPUnit\Framework\TestCase;
+use ReflectionClass;
 use SimpleSAML\Configuration;
 use SimpleSAML\Store;
 
@@ -199,22 +200,23 @@ class SQLTest extends TestCase
     protected function tearDown()
     {
         $config = Configuration::getInstance();
+
         /** @var \SimpleSAML\Store\SQL $store */
         $store = Store::getInstance();
 
-        $this->clearInstance($config, '\SimpleSAML\Configuration');
-        $this->clearInstance($store, '\SimpleSAML\Store');
+        $this->clearInstance($config, Configuration::class);
+        $this->clearInstance($store, Store::class);
     }
 
 
     /**
      * @param \SimpleSAML\Configuration|\SimpleSAML\Store $service
-     * @param string $className
+     * @param class-string $className
      * @return void
      */
     protected function clearInstance($service, $className)
     {
-        $reflectedClass = new \ReflectionClass($className);
+        $reflectedClass = new ReflectionClass($className);
         $reflectedInstance = $reflectedClass->getProperty('instance');
         $reflectedInstance->setAccessible(true);
         $reflectedInstance->setValue($service, null);
diff --git a/tests/lib/SimpleSAML/StoreTest.php b/tests/lib/SimpleSAML/StoreTest.php
index 65d429d362a2c1c5318b0455c3f14a4e7ddfd8c9..09412ff482b55cfc12bf239c63f50a4fd1d85d87 100644
--- a/tests/lib/SimpleSAML/StoreTest.php
+++ b/tests/lib/SimpleSAML/StoreTest.php
@@ -3,6 +3,7 @@
 namespace SimpleSAML\Test;
 
 use PHPUnit\Framework\TestCase;
+use ReflectionClass;
 use SimpleSAML\Configuration;
 use SimpleSAML\Error\CriticalConfigurationError;
 use SimpleSAML\Store;
@@ -139,12 +140,12 @@ class StoreTest extends TestCase
 
     /**
      * @param \SimpleSAML\Configuration|\SimpleSAML\Store $service
-     * @param string $className
+     * @param class-string $className
      * @return void
      */
     protected function clearInstance($service, $className)
     {
-        $reflectedClass = new \ReflectionClass($className);
+        $reflectedClass = new ReflectionClass($className);
         $reflectedInstance = $reflectedClass->getProperty('instance');
         $reflectedInstance->setAccessible(true);
         $reflectedInstance->setValue($service, null);
diff --git a/tests/lib/SimpleSAML/Utils/Config/MetadataTest.php b/tests/lib/SimpleSAML/Utils/Config/MetadataTest.php
index 39c959009549c51adfc48876d0f9d6db21953fe0..df7137d3204683da60ff62c93eaa796fc8aca324 100644
--- a/tests/lib/SimpleSAML/Utils/Config/MetadataTest.php
+++ b/tests/lib/SimpleSAML/Utils/Config/MetadataTest.php
@@ -2,6 +2,7 @@
 
 namespace SimpleSAML\Test\Utils\Config;
 
+use DOMDocument;
 use PHPUnit\Framework\TestCase;
 use SimpleSAML\Utils\Config\Metadata;
 
@@ -242,6 +243,13 @@ class MetadataTest extends TestCase
         ];
         $this->assertTrue(Metadata::isHiddenFromDiscovery($metadata));
 
+        // test for failure
+        $this->assertFalse(Metadata::isHiddenFromDiscovery([
+            'EntityAttributes' => [
+                Metadata::$ENTITY_CATEGORY => [],
+            ],
+        ]));
+
         // test for failures
         $this->assertFalse(Metadata::isHiddenFromDiscovery(['foo']));
         $this->assertFalse(Metadata::isHiddenFromDiscovery([
@@ -255,14 +263,9 @@ class MetadataTest extends TestCase
                 Metadata::$ENTITY_CATEGORY => '',
             ],
         ]));
-        $this->assertFalse(Metadata::isHiddenFromDiscovery([
-            'EntityAttributes' => [
-                Metadata::$ENTITY_CATEGORY => [],
-            ],
-        ]));
     }
 
-    
+
     /**
      * Test \SimpleSAML\Utils\Config\Metadata::parseNameIdPolicy().
      * @return void
diff --git a/tests/lib/SimpleSAML/Utils/CryptoTest.php b/tests/lib/SimpleSAML/Utils/CryptoTest.php
index ee5e011485b65e94cf23649e52dfb5667d078b38..9841299eb32a075a212ee15e589d5bff54936cd5 100644
--- a/tests/lib/SimpleSAML/Utils/CryptoTest.php
+++ b/tests/lib/SimpleSAML/Utils/CryptoTest.php
@@ -43,38 +43,6 @@ class CryptoTest extends TestCase
     }
 
 
-    /**
-     * Test invalid input provided to the aesDecrypt() method.
-     *
-     * @covers \SimpleSAML\Utils\Crypto::aesDecrypt
-     * @return void
-     */
-    public function testAesDecryptBadInput()
-    {
-        $this->expectException(\InvalidArgumentException::class);
-        $m = new \ReflectionMethod('\SimpleSAML\Utils\Crypto', 'aesDecryptInternal');
-        $m->setAccessible(true);
-
-        $m->invokeArgs(null, [[], 'SECRET']);
-    }
-
-
-    /**
-     * Test invalid input provided to the aesEncrypt() method.
-     *
-     * @covers \SimpleSAML\Utils\Crypto::aesEncrypt
-     * @return void
-     */
-    public function testAesEncryptBadInput()
-    {
-        $this->expectException(\InvalidArgumentException::class);
-        $m = new \ReflectionMethod('\SimpleSAML\Utils\Crypto', 'aesEncryptInternal');
-        $m->setAccessible(true);
-
-        $m->invokeArgs(null, [[], 'SECRET']);
-    }
-
-
     /**
      * Test that aesDecrypt() works properly, being able to decrypt some previously known (and correct)
      * ciphertext.
diff --git a/tests/lib/SimpleSAML/Utils/HTTPTest.php b/tests/lib/SimpleSAML/Utils/HTTPTest.php
index 504aaedf0a36c05479ac06642b58a1e846ee00c4..32cd3aac6513df6146dcc173bc719559888c2f11 100644
--- a/tests/lib/SimpleSAML/Utils/HTTPTest.php
+++ b/tests/lib/SimpleSAML/Utils/HTTPTest.php
@@ -15,7 +15,7 @@ class HTTPTest extends ClearStateTestCase
      * @param string $url The URL to use as the current one.
      * @return void
      */
-    private function setupEnvFromURL($url)
+    private function setupEnvFromURL(string $url)
     {
         $addr = parse_url($url);
         $_SERVER['HTTP_HOST'] = $addr['host'];
diff --git a/tests/lib/SimpleSAML/Utils/SystemTest.php b/tests/lib/SimpleSAML/Utils/SystemTest.php
index d96fa62d1c5a18b2d1e730ab376e8ed1964bb198..f2e5c854537a37619436bd1bd392724dde8dbc2f 100644
--- a/tests/lib/SimpleSAML/Utils/SystemTest.php
+++ b/tests/lib/SimpleSAML/Utils/SystemTest.php
@@ -2,8 +2,10 @@
 
 namespace SimpleSAML\Test\Utils;
 
+use InvalidArgumentException;
 use org\bovigo\vfs\vfsStream;
 use PHPUnit\Framework\TestCase;
+use ReflectionClass;
 use SimpleSAML\Configuration;
 use SimpleSAML\Utils\System;
 
@@ -162,7 +164,8 @@ class SystemTest extends TestCase
      */
     public function testWriteFileInvalidArguments()
     {
-        $this->expectException(\InvalidArgumentException::class);
+        $this->expectException(InvalidArgumentException::class);
+
         /** @psalm-suppress NullArgument */
         System::writeFile(null, null, null);
     }
@@ -184,7 +187,7 @@ class SystemTest extends TestCase
 
         $this->assertFileExists($filename);
 
-        $this->clearInstance($config, '\SimpleSAML\Configuration');
+        $this->clearInstance($config, Configuration::class);
     }
 
 
@@ -208,7 +211,7 @@ class SystemTest extends TestCase
 
         $this->assertEquals($expected, $res);
 
-        $this->clearInstance($config, '\SimpleSAML\Configuration');
+        $this->clearInstance($config, Configuration::class);
     }
 
 
@@ -232,7 +235,7 @@ class SystemTest extends TestCase
 
         $this->assertEquals($expected, $res);
 
-        $this->clearInstance($config, '\SimpleSAML\Configuration');
+        $this->clearInstance($config, Configuration::class);
     }
 
 
@@ -252,7 +255,7 @@ class SystemTest extends TestCase
         $this->assertEquals($expected, $res);
         $this->assertFileExists($res);
 
-        $this->clearInstance($config, '\SimpleSAML\Configuration');
+        $this->clearInstance($config, Configuration::class);
     }
 
 
@@ -272,7 +275,7 @@ class SystemTest extends TestCase
         $this->assertEquals($expected, $res);
         $this->assertFileExists($res);
 
-        $this->clearInstance($config, '\SimpleSAML\Configuration');
+        $this->clearInstance($config, Configuration::class);
     }
 
 
@@ -298,7 +301,7 @@ class SystemTest extends TestCase
         $this->expectException(\SimpleSAML\Error\Exception::class);
         System::getTempDir();
 
-        $this->clearInstance($config, '\SimpleSAML\Configuration');
+        $this->clearInstance($config, Configuration::class);
     }
 
 
@@ -306,7 +309,7 @@ class SystemTest extends TestCase
      * @param string $directory
      * @return \SimpleSAML\Configuration
      */
-    private function setConfigurationTempDir($directory)
+    private function setConfigurationTempDir(string $directory): Configuration
     {
         $config = Configuration::loadFromArray([
             'tempdir' => $directory,
@@ -318,12 +321,12 @@ class SystemTest extends TestCase
 
     /**
      * @param \SimpleSAML\Configuration $service
-     * @param string $className
+     * @param class-string $className
      * @return void
      */
     protected function clearInstance(Configuration $service, $className)
     {
-        $reflectedClass = new \ReflectionClass($className);
+        $reflectedClass = new ReflectionClass($className);
         $reflectedInstance = $reflectedClass->getProperty('instance');
         $reflectedInstance->setAccessible(true);
         $reflectedInstance->setValue($service, null);
diff --git a/tests/lib/SimpleSAML/XML/SignerTest.php b/tests/lib/SimpleSAML/XML/SignerTest.php
index 67b1f64f4c828d9f88e619c4532d270c957ebbd1..a8396d44fe42e0e0b59436984539151e3d48e36e 100644
--- a/tests/lib/SimpleSAML/XML/SignerTest.php
+++ b/tests/lib/SimpleSAML/XML/SignerTest.php
@@ -2,8 +2,12 @@
 
 namespace SimpleSAML\Test\XML;
 
+use DOMDocument;
+use DOMElement;
+use Exception;
 use org\bovigo\vfs\vfsStream;
 use PHPUnit\Framework\TestCase;
+use ReflectionClass;
 use SimpleSAML\Configuration;
 use SimpleSAML\Test\SigningTestCase;
 use SimpleSAML\XML\Signer;
@@ -78,12 +82,14 @@ NOWDOC;
      */
     public function testSignBasic()
     {
-        $node = new \DOMDocument();
+        $node = new DOMDocument();
         $node->loadXML('<?xml version="1.0"?><node>value</node>');
+
+        /** @psalm-var DOMElement $element */
         $element = $node->getElementsByTagName("node")->item(0);
 
-        $doc = new \DOMDocument();
-        $insertInto = $doc->appendChild(new \DOMElement('insert'));
+        $doc = new DOMDocument();
+        $insertInto = $doc->appendChild(new DOMElement('insert'));
 
         $signer = new Signer([]);
         $signer->loadPrivateKey($this->good_private_key_file, null, true);
@@ -117,12 +123,14 @@ NOWDOC;
      */
     public function testSignWithCertificate()
     {
-        $node = new \DOMDocument();
+        $node = new DOMDocument();
         $node->loadXML('<?xml version="1.0"?><node>value</node>');
+
+        /** @psalm-var DOMElement $element */
         $element = $node->getElementsByTagName("node")->item(0);
 
-        $doc = new \DOMDocument();
-        $insertInto = $doc->appendChild(new \DOMElement('insert'));
+        $doc = new DOMDocument();
+        $insertInto = $doc->appendChild(new DOMElement('insert'));
 
         $signer = new Signer([]);
         $signer->loadPrivateKey($this->good_private_key_file, null, true);
@@ -145,12 +153,14 @@ NOWDOC;
     {
         $this->other_certificate_file = $this->certdir . DIRECTORY_SEPARATOR . self::OTHER_CERTIFICATE;
 
-        $node = new \DOMDocument();
+        $node = new DOMDocument();
         $node->loadXML('<?xml version="1.0"?><node>value</node>');
+
+        /** @psalm-var DOMElement $element */
         $element = $node->getElementsByTagName("node")->item(0);
 
-        $doc = new \DOMDocument();
-        $insertInto = $doc->appendChild(new \DOMElement('insert'));
+        $doc = new DOMDocument();
+        $insertInto = $doc->appendChild(new DOMElement('insert'));
 
         $signer = new Signer([]);
         $signer->loadPrivateKey($this->good_private_key_file, null, true);
@@ -174,29 +184,31 @@ NOWDOC;
      */
     public function testSignMissingPrivateKey()
     {
-        $node = new \DOMDocument();
+        $node = new DOMDocument();
         $node->loadXML('<?xml version="1.0"?><node>value</node>');
+
+        /** @psalm-var DOMElement $element */
         $element = $node->getElementsByTagName("node")->item(0);
 
-        $doc = new \DOMDocument();
-        $insertInto = $doc->appendChild(new \DOMElement('insert'));
+        $doc = new DOMDocument();
+        $insertInto = $doc->appendChild(new DOMElement('insert'));
 
         $signer = new Signer([]);
 
-        $this->expectException(\Exception::class);
+        $this->expectException(Exception::class);
         $signer->sign($element, $insertInto);
     }
 
 
     /**
      * @param \SimpleSAML\Configuration $service
-     * @param string $className
+     * @param class-string $className
      * @param mixed|null $value
      * @return void
      */
     protected function clearInstance(Configuration $service, $className, $value = null)
     {
-        $reflectedClass = new \ReflectionClass($className);
+        $reflectedClass = new ReflectionClass($className);
         $reflectedInstance = $reflectedClass->getProperty('instance');
         $reflectedInstance->setAccessible(true);
         $reflectedInstance->setValue($service, $value);
diff --git a/tests/lib/SimpleSAML/XML/ValidatorTest.php b/tests/lib/SimpleSAML/XML/ValidatorTest.php
index c4092d341d65ef6979e64abadadcd0982f9684d6..f6bb17f6d87695e5c8d91e67f01e0ab805340cc8 100644
--- a/tests/lib/SimpleSAML/XML/ValidatorTest.php
+++ b/tests/lib/SimpleSAML/XML/ValidatorTest.php
@@ -2,6 +2,8 @@
 
 namespace SimpleSAML\Test\XML;
 
+use DOMDocument;
+use DOMElement;
 use org\bovigo\vfs\vfsStream;
 use PHPUnit\Framework\TestCase;
 use SimpleSAML\Test\SigningTestCase;
@@ -31,9 +33,10 @@ class ValidatorTest extends SigningTestCase
      */
     public function testGetX509Certificate()
     {
-        $doc = new \DOMDocument();
+        $doc = new DOMDocument();
         $doc->loadXML('<?xml version="1.0"?><node>value</node>');
 
+        /** @psalm-var DOMElement $node */
         $node = $doc->getElementsByTagName('node')->item(0);
 
         $signature_parent = $doc->appendChild(new \DOMElement('signature_parent'));
@@ -59,9 +62,10 @@ class ValidatorTest extends SigningTestCase
      */
     public function testCertFingerprintSuccess()
     {
-        $doc = new \DOMDocument();
+        $doc = new DOMDocument();
         $doc->loadXML('<?xml version="1.0"?><node>value</node>');
 
+        /** @psalm-var DOMElement $node */
         $node = $doc->getElementsByTagName('node')->item(0);
 
         $signature_parent = $doc->appendChild(new \DOMElement('signature_parent'));
@@ -88,9 +92,10 @@ class ValidatorTest extends SigningTestCase
      */
     public function testCertFingerprintFailure()
     {
-        $doc = new \DOMDocument();
+        $doc = new DOMDocument();
         $doc->loadXML('<?xml version="1.0"?><node>value</node>');
 
+        /** @psalm-var DOMElement $node */
         $node = $doc->getElementsByTagName('node')->item(0);
 
         $signature_parent = $doc->appendChild(new \DOMElement('signature_parent'));
@@ -110,9 +115,10 @@ class ValidatorTest extends SigningTestCase
      */
     public function testValidateFingerprintSuccess()
     {
-        $doc = new \DOMDocument();
+        $doc = new DOMDocument();
         $doc->loadXML('<?xml version="1.0"?><node>value</node>');
 
+        /** @psalm-var DOMElement $node */
         $node = $doc->getElementsByTagName('node')->item(0);
 
         $signature_parent = $doc->appendChild(new \DOMElement('signature_parent'));
@@ -136,9 +142,10 @@ class ValidatorTest extends SigningTestCase
      */
     public function testValidateFingerprintFailure()
     {
-        $doc = new \DOMDocument();
+        $doc = new DOMDocument();
         $doc->loadXML('<?xml version="1.0"?><node>value</node>');
 
+        /** @psalm-var DOMElement $node */
         $node = $doc->getElementsByTagName('node')->item(0);
 
         $signature_parent = $doc->appendChild(new \DOMElement('signature_parent'));
@@ -162,9 +169,10 @@ class ValidatorTest extends SigningTestCase
      */
     public function testIsNodeValidatedSuccess()
     {
-        $doc = new \DOMDocument();
+        $doc = new DOMDocument();
         $doc->loadXML('<?xml version="1.0"?><node>value</node>');
 
+        /** @psalm-var DOMElement $node */
         $node = $doc->getElementsByTagName('node')->item(0);
 
         $signature_parent = $doc->appendChild(new \DOMElement('signature_parent'));
@@ -190,10 +198,13 @@ class ValidatorTest extends SigningTestCase
      */
     public function testIsNodeValidatedFailure()
     {
-        $doc = new \DOMDocument();
+        $doc = new DOMDocument();
         $doc->loadXML('<?xml version="1.0"?><parent><node1>value1</node1><node2>value2</node2></parent>');
 
+        /** @psalm-var DOMElement $node1 */
         $node1 = $doc->getElementsByTagName('node1')->item(0);
+
+        /** @psalm-var DOMElement $node2 */
         $node2 = $doc->getElementsByTagName('node2')->item(0);
 
         $signature_parent = $doc->appendChild(new \DOMElement('signature_parent'));
diff --git a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
index 1adc23ba5491e7dfc3efde5f0d712100d6e66c42..136c5813702a48e35763d8544329d295abc7e572 100644
--- a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
+++ b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
@@ -20,7 +20,7 @@ class CardinalitySingleTest extends \PHPUnit\Framework\TestCase
      * @param  array $request The request state.
      * @return array  The state array after processing.
      */
-    private function processFilter(array $config, array $request)
+    private function processFilter(array $config, array $request): array
     {
         $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
         $_SERVER['REQUEST_METHOD'] = 'GET';
diff --git a/tests/modules/core/lib/Auth/Process/CardinalityTest.php b/tests/modules/core/lib/Auth/Process/CardinalityTest.php
index 21d2408f563de0e7dbc2ec223c87eb08780e3f5c..edcebb727cbfa501d64e94184dec55249573c62c 100644
--- a/tests/modules/core/lib/Auth/Process/CardinalityTest.php
+++ b/tests/modules/core/lib/Auth/Process/CardinalityTest.php
@@ -21,7 +21,7 @@ class CardinalityTest extends \PHPUnit\Framework\TestCase
      * @param  array $request The request state.
      * @return array  The state array after processing.
      */
-    private function processFilter(array $config, array $request)
+    private function processFilter(array $config, array $request): array
     {
         $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
         $_SERVER['REQUEST_METHOD'] = 'GET';
diff --git a/tests/modules/saml/lib/Auth/Process/FilterScopesTest.php b/tests/modules/saml/lib/Auth/Process/FilterScopesTest.php
index 8805be13101198784f90e4b6c69f89cfa2a4c7aa..561c78bbd94aea82f571bb381e209b6fe59792f1 100644
--- a/tests/modules/saml/lib/Auth/Process/FilterScopesTest.php
+++ b/tests/modules/saml/lib/Auth/Process/FilterScopesTest.php
@@ -19,7 +19,7 @@ class FilterScopesTest extends TestCase
      * @param array $request  The request state.
      * @return array  The state array after processing.
      */
-    private function processFilter(array $config, array $request)
+    private function processFilter(array $config, array $request): array
     {
         $filter = new \SimpleSAML\Module\saml\Auth\Process\FilterScopes($config, null);
         $filter->process($request);
diff --git a/tests/modules/saml/lib/Auth/Process/NameIDAttributeTest.php b/tests/modules/saml/lib/Auth/Process/NameIDAttributeTest.php
index ee5af3c337f20e7f420a2228df33a88880858912..13bd08314a128a2b3f8027376c3e366d75fd4f4c 100644
--- a/tests/modules/saml/lib/Auth/Process/NameIDAttributeTest.php
+++ b/tests/modules/saml/lib/Auth/Process/NameIDAttributeTest.php
@@ -23,7 +23,7 @@ class NameIDAttributeTest extends TestCase
      * @param array $request  The request state.
      * @return array  The state array after processing.
      */
-    private function processFilter(array $config, array $request)
+    private function processFilter(array $config, array $request): array
     {
         $filter = new NameIDAttribute($config, null);
         $filter->process($request);
diff --git a/tests/modules/saml/lib/Auth/Source/Auth_Source_SP_Test.php b/tests/modules/saml/lib/Auth/Source/Auth_Source_SP_Test.php
index 664c7f1cc064693a5364ee01004e2cdee7450ef1..181f7d4ff021f6c06b4cb078e0ca3f8fd3da1bc7 100644
--- a/tests/modules/saml/lib/Auth/Source/Auth_Source_SP_Test.php
+++ b/tests/modules/saml/lib/Auth/Source/Auth_Source_SP_Test.php
@@ -31,7 +31,7 @@ class SPTest extends ClearStateTestCase
     /**
      * @return \SimpleSAML\Configuration
      */
-    private function getIdpMetadata()
+    private function getIdpMetadata(): Configuration
     {
         if (!$this->idpMetadata) {
             $this->idpMetadata = new Configuration(
diff --git a/tests/modules/saml/lib/IdP/SAML2Test.php b/tests/modules/saml/lib/IdP/SAML2Test.php
index cf3415680cc1b33d9036e324aa05c6708eb1897d..94dcef988afeef1dc5dc123f52a72fcdc3feef35 100644
--- a/tests/modules/saml/lib/IdP/SAML2Test.php
+++ b/tests/modules/saml/lib/IdP/SAML2Test.php
@@ -158,7 +158,7 @@ class SAML2Test extends ClearStateTestCase
      * @param array $queryParams
      * @return array The state array used for handling the authentication request.
      */
-    private function idpInitiatedHelper(array $queryParams)
+    private function idpInitiatedHelper(array $queryParams): array
     {
         /** @var \PHPUnit_Framework_MockObject_MockObject $idpStub */
         $idpStub = $this->getMockBuilder(IdP::class)
diff --git a/tests/modules/saml/lib/IdP/SQLNameIDTest.php b/tests/modules/saml/lib/IdP/SQLNameIDTest.php
index be43071d2703d7f1bfdc85a51c0cc1af3b2b765b..17ef569278af2207b411557eac592de775b5d63a 100644
--- a/tests/modules/saml/lib/IdP/SQLNameIDTest.php
+++ b/tests/modules/saml/lib/IdP/SQLNameIDTest.php
@@ -96,7 +96,7 @@ class SQLNameIDTest extends TestCase
 
     /**
      * @param \SimpleSAML\Configuration|\SimpleSAML\Store $service
-     * @param string $className
+     * @param class-string $className
      * @return void
      */
     protected function clearInstance($service, $className)
diff --git a/tests/www/TemplateTest.php b/tests/www/TemplateTest.php
deleted file mode 100644
index 7bba7931b924de212b5423e438963f4535445f5d..0000000000000000000000000000000000000000
--- a/tests/www/TemplateTest.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-namespace SimpleSAML\Test\Web;
-
-use PHPUnit\Framework\TestCase;
-use SimpleSAML\Configuration;
-use SimpleSAML\XHTML\Template;
-use SimpleSAML\Module;
-use Twig\Error\SyntaxError;
-
-/**
- * Simple test for syntax-checking Twig-templates.
- *
- * @author Tim van Dijen <tvdijen@gmail.com>
- * @package SimpleSAMLphp
- */
-class TemplateTest extends TestCase
-{
-    /**
-     * @return void
-     */
-    public function testSyntax()
-    {
-        $config = Configuration::loadFromArray([
-            'usenewui' => true,
-            'module.enable' => array_fill_keys(Module::getModules(), true),
-        ]);
-        Configuration::setPreLoadedConfig($config);
-
-        $basedir = dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'templates';
-
-        // Base templates
-        $files = array_diff(scandir($basedir), ['.', '..']);
-        foreach ($files as $file) {
-            if (preg_match('/.twig$/', $file)) {
-                $t = new Template($config, $file);
-                ob_start();
-                try {
-                    $t->show();
-                    $this->addToAssertionCount(1);
-                } catch (SyntaxError $e) {
-                    $this->fail($e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
-                }
-                ob_end_clean();
-            }
-        }
-
-        // Module templates
-        foreach (Module::getModules() as $module) {
-            $basedir = Module::getModuleDir($module) . DIRECTORY_SEPARATOR . 'templates';
-            if (file_exists($basedir)) {
-                $files = array_diff(scandir($basedir), ['.', '..']);
-                foreach ($files as $file) {
-                    if (preg_match('/.twig$/', $file)) {
-                        $t = new Template($config, $module . ':' . $file);
-                        ob_start();
-                        try {
-                            $t->show();
-                            $this->addToAssertionCount(1);
-                        } catch (SyntaxError $e) {
-                            $this->fail($e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
-                        }
-                        ob_end_clean();
-                    }
-                }
-            }
-        }
-    }
-}