diff --git a/lib/SimpleSAML/Metadata/Sources/MDQ.php b/lib/SimpleSAML/Metadata/Sources/MDQ.php
index 278cc57a29e10dcadb1560dbb7a5df88e7f29d21..c789f2de99fe3cc4367103538dbe442f2fe7b9ed 100644
--- a/lib/SimpleSAML/Metadata/Sources/MDQ.php
+++ b/lib/SimpleSAML/Metadata/Sources/MDQ.php
@@ -4,21 +4,36 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Metadata\Sources;
 
+use Exception;
 use RobRichards\XMLSecLibs\XMLSecurityDSig;
 use SimpleSAML\Assert\Assert;
 use SimpleSAML\Configuration;
 use SimpleSAML\Error;
 use SimpleSAML\Logger;
+use SimpleSAML\Metadata\MetaDataStorageSource;
 use SimpleSAML\Metadata\SAMLParser;
 use SimpleSAML\Utils;
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\HttpFoundation\File\File;
+
+use function array_key_exists;
+use function error_get_last;
+use function is_array;
+use function serialize;
+use function sha1;
+use function sprintf;
+use function strval;
+use function time;
+use function unserialize;
+use function urlencode;
 
 /**
  * This class implements SAML Metadata Query Protocol
  *
- * @package SimpleSAMLphp
+ * @package simplesamlphp/simplesamlphp
  */
 
-class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
+class MDQ extends MetaDataStorageSource
 {
     /**
      * The URL of MDQ server (url:port)
@@ -34,7 +49,6 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
      */
     private ?string $cacheDir;
 
-
     /**
      * The maximum cache length, in seconds.
      *
@@ -42,6 +56,11 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
      */
     private int $cacheLength;
 
+    /**
+     * @var \Symfony\Component\Filesystem\Filesystem;
+     */
+    private Filesystem $fileSystem;
+
 
     /**
      * This function initializes the dynamic XML metadata source.
@@ -61,7 +80,7 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
     protected function __construct(array $config)
     {
         if (!array_key_exists('server', $config)) {
-            throw new \Exception(__CLASS__ . ": the 'server' configuration option is not set.");
+            throw new Exception(__CLASS__ . ": the 'server' configuration option is not set.");
         } else {
             $this->server = $config['server'];
         }
@@ -78,6 +97,8 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
         } else {
             $this->cacheLength = 86400;
         }
+
+        $this->fileSystem = new Filesystem();
     }
 
 
@@ -120,7 +141,7 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
      * @param string $set The metadata set this entity belongs to.
      * @param string $entityId The entity id of this entity.
      *
-     * @return array|NULL  The associative array with the metadata for this entity, or NULL
+     * @return array|null  The associative array with the metadata for this entity, or NULL
      *                     if the entity could not be found.
      * @throws \Exception If an error occurs while loading metadata from cache.
      */
@@ -130,41 +151,47 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
             return null;
         }
 
-        $cachefilename = $this->getCacheFilename($set, $entityId);
-        if (!file_exists($cachefilename)) {
+        $cacheFileName = $this->getCacheFilename($set, $entityId);
+        if (!$this->fileSystem->exists($cacheFileName)) {
             return null;
         }
-        if (!is_readable($cachefilename)) {
-            throw new \Exception(__CLASS__ . ': could not read cache file for entity [' . $cachefilename . ']');
+
+        $file = new file($cacheFileName);
+        if (!$file->isReadable()) {
+            throw new Exception(sprintf('%s: could not read cache file for entity [%s]', strval($file), __CLASS__));
         }
-        Logger::debug(__CLASS__ . ': reading cache [' . $entityId . '] => [' . $cachefilename . ']');
+        Logger::debug(sprintf('%s: reading cache [%s] => [%s]', __CLASS__, $entityId, strval($file)));
 
         /* Ensure that this metadata isn't older that the cachelength option allows. This
          * must be verified based on the file, since this option may be changed after the
          * file is written.
          */
-        $stat = stat($cachefilename);
-        if ($stat['mtime'] + $this->cacheLength <= time()) {
-            Logger::debug(__CLASS__ . ': cache file older that the cachelength option allows.');
+        if (($file->getMtime() + $this->cacheLength) <= time()) {
+            Logger::debug(sprintf('%s: cache file older that the cachelength option allows.', __CLASS__));
             return null;
         }
 
-        $rawData = file_get_contents($cachefilename);
+        $rawData = $file->getContent();
         if (empty($rawData)) {
             /** @var array $error */
             $error = error_get_last();
-            throw new \Exception(
-                __CLASS__ . ': error reading metadata from cache file "' . $cachefilename . '": ' . $error['message']
-            );
+            throw new Exception(sprintf(
+                '%s: error reading metadata from cache file "%s": %s',
+                __CLASS__,
+                strval($file),
+                $error['message'],
+            ));
         }
 
         $data = unserialize($rawData);
         if ($data === false) {
-            throw new \Exception(__CLASS__ . ': error unserializing cached data from file "' . $cachefilename . '".');
+            throw new Exception(
+                sprintf('%s: error unserializing cached data from file "%s".', __CLASS__, strval($file))
+            );
         }
 
         if (!is_array($data)) {
-            throw new \Exception(__CLASS__ . ': Cached metadata from "' . $cachefilename . '" wasn\'t an array.');
+            throw new Exception(sprintf('%s: Cached metadata from "%s" wasn\'t an array.', __CLASS__, strval($file)));
         }
 
         return $data;
@@ -186,12 +213,14 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
             return;
         }
 
-        $cachefilename = $this->getCacheFilename($set, $entityId);
-        if (!is_writable(dirname($cachefilename))) {
-            throw new \Exception(__CLASS__ . ': could not write cache file for entity [' . $cachefilename . ']');
+        $cacheFileName = $this->getCacheFilename($set, $entityId);
+        $file = new File($cacheFileName);
+        if (!$file->isWritable()) {
+            throw new Exception(sprintf('%s: could not write cache file for entity [%s]', __CLASS__, strval($file)));
         }
-        Logger::debug(__CLASS__ . ': Writing cache [' . $entityId . '] => [' . $cachefilename . ']');
-        file_put_contents($cachefilename, serialize($data));
+        Logger::debug(sprintf('%s: Writing cache [%s] => [%s]', __CLASS__, $entityId, strval($file)));
+
+        $this->fileSystem->appendToFile(strval($file), serialize($data), true);
     }
 
 
@@ -201,7 +230,7 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
      * @param \SimpleSAML\Metadata\SAMLParser $entity A SAML2Parser representing an entity.
      * @param string                         $set The metadata set we are looking for.
      *
-     * @return array|NULL  The associative array with the metadata, or NULL if no metadata for
+     * @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, string $set): ?array
@@ -214,7 +243,7 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
             case 'attributeauthority-remote':
                 return $entity->getAttributeAuthorities();
             default:
-                Logger::warning(__CLASS__ . ': unknown metadata set: \'' . $set . '\'.');
+                Logger::warning(sprintf('%s: unknown metadata set: \'%s\'.', __CLASS__, $set));
         }
 
         return null;
@@ -241,12 +270,12 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
      */
     public function getMetaData(string $entityId, string $set): ?array
     {
-        Logger::info(__CLASS__ . ': loading metadata entity [' . $entityId . '] from [' . $set . ']');
+        Logger::info(sprintf('%s: loading metadata entity [%s] from [%s]', __CLASS__, $entityId, $set));
 
         // read from cache if possible
         try {
             $data = $this->getFromCache($set, $entityId);
-        } catch (\Exception $e) {
+        } catch (Exception $e) {
             Logger::error($e->getMessage());
             // proceed with fetching metadata even if the cache is broken
             $data = null;
@@ -259,45 +288,49 @@ class MDQ extends \SimpleSAML\Metadata\MetaDataStorageSource
 
         if (isset($data)) {
             // metadata found in cache and not expired
-            Logger::debug(__CLASS__ . ': using cached metadata for: ' . $entityId . '.');
+            Logger::debug(sprintf('%s: using cached metadata for: %s.', __CLASS__, $entityId));
             return $data;
         }
 
         // look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt
         $mdq_url = $this->server . '/entities/' . urlencode($entityId);
 
-        Logger::debug(__CLASS__ . ': downloading metadata for "' . $entityId . '" from [' . $mdq_url . ']');
+        Logger::debug(sprintf('%s: downloading metadata for "%s" from [%s]', __CLASS__, $entityId, $mdq_url));
         $httpUtils = new Utils\HTTP();
         try {
             $xmldata = $httpUtils->fetch($mdq_url);
-        } catch (\Exception $e) {
+        } catch (Exception $e) {
             // Avoid propagating the exception, make sure we can handle the error later
             $xmldata = false;
         }
 
         if (empty($xmldata)) {
             $error = error_get_last();
-            Logger::info('Unable to fetch metadata for "' . $entityId . '" from ' . $mdq_url . ': ' .
-                (is_array($error) ? $error['message'] : 'no error available'));
+            Logger::info(sprintf(
+                'Unable to fetch metadata for "%s" from %s: %s',
+                $entityId,
+                $mdq_url,
+                (is_array($error) ? $error['message'] : 'no error available')
+            ));
             return null;
         }
 
         /** @var string $xmldata */
         $entity = SAMLParser::parseString($xmldata);
-        Logger::debug(__CLASS__ . ': completed parsing of [' . $mdq_url . ']');
+        Logger::debug(sprintf('%s: completed parsing of [%s]', __CLASS__, $mdq_url));
 
         $data = self::getParsedSet($entity, $set);
         if ($data === null) {
-            throw new \Exception(
-                __CLASS__ . ': no metadata for set "' . $set . '" available from "' . $entityId . '".'
+            throw new Exception(
+                sprintf('%s: no metadata for set "%s" available from "%s".', __CLASS__, $set, $entityId)
             );
         }
 
         try {
             $this->writeToCache($set, $entityId, $data);
-        } catch (\Exception $e) {
+        } catch (Exception $e) {
             // Proceed without writing to cache
-            Logger::error('Error writing MDQ result to cache: ' . $e->getMessage());
+            Logger::error(sprintf('Error writing MDQ result to cache: %s', $e->getMessage()));
         }
 
         return $data;