[Scummvm-git-logs] scummvm-web master -> d76478f1e6f1f22e69bed53d0c57b67208e18938

lephilousophe noreply at scummvm.org
Sun Aug 24 10:33:34 UTC 2025


This automated email contains information about 23 new commits which have been
pushed to the 'scummvm-web' repo located at https://api.github.com/repos/scummvm/scummvm-web .

Summary:
8c2e6ab345 WEB: Remove obsolete file
1c9380ef3e JANITORIAL: Fix code style
649ade2977 WEB: Make index a void function
261cca2dc3 WEB: Cleanup logic of exception handler
c743cc2187 WEB: Properly initialize the files array
d8ff9c3c75 WEB: Remove useless check
3d37c952a8 WEB: Use null instead of false when no data is available
d7c3bd0528 WEB: Various small fixes
51ce727edd WEB: Simplify subsections construction
cc9fa80181 WEB: Add typing
d2e028140b BUILD: Check codestyle after fixing trivial errors
271f169928 BUILD: Increase PHPStan level
710b95a5f4 WEB: Better error handling
7b0a2033de WEB: Gracefully handle cases when there are no extension in files
a1afb8a64d WEB: Handle cases when WebLink are mixed with Files
e2f72c8d8b WEB: Modify all extra attributes at once
d74cb92ec2 WEB: Make sure the Page object provides an index function
9a59454525 BUILD: Increase PHPStan level
93d3aa56a1 WEB: Handle null cases
f28c5198f4 WEB: Avoid returning null when there is no screenshot
a04dab5f7e BUILD: Increase PHPStan level
7f6baf107a BUILD: Add license in composer
d76478f1e6 WEB: Add a Github Actions CI check


Commit: 8c2e6ab3456d49d9128e3fd58443381db279ff52
    https://github.com/scummvm/scummvm-web/commit/8c2e6ab3456d49d9128e3fd58443381db279ff52
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:46+02:00

Commit Message:
WEB: Remove obsolete file

Changed paths:
  R include/Objects/DataObject.php


diff --git a/include/Objects/DataObject.php b/include/Objects/DataObject.php
deleted file mode 100644
index f9b1cdf1..00000000
--- a/include/Objects/DataObject.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-namespace ScummVM\Objects;
-
-/**
- * The DataObject class is inherited by all other new data objects
- * and houses all common functions.
- */
-abstract class DataObject
-{
-    protected $id;
-    const NO_ID = 'Data object %s is missing an ID field';
-    const BAD_KEY = 'Field %s is required and cannot be empty for %s';
-    const NO_KEY = "Key is required and can not be blank";
-
-    public function __construct($data)
-    {
-        try {
-            $this->id = $this->assignFromArray('id', $data);
-        } catch (\ErrorException $ex) {
-            throw new \ErrorException(\sprintf(self::NO_ID, \get_class($this)));
-        }
-    }
-
-     /* Get the ID. */
-    public function getId()
-    {
-        return $this->id;
-    }
-
-    public function __toString()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Helper method to safely retrieve an object
-     * from an array.
-     */
-    protected function assignFromArray($key, $array, $required = false)
-    {
-        if ($required) {
-            if ($key === '') {
-                throw new \ErrorException(self::NO_KEY);
-            }
-
-            if (!isset($array[$key]) || $array[$key] === '') {
-                throw new \ErrorException(\sprintf(self::BAD_KEY, $key, \get_class($this)));
-            }
-        }
-        return $array[$key];
-    }
-}


Commit: 1c9380ef3ebf852f959f0ac2d00416727c2773de
    https://github.com/scummvm/scummvm-web/commit/1c9380ef3ebf852f959f0ac2d00416727c2773de
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:46+02:00

Commit Message:
JANITORIAL: Fix code style

This should not have any behavioral changes

Changed paths:
    include/Constants.php
    include/DataUtils.php
    include/LocalizationUtils.php
    include/Models/DirectorDemosModel.php
    include/Models/DownloadsModel.php
    include/Models/NewsModel.php
    include/Models/ScreenshotsModel.php
    include/Objects/File.php
    include/Objects/News.php
    include/OrmObjects/Compatibility.php
    include/OrmObjects/ScreenshotQuery.php
    public_html/index.php


diff --git a/include/Constants.php b/include/Constants.php
index c145e70b..dd2ac078 100644
--- a/include/Constants.php
+++ b/include/Constants.php
@@ -1,4 +1,4 @@
-<?php
+<?php // phpcs:ignore PSR1.Files.SideEffects.FoundWithSymbols
 namespace ScummVM;
 
 class Constants
diff --git a/include/DataUtils.php b/include/DataUtils.php
index 3b5128a1..27be50b0 100644
--- a/include/DataUtils.php
+++ b/include/DataUtils.php
@@ -1,4 +1,4 @@
-<?php
+<?php // phpcs:ignore PSR1.Files.SideEffects.FoundWithSymbols -- Script directly executed
 namespace ScummVM;
 
 require_once __DIR__ . '/../vendor/autoload.php';
@@ -21,6 +21,7 @@ use Propel\Runtime\Map\TableMap;
  */
 class DataUtils
 {
+    // phpcs:ignore Generic.Files.LineLength
     const SHEET_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQamumX0p-DYQa5Umi3RxX-pHM6RZhAj1qvUP0jTmaqutN9FwzyriRSXlO9rq6kR60pGIuPvCDzZL3s/pub?output=tsv';
     // filename => sheet id
     const SHEET_IDS = [
diff --git a/include/LocalizationUtils.php b/include/LocalizationUtils.php
index b668d5f1..3c23863d 100644
--- a/include/LocalizationUtils.php
+++ b/include/LocalizationUtils.php
@@ -1,4 +1,4 @@
-<?php
+<?php // phpcs:ignore PSR1.Files.SideEffects.FoundWithSymbols -- Script directly executed
 namespace ScummVM;
 
 require_once __DIR__ . '/../vendor/autoload.php';
@@ -58,7 +58,9 @@ class LocalizationUtils
             $l10n = json_decode(file_get_contents($newsFile));
 
             foreach ($l10n as $key => $translatedArticle) {
-                $englishArticle = YamlFrontMatter::parse(file_get_contents(join(DIRECTORY_SEPARATOR, [DIR_DATA, DEFAULT_LOCALE, 'news', "{$key}.markdown"])));
+                $englishArticle = YamlFrontMatter::parse(file_get_contents(
+                    join(DIRECTORY_SEPARATOR, [DIR_DATA, DEFAULT_LOCALE, 'news', "{$key}.markdown"])
+                ));
 
                 $date = self::$purifier->purify($englishArticle->date);
                 $author = self::$purifier->purify($englishArticle->author);
@@ -103,7 +105,11 @@ class LocalizationUtils
 
             file_put_contents(
                 $newsFile,
-                \str_replace('\r\n', '\n', json_encode($news, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES |  JSON_UNESCAPED_UNICODE) . "\n")
+                \str_replace(
+                    '\r\n',
+                    '\n',
+                    json_encode($news, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES |  JSON_UNESCAPED_UNICODE) . "\n"
+                )
             );
         }
     }
diff --git a/include/Models/DirectorDemosModel.php b/include/Models/DirectorDemosModel.php
index 735eeb38..f967b727 100644
--- a/include/Models/DirectorDemosModel.php
+++ b/include/Models/DirectorDemosModel.php
@@ -16,7 +16,7 @@ class DirectorDemosModel extends BasicModel
             $demos = DirectorDemoQuery::create()
                 ->orderByVersion()
                 ->orderByTitle()
-            ->find();
+                ->find();
             $groupedData =  $this->createGroups($demos);
             $this->saveToCache($groupedData);
         }
diff --git a/include/Models/DownloadsModel.php b/include/Models/DownloadsModel.php
index 2d73aa0f..6df855af 100644
--- a/include/Models/DownloadsModel.php
+++ b/include/Models/DownloadsModel.php
@@ -18,7 +18,7 @@ class DownloadsModel extends BasicModel
         $sections = $this->getFromCache();
         if (is_null($sections)) {
             $parsedData = DownloadQuery::create()
-                    ->findByEnabled(true);
+                ->findByEnabled(true);
             $sections = [];
             $sectionsData = $this->getSectionData();
             foreach ($parsedData as $data) {
@@ -75,13 +75,21 @@ class DownloadsModel extends BasicModel
     {
         return [
             "current"=>["title"=>"{#downloadsXMLTitle#} {#downloadsXMLVersion#}"],
-            "release"=>["title"=>"{#downloadsBinaries#}","notes"=>"{#downloadsBinariesNote1#} <a href='https://downloads.scummvm.org/frs/scummvm/{ldelim}release{rdelim}/ReleaseNotes.html'>{#downloadsBinariesNote2#}</a>.<p>{#downloadsBinariesNote3#}</p>"],
+            "release"=>["title"=>"{#downloadsBinaries#}",
+                "notes"=>"{#downloadsBinariesNote1#} <a href='https://downloads.scummvm.org/frs/scummvm/" .
+                "{ldelim}release{rdelim}/ReleaseNotes.html'>{#downloadsBinariesNote2#}</a>." .
+                "<p>{#downloadsBinariesNote3#}</p>"],
             "source"=>["title"=>"{#downloadsSourceCode#}"],
             "scummvm-tools"=>["title"=>"{#downloadsTools#}"],
             "legacy"=>["title"=>"{#downloadsOldBinaries#}"],
-            "old"=>["title"=>"{#downloadsOld#}","notes"=>"{#downloadsOldBinariesNote#} {#downloadsOldBinariesFrsNote1#} <a href='https://downloads.scummvm.org/frs/scummvm/'>{#downloadsOldBinariesFrsNote2#}</a> {#downloadsOldBinariesFrsNote3#}"],
+            "old"=>["title"=>"{#downloadsOld#}",
+                "notes"=>"{#downloadsOldBinariesNote#} {#downloadsOldBinariesFrsNote1#} " .
+                "<a href='https://downloads.scummvm.org/frs/scummvm/'>{#downloadsOldBinariesFrsNote2#}</a> " .
+                "{#downloadsOldBinariesFrsNote3#}"],
             "daily"=>["title"=>"{#downloadsDailyBuilds#}"],
-            "daily_downloads"=>["title"=>"{#downloadsDailyBuilds#}","notes"=>"<strong>{#downloadsDailyNote1#}</strong> {#downloadsDailyNote2#}<p>{#downloadsDailyNote3#}</p>"],
+            "daily_downloads"=>["title"=>"{#downloadsDailyBuilds#}",
+                "notes"=>"<strong>{#downloadsDailyNote1#}</strong> {#downloadsDailyNote2#}" .
+                "<p>{#downloadsDailyNote3#}</p>"],
             "libs"=>["title"=>"{#downloadsLibraries#}"],
             "required"=>["title"=>"{#downloadsRequiredLibraries#}"],
             "optional"=>["title"=>"{#downloadsOptionalLibraries#}"]
diff --git a/include/Models/NewsModel.php b/include/Models/NewsModel.php
index 94e58114..945d2596 100644
--- a/include/Models/NewsModel.php
+++ b/include/Models/NewsModel.php
@@ -46,7 +46,10 @@ class NewsModel extends BasicModel
                 if (!is_file(($fname = join(DIRECTORY_SEPARATOR, [DIR_DATA, $lang, 'news', basename($filename)])))
                     || !is_readable($fname) || !($data = @file_get_contents($fname))
                 ) {
-                    if (!($data = @file_get_contents(join(DIRECTORY_SEPARATOR, [DIR_DATA, DEFAULT_LOCALE, 'news', $filename])))) {
+                    if (!($data = @file_get_contents(join(
+                        DIRECTORY_SEPARATOR,
+                        [DIR_DATA, DEFAULT_LOCALE, 'news', $filename]
+                    )))) {
                         continue;
                     }
                 }
diff --git a/include/Models/ScreenshotsModel.php b/include/Models/ScreenshotsModel.php
index 3481367f..8696cd60 100644
--- a/include/Models/ScreenshotsModel.php
+++ b/include/Models/ScreenshotsModel.php
@@ -152,7 +152,8 @@ class ScreenshotsModel extends BasicModel
         $count = 0;
         // Iterate over each game and count the number of screenshot files
         foreach ($games as $game) {
-            $count += count(glob(DIR_STATIC . DIR_SCREENSHOTS . '/' .  str_replace(':', '/', $game->getId()) . '/*_full.png'));
+            $count += count(glob(DIR_STATIC . DIR_SCREENSHOTS . '/' .
+                str_replace(':', '/', $game->getId()) . '/*_full.png'));
         }
         return $count;
     }
diff --git a/include/Objects/File.php b/include/Objects/File.php
index 4eaf40e9..a9697846 100644
--- a/include/Objects/File.php
+++ b/include/Objects/File.php
@@ -14,7 +14,6 @@ class File extends BasicObject
     private $url;
     private $extra_info;
     private $notes;
-    //private $subcategory;
     private $user_agent;
     private $version;
 
@@ -25,7 +24,6 @@ class File extends BasicObject
         $this->category = $data['category'];
         $this->category_icon = $data['category_icon'];
         $this->notes = isset($data['notes']) ? $data['notes'] : '';
-        //$this->subcategory = $data['subcategory'] ?? null;
         $this->user_agent = isset($data["user_agent"]) ? $data["user_agent"] : "";
         $this->version = isset($data['version']) ? strtolower($data['version']) : null;
 
diff --git a/include/Objects/News.php b/include/Objects/News.php
index 31676542..ebbd230f 100644
--- a/include/Objects/News.php
+++ b/include/Objects/News.php
@@ -24,7 +24,8 @@ class News
         $object = YamlFrontMatter::parse($data);
         $Parsedown = new \Parsedown();
 
-        $this->title = $processContent ? $this->processText($Parsedown->line($object->title)) : $Parsedown->line($object->title);
+        $this->title = $processContent ? $this->processText($Parsedown->line($object->title)) :
+            $Parsedown->line($object->title);
         $this->date = $object->date;
         $this->author = $object->author;
         $body = $this->localizeLinks($object->body());
diff --git a/include/OrmObjects/Compatibility.php b/include/OrmObjects/Compatibility.php
index 585d4101..95b86457 100644
--- a/include/OrmObjects/Compatibility.php
+++ b/include/OrmObjects/Compatibility.php
@@ -68,7 +68,8 @@ class Compatibility extends BaseCompatibility
             if (str_starts_with($dataFiles, "https://")) {
                 $wikiLink = "- [ScummVM Wiki]({$this->getGame()->getDataFiles()})";
             } else {
-                $wikiLink = "- [ScummVM Wiki](https://wiki.scummvm.org/index.php?title={$this->getGame()->getDataFiles()})";
+                $wikiLink = "- [ScummVM Wiki](https://wiki.scummvm.org/index.php?" .
+                    "title={$this->getGame()->getDataFiles()})";
             }
             $links[] = $wikiLink . " (includes list of required data files)";
         }
@@ -98,7 +99,8 @@ class Compatibility extends BaseCompatibility
         }
         $zoomId = $this->getGame()->getZoomId();
         if ($zoomId) {
-            $availableSites[] = "- [ZOOM Platform](" . ZOOM_URL_PREFIX . $zoomId . ZOOM_URL_SUFFIX . ") (affiliate link)";
+            $availableSites[] = "- [ZOOM Platform](" . ZOOM_URL_PREFIX . $zoomId .
+                ZOOM_URL_SUFFIX . ") (affiliate link)";
         }
         // Additional stores could include the ScummVM Freeware Games page
         $additionalStores = $this->getGame()->getAdditionalStores();
@@ -107,7 +109,8 @@ class Compatibility extends BaseCompatibility
         }
         if ($availableSites) {
             $notes .= "\n\n### Available At\n";
-            $notes .= "The ScummVM project does not endorse any individual supplier of games. This list is for reference purposes only.\n";
+            $notes .= "The ScummVM project does not endorse any individual supplier of games. " .
+                "This list is for reference purposes only.\n";
             $notes .= join("\n", $availableSites);
         }
 
diff --git a/include/OrmObjects/ScreenshotQuery.php b/include/OrmObjects/ScreenshotQuery.php
index f62fbb9a..a28b305f 100644
--- a/include/OrmObjects/ScreenshotQuery.php
+++ b/include/OrmObjects/ScreenshotQuery.php
@@ -27,7 +27,8 @@ class ScreenshotQuery extends BaseScreenshotQuery
             $con = Propel::getServiceContainer()->getReadConnection(ScreenshotTableMap::DATABASE_NAME);
         }
 
-        $sql = 'SELECT id, variant, platform_id, language, variant_id, auto_id FROM screenshot ORDER BY RANDOM() LIMIT 1';
+        $sql = "SELECT id, variant, platform_id, language, variant_id, auto_id
+                FROM screenshot ORDER BY RANDOM() LIMIT 1";
         try {
             $stmt = $con->prepare($sql);
             $stmt->execute();
diff --git a/public_html/index.php b/public_html/index.php
index a8d8ec29..46931353 100644
--- a/public_html/index.php
+++ b/public_html/index.php
@@ -1,4 +1,4 @@
-<?php
+<?php // phpcs:ignore PSR1.Files.SideEffects.FoundWithSymbols -- Script directly executed
 namespace ScummVM;
 
 /**
@@ -10,12 +10,12 @@ namespace ScummVM;
  * if the DEV_SERVER environment variable is set to 1
  */
 if (isset($_SERVER['SERVER_SOFTWARE']) &&
-    \preg_match("/PHP [\d\.]+ Development Server/",$_SERVER['SERVER_SOFTWARE'])) {
+    \preg_match("/PHP [\d\.]+ Development Server/", $_SERVER['SERVER_SOFTWARE'])) {
     if (\is_file(__DIR__ . '/' . strtok($_SERVER["REQUEST_URI"], '?'))) {
         return false;
     }
     define('DEV_SERVER', true);
-} else if (getenv('DEV_SERVER') === "1") {
+} elseif (getenv('DEV_SERVER') === "1") {
     define('DEV_SERVER', true);
 } else {
     define('DEV_SERVER', false);
@@ -30,11 +30,11 @@ require_once __DIR__ . '/../include/SiteUtils.php';
  * Multilingual support
  */
 global $lang, $available_languages;
-$languages = array_slice(scandir(DIR_DATA),2);
+$languages = array_slice(scandir(DIR_DATA), 2);
 $available_languages = [];
 foreach ($languages as $l) {
     if (!\is_dir(DIR_DATA . "/$l")) {
-      continue;
+        continue;
     }
     $available_languages[$l] = \locale_get_display_name($l, $l);
 }
@@ -53,30 +53,30 @@ $oldLangs = [
   "ru_RU" => "ru"
 ];
 if (!empty($_GET['lang'])) {
-  $lang = $_GET['lang'];
-  $uri = \preg_replace("/[?&]lang=$lang/i", "", $_SERVER['REQUEST_URI']);
-  if (array_key_exists($lang, $available_languages)) {
-    header("Location: " . "/$lang" . $uri);
-  } elseif (array_key_exists($lang, $oldLangs)) {
-    header("Location: /" . $oldLangs[$lang] . $uri);
-  }
+    $lang = $_GET['lang'];
+    $uri = \preg_replace("/[?&]lang=$lang/i", "", $_SERVER['REQUEST_URI']);
+    if (array_key_exists($lang, $available_languages)) {
+        header("Location: " . "/$lang" . $uri);
+    } elseif (array_key_exists($lang, $oldLangs)) {
+        header("Location: /" . $oldLangs[$lang] . $uri);
+    }
 } elseif (!empty($_COOKIE['lang'])) {
-  $lang = $_COOKIE['lang'];
-  $cookie_options = [
+    $lang = $_COOKIE['lang'];
+    $cookie_options = [
     'expires' => time()-86400,
     'path' => '/',
     'domain' => $_SERVER['HTTP_HOST'],
     'secure' => true,
     'samesite' => 'None'
-  ];
-  if (\strpos($_SERVER['REQUEST_URI'], "/$lang/") === false) {
-    if (array_key_exists($lang, $available_languages)) {
-      header("Location: " . "/$lang" . $_SERVER['REQUEST_URI']);
-    } elseif (array_key_exists($lang, $oldLangs)) {
-      header("Location: /" . $oldLangs[$lang] . $_SERVER['REQUEST_URI']);
+    ];
+    if (\strpos($_SERVER['REQUEST_URI'], "/$lang/") === false) {
+        if (array_key_exists($lang, $available_languages)) {
+            header("Location: " . "/$lang" . $_SERVER['REQUEST_URI']);
+        } elseif (array_key_exists($lang, $oldLangs)) {
+            header("Location: /" . $oldLangs[$lang] . $_SERVER['REQUEST_URI']);
+        }
     }
-  }
-  setcookie("lang", "", $cookie_options);
+    setcookie("lang", "", $cookie_options);
 }
 
 $langs = join("|", array_keys($available_languages));
@@ -102,7 +102,7 @@ if (!is_writeable(SMARTY_DIR_COMPILE)) {
 }
 
 /* Exception handling. */
-set_exception_handler(array('ScummVM\ExceptionHandler', 'handleException'));
+set_exception_handler(array('\ScummVM\ExceptionHandler', 'handleException'));
 
 /* Page mapping. */
 $pages = array(


Commit: 649ade2977d853436721149c899bee57022d934c
    https://github.com/scummvm/scummvm-web/commit/649ade2977d853436721149c899bee57022d934c
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:46+02:00

Commit Message:
WEB: Make index a void function

It's used with Smarty::display which is void so there is never anything
to return and the return value was never checked.
Propagate this change everywhere.

Changed paths:
    include/Controller.php
    include/ExceptionHandler.php
    include/Pages/ArticlePage.php
    include/Pages/CompatibilityPage.php
    include/Pages/DemosPage.php
    include/Pages/DirectorDemosPage.php
    include/Pages/DownloadsPage.php
    include/Pages/FeedsPage.php
    include/Pages/GamesPage.php
    include/Pages/LinksPage.php
    include/Pages/NewsPage.php
    include/Pages/ScreenshotsPage.php
    include/Pages/SimplePage.php
    public_html/index.php


diff --git a/include/Controller.php b/include/Controller.php
index d513a5ce..fa1f8338 100644
--- a/include/Controller.php
+++ b/include/Controller.php
@@ -164,7 +164,7 @@ class Controller
             'content' => $content,
         );
         $this->smarty->assign($vars);
-        return $this->smarty->display('pages/index.tpl');
+        $this->smarty->display('pages/index.tpl');
     }
 
     /* Render the HTML using the template and any set variables and returns it. */
@@ -179,7 +179,7 @@ class Controller
     /* Set up the variables used by the template and render the page. */
     public function renderPage($vars)
     {
-        return $this->display($this->fetch($this->template, $vars));
+        $this->display($this->fetch($this->template, $vars));
     }
 
     /* Assign extra CSS files needed by the different pages/templates. */
diff --git a/include/ExceptionHandler.php b/include/ExceptionHandler.php
index fddcc21d..c0c7d18e 100644
--- a/include/ExceptionHandler.php
+++ b/include/ExceptionHandler.php
@@ -38,6 +38,6 @@ abstract class ExceptionHandler
         self::$exception = $e;
 
         $ep = new SimplePage('exception');
-        return $ep->index($e);
+        $ep->index($e);
     }
 }
diff --git a/include/Pages/ArticlePage.php b/include/Pages/ArticlePage.php
index ba13672b..0803d739 100644
--- a/include/Pages/ArticlePage.php
+++ b/include/Pages/ArticlePage.php
@@ -55,7 +55,7 @@ class ArticlePage extends Controller
         $author = $purifier->purify($article->author);
         $content = $purifier->purify($Parsedown->text($article->body()));
 
-        return $this->renderPage([
+        $this->renderPage([
                 'title' => $title,
                 'description' => htmlentities($this->getHeadline($content)),
                 'content_title' => $title,
diff --git a/include/Pages/CompatibilityPage.php b/include/Pages/CompatibilityPage.php
index 1bda80c7..79b146a5 100644
--- a/include/Pages/CompatibilityPage.php
+++ b/include/Pages/CompatibilityPage.php
@@ -68,9 +68,9 @@ class CompatibilityPage extends Controller
         }
 
         if (!empty($target)) {
-            return $this->getGame($target, $version);
+            $this->getGame($target, $version);
         } else {
-            return $this->getAll($version, $versions);
+            $this->getAll($version, $versions);
         }
     }
 
@@ -87,7 +87,7 @@ class CompatibilityPage extends Controller
 
         $this->template = 'components/compatibility_details.tpl';
 
-        return $this->renderPage(
+        $this->renderPage(
             array(
                 'title' => preg_replace('/{version}/', $version, $this->getConfigVars('compatibilityTitle')),
                 'subtitle' => $game->getGame()->getName(),
@@ -115,7 +115,7 @@ class CompatibilityPage extends Controller
         $last_updated = $this->compatibilityModel->getLastUpdated();
         $this->template = 'pages/compatibility.tpl';
 
-        return $this->renderPage(
+        $this->renderPage(
             [
                 'title' => preg_replace('/{version}/', $version, $this->getConfigVars('compatibilityTitle')),
                 'description' => $this->getConfigVars('compatibilityIntro'),
diff --git a/include/Pages/DemosPage.php b/include/Pages/DemosPage.php
index d80599a0..4899aa56 100644
--- a/include/Pages/DemosPage.php
+++ b/include/Pages/DemosPage.php
@@ -20,7 +20,7 @@ class DemosPage extends Controller
     public function index()
     {
         $demos = $this->gameDemosModel->getAllGroupsAndDemos();
-        return $this->renderPage(
+        $this->renderPage(
             array(
                 'title' => $this->getConfigVars('demosTitle'),
                 'description' => $this->getConfigVars('gamesDemosContentP1'),
diff --git a/include/Pages/DirectorDemosPage.php b/include/Pages/DirectorDemosPage.php
index 62bc08c3..016259a9 100644
--- a/include/Pages/DirectorDemosPage.php
+++ b/include/Pages/DirectorDemosPage.php
@@ -20,7 +20,7 @@ class DirectorDemosPage extends Controller
     public function index()
     {
         $demos = $this->gameDemosModel->getAllGroupsAndDemos();
-        return $this->renderPage(
+        $this->renderPage(
             array(
                 'title' => $this->getConfigVars('directorDemosTitle'),
                 'description' => $this->getConfigVars('gamesDemosContentP1'),
diff --git a/include/Pages/DownloadsPage.php b/include/Pages/DownloadsPage.php
index 33086f12..823b9f15 100644
--- a/include/Pages/DownloadsPage.php
+++ b/include/Pages/DownloadsPage.php
@@ -20,7 +20,7 @@ class DownloadsPage extends Controller
     {
         $downloads = $this->downloadsModel->getAllDownloads();
         $recommendedDownload = $this->downloadsModel->getRecommendedDownload();
-        return $this->renderPage(
+        $this->renderPage(
             array(
                 'title' => $this->getConfigVars('downloadsTitle'),
                 'description' => \strip_tags($this->getConfigVars('downloadsContentP1')),
diff --git a/include/Pages/FeedsPage.php b/include/Pages/FeedsPage.php
index a97215d4..20114a6f 100644
--- a/include/Pages/FeedsPage.php
+++ b/include/Pages/FeedsPage.php
@@ -38,6 +38,5 @@ class FeedsPage extends Controller
                 'news' => $news_items,
             )
         );
-        return true;
     }
 }
diff --git a/include/Pages/GamesPage.php b/include/Pages/GamesPage.php
index cf4757e5..74c8dc9e 100644
--- a/include/Pages/GamesPage.php
+++ b/include/Pages/GamesPage.php
@@ -20,7 +20,7 @@ class GamesPage extends Controller
     public function index()
     {
         $downloads = $this->gameDownloadsModel->getAllDownloads();
-        return $this->renderPage(
+        $this->renderPage(
             array(
                 'title' => $this->getConfigVars('gamesTitle'),
                 'description' => strip_tags($this->getConfigVars('gamesContentP1')),
diff --git a/include/Pages/LinksPage.php b/include/Pages/LinksPage.php
index ef0d8f86..7765e6db 100644
--- a/include/Pages/LinksPage.php
+++ b/include/Pages/LinksPage.php
@@ -21,7 +21,7 @@ class LinksPage extends Controller
     public function index()
     {
         $links = $this->linksModel->getAllGroupsAndLinks();
-        return $this->renderPage(
+        $this->renderPage(
             array(
                 'title' => $this->getConfigVars('linksTitle'),
                 // TODO: add a description
diff --git a/include/Pages/NewsPage.php b/include/Pages/NewsPage.php
index 27100f09..d0f3e16b 100644
--- a/include/Pages/NewsPage.php
+++ b/include/Pages/NewsPage.php
@@ -24,13 +24,15 @@ class NewsPage extends Controller
     {
         $filename = $args['date'] ?? null;
 
-        if ($filename != null) {
-            if (strtolower($filename) == 'archive' || $filename == '') {
-                $filename = null;
-            }
-            return $this->getNews($filename);
+        if ($filename === null) {
+            $this->getNewsIntro();
+            return;
         }
-        return $this->getNewsIntro();
+
+        if (strtolower($filename) == 'archive' || $filename == '') {
+            $filename = null;
+        }
+        $this->getNews($filename);
     }
 
     /* Display a specific news item, or all news items. */
@@ -46,7 +48,7 @@ class NewsPage extends Controller
             $description = htmlentities($this->getHeadline($news_items[0]->getContent()));
         }
 
-        return $this->renderPage(
+        $this->renderPage(
             array(
                 'title' => $this->getConfigVars('newsTitle'),
                 'subtitle' => $subtitle,
@@ -71,7 +73,7 @@ class NewsPage extends Controller
             )
         );
 
-        return $this->renderPage(
+        $this->renderPage(
             array(
                 'title' => $this->getConfigVars('homeTitle'),
                 'description' => $this->getConfigVars('introHeaderContentP1'),
diff --git a/include/Pages/ScreenshotsPage.php b/include/Pages/ScreenshotsPage.php
index 8f6b7878..54c10754 100644
--- a/include/Pages/ScreenshotsPage.php
+++ b/include/Pages/ScreenshotsPage.php
@@ -28,7 +28,8 @@ class ScreenshotsPage extends Controller
         );
 
         if ($category) {
-            return $this->getCategory($category, $game);
+            $this->getCategory($category, $game);
+            return;
         }
 
         $screenshot  = $this->screenshotsModel->getAllCategories();
@@ -36,7 +37,7 @@ class ScreenshotsPage extends Controller
 
         $this->template = 'pages/screenshots.tpl';
 
-        return $this->renderPage(
+        $this->renderPage(
             [
                 'title' => $this->getConfigVars('screenshotsTitle'),
                 // TODO: Add a description
@@ -62,7 +63,7 @@ class ScreenshotsPage extends Controller
         }
         $this->template = 'pages/screenshots_category.tpl';
 
-        return $this->renderPage(
+        $this->renderPage(
             [
                 'title' => $this->getConfigVars('screenshotsTitle'),
                 // TODO: Add a description
diff --git a/include/Pages/SimplePage.php b/include/Pages/SimplePage.php
index ab4797c2..9ab5f4d6 100644
--- a/include/Pages/SimplePage.php
+++ b/include/Pages/SimplePage.php
@@ -40,7 +40,7 @@ class SimplePage extends Controller
             $data = $this->model->getAllData();
         }
 
-        return $this->renderPage([
+        $this->renderPage([
             'title' => $this->getConfigVars("{$this->key}Title"),
             'description' => $this->getConfigVars("{$this->key}Description"),
             'content_title' => $this->getConfigVars("{$this->key}ContentTitle"),
diff --git a/public_html/index.php b/public_html/index.php
index 46931353..5099ad26 100644
--- a/public_html/index.php
+++ b/public_html/index.php
@@ -146,19 +146,18 @@ foreach ($pages as $key => $value) {
 
 $match = $router->match(strtolower($_SERVER['REQUEST_URI']));
 
-if ($match) {
-    if ($match['target'] === '\ScummVM\Pages\SimplePage' || $match['target'] === '\ScummVM\Pages\StaticPage') {
-      $page = new $match['target']($match['name']);
-    } else if (strpos($match['target'],"http") === 0) {
-      header("Location: {$match['target']}");
-      return;
-    } else {
-      $page = new $match['target']();
-    }
-    return $page->index($match['params']);
-} else {
-  $page = new \ScummVM\Pages\NewsPage();
-  return $page->index(array());
+if (!$match) {
+    $page = new \ScummVM\Pages\NewsPage();
+    $page->index(array());
+    return;
 }
 
-
+if ($match['target'] === '\ScummVM\Pages\SimplePage' || $match['target'] === '\ScummVM\Pages\StaticPage') {
+    $page = new $match['target']($match['name']);
+} elseif (strpos($match['target'], "http") === 0) {
+    header("Location: {$match['target']}");
+    return;
+} else {
+    $page = new $match['target']();
+}
+$page->index($match['params']);


Commit: 261cca2dc3b83d669f3490af6ce0f47a4338b120
    https://github.com/scummvm/scummvm-web/commit/261cca2dc3b83d669f3490af6ce0f47a4338b120
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:46+02:00

Commit Message:
WEB: Cleanup logic of exception handler

Also use isset to check if the Exception has been set.

Changed paths:
    include/ExceptionHandler.php


diff --git a/include/ExceptionHandler.php b/include/ExceptionHandler.php
index c0c7d18e..523057be 100644
--- a/include/ExceptionHandler.php
+++ b/include/ExceptionHandler.php
@@ -13,23 +13,23 @@ abstract class ExceptionHandler
     /* If the MenuModel cause the exception we need to skip them. */
     public static function skipMenus()
     {
-        $skip_menus = false;
+        if (!isset(self::$exception)) {
+            return false;
+        }
+
         $e = self::$exception;
 
-        if (!is_null($e)) {
-            if (basename($e->getFile()) == 'MenuModel.php') {
-                $skip_menus = true;
-            } else {
-                foreach ($e->getTrace() as $t) {
-                    if (basename($t['file'] ?? '') == 'MenuModel.php') {
-                        $skip_menus = true;
-                        break;
-                    }
-                }
+        if (basename($e->getFile()) == 'MenuModel.php') {
+            return true;
+        }
+
+        foreach ($e->getTrace() as $t) {
+            if (basename($t['file'] ?? '') == 'MenuModel.php') {
+                return true;
             }
         }
 
-        return $skip_menus;
+        return false;
     }
 
     /* Handle exceptions. */


Commit: c743cc2187cad1e8ecf4026f034e7b8f527741c2
    https://github.com/scummvm/scummvm-web/commit/c743cc2187cad1e8ecf4026f034e7b8f527741c2
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:46+02:00

Commit Message:
WEB: Properly initialize the files array

Creating an array using the [] operator is allowed but not recommended.
Also check if it exists using isset.

Changed paths:
    include/OrmObjects/Screenshot.php


diff --git a/include/OrmObjects/Screenshot.php b/include/OrmObjects/Screenshot.php
index 990f6642..5f78967e 100644
--- a/include/OrmObjects/Screenshot.php
+++ b/include/OrmObjects/Screenshot.php
@@ -19,7 +19,8 @@ class Screenshot extends BaseScreenshot
 
     public function getFiles()
     {
-        if (!$this->files) {
+        if (!isset($this->files)) {
+            $this->files = [];
             $gameId = str_replace(':', '/', $this->getGame()->getId());
             foreach (glob(DIR_STATIC . DIR_SCREENSHOTS . '/' . $gameId . '/' . $this->getFileMask()) as $file) {
                 // Remove the base folder
@@ -38,7 +39,7 @@ class Screenshot extends BaseScreenshot
 
     public function addFiles($files)
     {
-        if (is_array($this->files)) {
+        if (isset($this->files)) {
             $this->files = array_merge($this->files, $files);
         } else {
             $this->files = $files;


Commit: d8ff9c3c753450e3f3149be6663b333581b1212b
    https://github.com/scummvm/scummvm-web/commit/d8ff9c3c753450e3f3149be6663b333581b1212b
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:46+02:00

Commit Message:
WEB: Remove useless check

The function is expected to take an array or a string.
Array is checked above so what remains is a string.
Any other use is an error that should be reported (strlen will fail).

Changed paths:
    include/Controller.php


diff --git a/include/Controller.php b/include/Controller.php
index fa1f8338..bb580344 100644
--- a/include/Controller.php
+++ b/include/Controller.php
@@ -190,7 +190,7 @@ class Controller
                 $this->css_files,
                 $extra_css
             );
-        } elseif (is_string($extra_css) && strlen($extra_css) > 0) {
+        } elseif (strlen($extra_css) > 0) {
             $this->css_files[] = $extra_css;
         }
     }
@@ -203,7 +203,7 @@ class Controller
                 $this->js_files,
                 $extra_js
             );
-        } elseif (is_string($extra_js) && strlen($extra_js) > 0) {
+        } elseif (strlen($extra_js) > 0) {
             $this->js_files[] = $extra_js;
         }
     }


Commit: 3d37c952a8ba38f6d5a53b7bad756db172b19e6f
    https://github.com/scummvm/scummvm-web/commit/3d37c952a8ba38f6d5a53b7bad756db172b19e6f
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:46+02:00

Commit Message:
WEB: Use null instead of false when no data is available

This allows to use the ? type prefix.

Changed paths:
    include/Models/DownloadsModel.php
    include/Models/ScreenshotsModel.php


diff --git a/include/Models/DownloadsModel.php b/include/Models/DownloadsModel.php
index 6df855af..2283248a 100644
--- a/include/Models/DownloadsModel.php
+++ b/include/Models/DownloadsModel.php
@@ -100,7 +100,7 @@ class DownloadsModel extends BasicModel
     public function getRecommendedDownload()
     {
         if (!isset($_SERVER['HTTP_USER_AGENT'])) {
-            return false;
+            return null;
         }
 
         $osParser = new OsParser();
@@ -140,6 +140,6 @@ class DownloadsModel extends BasicModel
                 'url' => $url,
             ];
         }
-        return false;
+        return null;
     }
 }
diff --git a/include/Models/ScreenshotsModel.php b/include/Models/ScreenshotsModel.php
index 8696cd60..b666184c 100644
--- a/include/Models/ScreenshotsModel.php
+++ b/include/Models/ScreenshotsModel.php
@@ -78,7 +78,7 @@ class ScreenshotsModel extends BasicModel
                 ->find();
 
             $combinedScreenshot = $this->combineScreenshots($screenshots);
-            if (!$combinedScreenshot) {
+            if (is_null($combinedScreenshot)) {
                 throw new \ErrorException(self::INVALID_TARGET);
             }
 
@@ -92,13 +92,13 @@ class ScreenshotsModel extends BasicModel
      * Combines multiple screenshots into a single screenshot
      *
      * @param  Collection $screenshots
-     * @return Screenshot|bool
+     * @return ?Screenshot
      */
     private function combineScreenshots(iterable $screenshots)
     {
         $count = $screenshots->count();
         if ($count === 0) {
-            return false;
+            return null;
         } elseif ($count === 1) {
             $screenshots[0]->getFiles();
             return $screenshots[0];


Commit: d7c3bd0528f28ca4108f2ac62f8ad06fcaa93f62
    https://github.com/scummvm/scummvm-web/commit/d7c3bd0528f28ca4108f2ac62f8ad06fcaa93f62
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Various small fixes

These are either cosmetic or spotted by PHPStan

Changed paths:
    include/Controller.php
    include/FileUtils.php
    include/Models/BasicModel.php
    include/Models/GameDownloadsModel.php
    include/Models/ScreenshotsModel.php
    include/Objects/BasicObject.php
    include/Objects/File.php
    include/Pages/ArticlePage.php
    include/Pages/SimplePage.php


diff --git a/include/Controller.php b/include/Controller.php
index bb580344..30c8addc 100644
--- a/include/Controller.php
+++ b/include/Controller.php
@@ -105,7 +105,7 @@ class Controller
     private function isRtl($localeName)
     {
         $rtl_chars_pattern = '/[\x{0590}-\x{05ff}\x{0600}-\x{06ff}]/u';
-        return preg_match($rtl_chars_pattern, $localeName);
+        return preg_match($rtl_chars_pattern, $localeName) === 1;
     }
 
     /**
diff --git a/include/FileUtils.php b/include/FileUtils.php
index b5afe195..0fe870a1 100644
--- a/include/FileUtils.php
+++ b/include/FileUtils.php
@@ -8,6 +8,8 @@ use DateTime;
 */
 class FileUtils
 {
+    private const DOUBLE_EXTENSIONS = ['.bz2', '.gz', '.lz', '.xz', '.7z'];
+
     /**
     * Returns whether or not the file exists and is readable
     *
@@ -60,7 +62,7 @@ class FileUtils
         $extension = substr($path, (strrpos($path, '.')));
 
         // For certain extensions, check for another extension (e.g. foo.tar.gz => tar.gz)
-        if ($extension == '.bz2' || $extension == '.gz' || $extension == '.lz' || $extension == '.xz' || $extension == '.7z') {
+        if (in_array($extension, self::DOUBLE_EXTENSIONS)) {
             $extension = substr($path, strrpos($path, '.', -(strlen($path) - strrpos($path, '.') + 1)));
         }
         return $extension;
diff --git a/include/Models/BasicModel.php b/include/Models/BasicModel.php
index 2dbfe8a1..02edc29e 100644
--- a/include/Models/BasicModel.php
+++ b/include/Models/BasicModel.php
@@ -14,7 +14,7 @@ abstract class BasicModel
 
     public function __construct()
     {
-        if (is_null(self::$cache)) {
+        if (!isset(self::$cache)) {
             try {
                 $driver = extension_loaded('redis') ? 'redis' : 'predis';
                 $database = DEV_SERVER ? 7 : 8;
diff --git a/include/Models/GameDownloadsModel.php b/include/Models/GameDownloadsModel.php
index c986c911..2fc83c6a 100644
--- a/include/Models/GameDownloadsModel.php
+++ b/include/Models/GameDownloadsModel.php
@@ -24,7 +24,7 @@ class GameDownloadsModel extends BasicModel
             foreach ($categories as $category) {
                 $sections[$category] = new DownloadsSection([
                         'anchor' => $category,
-                        'title' => $sectionsData[$category]['title'],
+                        'title' => $sectionsData[$category]['title'] ?? null,
                         'notes' => $sectionsData[$category]['notes'] ?? null
                     ]);
             }
diff --git a/include/Models/ScreenshotsModel.php b/include/Models/ScreenshotsModel.php
index b666184c..0a9db4fe 100644
--- a/include/Models/ScreenshotsModel.php
+++ b/include/Models/ScreenshotsModel.php
@@ -131,7 +131,7 @@ class ScreenshotsModel extends BasicModel
                 $combined[$subcategory] = $screenshot;
             }
         }
-        \sort($combined, SORT_STRING);
+        \ksort($combined, SORT_STRING);
         return $combined;
     }
 
diff --git a/include/Objects/BasicObject.php b/include/Objects/BasicObject.php
index 8ae77ae4..aa41d792 100644
--- a/include/Objects/BasicObject.php
+++ b/include/Objects/BasicObject.php
@@ -18,7 +18,7 @@ abstract class BasicObject
 
     public function __toString()
     {
-        return $this->getName();
+        return $this->getName() ?? '';
     }
 
      /* Get the name. */
diff --git a/include/Objects/File.php b/include/Objects/File.php
index a9697846..6a2d54ff 100644
--- a/include/Objects/File.php
+++ b/include/Objects/File.php
@@ -23,8 +23,8 @@ class File extends BasicObject
         $this->autoId = $data['auto_id'] ?? null;
         $this->category = $data['category'];
         $this->category_icon = $data['category_icon'];
-        $this->notes = isset($data['notes']) ? $data['notes'] : '';
-        $this->user_agent = isset($data["user_agent"]) ? $data["user_agent"] : "";
+        $this->notes = $data['notes'] ?? '';
+        $this->user_agent = $data['user_agent'] ?? '';
         $this->version = isset($data['version']) ? strtolower($data['version']) : null;
 
         /* If it's not an array, we didn't get any attributes. */
diff --git a/include/Pages/ArticlePage.php b/include/Pages/ArticlePage.php
index 0803d739..dd9efab9 100644
--- a/include/Pages/ArticlePage.php
+++ b/include/Pages/ArticlePage.php
@@ -39,7 +39,7 @@ class ArticlePage extends Controller
     /* Display the index page. */
     public function index($params)
     {
-        if (!$params['article']) {
+        if (empty($params['article'])) {
             throw new \ErrorException(self::ARTICLE_NAME_MISSING);
         }
         $filename = $params['article'] . '.markdown';
diff --git a/include/Pages/SimplePage.php b/include/Pages/SimplePage.php
index 9ab5f4d6..582ab14b 100644
--- a/include/Pages/SimplePage.php
+++ b/include/Pages/SimplePage.php
@@ -28,7 +28,7 @@ class SimplePage extends Controller
             throw new \ErrorException(\sprintf(self::FILE_NOT_FOUND, $templateFile));
         }
         if (array_key_exists($key, self::PAGE_MODELS)) {
-            [$model, $data] = self::PAGE_MODELS[$key];
+            list($model, $data) = self::PAGE_MODELS[$key];
             $this->model = new SimpleYamlModel($model, $data);
         }
     }
@@ -36,7 +36,7 @@ class SimplePage extends Controller
     /* Display the index page. */
     public function index($data = null)
     {
-        if ($this->model) {
+        if (isset($this->model)) {
             $data = $this->model->getAllData();
         }
 


Commit: 51ce727edd063019e7d277a74a22139e7ea5d944
    https://github.com/scummvm/scummvm-web/commit/51ce727edd063019e7d277a74a22139e7ea5d944
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Simplify subsections construction

This will ease typing

Changed paths:
    include/Objects/BasicSection.php


diff --git a/include/Objects/BasicSection.php b/include/Objects/BasicSection.php
index 7b097500..2559a64d 100644
--- a/include/Objects/BasicSection.php
+++ b/include/Objects/BasicSection.php
@@ -4,25 +4,25 @@ namespace ScummVM\Objects;
 /**
  * The BasicSection class is inherited by all other Sections and houses all common
  * functions.
+ *
+ * @phpstan-consistent-constructor
  */
 abstract class BasicSection extends BasicObject
 {
     protected $title;
     protected $anchor;
     protected $subsections;
-    private $className;
 
     public function __construct($data)
     {
         parent::__construct($data);
         $this->title = $data['title'];
         $this->anchor = $data['anchor'];
-        $this->className = static::class;
         $this->subsections = array();
         if (isset($data['subsection'])) {
             parent::toArray($data['subsection']);
             foreach ($data['subsection'] as $value) {
-                $this->subsections[] = new $this->className($value);
+                $this->subsections[] = new static($value);
             }
         }
     }


Commit: cc9fa801811cc344e4ab86b0a6f0552d20055460
    https://github.com/scummvm/scummvm-web/commit/cc9fa801811cc344e4ab86b0a6f0552d20055460
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Add typing

Changed paths:
    include/Controller.php
    include/DataUtils.php
    include/ExceptionHandler.php
    include/FileUtils.php
    include/LocalizationUtils.php
    include/Models/BasicModel.php
    include/Models/CompatibilityModel.php
    include/Models/DirectorDemosModel.php
    include/Models/DownloadsModel.php
    include/Models/GameDemosModel.php
    include/Models/GameDownloadsModel.php
    include/Models/GameModel.php
    include/Models/LinksModel.php
    include/Models/NewsModel.php
    include/Models/ScreenshotsModel.php
    include/Models/SimpleYamlModel.php
    include/Models/VersionsModel.php
    include/Objects/Article.php
    include/Objects/BasicObject.php
    include/Objects/BasicSection.php
    include/Objects/CreditsSection.php
    include/Objects/DownloadsSection.php
    include/Objects/File.php
    include/Objects/MenuItem.php
    include/Objects/News.php
    include/Objects/Person.php
    include/Objects/Sponsor.php
    include/Objects/WebLink.php
    include/OrmObjects/Compatibility.php
    include/OrmObjects/Demo.php
    include/OrmObjects/Screenshot.php
    include/OrmObjects/ScreenshotQuery.php
    include/Pages/ArticlePage.php
    include/Pages/CompatibilityPage.php
    include/Pages/DemosPage.php
    include/Pages/DirectorDemosPage.php
    include/Pages/DownloadsPage.php
    include/Pages/FeedsPage.php
    include/Pages/GamesPage.php
    include/Pages/LinksPage.php
    include/Pages/NewsPage.php
    include/Pages/ScreenshotsPage.php
    include/Pages/SimplePage.php
    include/Pages/StaticPage.php
    include/SiteUtils.php


diff --git a/include/Controller.php b/include/Controller.php
index 30c8addc..308cd678 100644
--- a/include/Controller.php
+++ b/include/Controller.php
@@ -2,6 +2,7 @@
 namespace ScummVM;
 
 use Smarty\Smarty;
+use Smarty\Template;
 use ScummVM\Models\SimpleYamlModel;
 use ScummVM\SiteUtils;
 
@@ -12,11 +13,13 @@ use ScummVM\SiteUtils;
  */
 class Controller
 {
-    protected $template;
-    private $smarty;
-    private $css_files;
-    private $js_files;
-    private $menuModel;
+    protected string $template;
+    private Smarty $smarty;
+    /** @var string[] */
+    private array $css_files;
+    /** @var string[] */
+    private array $js_files;
+    private SimpleYamlModel $menuModel;
 
     /**
      * Constructor that will create a Smarty object and configure it according
@@ -102,7 +105,7 @@ class Controller
     /**
      * Checks whether a locale string is RTL or LTR
      */
-    private function isRtl($localeName)
+    private function isRtl(string $localeName): bool
     {
         $rtl_chars_pattern = '/[\x{0590}-\x{05ff}\x{0600}-\x{06ff}]/u';
         return preg_match($rtl_chars_pattern, $localeName) === 1;
@@ -111,7 +114,7 @@ class Controller
     /**
      * Smarty outputfilter, run just before displaying.
      */
-    public function outputFilter($string, $smarty)
+    public function outputFilter(string $string, Template $smarty): string
     {
         /* Properly encode all ampersands as "&". */
         $string = preg_replace('/&(?!([a-z]+|(#\d+));)/i', '&', $string);
@@ -122,7 +125,7 @@ class Controller
     /**
      * Formating of dateAs, registered as a modifier for Smarty templates.
      */
-    public function dateLocalizedSmartyModifier($timestamp)
+    public function dateLocalizedSmartyModifier(int $timestamp): string
     {
         global $lang;
         $formatter = datefmt_create($lang, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE);
@@ -132,7 +135,7 @@ class Controller
     /**
      * Formating of download URLs, registered as a modifier for Smarty templates.
      */
-    public function downloadsSmartyModifier($path)
+    public function downloadsSmartyModifier(string $path): string
     {
         if (\strpos($path, "http") === 0) {
             return $path;
@@ -148,7 +151,7 @@ class Controller
     /**
      * Formating of version, registered as a modifier for Smarty templates.
      */
-    public function releaseSmartyModifier($string)
+    public function releaseSmartyModifier(string $string): string
     {
         $string = preg_replace("/\{[$]?release\}/", RELEASE, $string);
         $string = preg_replace("/\{[$]?release_tools\}/", RELEASE_TOOLS, $string);
@@ -156,7 +159,7 @@ class Controller
     }
 
     /* Render the HTML using the template and any set variables and displays it. */
-    public function display($content)
+    public function display(string $content): void
     {
         $vars = array(
             'css_files' => $this->css_files,
@@ -168,7 +171,10 @@ class Controller
     }
 
     /* Render the HTML using the template and any set variables and returns it. */
-    public function fetch($template, $vars = null)
+    /**
+     * @param ?array<string, mixed> $vars
+     */
+    public function fetch(string $template, ?array $vars = null): string
     {
         if (!is_null($vars)) {
             $this->smarty->assign($vars);
@@ -177,13 +183,19 @@ class Controller
     }
 
     /* Set up the variables used by the template and render the page. */
-    public function renderPage($vars)
+    /**
+     * @param ?array<string, mixed> $vars
+     */
+    public function renderPage(?array $vars): void
     {
         $this->display($this->fetch($this->template, $vars));
     }
 
     /* Assign extra CSS files needed by the different pages/templates. */
-    public function addCSSFiles($extra_css)
+    /**
+     * @param string|string[] $extra_css
+     */
+    public function addCSSFiles(string|array $extra_css): void
     {
         if (is_array($extra_css)) {
             $this->css_files = array_merge(
@@ -196,7 +208,10 @@ class Controller
     }
 
     /* Assign javascripts files needed by the different pages/templates. */
-    public function addJSFiles($extra_js)
+    /**
+     * @param string|string[] $extra_js
+     */
+    public function addJSFiles(string|array $extra_js): void
     {
         if (is_array($extra_js)) {
             $this->js_files = array_merge(
@@ -208,12 +223,12 @@ class Controller
         }
     }
 
-    protected function getConfigVars($title)
+    protected function getConfigVars(string $title): ?string
     {
         return $this->smarty->getConfigVars($title);
     }
 
-    protected function getHeadline($body)
+    protected function getHeadline(string $body): string
     {
         $headline = '';
         for ($line = \strtok($body, PHP_EOL); $line !== false; $line = \strtok(PHP_EOL)) {
diff --git a/include/DataUtils.php b/include/DataUtils.php
index 27be50b0..2b35d9ae 100644
--- a/include/DataUtils.php
+++ b/include/DataUtils.php
@@ -10,6 +10,7 @@ use League\Csv\Statement;
 use Symfony\Component\Yaml\Yaml;
 use GuzzleHttp\Client;
 use GuzzleHttp\Promise;
+use GuzzleHttp\Psr7\Response;
 use Propel\Runtime\Propel;
 use Propel\Runtime\Connection\Exception\RollbackException;
 use Propel\Runtime\Map\TableMap;
@@ -59,7 +60,7 @@ class DataUtils
      *
      * @return void
      */
-    public static function updateData()
+    public static function updateData(): void
     {
         $client = new Client();
         $promises = [];
@@ -78,7 +79,7 @@ class DataUtils
         \file_put_contents('.clear-cache', '');
     }
 
-    private static function doUpdateData($name, $response)
+    private static function doUpdateData(string $name, Response $response): void
     {
         $tsv = $response->getBody();
         $reader = Reader::createFromString($tsv);
@@ -111,7 +112,7 @@ class DataUtils
         \file_put_contents($outFile, $yaml);
     }
 
-    private static function convertYamlToOrm()
+    private static function convertYamlToOrm(): void
     {
         foreach (self::OBJECT_NAMES as $name => $object) {
             $failures = array();
diff --git a/include/ExceptionHandler.php b/include/ExceptionHandler.php
index 523057be..0ebf0dcd 100644
--- a/include/ExceptionHandler.php
+++ b/include/ExceptionHandler.php
@@ -8,10 +8,10 @@ use ScummVM\Pages\SimplePage;
  */
 abstract class ExceptionHandler
 {
-    private static $exception;
+    private static \Throwable $exception;
 
     /* If the MenuModel cause the exception we need to skip them. */
-    public static function skipMenus()
+    public static function skipMenus(): bool
     {
         if (!isset(self::$exception)) {
             return false;
@@ -33,7 +33,7 @@ abstract class ExceptionHandler
     }
 
     /* Handle exceptions. */
-    public static function handleException($e)
+    public static function handleException(\Throwable $e): void
     {
         self::$exception = $e;
 
diff --git a/include/FileUtils.php b/include/FileUtils.php
index 0fe870a1..eeab5c89 100644
--- a/include/FileUtils.php
+++ b/include/FileUtils.php
@@ -16,7 +16,7 @@ class FileUtils
     * @param $path the path to the file that will be analyzed
     * @return bool whether or not the file exists
     */
-    public static function exists($path)
+    public static function exists(string $path): bool
     {
         $path = FileUtils::toAbsolutePathIfOnServer($path);
         return is_file($path) && is_readable($path);
@@ -28,7 +28,7 @@ class FileUtils
     * @param $path the path to the file that will be analyzed
     * @return string the file size in a human-readable form
     */
-    public static function getFileSize($path)
+    public static function getFileSize(string $path): string
     {
         $path = FileUtils::toAbsolutePathIfOnServer($path);
         // Get the file size, rounded to the nearest kilobyte
@@ -55,7 +55,7 @@ class FileUtils
     * @param $path the path to the file that will be analyzed
     * @return string the extension
     */
-    public static function getExtension($path)
+    public static function getExtension(string $path): string
     {
         $path = FileUtils::toAbsolutePathIfOnServer($path);
         // Get everything to the right of the last period
@@ -74,7 +74,7 @@ class FileUtils
     * @param $path the path to the file that will be analyzed
     * @return string the SHA-256 hash
     */
-    public static function getSha256($path)
+    public static function getSha256(string $path): string
     {
         $path = FileUtils::toAbsolutePathIfOnServer($path);
         // Check if we already have a generated hash file
@@ -97,7 +97,7 @@ class FileUtils
     * @param $path the path to the file that will be analyzed
     * @return string the date
     */
-    public static function getLastModified($path)
+    public static function getLastModified(string $path): string
     {
         $path = FileUtils::toAbsolutePathIfOnServer($path);
         $date = new DateTime();
@@ -116,7 +116,7 @@ class FileUtils
     * @param $path the relative path to the file that will be analyzed
     * @return string the path of the file, either relative or absolute
     */
-    private static function toAbsolutePathIfOnServer($relativePath)
+    private static function toAbsolutePathIfOnServer(string $relativePath): string
     {
         return is_file(DIR_SERVER_ROOT . '/'. $relativePath) ? DIR_SERVER_ROOT . '/'. $relativePath : $relativePath;
     }
diff --git a/include/LocalizationUtils.php b/include/LocalizationUtils.php
index 3c23863d..20cc00ad 100644
--- a/include/LocalizationUtils.php
+++ b/include/LocalizationUtils.php
@@ -9,11 +9,11 @@ use Erusev\Parsedown;
 
 class LocalizationUtils
 {
-    private static $purifier;
+    private static \HTMLPurifier $purifier;
 
     const NO_FILES = 'No Localization Files Found';
 
-    public static function localize()
+    public static function localize(): void
     {
         $config = \HTMLPurifier_Config::createDefault();
         self::$purifier = new \HTMLPurifier($config);
@@ -27,7 +27,7 @@ class LocalizationUtils
         }
     }
 
-    private static function convertLanguageJsonToSmartyIni($lang)
+    private static function convertLanguageJsonToSmartyIni(string $lang): void
     {
         $Parsedown = new \Parsedown();
         $Parsedown->setBreaksEnabled(true);
@@ -46,7 +46,7 @@ class LocalizationUtils
         file_put_contents(join(DIRECTORY_SEPARATOR, [DIR_DATA, $lang, "strings.ini"]), $output);
     }
 
-    private static function updateNewsL10n($lang)
+    private static function updateNewsL10n(string $lang): void
     {
         $newsFile = join(DIRECTORY_SEPARATOR, [DIR_DATA, $lang, "news.json"]);
         // For non-english, create/overwrite JSON files from our l10n file
@@ -79,7 +79,7 @@ class LocalizationUtils
                 if ($lang === 'fr') {
                     $content = preg_replace_callback(
                         "/(?<=\(http)(.*?)(?=\))/u",
-                        function ($matches) {
+                        function (array $matches): string {
                             return preg_replace("/\x{202f}/u", "", $matches[1]);
                         },
                         $content
@@ -114,7 +114,10 @@ class LocalizationUtils
         }
     }
 
-    private static function getAllNews($lang)
+    /**
+     * @return array<string, array{'title': string, 'content': string}>
+     */
+    private static function getAllNews(string $lang): array
     {
         $dir = join(DIRECTORY_SEPARATOR, [DIR_DATA, $lang, 'news']);
 
diff --git a/include/Models/BasicModel.php b/include/Models/BasicModel.php
index 02edc29e..d084a825 100644
--- a/include/Models/BasicModel.php
+++ b/include/Models/BasicModel.php
@@ -10,7 +10,7 @@ abstract class BasicModel
 {
     const FILE_NOT_FOUND = 'The filename %s could not be found';
 
-    protected static $cache;
+    protected static Psr16Adapter $cache;
 
     public function __construct()
     {
@@ -34,7 +34,7 @@ abstract class BasicModel
         }
     }
 
-    protected function getLocalizedFile($filename)
+    protected function getLocalizedFile(string $filename): string
     {
         global $lang;
         if (!$lang) {
@@ -51,7 +51,7 @@ abstract class BasicModel
         }
     }
 
-    protected function saveToCache($data, $key = '')
+    protected function saveToCache(mixed $data, string $key = ''): void
     {
         if ($key) {
             $key = "_$key";
@@ -62,7 +62,7 @@ abstract class BasicModel
         self::$cache->set($cacheKey, $data, 3600);
     }
 
-    protected function getFromCache($key = '')
+    protected function getFromCache(string $key = ''): mixed
     {
         if (\file_exists(DIR_BASE . '/.no-cache')) {
             return null;
diff --git a/include/Models/CompatibilityModel.php b/include/Models/CompatibilityModel.php
index 4150707f..16165d3d 100644
--- a/include/Models/CompatibilityModel.php
+++ b/include/Models/CompatibilityModel.php
@@ -2,6 +2,9 @@
 namespace ScummVM\Models;
 
 use Propel\Runtime\ActiveQuery\Criteria;
+use Propel\Runtime\Collection\Collection;
+
+use ScummVM\OrmObjects\Compatibility;
 use ScummVM\OrmObjects\CompatibilityQuery;
 use ScummVM\OrmObjects\VersionQuery;
 
@@ -14,13 +17,13 @@ class CompatibilityModel extends BasicModel
     const NO_VERSION_TARGET = 'No version and/or target specified.';
     const NOT_FOUND = 'Did not find any games for the specified version.';
 
-    public function getLastUpdated()
+    public function getLastUpdated(): int|false
     {
         return filemtime($this->getLocalizedFile("compatibility.yaml"));
     }
 
     /* Get all the groups and the respectively demos for the specified ScummVM version. */
-    public function getAllData($version)
+    public function getAllData(string $version): Collection
     {
         $data = $this->getFromCache($version);
         if (\is_null($data)) {
@@ -51,7 +54,7 @@ class CompatibilityModel extends BasicModel
     }
 
     /* Get a specific CompatGame-object for the requested version. */
-    public function getGameData($version, $target)
+    public function getGameData(?string $version, ?string $target): Compatibility
     {
         if (!is_string($version) || !is_string($target)) {
             throw new \ErrorException(self::NO_VERSION_TARGET);
@@ -78,7 +81,10 @@ class CompatibilityModel extends BasicModel
         return $gameData;
     }
 
-    public function getAllDataGroups($version)
+    /**
+     * @return array<string, array<Compatibility>>
+     */
+    public function getAllDataGroups(string $version): array
     {
         $data = $this->getAllData($version);
         $compat_data = [];
diff --git a/include/Models/DirectorDemosModel.php b/include/Models/DirectorDemosModel.php
index f967b727..e2084943 100644
--- a/include/Models/DirectorDemosModel.php
+++ b/include/Models/DirectorDemosModel.php
@@ -1,15 +1,22 @@
 <?php
 namespace ScummVM\Models;
 
+use ScummVM\OrmObjects\DirectorDemo;
 use ScummVM\OrmObjects\DirectorDemoQuery;
 
+use Propel\Runtime\Collection\Collection;
+
 /**
  * The DirectorDemosModel class will generate DirectorDemos objects.
  */
 class DirectorDemosModel extends BasicModel
 {
-    /* Get all the groups and their respective demos. */
-    public function getAllGroupsAndDemos()
+    /**
+     * Get all the groups and their respective demos.
+     *
+     * @return array<?int, array{'name': string, 'href': ?int, 'demos': DirectorDemo[]}>
+     */
+    public function getAllGroupsAndDemos(): array
     {
         $groupedData = $this->getFromCache();
         if (is_null($groupedData)) {
@@ -23,7 +30,10 @@ class DirectorDemosModel extends BasicModel
         return $groupedData;
     }
 
-    private function createGroups($demos)
+    /**
+     * @return array<?int, array{'name': string, 'href': ?int, 'demos': DirectorDemo[]}>
+     */
+    private function createGroups(Collection $demos): array
     {
         $groups = [];
         foreach ($demos as $demo) {
diff --git a/include/Models/DownloadsModel.php b/include/Models/DownloadsModel.php
index 2283248a..5ae2b420 100644
--- a/include/Models/DownloadsModel.php
+++ b/include/Models/DownloadsModel.php
@@ -13,7 +13,10 @@ use ScummVM\OrmObjects\DownloadQuery;
 class DownloadsModel extends BasicModel
 {
     /* Get all download entries. */
-    public function getAllDownloads()
+    /**
+     * @return array<string, DownloadsSection>
+     */
+    public function getAllDownloads(): array
     {
         $sections = $this->getFromCache();
         if (is_null($sections)) {
@@ -71,7 +74,10 @@ class DownloadsModel extends BasicModel
         return $sections;
     }
 
-    private function getSectionData()
+    /**
+     * @return array<string, array{'title': string, 'notes'?: string}>
+     */
+    private function getSectionData(): array
     {
         return [
             "current"=>["title"=>"{#downloadsXMLTitle#} {#downloadsXMLVersion#}"],
@@ -97,7 +103,10 @@ class DownloadsModel extends BasicModel
     }
 
     /* Get the recommended download */
-    public function getRecommendedDownload()
+    /**
+     * @return ?array{'os': string, 'version': string, 'extra_text': string, 'url': string}
+     */
+    public function getRecommendedDownload(): ?array
     {
         if (!isset($_SERVER['HTTP_USER_AGENT'])) {
             return null;
diff --git a/include/Models/GameDemosModel.php b/include/Models/GameDemosModel.php
index a78d60bd..605d6c00 100644
--- a/include/Models/GameDemosModel.php
+++ b/include/Models/GameDemosModel.php
@@ -1,15 +1,22 @@
 <?php
 namespace ScummVM\Models;
 
+use ScummVM\OrmObjects\Demo;
 use ScummVM\OrmObjects\DemoQuery;
 
+use Propel\Runtime\Collection\Collection;
+
 /**
- * The GameDemosModel class will generate GameDemo objects.
+ * The GameDemosModel class will generate Demo objects.
  */
 class GameDemosModel extends BasicModel
 {
-    /* Get all the groups and their respective demos. */
-    public function getAllGroupsAndDemos()
+    /**
+     * Get all the groups and their respective demos.
+     *
+     * @return array<?int, array{'name': string, 'href': ?int, 'demos': Demo[]}>
+     */
+    public function getAllGroupsAndDemos(): array
     {
         $groupedData = $this->getFromCache();
         if (is_null($groupedData)) {
@@ -24,7 +31,10 @@ class GameDemosModel extends BasicModel
         return $groupedData;
     }
 
-    private function createGroups($demos)
+    /**
+     * @return array<?int, array{'name': string, 'href': ?int, 'demos': Demo[]}>
+     */
+    private function createGroups(Collection $demos): array
     {
         $groups = [];
         foreach ($demos as $demo) {
diff --git a/include/Models/GameDownloadsModel.php b/include/Models/GameDownloadsModel.php
index 2fc83c6a..66fb72f7 100644
--- a/include/Models/GameDownloadsModel.php
+++ b/include/Models/GameDownloadsModel.php
@@ -2,6 +2,7 @@
 namespace ScummVM\Models;
 
 use ScummVM\Objects\DownloadsSection;
+use ScummVM\OrmObjects\GameDownload;
 use ScummVM\OrmObjects\GameDownloadQuery;
 
 /**
@@ -9,7 +10,11 @@ use ScummVM\OrmObjects\GameDownloadQuery;
  */
 class GameDownloadsModel extends BasicModel
 {
-    /* Get all download entries. */
+    /**
+     * Get all download entries.
+     *
+     * @return array<string, DownloadsSection>
+     */
     public function getAllDownloads()
     {
         $sections = $this->getFromCache();
@@ -56,6 +61,9 @@ class GameDownloadsModel extends BasicModel
         return $sections;
     }
 
+    /**
+     * @return array<string, array{'title'?: string, 'notes'?: string}>
+     */
     private function getSectionData()
     {
         return [
diff --git a/include/Models/GameModel.php b/include/Models/GameModel.php
index 35022046..d0bebae9 100644
--- a/include/Models/GameModel.php
+++ b/include/Models/GameModel.php
@@ -1,6 +1,7 @@
 <?php
 namespace ScummVM\Models;
 
+use ScummVM\OrmObjects\Game;
 use ScummVM\OrmObjects\GameQuery;
 
 /**
@@ -8,8 +9,12 @@ use ScummVM\OrmObjects\GameQuery;
  */
 class GameModel extends BasicModel
 {
-    /* Get all Games from YAML */
-    public function getAllGames()
+    /**
+     * Get all Games from YAML
+     *
+     * @return Game[]
+     */
+    public function getAllGames(): array
     {
         $data = $this->getFromCache();
         if (is_null($data)) {
diff --git a/include/Models/LinksModel.php b/include/Models/LinksModel.php
index d9a600e8..c0ff5ca3 100644
--- a/include/Models/LinksModel.php
+++ b/include/Models/LinksModel.php
@@ -10,8 +10,12 @@ use Symfony\Component\Yaml\Yaml;
  */
 class LinksModel extends BasicModel
 {
-    /* Get all the groups and the respectively demos. */
-    public function getAllGroupsAndLinks()
+    /**
+     * Get all the groups and the respectively demos.
+     *
+     * @return array<array{'name': string, 'notes': string, 'links': array<WebLink>}>
+     */
+    public function getAllGroupsAndLinks(): array
     {
         $entries = $this->getFromCache();
         if (is_null($entries)) {
diff --git a/include/Models/NewsModel.php b/include/Models/NewsModel.php
index 945d2596..7ea9947c 100644
--- a/include/Models/NewsModel.php
+++ b/include/Models/NewsModel.php
@@ -12,8 +12,12 @@ class NewsModel extends BasicModel
     const INVALID_DATE = 'Invalid date, use yyyyMMdd. or yyyyMMddHHmm';
     const FILE_NOT_FOUND = 'The requested news file doesn\'t exist.';
 
-    /* Get a list of all the available news files. */
-    private function getListOfNewsFilenames()
+    /**
+     * Get a list of all the available news files.
+     *
+     * @return string[]
+     */
+    private function getListOfNewsFilenames(): array
     {
         if (!($files = scandir(join(DIRECTORY_SEPARATOR, [DIR_DATA, DEFAULT_LOCALE, 'news'])))) {
             throw new \ErrorException(self::NO_FILES);
@@ -29,8 +33,12 @@ class NewsModel extends BasicModel
         return $filenames;
     }
 
-    /* Get all news items ordered by date, descending. */
-    public function getAllNews($processContent = false)
+    /**
+     * Get all news items ordered by date, descending.
+     *
+     * @return News[]
+     */
+    public function getAllNews(bool $processContent = false): array
     {
         $news = $this->getFromCache();
         if (is_null($news)) {
@@ -61,8 +69,12 @@ class NewsModel extends BasicModel
         return $news;
     }
 
-    /* Get the latest number of news items, or if no number is specified get all news items. */
-    public function getLatestNews($num = -1, $processContent = false)
+    /**
+     * Get the latest number of news items, or if no number is specified get all news items.
+     *
+     * @return News[]
+     */
+    public function getLatestNews(int $num = -1, bool $processContent = false): array
     {
         if ($num == -1) {
             return $this->getAllNews($processContent);
@@ -81,7 +93,7 @@ class NewsModel extends BasicModel
     }
 
     /* Get the news item that was posted on a specific date. */
-    public function getOneByFilename($filename, $processContent = false)
+    public function getOneByFilename(?string $filename, bool $processContent = false): News
     {
         if (is_null($filename) || !preg_match('/^\d{8,12}[a-z]?$/', $filename)) {
             throw new \ErrorException(self::INVALID_DATE);
diff --git a/include/Models/ScreenshotsModel.php b/include/Models/ScreenshotsModel.php
index 0a9db4fe..8a207ff2 100644
--- a/include/Models/ScreenshotsModel.php
+++ b/include/Models/ScreenshotsModel.php
@@ -5,7 +5,6 @@ use ScummVM\OrmObjects\GameQuery;
 use ScummVM\OrmObjects\Screenshot;
 use ScummVM\OrmObjects\ScreenshotQuery;
 use Propel\Runtime\Collection\Collection;
-use Propel\Runtime\Collection\ObjectCollection;
 
 /**
  * The ScreenshotsModel will generate Screenshot objects.
@@ -16,7 +15,11 @@ class ScreenshotsModel extends BasicModel
     const INVALID_CATEGORY = 'Invalid category specified.';
     const SUBCATEGORY_COLUMN = '(case when game.series_id is null then screenshot.id else game.series_id end)';
 
-    public function getAllCategories()
+    /**
+     * @return array<string, array{'title': string, 'category': string,
+     *      'games': array<string, array{'name': string, 'count': int}>}>
+     */
+    public function getAllCategories(): array
     {
         $categories = ScreenshotQuery::create()->findCategories();
         $data = [];
@@ -40,8 +43,12 @@ class ScreenshotsModel extends BasicModel
         return $data;
     }
 
-    /* Get all screenshots in one category. */
-    public function getScreenshotsByCompanyId($companyId)
+    /**
+     * Get all screenshots in one category.
+     *
+     * @return array{'title': string, 'category': string, 'games': array<string, Screenshot>}
+     */
+    public function getScreenshotsByCompanyId(string $companyId): array
     {
         $cache_key = "c_{$companyId}";
         $data = $this->getFromCache($cache_key);
@@ -65,8 +72,12 @@ class ScreenshotsModel extends BasicModel
         return $data;
     }
 
-    /* Get screenshots for a specific target. */
-    public function getScreenshotsBySubcategory($target)
+    /**
+     * Get screenshots for a specific target.
+     *
+     * @return Screenshot[]
+     */
+    public function getScreenshotsBySubcategory(string $target)
     {
         $cache_key = "s_{$target}";
         $data = $this->getFromCache($cache_key);
@@ -92,9 +103,8 @@ class ScreenshotsModel extends BasicModel
      * Combines multiple screenshots into a single screenshot
      *
      * @param  Collection $screenshots
-     * @return ?Screenshot
      */
-    private function combineScreenshots(iterable $screenshots)
+    private function combineScreenshots(iterable $screenshots): ?Screenshot
     {
         $count = $screenshots->count();
         if ($count === 0) {
@@ -116,7 +126,7 @@ class ScreenshotsModel extends BasicModel
      * combineSubcategories
      *
      * @param  Collection $screenshots
-     * @return Screenshot[]
+     * @return array<string, Screenshot>
      */
     private function combineSubcategories(iterable $screenshots)
     {
@@ -158,8 +168,10 @@ class ScreenshotsModel extends BasicModel
         return $count;
     }
 
-    /* Get a random screenshot (an object and not a filename) .*/
-    public function getRandomScreenshot()
+    /**
+     * Get a random screenshot (an object and not a filename).
+     */
+    public function getRandomScreenshot(): Screenshot
     {
         return ScreenshotQuery::create()->findRandom();
     }
diff --git a/include/Models/SimpleYamlModel.php b/include/Models/SimpleYamlModel.php
index a9d80404..770957ec 100644
--- a/include/Models/SimpleYamlModel.php
+++ b/include/Models/SimpleYamlModel.php
@@ -9,20 +9,23 @@ use Symfony\Component\Yaml\Yaml;
  */
 class SimpleYamlModel extends BasicModel
 {
-    private $filename;
-    private $type;
+    private string $filename;
+    private string $type;
 
     const FILE_NOT_FOUND = 'The filename %s could not be found';
     const YAML_PARSE_FAILED = 'Unable to parse the contents of the file %s';
 
-    public function __construct($type, $filename)
+    public function __construct(string $type, string $filename)
     {
         parent::__construct();
         $this->filename = $this->getLocalizedFile($filename);
         $this->type = "ScummVM\Objects\\$type";
     }
 
-    public function getAllData($assignIdsToArray = true)
+    /**
+     * @return array<string|int, mixed>
+     */
+    public function getAllData(bool $assignIdsToArray = true) : array
     {
         $objects = $this->getFromCache();
         if (is_null($objects)) {
diff --git a/include/Models/VersionsModel.php b/include/Models/VersionsModel.php
index 83ea72b4..1a165620 100644
--- a/include/Models/VersionsModel.php
+++ b/include/Models/VersionsModel.php
@@ -1,6 +1,7 @@
 <?php
 namespace ScummVM\Models;
 
+use ScummVM\OrmObjects\Version;
 use ScummVM\OrmObjects\VersionQuery;
 
 /**
@@ -8,8 +9,12 @@ use ScummVM\OrmObjects\VersionQuery;
  */
 class VersionsModel extends BasicModel
 {
-    /* Get all versions from YAML */
-    public function getAllVersions()
+    /**
+     * Get all versions from YAML
+     *
+     * @return Version[]
+     */
+    public function getAllVersions(): array
     {
         $data = $this->getFromCache();
         if (is_null($data)) {
diff --git a/include/Objects/Article.php b/include/Objects/Article.php
index e7df7e87..0a14aef0 100644
--- a/include/Objects/Article.php
+++ b/include/Objects/Article.php
@@ -7,13 +7,16 @@ namespace ScummVM\Objects;
  */
 class Article extends BasicObject
 {
-    private $url;
-    private $language;
-    private $source;
-    private $date;
+    private ?string $url;
+    private ?string $language;
+    private ?string $source;
+    private ?string $date;
 
-    /* Article object constructor. */
-    public function __construct($data)
+    /**
+     * Article object constructor.
+     * @param array{'url'?: string, 'language'?: string, 'source'?: string, 'date'?: string} $data
+     */
+    public function __construct(array $data)
     {
         parent::__construct($data);
         $this->url = $data['url'] ?? null;
@@ -23,25 +26,25 @@ class Article extends BasicObject
     }
 
     /* Get the URL. */
-    public function getURL()
+    public function getURL(): ?string
     {
         return $this->url;
     }
 
     /* Get the language. */
-    public function getLanguage()
+    public function getLanguage(): ?string
     {
         return $this->language;
     }
 
     /* Get the source that published it. */
-    public function getSource()
+    public function getSource(): ?string
     {
         return $this->source;
     }
 
     /* Get the date it was posted. */
-    public function getDate()
+    public function getDate(): ?string
     {
         return $this->date;
     }
diff --git a/include/Objects/BasicObject.php b/include/Objects/BasicObject.php
index aa41d792..2adba544 100644
--- a/include/Objects/BasicObject.php
+++ b/include/Objects/BasicObject.php
@@ -7,27 +7,30 @@ namespace ScummVM\Objects;
  */
 abstract class BasicObject
 {
-    protected $name;
-    protected $description;
+    protected ?string $name;
+    protected ?string $description;
 
-    public function __construct($data)
+    /**
+     * @param array{'description'?: string, 'name'?: string} $data
+     */
+    public function __construct(array $data)
     {
         $this->description = $data['description'] ?? null;
         $this->name = $data['name'] ?? null;
     }
 
-    public function __toString()
+    public function __toString(): string
     {
         return $this->getName() ?? '';
     }
 
      /* Get the name. */
-    public function getName()
+    public function getName(): ?string
     {
         return $this->name;
     }
 
-    public function getDescription()
+    public function getDescription(): ?string
     {
         return $this->description;
     }
@@ -36,9 +39,10 @@ abstract class BasicObject
      * If the input array doesn't contain the numerical key 0, wrap it inside
      * an array. This functions operates on the data directly.
      *
-     * @param mixed $data the input
+     * @param $data the input
+     * @param-out mixed[] $data
      */
-    public function toArray(&$data)
+    public function toArray(mixed &$data): void
     {
         if (!is_array($data) || !array_key_exists(0, $data)) {
             $data = array($data);
diff --git a/include/Objects/BasicSection.php b/include/Objects/BasicSection.php
index 2559a64d..3f61c5e9 100644
--- a/include/Objects/BasicSection.php
+++ b/include/Objects/BasicSection.php
@@ -9,11 +9,15 @@ namespace ScummVM\Objects;
  */
 abstract class BasicSection extends BasicObject
 {
-    protected $title;
-    protected $anchor;
-    protected $subsections;
+    protected string $title;
+    protected string $anchor;
+    /** @var array<static> */
+    protected array $subsections;
 
-    public function __construct($data)
+    /**
+     * @param array{'title': string, 'anchor': string, 'subsection'?: array<mixed>, ...} $data
+     */
+    public function __construct(array $data)
     {
         parent::__construct($data);
         $this->title = $data['title'];
@@ -28,19 +32,23 @@ abstract class BasicSection extends BasicObject
     }
 
     /* Get the title. */
-    public function getTitle()
+    public function getTitle(): string
     {
         return $this->title;
     }
 
     /* Get the anchor. */
-    public function getAnchor()
+    public function getAnchor(): string
     {
         return $this->anchor;
     }
 
-    /* Get the optional list of subsections. */
-    public function getSubSections()
+    /**
+     * Get the optional list of subsections.
+     *
+     * @return array<static>
+     */
+    public function getSubSections(): array
     {
         return $this->subsections;
     }
diff --git a/include/Objects/CreditsSection.php b/include/Objects/CreditsSection.php
index 7b01b44b..e4da70b2 100644
--- a/include/Objects/CreditsSection.php
+++ b/include/Objects/CreditsSection.php
@@ -7,11 +7,21 @@ namespace ScummVM\Objects;
  */
 class CreditsSection extends BasicSection
 {
-    private $groups;
-    private $paragraphs;
+    /** @var array<array{'name': string, 'persons': array<Person>}> */
+    private array $groups;
+    /** @var string[] */
+    private array $paragraphs;
 
-    /* CreditsSection object constructor. */
-    public function __construct($data)
+    /**
+     * CreditsSection object constructor.
+     *
+     * @param array{'title': string, 'anchor': string, 'subsection'?: array<mixed>,
+     *      'group'?: array<array{
+     *          'person': array<array{'description'?: string, 'name'?: string, 'alias': string}>,
+     *          'name': string}>,
+     *      'paragraph'?: string[]} $data
+     */
+    public function __construct(array $data)
     {
         parent::__construct($data);
         $this->groups = [];
@@ -36,14 +46,22 @@ class CreditsSection extends BasicSection
         }
     }
 
-    /* Get the optional list of groups. */
-    public function getGroups()
+    /**
+     * Get the optional list of groups.
+     *
+     * @return array<array{'name': string, 'persons': array<Person>}>
+     */
+    public function getGroups(): array
     {
         return $this->groups;
     }
 
-    /* Get the optional list of paragraphs. */
-    public function getParagraphs()
+    /**
+     * Get the optional list of paragraphs.
+     *
+     * @return string[]
+     */
+    public function getParagraphs(): array
     {
         return $this->paragraphs;
     }
diff --git a/include/Objects/DownloadsSection.php b/include/Objects/DownloadsSection.php
index 3173a9e9..038b81e4 100644
--- a/include/Objects/DownloadsSection.php
+++ b/include/Objects/DownloadsSection.php
@@ -1,6 +1,9 @@
 <?php
 namespace ScummVM\Objects;
 
+use ScummVM\OrmObjects\Download;
+use ScummVM\OrmObjects\GameDownload;
+
 use Propel\Runtime\Map\TableMap;
 
 /**
@@ -8,23 +11,24 @@ use Propel\Runtime\Map\TableMap;
  */
 class DownloadsSection extends BasicSection
 {
-    private $notes;
-    private $items;
+    private string $notes;
+    /** @var array<WebLink|File> */
+    private array $items;
 
     /**
-     * __construct
-     *
-     * @param  mixed $data [id, notes, anchor, title]
-     * @return void
+     * @param array{'title': string, 'anchor': string, 'subsection'?: array<mixed>, 'notes': string} $data
      */
-    public function __construct($data)
+    public function __construct(array $data)
     {
         parent::__construct($data);
         $this->notes = $data['notes'];
         $this->items = [];
     }
 
-    public function addItem($item)
+    /**
+     * @param Download|GameDownload $item
+     */
+    public function addItem(object $item): void
     {
         if ($item->getCategoryIcon()) {
             $this->items[] = new File($item->toArray(TableMap::TYPE_FIELDNAME));
@@ -46,24 +50,36 @@ class DownloadsSection extends BasicSection
         }
     }
 
-    /* Get the optional notes. */
-    public function getNotes()
+    /**
+     * Get the optional notes.
+     */
+    public function getNotes(): ?string
     {
         return $this->notes;
     }
 
-    /* Get the list of items. */
-    public function getItems()
+    /**
+     * Get the list of items.
+     *
+     * @return array<WebLink|File>
+     */
+    public function getItems(): array
     {
         return $this->items;
     }
 
-    public function addSubsection($section)
+    /**
+     * @param static $section
+     */
+    public function addSubsection(DownloadsSection $section): void
     {
         $this->subsections[$section->getAnchor()] = $section;
     }
 
-    private function hasOldVersion($item)
+    /**
+     * @param Download|GameDownload $item
+     */
+    private function hasOldVersion(object $item): bool
     {
         return method_exists($item, 'getVersion') && $item->getVersion() !== RELEASE && $item->getVersion() !== 'Daily';
     }
diff --git a/include/Objects/File.php b/include/Objects/File.php
index 6a2d54ff..a77086b4 100644
--- a/include/Objects/File.php
+++ b/include/Objects/File.php
@@ -8,15 +8,21 @@ use ScummVM\FileUtils;
  */
 class File extends BasicObject
 {
-    private $autoId;
-    private $category;
-    private $category_icon;
-    private $url;
-    private $extra_info;
-    private $notes;
-    private $user_agent;
-    private $version;
+    private ?int $autoId;
+    private string $category;
+    private string $category_icon;
+    private string $url;
+    /** @var array{'size': string, 'sha256': string, 'ext': string, 'date': string}|array{} */
+    private array $extra_info;
+    private string $notes;
+    private string $user_agent;
+    private ?string $version;
 
+    /**
+     * @param array{'description'?: string, 'name'?: string, 'auto_id'?: int, 'category': string,
+     *      'category_icon': string, 'notes'?: string, 'user_agent'?: string, 'version'?: string,
+     *      'url': string|array{0: string, '@attributes': array<mixed>}} $data
+     */
     public function __construct($data)
     {
         parent::__construct($data);
@@ -64,30 +70,34 @@ class File extends BasicObject
     }
 
     /* Get the category icon. */
-    public function getCategoryIcon()
+    public function getCategoryIcon(): string
     {
         return $this->category_icon;
     }
 
     /* Get the URL. */
-    public function getURL()
+    public function getURL(): string
     {
         return $this->url;
     }
 
-    /* Get the extra information. */
-    public function getExtraInfo()
+    /**
+     * Get the extra information.
+     *
+     * @return array{'size': string, 'sha256': string, 'ext': string, 'date': string}|array{}
+     */
+    public function getExtraInfo(): array
     {
         return $this->extra_info;
     }
 
-    public function getNotes()
+    public function getNotes(): string
     {
         return $this->notes;
     }
 
     /* Get the user-agent. */
-    public function getUserAgent()
+    public function getUserAgent(): string
     {
         return $this->user_agent;
     }
diff --git a/include/Objects/MenuItem.php b/include/Objects/MenuItem.php
index abf4ac1c..200beff6 100644
--- a/include/Objects/MenuItem.php
+++ b/include/Objects/MenuItem.php
@@ -7,10 +7,15 @@ namespace ScummVM\Objects;
 class MenuItem extends BasicObject
 {
 
-    private $class;
-    private $entries;
+    private string $class;
+    /** @var array<string, string> */
+    private array $entries;
 
-    /* Menu object constructor. */
+    /**
+     * Menu object constructor.
+     * @param array{'description'?: string, 'name'?: string, 'class': string,
+     *      'links': array<array{'name': string, 'href': string}>} $data
+     */
     public function __construct($data)
     {
         parent::__construct($data);
@@ -22,13 +27,17 @@ class MenuItem extends BasicObject
     }
 
     /* Get the CSS class. */
-    public function getClass()
+    public function getClass(): string
     {
         return $this->class;
     }
 
-    /* Get the list of links, with the name as key and URL as value. */
-    public function getEntries()
+    /**
+     * Get the list of links, with the name as key and URL as value.
+     *
+     * @return array<string, string>
+     */
+    public function getEntries(): array
     {
         return $this->entries;
     }
diff --git a/include/Objects/News.php b/include/Objects/News.php
index ebbd230f..65b28205 100644
--- a/include/Objects/News.php
+++ b/include/Objects/News.php
@@ -10,16 +10,16 @@ use Erusev\Parsedown;
  */
 class News
 {
-    private $title;
-    private $date;
-    private $author;
-    private $content;
-    private $filename;
+    private string $title;
+    private string $date;
+    private string $author;
+    private string $content;
+    private string $filename;
 
     /**
      * News object constructor that extracts the data from the YAML frontmatter.
      */
-    public function __construct($data, $filename, $processContent = false)
+    public function __construct(string $data, string $filename, bool $processContent = false)
     {
         $object = YamlFrontMatter::parse($data);
         $Parsedown = new \Parsedown();
@@ -42,12 +42,12 @@ class News
      * http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
      * for a list of valid entities for both XML and HTML
      */
-    public function processText($text)
+    public function processText(string $text): string
     {
         return html_entity_decode($text, ENT_COMPAT, 'UTF-8');
     }
 
-    private function localizeLinks($body)
+    private function localizeLinks(string $body): string
     {
         global $lang;
         if ($lang == DEFAULT_LOCALE || !$lang) {
@@ -70,37 +70,37 @@ class News
     }
 
     /* Get the title. */
-    public function getTitle()
+    public function getTitle(): string
     {
         return $this->title;
     }
 
     /* Get the date. */
-    public function getDate()
+    public function getDate(): string
     {
         return $this->date;
     }
 
     /* Get the author. */
-    public function getAuthor()
+    public function getAuthor(): string
     {
         return $this->author;
     }
 
     /* Get the content. */
-    public function getContent()
+    public function getContent(): string
     {
         return $this->content;
     }
 
     /* Get the filename. */
-    public function getFilename()
+    public function getFilename(): string
     {
         return $this->filename;
     }
 
     /* Get the News link. */
-    public function getLink()
+    public function getLink(): string
     {
         return URL_BASE . 'news/' . substr($this->filename, 0, -9);
     }
diff --git a/include/Objects/Person.php b/include/Objects/Person.php
index c35a5781..5bd0038d 100644
--- a/include/Objects/Person.php
+++ b/include/Objects/Person.php
@@ -7,9 +7,12 @@ namespace ScummVM\Objects;
  */
 class Person extends BasicObject
 {
-    private $alias;
+    private string $alias;
 
-    /* Person object constructor. */
+    /**
+     * Person object constructor.
+     * @param array{'description'?: string, 'name'?: string, 'alias': string} $data
+     */
     public function __construct($data)
     {
         parent::__construct($data);
@@ -17,7 +20,7 @@ class Person extends BasicObject
     }
 
     /* Get the alias. */
-    public function getAlias()
+    public function getAlias(): string
     {
         return $this->alias;
     }
diff --git a/include/Objects/Sponsor.php b/include/Objects/Sponsor.php
index 96b46b1c..24c4d508 100644
--- a/include/Objects/Sponsor.php
+++ b/include/Objects/Sponsor.php
@@ -2,15 +2,18 @@
 namespace ScummVM\Objects;
 
 /**
- * The article class represents a link on the website to an article covering
+ * The sponsor class represents a link on the website to a sponsor and its logo and supporting
  * ScummVM in some way.
  */
 class Sponsor extends BasicObject
 {
-    private $link;
-    private $image;
+    private string $link;
+    private string $image;
 
-    /* Article object constructor. */
+    /**
+     * Sponsor object constructor.
+     * @param array{'description'?: string, 'name'?: string, 'link': string, 'image': string} $data
+     */
     public function __construct($data)
     {
         parent::__construct($data);
@@ -19,13 +22,13 @@ class Sponsor extends BasicObject
     }
 
     /* Get the sponsor link. */
-    public function getLink()
+    public function getLink(): string
     {
         return $this->link;
     }
 
     /* Get the sponsor image */
-    public function getImage()
+    public function getImage(): string
     {
         return $this->image;
     }
diff --git a/include/Objects/WebLink.php b/include/Objects/WebLink.php
index 3e4b0f1a..43b1b12e 100644
--- a/include/Objects/WebLink.php
+++ b/include/Objects/WebLink.php
@@ -6,10 +6,13 @@ namespace ScummVM\Objects;
  */
 class WebLink extends BasicObject
 {
-    private $notes;
-    private $url;
+    private string $notes;
+    private string $url;
 
-    /* WebLink object constructor. */
+    /**
+     * WebLink object constructor.
+     * @param array{'description'?: string, 'name'?: string, 'notes': string, 'url': string} $data
+     */
     public function __construct($data)
     {
         parent::__construct($data);
@@ -17,19 +20,19 @@ class WebLink extends BasicObject
         $this->url = $data['url'];
     }
 
-    public function getNotes()
+    public function getNotes(): string
     {
         return $this->notes;
     }
 
     /* Get the URL of the link. */
-    public function getURL()
+    public function getURL(): string
     {
         return $this->url;
     }
 
     /* Get the user-agent. */
-    public function getUserAgent()
+    public function getUserAgent(): string
     {
         return "";
     }
diff --git a/include/OrmObjects/Compatibility.php b/include/OrmObjects/Compatibility.php
index 95b86457..4cb991e7 100644
--- a/include/OrmObjects/Compatibility.php
+++ b/include/OrmObjects/Compatibility.php
@@ -15,7 +15,7 @@ use ScummVM\OrmObjects\Base\Compatibility as BaseCompatibility;
  */
 class Compatibility extends BaseCompatibility
 {
-    private function processPlatforms($values)
+    private function processPlatforms(string $values): string
     {
         $platforms = \explode(",", $values);
 
@@ -27,7 +27,7 @@ class Compatibility extends BaseCompatibility
         return $retVal;
     }
 
-    public function getScummVmId($version)
+    public function getScummVmId(string $version): string
     {
         $gameId = $this->getGame()->getId();
         if ($version == "DEV" || version_compare($version, "2.2.0") > 0) {
@@ -39,7 +39,7 @@ class Compatibility extends BaseCompatibility
         }
     }
 
-    public function getNotes()
+    public function getNotes(): string
     {
         $notes = "### Support Level\n\n";
         $notes .= "[[support_description]]\n\n";
diff --git a/include/OrmObjects/Demo.php b/include/OrmObjects/Demo.php
index 6ff7e11b..9c5d5d6b 100644
--- a/include/OrmObjects/Demo.php
+++ b/include/OrmObjects/Demo.php
@@ -15,7 +15,7 @@ use ScummVM\OrmObjects\Base\Demo as BaseDemo;
  */
 class Demo extends BaseDemo
 {
-    public function getName()
+    public function getName(): string
     {
         $gameName = $this->getGame()->getName();
         $platformName = $this->getPlatform()->getName();
@@ -23,7 +23,7 @@ class Demo extends BaseDemo
         return "$gameName ($platformName $category Demo)";
     }
 
-    public function __toString()
+    public function __toString(): string
     {
         return $this->getName();
     }
diff --git a/include/OrmObjects/Screenshot.php b/include/OrmObjects/Screenshot.php
index 5f78967e..942ce872 100644
--- a/include/OrmObjects/Screenshot.php
+++ b/include/OrmObjects/Screenshot.php
@@ -15,9 +15,15 @@ use ScummVM\OrmObjects\Base\Screenshot as BaseScreenshot;
  */
 class Screenshot extends BaseScreenshot
 {
-    private $files;
+    /**
+     * @var array<array{'filename': string, 'caption': string, 'url': string}>
+     */
+    private array $files;
 
-    public function getFiles()
+    /**
+     * @return array<array{'filename': string, 'caption': string, 'url': string}>
+     */
+    public function getFiles(): array
     {
         if (!isset($this->files)) {
             $this->files = [];
@@ -37,7 +43,10 @@ class Screenshot extends BaseScreenshot
         return $this->files;
     }
 
-    public function addFiles($files)
+    /**
+     * @param array<array{'filename': string, 'caption': string, 'url': string}> $files
+     */
+    public function addFiles(array $files): void
     {
         if (isset($this->files)) {
             $this->files = array_merge($this->files, $files);
@@ -46,7 +55,7 @@ class Screenshot extends BaseScreenshot
         }
     }
 
-    public function getCategory()
+    public function getCategory(): string
     {
         $series = $this->getGame()->getSeries();
         if ($series) {
@@ -57,7 +66,7 @@ class Screenshot extends BaseScreenshot
         }
     }
 
-    public function getName()
+    public function getName(): string
     {
         $series = $this->getGame()->getSeries();
         if ($series) {
@@ -67,7 +76,7 @@ class Screenshot extends BaseScreenshot
         }
     }
 
-    public function getCaption()
+    public function getCaption(): string
     {
         $name = $this->getGame()->getName();
         $extras = [];
@@ -90,7 +99,7 @@ class Screenshot extends BaseScreenshot
         return htmlspecialchars($name);
     }
 
-    public function getFileMask()
+    public function getFileMask(): string
     {
         // Remove engine prefix
         $game_short_id = substr($this->getGame()->getId(), strpos($this->getGame()->getId(), ':') + 1);
@@ -106,7 +115,7 @@ class Screenshot extends BaseScreenshot
         return $mask;
     }
 
-    public function __toString()
+    public function __toString(): string
     {
         return $this->getCaption();
     }
diff --git a/include/OrmObjects/ScreenshotQuery.php b/include/OrmObjects/ScreenshotQuery.php
index a28b305f..62818696 100644
--- a/include/OrmObjects/ScreenshotQuery.php
+++ b/include/OrmObjects/ScreenshotQuery.php
@@ -21,7 +21,7 @@ use ScummVM\OrmObjects\Map\ScreenshotTableMap;
  */
 class ScreenshotQuery extends BaseScreenshotQuery
 {
-    public function findRandom(ConnectionInterface $con = null)
+    public function findRandom(ConnectionInterface $con = null): ChildScreenshot
     {
         if ($con === null) {
             $con = Propel::getServiceContainer()->getReadConnection(ScreenshotTableMap::DATABASE_NAME);
@@ -48,7 +48,11 @@ class ScreenshotQuery extends BaseScreenshotQuery
         return $obj;
     }
 
-    public function findCategories(ConnectionInterface $con = null)
+    /**
+     * @return array<array{'category_key': string, 'category_name': string, 'subcategory_key': string,
+     *          'subcategory_name': string}>
+     */
+    public function findCategories(ConnectionInterface $con = null): array
     {
         if ($con === null) {
             $con = Propel::getServiceContainer()->getReadConnection(ScreenshotTableMap::DATABASE_NAME);
@@ -99,7 +103,7 @@ class ScreenshotQuery extends BaseScreenshotQuery
         return $obj;
     }
 
-    public function filterByCompanyId($companyId, ConnectionInterface $con = null)
+    public function filterByCompanyId(string $companyId, ConnectionInterface $con = null): self
     {
         if ($companyId !== 'other') {
             return $this->useGameQuery()
diff --git a/include/Pages/ArticlePage.php b/include/Pages/ArticlePage.php
index dd9efab9..9ee5d349 100644
--- a/include/Pages/ArticlePage.php
+++ b/include/Pages/ArticlePage.php
@@ -2,6 +2,7 @@
 namespace ScummVM\Pages;
 
 use ScummVM\Controller;
+use Spatie\YamlFrontMatter\Document;
 use Spatie\YamlFrontMatter\YamlFrontMatter;
 use Erusev\Parsedown;
 
@@ -17,7 +18,7 @@ class ArticlePage extends Controller
         $this->template = 'pages/article.tpl';
     }
 
-    private function getArticle($filename)
+    private function getArticle(string $filename): Document
     {
         global $lang;
         if (!$lang) {
@@ -36,8 +37,12 @@ class ArticlePage extends Controller
         return YamlFrontMatter::parse(file_get_contents($fname));
     }
 
-    /* Display the index page. */
-    public function index($params)
+    /**
+     *  Display the index page.
+     *
+     *  @param array{'article'?: string} $params
+     */
+    public function index(array $params): void
     {
         if (empty($params['article'])) {
             throw new \ErrorException(self::ARTICLE_NAME_MISSING);
diff --git a/include/Pages/CompatibilityPage.php b/include/Pages/CompatibilityPage.php
index 79b146a5..d5de41ed 100644
--- a/include/Pages/CompatibilityPage.php
+++ b/include/Pages/CompatibilityPage.php
@@ -12,10 +12,16 @@ use ScummVM\OrmObjects\VersionQuery;
  */
 class CompatibilityPage extends Controller
 {
-    private $supportLevel;
-    private $supportLevelDescriptions;
-    private $supportLevelClass;
-    private $compatibilityModel;
+    // Would be useful for PHPStan
+    //private const LEVELS = ['untested', 'broken', 'bugged', 'good', 'excellent'];
+
+    /** @var array<value-of<self::LEVELS>, ?string> */
+    private array $supportLevel;
+    /** @var array<value-of<self::LEVELS>, ?string> */
+    private array $supportLevelDescriptions;
+    /** @var array<value-of<self::LEVELS>, ?string> */
+    private array $supportLevelClass;
+    private CompatibilityModel $compatibilityModel;
 
     /* Constructor. */
     public function __construct()
@@ -49,8 +55,12 @@ class CompatibilityPage extends Controller
         $this->compatibilityModel = new CompatibilityModel();
     }
 
-    /* Display the index page. */
-    public function index($args)
+    /**
+     * Display the index page.
+     *
+     * @param array{'version'?: string, 'game'?: string} $args
+     */
+    public function index($args): void
     {
         $version = $args['version'] ?? null;
         $target = $args['game'] ?? null;
@@ -75,7 +85,7 @@ class CompatibilityPage extends Controller
     }
 
     /* We should show detailed information for a specific target. */
-    public function getGame($target, $version)
+    public function getGame(string $target, string $version): void
     {
         $game = $this->compatibilityModel->getGameData($version, $target);
         // Redirect to main compatibility page if the requested game doesn't exist
@@ -107,7 +117,10 @@ class CompatibilityPage extends Controller
     }
 
     /* We should show all the compatibility stats for a specific version. */
-    public function getAll($version, $versions)
+    /**
+     * @param string[] $versions
+     */
+    public function getAll(string $version, array $versions): void
     {
 
         $compat_data = $this->compatibilityModel->getAllDataGroups($version);
diff --git a/include/Pages/DemosPage.php b/include/Pages/DemosPage.php
index 4899aa56..db3cb77f 100644
--- a/include/Pages/DemosPage.php
+++ b/include/Pages/DemosPage.php
@@ -6,7 +6,7 @@ use ScummVM\Models\GameDemosModel;
 
 class DemosPage extends Controller
 {
-    private $gameDemosModel;
+    private GameDemosModel $gameDemosModel;
 
     /* Constructor. */
     public function __construct()
@@ -17,7 +17,7 @@ class DemosPage extends Controller
     }
 
     /* Display the index page. */
-    public function index()
+    public function index(): void
     {
         $demos = $this->gameDemosModel->getAllGroupsAndDemos();
         $this->renderPage(
diff --git a/include/Pages/DirectorDemosPage.php b/include/Pages/DirectorDemosPage.php
index 016259a9..2f0db58b 100644
--- a/include/Pages/DirectorDemosPage.php
+++ b/include/Pages/DirectorDemosPage.php
@@ -6,7 +6,7 @@ use ScummVM\Models\DirectorDemosModel;
 
 class DirectorDemosPage extends Controller
 {
-    private $gameDemosModel;
+    private DirectorDemosModel $gameDemosModel;
 
     /* Constructor. */
     public function __construct()
@@ -17,7 +17,7 @@ class DirectorDemosPage extends Controller
     }
 
     /* Display the index page. */
-    public function index()
+    public function index(): void
     {
         $demos = $this->gameDemosModel->getAllGroupsAndDemos();
         $this->renderPage(
diff --git a/include/Pages/DownloadsPage.php b/include/Pages/DownloadsPage.php
index 823b9f15..6c864a4c 100644
--- a/include/Pages/DownloadsPage.php
+++ b/include/Pages/DownloadsPage.php
@@ -6,7 +6,7 @@ use ScummVM\Models\DownloadsModel;
 
 class DownloadsPage extends Controller
 {
-    private $downloadsModel;
+    private DownloadsModel $downloadsModel;
     /* Constructor. */
     public function __construct()
     {
@@ -16,7 +16,7 @@ class DownloadsPage extends Controller
     }
 
     /* Display the index page. */
-    public function index()
+    public function index(): void
     {
         $downloads = $this->downloadsModel->getAllDownloads();
         $recommendedDownload = $this->downloadsModel->getRecommendedDownload();
diff --git a/include/Pages/FeedsPage.php b/include/Pages/FeedsPage.php
index 20114a6f..d1e3cfa2 100644
--- a/include/Pages/FeedsPage.php
+++ b/include/Pages/FeedsPage.php
@@ -6,9 +6,9 @@ use ScummVM\Models\NewsModel;
 
 class FeedsPage extends Controller
 {
-    private $template_rss;
-    private $template_atom;
-    private $newsModel;
+    private string $template_rss;
+    private string $template_atom;
+    private NewsModel $newsModel;
 
     /* Constructor. */
     public function __construct()
@@ -19,8 +19,12 @@ class FeedsPage extends Controller
         $this->newsModel = new NewsModel();
     }
 
-    /* Display the index page. */
-    public function index($args)
+    /**
+     * Display the index page.
+     *
+     * @param array{'type'?: string} $args
+     */
+    public function index(array $args): void
     {
         $feed = isset($args['type']) ? $args['type'] : '';
         if ($feed == 'atom') {
diff --git a/include/Pages/GamesPage.php b/include/Pages/GamesPage.php
index 74c8dc9e..ebf4aed3 100644
--- a/include/Pages/GamesPage.php
+++ b/include/Pages/GamesPage.php
@@ -6,7 +6,7 @@ use ScummVM\Models\GameDownloadsModel;
 
 class GamesPage extends Controller
 {
-    private $gameDownloadsModel;
+    private GameDownloadsModel $gameDownloadsModel;
 
     /* Constructor. */
     public function __construct()
@@ -17,7 +17,7 @@ class GamesPage extends Controller
     }
 
     /* Display the index page. */
-    public function index()
+    public function index(): void
     {
         $downloads = $this->gameDownloadsModel->getAllDownloads();
         $this->renderPage(
diff --git a/include/Pages/LinksPage.php b/include/Pages/LinksPage.php
index 7765e6db..fa0c3973 100644
--- a/include/Pages/LinksPage.php
+++ b/include/Pages/LinksPage.php
@@ -7,7 +7,7 @@ use ScummVM\Models\LinksModel;
 class LinksPage extends Controller
 {
 
-    private $linksModel;
+    private LinksModel $linksModel;
 
     /* Constructor. */
     public function __construct()
@@ -18,7 +18,7 @@ class LinksPage extends Controller
     }
 
     /* Display the index page. */
-    public function index()
+    public function index(): void
     {
         $links = $this->linksModel->getAllGroupsAndLinks();
         $this->renderPage(
diff --git a/include/Pages/NewsPage.php b/include/Pages/NewsPage.php
index d0f3e16b..9f26ff48 100644
--- a/include/Pages/NewsPage.php
+++ b/include/Pages/NewsPage.php
@@ -7,8 +7,8 @@ use ScummVM\Models\ScreenshotsModel;
 
 class NewsPage extends Controller
 {
-    private $newsModel;
-    private $screenshotModels;
+    private NewsModel $newsModel;
+    private ScreenshotsModel $screenshotModels;
 
     /* Constructor. */
     public function __construct()
@@ -19,8 +19,12 @@ class NewsPage extends Controller
         $this->screenshotModels = new ScreenshotsModel();
     }
 
-    /* Display the index page. */
-    public function index($args)
+    /**
+     * Display the index page.
+     *
+     * @param array{'date'?: string} $args
+     */
+    public function index(array $args): void
     {
         $filename = $args['date'] ?? null;
 
@@ -36,7 +40,7 @@ class NewsPage extends Controller
     }
 
     /* Display a specific news item, or all news items. */
-    public function getNews($filename = null)
+    public function getNews(?string $filename = null): void
     {
         if ($filename == null) {
             $news_items = $this->newsModel->getAllNews();
@@ -62,7 +66,7 @@ class NewsPage extends Controller
     }
 
     /* Display the main page with limited news items and intro text. */
-    public function getNewsIntro()
+    public function getNewsIntro(): void
     {
         $news_items = $this->newsModel->getLatestNews(NEWS_ITEMS);
         $random_shot = $this->screenshotModels->getRandomScreenshot();
diff --git a/include/Pages/ScreenshotsPage.php b/include/Pages/ScreenshotsPage.php
index 54c10754..4fbe18bd 100644
--- a/include/Pages/ScreenshotsPage.php
+++ b/include/Pages/ScreenshotsPage.php
@@ -6,7 +6,7 @@ use ScummVM\Models\ScreenshotsModel;
 
 class ScreenshotsPage extends Controller
 {
-    private $screenshotsModel;
+    private ScreenshotsModel $screenshotsModel;
 
     /* Constructor. */
     public function __construct()
@@ -15,8 +15,12 @@ class ScreenshotsPage extends Controller
         $this->screenshotsModel = new ScreenshotsModel();
     }
 
-    /* Display the index page. */
-    public function index($args)
+    /**
+     * Display the index page.
+     *
+     * @param array{'category'?: string, 'game'?: string} $args
+     */
+    public function index(array $args): void
     {
         $category = $args['category'] ?? null;
         $game = $args['game'] ?? null;
@@ -49,7 +53,7 @@ class ScreenshotsPage extends Controller
     }
 
     /* Display the selected category. */
-    public function getCategory($category, $game)
+    public function getCategory(string $category, ?string $game): void
     {
         if (empty($game)) {
             $screenshots = $this->screenshotsModel->getScreenshotsByCompanyId($category);
diff --git a/include/Pages/SimplePage.php b/include/Pages/SimplePage.php
index 582ab14b..23fde5e2 100644
--- a/include/Pages/SimplePage.php
+++ b/include/Pages/SimplePage.php
@@ -2,6 +2,7 @@
 namespace ScummVM\Pages;
 
 use ScummVM\Controller;
+use ScummVM\Models\BasicModel;
 use ScummVM\Models\SimpleYamlModel;
 
 class SimplePage extends Controller
@@ -14,11 +15,11 @@ class SimplePage extends Controller
         'credits' => ['CreditsSection', 'credits.yaml'],
     ];
 
-    private $model;
-    private $key;
+    private SimpleYamlModel $model;
+    private string $key;
 
     /* Constructor. */
-    public function __construct($key)
+    public function __construct(string $key)
     {
         parent::__construct();
         $this->template = "pages/$key.tpl";
@@ -34,7 +35,7 @@ class SimplePage extends Controller
     }
 
     /* Display the index page. */
-    public function index($data = null)
+    public function index(mixed $data = null): void
     {
         if (isset($this->model)) {
             $data = $this->model->getAllData();
diff --git a/include/Pages/StaticPage.php b/include/Pages/StaticPage.php
index 0eba0673..f5216fd4 100644
--- a/include/Pages/StaticPage.php
+++ b/include/Pages/StaticPage.php
@@ -6,9 +6,9 @@ use ScummVM\Controller;
 class StaticPage extends Controller
 {
     const FILE_NOT_FOUND = 'The filename %s could not be found';
-    private $filename;
+    private string $filename;
 
-    public function __construct($key)
+    public function __construct(string $key)
     {
         parent::__construct();
 
@@ -19,7 +19,7 @@ class StaticPage extends Controller
     }
 
     /* Display the index page. */
-    public function index($data = null)
+    public function index(): void
     {
         readfile($this->filename);
     }
diff --git a/include/SiteUtils.php b/include/SiteUtils.php
index d726bc0a..b95b73fe 100644
--- a/include/SiteUtils.php
+++ b/include/SiteUtils.php
@@ -3,7 +3,7 @@ namespace ScummVM;
 
 class SiteUtils
 {
-    public static function localizePath($path, $checkStaticFiles = false)
+    public static function localizePath(string $path, bool $checkStaticFiles = false): string
     {
         global $lang;
         // No lang needed


Commit: d2e028140b432c14561ca7da96ec1276364ac78a
    https://github.com/scummvm/scummvm-web/commit/d2e028140b432c14561ca7da96ec1276364ac78a
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
BUILD: Check codestyle after fixing trivial errors

Also check index.php

Changed paths:
    composer.json


diff --git a/composer.json b/composer.json
index 7a5cbc2c..3b8af907 100644
--- a/composer.json
+++ b/composer.json
@@ -57,7 +57,8 @@
       "@start"
     ],
     "lint": [
-      "phpcbf --standard=psr2 --ignore=./include/OrmObjects/Base,./include/OrmObjects/Map ./include",
+      "phpcbf --standard=psr2 --ignore=./include/OrmObjects/Base,./include/OrmObjects/Map ./include public_html/index.php",
+      "phpcs  --standard=psr2 --ignore=./include/OrmObjects/Base,./include/OrmObjects/Map ./include public_html/index.php",
       "phpstan"
     ],
     "update-data": [


Commit: 271f169928ddc83217b784209d14d9aeeb96e959
    https://github.com/scummvm/scummvm-web/commit/271f169928ddc83217b784209d14d9aeeb96e959
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
BUILD: Increase PHPStan level

Changed paths:
    phpstan.neon


diff --git a/phpstan.neon b/phpstan.neon
index 6e52cd2c..61af394b 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,5 +1,5 @@
 parameters:
-	level: 5
+	level: 6
 	treatPhpDocTypesAsCertain: false
 	bootstrapFiles:
 		- include/Constants.php


Commit: 710b95a5f41de6e5ed630dff181a04d9c633a42f
    https://github.com/scummvm/scummvm-web/commit/710b95a5f41de6e5ed630dff181a04d9c633a42f
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Better error handling

When PHP functions fail they often return false instead of the expected
object type.
CHeck for this value and fail properly.

Changed paths:
    include/Controller.php
    include/DataUtils.php
    include/FileUtils.php
    include/LocalizationUtils.php
    include/Models/NewsModel.php
    include/Models/ScreenshotsModel.php
    include/OrmObjects/Screenshot.php
    include/OrmObjects/ScreenshotQuery.php
    include/Pages/ArticlePage.php
    public_html/index.php


diff --git a/include/Controller.php b/include/Controller.php
index 308cd678..8b335a11 100644
--- a/include/Controller.php
+++ b/include/Controller.php
@@ -128,8 +128,12 @@ class Controller
     public function dateLocalizedSmartyModifier(int $timestamp): string
     {
         global $lang;
-        $formatter = datefmt_create($lang, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE);
-        return $formatter->format($timestamp);
+        $formatter = new \IntlDateFormatter($lang, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE);
+        $ret = $formatter->format($timestamp);
+        if ($ret === false) {
+            $ret = '';
+        }
+        return $ret;
     }
 
     /**
diff --git a/include/DataUtils.php b/include/DataUtils.php
index 2b35d9ae..a9befb68 100644
--- a/include/DataUtils.php
+++ b/include/DataUtils.php
@@ -91,6 +91,9 @@ class DataUtils
         // Convert to JSON because records are serializable
         // and cannot be converted directly to yaml
         $json = \json_encode($records);
+        if ($json === false) {
+            throw new \Exception("Can't encode data as JSON");
+        }
         $data = \json_decode($json, true);
 
         // Convert TRUE/FALSE strings to Booleans
diff --git a/include/FileUtils.php b/include/FileUtils.php
index eeab5c89..c555d0f0 100644
--- a/include/FileUtils.php
+++ b/include/FileUtils.php
@@ -82,13 +82,18 @@ class FileUtils
             && (@filemtime($path . '.sha256') > @filemtime($path))
         ) {
             // Read the file and return the included hash
-            return file_get_contents($path . '.sha256');
-        } else {
-            // Generate a SHA-256 hash, save it to a file for later, then return the hash
-            $hash = hash_file('sha256', $path);
-            file_put_contents($path . '.sha256', $hash);
-            return $hash;
+            $contents = file_get_contents($path . '.sha256');
+            if ($contents !== false) {
+                return $contents;
+            }
         }
+        // Generate a SHA-256 hash, save it to a file for later, then return the hash
+        $hash = hash_file('sha256', $path);
+        if ($hash === false) {
+            return '';
+        }
+        file_put_contents($path . '.sha256', $hash);
+        return $hash;
     }
 
     /**
@@ -100,8 +105,12 @@ class FileUtils
     public static function getLastModified(string $path): string
     {
         $path = FileUtils::toAbsolutePathIfOnServer($path);
+        $mtime = @filemtime($path);
+        if ($mtime === false) {
+            return '';
+        }
         $date = new DateTime();
-        return $date->setTimestamp(@filemtime($path))->format("Y-m-d");
+        return $date->setTimestamp($mtime)->format("Y-m-d");
     }
 
     /**
diff --git a/include/LocalizationUtils.php b/include/LocalizationUtils.php
index 20cc00ad..632c11c3 100644
--- a/include/LocalizationUtils.php
+++ b/include/LocalizationUtils.php
@@ -34,6 +34,9 @@ class LocalizationUtils
         $filename = JOIN(DIRECTORY_SEPARATOR, [DIR_DATA, $lang, "strings.json"]);
         echo("Converting {$filename} from JSON to INI\n");
         $jsonString = file_get_contents($filename);
+        if ($jsonString === false) {
+            throw new \Exception("Can't read file $filename");
+        }
         $json = json_decode($jsonString);
 
         $output = "";
@@ -55,12 +58,19 @@ class LocalizationUtils
                 return;
             }
             echo("Converting " . $newsFile . " to individual Markdown files\n");
-            $l10n = json_decode(file_get_contents($newsFile));
+            $jsonString = file_get_contents($newsFile);
+            if ($jsonString === false) {
+                throw new \Exception("Can't read news JSON $newsFile");
+            }
+            $l10n = json_decode($jsonString);
 
             foreach ($l10n as $key => $translatedArticle) {
-                $englishArticle = YamlFrontMatter::parse(file_get_contents(
-                    join(DIRECTORY_SEPARATOR, [DIR_DATA, DEFAULT_LOCALE, 'news', "{$key}.markdown"])
-                ));
+                $newsFile = join(DIRECTORY_SEPARATOR, [DIR_DATA, DEFAULT_LOCALE, 'news', "{$key}.markdown"]);
+                $englishContents = file_get_contents($newsFile);
+                if ($englishContents === false) {
+                    throw new \Exception("Can't read news file $newsFile");
+                }
+                $englishArticle = YamlFrontMatter::parse($englishContents);
 
                 $date = self::$purifier->purify($englishArticle->date);
                 $author = self::$purifier->purify($englishArticle->author);
diff --git a/include/Models/NewsModel.php b/include/Models/NewsModel.php
index 7ea9947c..aac21131 100644
--- a/include/Models/NewsModel.php
+++ b/include/Models/NewsModel.php
@@ -54,10 +54,10 @@ class NewsModel extends BasicModel
                 if (!is_file(($fname = join(DIRECTORY_SEPARATOR, [DIR_DATA, $lang, 'news', basename($filename)])))
                     || !is_readable($fname) || !($data = @file_get_contents($fname))
                 ) {
-                    if (!($data = @file_get_contents(join(
+                    if (($data = @file_get_contents(join(
                         DIRECTORY_SEPARATOR,
                         [DIR_DATA, DEFAULT_LOCALE, 'news', $filename]
-                    )))) {
+                    ))) === false) {
                         continue;
                     }
                 }
@@ -100,6 +100,9 @@ class NewsModel extends BasicModel
         }
         $fname = $this->getLocalizedFile("news/$filename.markdown");
         $data = @file_get_contents($fname);
+        if ($data === false) {
+            throw new \ErrorException(self::FILE_NOT_FOUND);
+        }
 
         return new News($data, $fname, $processContent);
     }
diff --git a/include/Models/ScreenshotsModel.php b/include/Models/ScreenshotsModel.php
index 8a207ff2..0a5a246a 100644
--- a/include/Models/ScreenshotsModel.php
+++ b/include/Models/ScreenshotsModel.php
@@ -162,8 +162,12 @@ class ScreenshotsModel extends BasicModel
         $count = 0;
         // Iterate over each game and count the number of screenshot files
         foreach ($games as $game) {
-            $count += count(glob(DIR_STATIC . DIR_SCREENSHOTS . '/' .
-                str_replace(':', '/', $game->getId()) . '/*_full.png'));
+            $list = glob(DIR_STATIC . DIR_SCREENSHOTS . '/' .
+                str_replace(':', '/', $game->getId()) . '/*_full.png');
+            if ($list === false) {
+                continue;
+            }
+            $count += count($list);
         }
         return $count;
     }
diff --git a/include/OrmObjects/Screenshot.php b/include/OrmObjects/Screenshot.php
index 942ce872..4d6f3aa4 100644
--- a/include/OrmObjects/Screenshot.php
+++ b/include/OrmObjects/Screenshot.php
@@ -28,7 +28,11 @@ class Screenshot extends BaseScreenshot
         if (!isset($this->files)) {
             $this->files = [];
             $gameId = str_replace(':', '/', $this->getGame()->getId());
-            foreach (glob(DIR_STATIC . DIR_SCREENSHOTS . '/' . $gameId . '/' . $this->getFileMask()) as $file) {
+            $screenshots = glob(DIR_STATIC . DIR_SCREENSHOTS . '/' . $gameId . '/' . $this->getFileMask());
+            if ($screenshots === false) {
+                $screenshots = array();
+            }
+            foreach ($screenshots as $file) {
                 // Remove the base folder
                 $name = str_replace(DIR_STATIC . DIR_SCREENSHOTS . '/', '', $file);
                 // Remove the suffix, eg. "_full.png"
diff --git a/include/OrmObjects/ScreenshotQuery.php b/include/OrmObjects/ScreenshotQuery.php
index 62818696..463e978f 100644
--- a/include/OrmObjects/ScreenshotQuery.php
+++ b/include/OrmObjects/ScreenshotQuery.php
@@ -31,6 +31,7 @@ class ScreenshotQuery extends BaseScreenshotQuery
                 FROM screenshot ORDER BY RANDOM() LIMIT 1";
         try {
             $stmt = $con->prepare($sql);
+            \assert($stmt !== false);
             $stmt->execute();
         } catch (\Exception $e) {
             Propel::log($e->getMessage(), Propel::LOG_ERR);
@@ -92,6 +93,7 @@ class ScreenshotQuery extends BaseScreenshotQuery
                          subcategory_name";
         try {
             $stmt = $con->prepare($sql);
+            \assert($stmt !== false);
             $stmt->execute();
         } catch (\Exception $e) {
             Propel::log($e->getMessage(), Propel::LOG_ERR);
diff --git a/include/Pages/ArticlePage.php b/include/Pages/ArticlePage.php
index 9ee5d349..b766e39d 100644
--- a/include/Pages/ArticlePage.php
+++ b/include/Pages/ArticlePage.php
@@ -34,7 +34,11 @@ class ArticlePage extends Controller
             throw new \ErrorException(\sprintf(self::FILE_NOT_FOUND, $filename));
         }
 
-        return YamlFrontMatter::parse(file_get_contents($fname));
+        $article = @file_get_contents($fname);
+        if ($article === false) {
+            throw new \ErrorException(\sprintf(self::FILE_NOT_FOUND, $filename));
+        }
+        return YamlFrontMatter::parse($article);
     }
 
     /**
diff --git a/public_html/index.php b/public_html/index.php
index 5099ad26..800b3358 100644
--- a/public_html/index.php
+++ b/public_html/index.php
@@ -146,7 +146,7 @@ foreach ($pages as $key => $value) {
 
 $match = $router->match(strtolower($_SERVER['REQUEST_URI']));
 
-if (!$match) {
+if ($match === false) {
     $page = new \ScummVM\Pages\NewsPage();
     $page->index(array());
     return;


Commit: 7b0a2033dea421aa40152640bfb083f77744075b
    https://github.com/scummvm/scummvm-web/commit/7b0a2033dea421aa40152640bfb083f77744075b
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Gracefully handle cases when there are no extension in files

Changed paths:
    include/FileUtils.php


diff --git a/include/FileUtils.php b/include/FileUtils.php
index c555d0f0..30fe5b82 100644
--- a/include/FileUtils.php
+++ b/include/FileUtils.php
@@ -59,11 +59,16 @@ class FileUtils
     {
         $path = FileUtils::toAbsolutePathIfOnServer($path);
         // Get everything to the right of the last period
-        $extension = substr($path, (strrpos($path, '.')));
+        if (($pos = strrpos($path, '.')) !== false) {
+            $extension = substr($path, (strrpos($path, '.')));
+        } else {
+            $extension = '';
+        }
 
         // For certain extensions, check for another extension (e.g. foo.tar.gz => tar.gz)
-        if (in_array($extension, self::DOUBLE_EXTENSIONS)) {
-            $extension = substr($path, strrpos($path, '.', -(strlen($path) - strrpos($path, '.') + 1)));
+        if (in_array($extension, self::DOUBLE_EXTENSIONS) &&
+            ($pos = strrpos($path, '.', -(strlen($path) - $pos + 1))) !== false) {
+            $extension = substr($path, $pos);
         }
         return $extension;
     }


Commit: a1afb8a64d3d5f72623cb2656a6f4153866f8007
    https://github.com/scummvm/scummvm-web/commit/a1afb8a64d3d5f72623cb2656a6f4153866f8007
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Handle cases when WebLink are mixed with Files

Also make hasOldVersion static as it doesn't need access to the
instance.

Changed paths:
    include/Objects/DownloadsSection.php


diff --git a/include/Objects/DownloadsSection.php b/include/Objects/DownloadsSection.php
index 038b81e4..4516296a 100644
--- a/include/Objects/DownloadsSection.php
+++ b/include/Objects/DownloadsSection.php
@@ -33,8 +33,19 @@ class DownloadsSection extends BasicSection
         if ($item->getCategoryIcon()) {
             $this->items[] = new File($item->toArray(TableMap::TYPE_FIELDNAME));
             // If this item is for an old version, sort all items by version, descending, then by autoId
-            if ($this->hasOldVersion($item)) {
+            if (self::hasOldVersion($item)) {
                 usort($this->items, function ($a, $b) {
+                    $aFile = $a instanceof File;
+                    $bFile = $b instanceof File;
+
+                    if (!$aFile && !$bFile) {
+                        return strcmp($a->getURL(), $b->getURL());
+                    } elseif (!$aFile) {
+                        return 1;
+                    } elseif (!$bFile) {
+                        return -1;
+                    }
+
                     // Return 0 if equal, -1 if $a->getVersion() is larger, 1 if $b->getVersion() is larger
                     $versionSortResult = -version_compare($a->getVersion(), $b->getVersion());
                     if ($versionSortResult == 0) {
@@ -79,7 +90,7 @@ class DownloadsSection extends BasicSection
     /**
      * @param Download|GameDownload $item
      */
-    private function hasOldVersion(object $item): bool
+    private static function hasOldVersion(object $item): bool
     {
         return method_exists($item, 'getVersion') && $item->getVersion() !== RELEASE && $item->getVersion() !== 'Daily';
     }


Commit: e2f72c8d8b25b5edeb8b9a3506cd6aada451ac39
    https://github.com/scummvm/scummvm-web/commit/e2f72c8d8b25b5edeb8b9a3506cd6aada451ac39
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Modify all extra attributes at once

This makes PHPStan happy (because the array type is expected to be
either empty or full).

Changed paths:
    include/Objects/File.php


diff --git a/include/Objects/File.php b/include/Objects/File.php
index a77086b4..677d83a3 100644
--- a/include/Objects/File.php
+++ b/include/Objects/File.php
@@ -60,10 +60,12 @@ class File extends BasicObject
             $fname = str_replace('{$version}', "$this->version", $fname);
 
             if (FileUtils::exists($fname)) {
-                $this->extra_info['size'] = FileUtils::getFileSize($fname);
-                $this->extra_info['sha256'] = FileUtils::getSha256($fname);
-                $this->extra_info['ext'] = FileUtils::getExtension($fname);
-                $this->extra_info['date'] = FileUtils::getLastModified($fname);
+                $extra_info = [];
+                $extra_info['size'] = FileUtils::getFileSize($fname);
+                $extra_info['sha256'] = FileUtils::getSha256($fname);
+                $extra_info['ext'] = FileUtils::getExtension($fname);
+                $extra_info['date'] = FileUtils::getLastModified($fname);
+                $this->extra_info = $extra_info;
             }
             $this->url = $fname;
         }


Commit: d74cb92ec2c6ea6bc1e43875462c78280ea023f5
    https://github.com/scummvm/scummvm-web/commit/d74cb92ec2c6ea6bc1e43875462c78280ea023f5
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Make sure the Page object provides an index function

Changed paths:
    public_html/index.php


diff --git a/public_html/index.php b/public_html/index.php
index 800b3358..1a4ff68b 100644
--- a/public_html/index.php
+++ b/public_html/index.php
@@ -160,4 +160,6 @@ if ($match['target'] === '\ScummVM\Pages\SimplePage' || $match['target'] === '\S
 } else {
     $page = new $match['target']();
 }
+
+assert(method_exists($page, 'index'));
 $page->index($match['params']);


Commit: 9a594545250715b54929c445947d5b7488c2888a
    https://github.com/scummvm/scummvm-web/commit/9a594545250715b54929c445947d5b7488c2888a
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
BUILD: Increase PHPStan level

The AltoRouter::match function has a bad signature: fix it using a stub.

Changed paths:
  A stubs/altorouter.stub
    composer.json
    include/Models/CompatibilityModel.php
    include/Objects/DownloadsSection.php
    include/OrmObjects/ScreenshotQuery.php
    phpstan.neon


diff --git a/composer.json b/composer.json
index 3b8af907..41df5fcd 100644
--- a/composer.json
+++ b/composer.json
@@ -57,8 +57,8 @@
       "@start"
     ],
     "lint": [
-      "phpcbf --standard=psr2 --ignore=./include/OrmObjects/Base,./include/OrmObjects/Map ./include public_html/index.php",
-      "phpcs  --standard=psr2 --ignore=./include/OrmObjects/Base,./include/OrmObjects/Map ./include public_html/index.php",
+      "phpcbf --standard=psr2 --ignore=./include/OrmObjects/Base,./include/OrmObjects/Map ./include ./stubs public_html/index.php",
+      "phpcs  --standard=psr2 --ignore=./include/OrmObjects/Base,./include/OrmObjects/Map ./include ./stubs public_html/index.php",
       "phpstan"
     ],
     "update-data": [
diff --git a/include/Models/CompatibilityModel.php b/include/Models/CompatibilityModel.php
index 16165d3d..dc61d41f 100644
--- a/include/Models/CompatibilityModel.php
+++ b/include/Models/CompatibilityModel.php
@@ -34,6 +34,7 @@ class CompatibilityModel extends BasicModel
             }
             $cachekey = $version;
             $version = \explode('.', $version);
+            /** @phpstan-ignore method.notFound */
             $data = CompatibilityQuery::create()
                 ->withColumn("max(release_date)")
                 ->groupById()
diff --git a/include/Objects/DownloadsSection.php b/include/Objects/DownloadsSection.php
index 4516296a..c9ffb764 100644
--- a/include/Objects/DownloadsSection.php
+++ b/include/Objects/DownloadsSection.php
@@ -31,6 +31,7 @@ class DownloadsSection extends BasicSection
     public function addItem(object $item): void
     {
         if ($item->getCategoryIcon()) {
+            /** @phpstan-ignore argument.type */
             $this->items[] = new File($item->toArray(TableMap::TYPE_FIELDNAME));
             // If this item is for an old version, sort all items by version, descending, then by autoId
             if (self::hasOldVersion($item)) {
@@ -57,6 +58,7 @@ class DownloadsSection extends BasicSection
                 });
             }
         } else {
+            /** @phpstan-ignore argument.type */
             $this->items[] = new WebLink($item->toArray(TableMap::TYPE_FIELDNAME));
         }
     }
diff --git a/include/OrmObjects/ScreenshotQuery.php b/include/OrmObjects/ScreenshotQuery.php
index 463e978f..18025909 100644
--- a/include/OrmObjects/ScreenshotQuery.php
+++ b/include/OrmObjects/ScreenshotQuery.php
@@ -108,6 +108,7 @@ class ScreenshotQuery extends BaseScreenshotQuery
     public function filterByCompanyId(string $companyId, ConnectionInterface $con = null): self
     {
         if ($companyId !== 'other') {
+            /** @phpstan-ignore return.type */
             return $this->useGameQuery()
                 ->filterByCompanyId($companyId)
                 ->endUse();
diff --git a/phpstan.neon b/phpstan.neon
index 61af394b..8e67eaa3 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,6 +1,8 @@
 parameters:
-	level: 6
+	level: 7
 	treatPhpDocTypesAsCertain: false
+	stubFiles:
+		- stubs/altorouter.stub
 	bootstrapFiles:
 		- include/Constants.php
 	paths:
diff --git a/stubs/altorouter.stub b/stubs/altorouter.stub
new file mode 100644
index 00000000..ea43ecc9
--- /dev/null
+++ b/stubs/altorouter.stub
@@ -0,0 +1,12 @@
+<?php
+
+class AltoRouter
+{
+
+    /**
+     * @param string $requestUrl
+     * @param string $requestMethod
+     * @return array{'target': mixed, 'params': string[], 'name': ?string}|false
+     */
+    public function match(?string $requestUrl = null, ?string $requestMethod = null);
+}


Commit: 93d3aa56a1e0824db219707c2cc2fe4e902e4f1b
    https://github.com/scummvm/scummvm-web/commit/93d3aa56a1e0824db219707c2cc2fe4e902e4f1b
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Handle null cases

Sometimes variables may be null. Handle the cases when passing to
functions expecting something non-null.

Changed paths:
    include/Controller.php
    include/LocalizationUtils.php
    include/Models/DownloadsModel.php
    include/Models/GameDownloadsModel.php
    include/Objects/DownloadsSection.php
    include/Objects/News.php
    include/OrmObjects/Compatibility.php
    include/Pages/CompatibilityPage.php
    include/Pages/DownloadsPage.php
    include/Pages/GamesPage.php
    public_html/index.php


diff --git a/include/Controller.php b/include/Controller.php
index 8b335a11..043c0b55 100644
--- a/include/Controller.php
+++ b/include/Controller.php
@@ -118,6 +118,7 @@ class Controller
     {
         /* Properly encode all ampersands as "&". */
         $string = preg_replace('/&(?!([a-z]+|(#\d+));)/i', '&', $string);
+        assert($string !== null);
         /* Replace weird characters that appears in some of the data. */
         return $string;
     }
@@ -158,7 +159,9 @@ class Controller
     public function releaseSmartyModifier(string $string): string
     {
         $string = preg_replace("/\{[$]?release\}/", RELEASE, $string);
+        assert($string !== null);
         $string = preg_replace("/\{[$]?release_tools\}/", RELEASE_TOOLS, $string);
+        assert($string !== null);
         return $string;
     }
 
diff --git a/include/LocalizationUtils.php b/include/LocalizationUtils.php
index 632c11c3..45e46a06 100644
--- a/include/LocalizationUtils.php
+++ b/include/LocalizationUtils.php
@@ -90,7 +90,9 @@ class LocalizationUtils
                     $content = preg_replace_callback(
                         "/(?<=\(http)(.*?)(?=\))/u",
                         function (array $matches): string {
-                            return preg_replace("/\x{202f}/u", "", $matches[1]);
+                            $ret = preg_replace("/\x{202f}/u", "", $matches[1]);
+                            assert($ret !== null);
+                            return $ret;
                         },
                         $content
                     );
diff --git a/include/Models/DownloadsModel.php b/include/Models/DownloadsModel.php
index 5ae2b420..cd33c485 100644
--- a/include/Models/DownloadsModel.php
+++ b/include/Models/DownloadsModel.php
@@ -53,7 +53,7 @@ class DownloadsModel extends BasicModel
                     $sections[$category] = new DownloadsSection([
                         'anchor' => $category,
                         'title' => $sectionsData[$category]['title'],
-                        'notes' => $sectionsData[$category]['notes'] ?? null
+                        'notes' => $sectionsData[$category]['notes'] ?? ''
                     ]);
                 }
 
@@ -62,7 +62,7 @@ class DownloadsModel extends BasicModel
                     $sections[$category]->addSubsection(new DownloadsSection([
                         'anchor' => $subCategory,
                         'title' => $sectionsData[$subCategory]['title'],
-                        'notes' => $sectionsData[$subCategory]['notes'] ?? null
+                        'notes' => $sectionsData[$subCategory]['notes'] ?? ''
                     ]));
                 }
 
@@ -115,6 +115,8 @@ class DownloadsModel extends BasicModel
         $osParser = new OsParser();
         $osParser->setUserAgent($_SERVER['HTTP_USER_AGENT']);
         $os = $osParser->parse();
+        // Should never happen: the DeviceDetector signature seems wrong
+        assert($os !== null);
 
         $downloads = DownloadQuery::create()
             ->setIgnoreCase(true)
diff --git a/include/Models/GameDownloadsModel.php b/include/Models/GameDownloadsModel.php
index 66fb72f7..ba857efb 100644
--- a/include/Models/GameDownloadsModel.php
+++ b/include/Models/GameDownloadsModel.php
@@ -29,8 +29,8 @@ class GameDownloadsModel extends BasicModel
             foreach ($categories as $category) {
                 $sections[$category] = new DownloadsSection([
                         'anchor' => $category,
-                        'title' => $sectionsData[$category]['title'] ?? null,
-                        'notes' => $sectionsData[$category]['notes'] ?? null
+                        'title' => $sectionsData[$category]['title'] ?? '',
+                        'notes' => $sectionsData[$category]['notes'] ?? ''
                     ]);
             }
 
@@ -48,7 +48,7 @@ class GameDownloadsModel extends BasicModel
                     $sections[$category]->addSubsection(new DownloadsSection([
                         'anchor' => $gameId,
                         'title' => $gameName,
-                        'notes' => $sectionsData[$gameId]['notes'] ?? null
+                        'notes' => $sectionsData[$gameId]['notes'] ?? ''
                     ]));
                 }
 
diff --git a/include/Objects/DownloadsSection.php b/include/Objects/DownloadsSection.php
index c9ffb764..dc295ce4 100644
--- a/include/Objects/DownloadsSection.php
+++ b/include/Objects/DownloadsSection.php
@@ -48,7 +48,7 @@ class DownloadsSection extends BasicSection
                     }
 
                     // Return 0 if equal, -1 if $a->getVersion() is larger, 1 if $b->getVersion() is larger
-                    $versionSortResult = -version_compare($a->getVersion(), $b->getVersion());
+                    $versionSortResult = -version_compare($a->getVersion() ?? '', $b->getVersion() ?? '');
                     if ($versionSortResult == 0) {
                         // Return 0 if equal, -1 if $a->getAutoId() is smaller, 1 if $b->getAutoId() is smaller
                         return $a->getAutoId() <=> $b->getAutoId();
diff --git a/include/Objects/News.php b/include/Objects/News.php
index 65b28205..ccd68785 100644
--- a/include/Objects/News.php
+++ b/include/Objects/News.php
@@ -66,6 +66,7 @@ class News
             return substr_replace($full, $lurl, $urlOffset, strlen($url));
         }, $body, flags: PREG_OFFSET_CAPTURE);
 
+        assert($body !== null);
         return $body;
     }
 
diff --git a/include/OrmObjects/Compatibility.php b/include/OrmObjects/Compatibility.php
index 4cb991e7..9f670f06 100644
--- a/include/OrmObjects/Compatibility.php
+++ b/include/OrmObjects/Compatibility.php
@@ -56,7 +56,7 @@ class Compatibility extends BaseCompatibility
 
         if ($this->notes) {
             $notes .= "### Additional Notes\n";
-            $notes .= str_replace("- ", "\n- ", parent::getNotes()) . "\n";
+            $notes .= str_replace("- ", "\n- ", parent::getNotes() ?? '') . "\n";
         }
 
         $links = [];
diff --git a/include/Pages/CompatibilityPage.php b/include/Pages/CompatibilityPage.php
index d5de41ed..dc196684 100644
--- a/include/Pages/CompatibilityPage.php
+++ b/include/Pages/CompatibilityPage.php
@@ -73,7 +73,7 @@ class CompatibilityPage extends Controller
             ->find()->toArray();
 
         /* Default to DEV */
-        if (!in_array($version, $versions)) {
+        if (empty($version) || !in_array($version, $versions)) {
             $version = 'DEV';
         }
 
@@ -99,13 +99,13 @@ class CompatibilityPage extends Controller
 
         $this->renderPage(
             array(
-                'title' => preg_replace('/{version}/', $version, $this->getConfigVars('compatibilityTitle')),
+                'title' => preg_replace('/{version}/', $version, $this->getConfigVars('compatibilityTitle') ?? ''),
                 'subtitle' => $game->getGame()->getName(),
                 'description' => $this->supportLevelDescriptions[$game->getSupport()],
                 'content_title' => preg_replace(
                     '/{version}/',
                     $version,
-                    $this->getConfigVars('compatibilityContentTitle')
+                    $this->getConfigVars('compatibilityContentTitle') ?? ''
                 ),
                 'version' => $version,
                 'game' => $game,
@@ -130,12 +130,12 @@ class CompatibilityPage extends Controller
 
         $this->renderPage(
             [
-                'title' => preg_replace('/{version}/', $version, $this->getConfigVars('compatibilityTitle')),
+                'title' => preg_replace('/{version}/', $version, $this->getConfigVars('compatibilityTitle') ?? ''),
                 'description' => $this->getConfigVars('compatibilityIntro'),
                 'content_title' => preg_replace(
                     '/{version}/',
                     $version,
-                    $this->getConfigVars('compatibilityContentTitle')
+                    $this->getConfigVars('compatibilityContentTitle') ?? ''
                 ),
                 'version' => $version,
                 'compat_data' => $compat_data,
diff --git a/include/Pages/DownloadsPage.php b/include/Pages/DownloadsPage.php
index 6c864a4c..a5ee54e3 100644
--- a/include/Pages/DownloadsPage.php
+++ b/include/Pages/DownloadsPage.php
@@ -23,7 +23,7 @@ class DownloadsPage extends Controller
         $this->renderPage(
             array(
                 'title' => $this->getConfigVars('downloadsTitle'),
-                'description' => \strip_tags($this->getConfigVars('downloadsContentP1')),
+                'description' => \strip_tags($this->getConfigVars('downloadsContentP1') ?? ''),
                 'content_title' => $this->getConfigVars('downloadsContentTitle'),
                 'downloads' => $downloads,
                 'recommendedDownload' => $recommendedDownload
diff --git a/include/Pages/GamesPage.php b/include/Pages/GamesPage.php
index ebf4aed3..88d546ed 100644
--- a/include/Pages/GamesPage.php
+++ b/include/Pages/GamesPage.php
@@ -23,7 +23,7 @@ class GamesPage extends Controller
         $this->renderPage(
             array(
                 'title' => $this->getConfigVars('gamesTitle'),
-                'description' => strip_tags($this->getConfigVars('gamesContentP1')),
+                'description' => strip_tags($this->getConfigVars('gamesContentP1') ?? ''),
                 'content_title' => $this->getConfigVars('gamesContentTitle'),
                 'downloads' => $downloads,
             )
diff --git a/public_html/index.php b/public_html/index.php
index 1a4ff68b..82097ed0 100644
--- a/public_html/index.php
+++ b/public_html/index.php
@@ -153,6 +153,7 @@ if ($match === false) {
 }
 
 if ($match['target'] === '\ScummVM\Pages\SimplePage' || $match['target'] === '\ScummVM\Pages\StaticPage') {
+    assert($match['name'] !== null);
     $page = new $match['target']($match['name']);
 } elseif (strpos($match['target'], "http") === 0) {
     header("Location: {$match['target']}");


Commit: f28c5198f4b774e9faaaf02772d6b74fa0b314af
    https://github.com/scummvm/scummvm-web/commit/f28c5198f4b774e9faaaf02772d6b74fa0b314af
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Avoid returning null when there is no screenshot

Instead, return an empty screenshot object.

Changed paths:
    include/OrmObjects/Screenshot.php
    include/OrmObjects/ScreenshotQuery.php


diff --git a/include/OrmObjects/Screenshot.php b/include/OrmObjects/Screenshot.php
index 4d6f3aa4..a02d915b 100644
--- a/include/OrmObjects/Screenshot.php
+++ b/include/OrmObjects/Screenshot.php
@@ -27,6 +27,11 @@ class Screenshot extends BaseScreenshot
     {
         if (!isset($this->files)) {
             $this->files = [];
+
+            if ($this->isNew()) {
+                 return $this->files;
+            }
+
             $gameId = str_replace(':', '/', $this->getGame()->getId());
             $screenshots = glob(DIR_STATIC . DIR_SCREENSHOTS . '/' . $gameId . '/' . $this->getFileMask());
             if ($screenshots === false) {
diff --git a/include/OrmObjects/ScreenshotQuery.php b/include/OrmObjects/ScreenshotQuery.php
index 18025909..99f513a2 100644
--- a/include/OrmObjects/ScreenshotQuery.php
+++ b/include/OrmObjects/ScreenshotQuery.php
@@ -37,10 +37,9 @@ class ScreenshotQuery extends BaseScreenshotQuery
             Propel::log($e->getMessage(), Propel::LOG_ERR);
             throw new PropelException(sprintf('Unable to execute SELECT statement [%s]', $sql), 0, $e);
         }
-        $obj = null;
+        $obj = new ChildScreenshot();
         if ($row = $stmt->fetch(\PDO::FETCH_NUM)) {
             /** @var ChildScreenshot $obj */
-            $obj = new ChildScreenshot();
             $obj->hydrate($row);
             ScreenshotTableMap::addInstanceToPool($obj, null);
         }


Commit: a04dab5f7ec5b6bbea70ac075c097f48dd8e3ea1
    https://github.com/scummvm/scummvm-web/commit/a04dab5f7ec5b6bbea70ac075c097f48dd8e3ea1
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
BUILD: Increase PHPStan level

Changed paths:
    phpstan.neon


diff --git a/phpstan.neon b/phpstan.neon
index 8e67eaa3..9f2a8423 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,5 +1,5 @@
 parameters:
-	level: 7
+	level: 8
 	treatPhpDocTypesAsCertain: false
 	stubFiles:
 		- stubs/altorouter.stub


Commit: 7f6baf107a6a991318a77e5235fea4cab6af6959
    https://github.com/scummvm/scummvm-web/commit/7f6baf107a6a991318a77e5235fea4cab6af6959
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
BUILD: Add license in composer

Changed paths:
    composer.json


diff --git a/composer.json b/composer.json
index 41df5fcd..99d578d0 100644
--- a/composer.json
+++ b/composer.json
@@ -8,6 +8,7 @@
       "email": "mataniko at scummvm.org"
     }
   ],
+  "license": "GPL-3.0-or-later",
   "require": {
     "php": ">=8.2.0",
     "ext-intl": "*",


Commit: d76478f1e6f1f22e69bed53d0c57b67208e18938
    https://github.com/scummvm/scummvm-web/commit/d76478f1e6f1f22e69bed53d0c57b67208e18938
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-24T12:32:47+02:00

Commit Message:
WEB: Add a Github Actions CI check

Changed paths:
  A .github/workflows/ci.yml
    composer.json
    package.json


diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..6709a0e8
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,36 @@
+name: Web CI
+on: [push, pull_request]
+permissions:
+  contents: read
+jobs:
+  build:
+    name: Check
+    runs-on: ubuntu-latest
+    steps:
+    - name: Install glue
+      # Patch Glue to let it run on modern OS
+      run: |
+        sudo apt install -y glue-sprite && \
+        sudo ln -s glue-sprite /usr/bin/glue && \
+        sudo sed -i -e 's/PILImage\.ANTIALIAS/PILImage.LANCZOS/' /usr/lib/python3/dist-packages/glue/formats/img.py && \
+        sudo sed -i -e 's/re\.compile(/re.compile(r/' /usr/lib/python3/dist-packages/glue/core.py && \
+        sudo sed -i -e 's/PImage\.VERSION/PImage.__version__/' /usr/lib/python3/dist-packages/glue/bin.py
+    - uses: actions/checkout at v4
+    - name: Validate composer.json and composer.lock
+      run: composer validate --strict
+    - name: Setup
+      uses: actions/setup-node at v4
+      with:
+        node-version: ${{ matrix.node-version }}
+    - name: Cache Composer packages
+      id: packages-cache
+      uses: actions/cache at v4
+      with:
+        path: "vendor\nnode_modules\ndumper-companion/node_modules"
+        key: ${{ runner.os }}-phpjs-${{ hashFiles('**/composer.lock', '**/package-lock.json') }}
+        restore-keys: |
+          ${{ runner.os }}-phpjs-
+    - name: Install dependencies
+      run: composer install --prefer-dist --no-progress
+    - name: Run CI checks
+      run: composer run-script ci
diff --git a/composer.json b/composer.json
index 99d578d0..a9b59801 100644
--- a/composer.json
+++ b/composer.json
@@ -49,6 +49,13 @@
       "@build-common",
       "npm install"
     ],
+    "ci": [
+      "composer install",
+      "@build-common",
+      "@lint",
+      "npm ci",
+      "npm run lint"
+    ],
     "start": [
       "Composer\\Config::disableProcessTimeout",
       "php -S localhost:8000 -t ./public_html ./public_html/index.php"
diff --git a/package.json b/package.json
index 4cbcf8ce..34117d60 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
   },
   "scripts": {
     "postinstall": "(cd dumper-companion && npm ci --omit=dev && npm run build) && node build.js",
-    "scss:watch": "sass --watch ./scss:./public_html/css"
+    "scss:watch": "sass --watch ./scss:./public_html/css",
+    "lint": "(cd dumper-companion && npm ci && npm run lint)"
   }
 }




More information about the Scummvm-git-logs mailing list