Przeglądaj źródła

Require PHP 7.4+ (#5720)

* Require PHP 7.4+
https://github.com/FreshRSS/FreshRSS/discussions/5474

* Update Docker oldest
Alpine 3.13 with PHP 7.4.26

* Add missing packets to Docker oldest

* Update to typed properties
https://php.net/migration74.new-features#migration74.new-features.core.typed-properties

* More types
Alexandre Alapetite 2 lat temu
rodzic
commit
06d0099504
59 zmienionych plików z 363 dodań i 623 usunięć
  1. 2 2
      Docker/Dockerfile-Oldest
  2. 1 1
      README.fr.md
  3. 1 1
      README.md
  4. 1 2
      app/Controllers/entryController.php
  5. 2 4
      app/Controllers/importExportController.php
  6. 1 2
      app/Controllers/tagController.php
  7. 1 2
      app/Exceptions/AlreadySubscribedException.php
  8. 1 2
      app/Exceptions/FeedNotAddedException.php
  9. 1 2
      app/Exceptions/ZipException.php
  10. 1 2
      app/Models/Auth.php
  11. 3 5
      app/Models/BooleanSearch.php
  12. 9 16
      app/Models/Category.php
  13. 20 65
      app/Models/Context.php
  14. 17 29
      app/Models/Entry.php
  15. 1 2
      app/Models/EntryDAO.php
  16. 24 46
      app/Models/Feed.php
  17. 2 3
      app/Models/FilterAction.php
  18. 4 6
      app/Models/Log.php
  19. 5 17
      app/Models/ReadingMode.php
  20. 27 28
      app/Models/Search.php
  21. 16 30
      app/Models/Share.php
  22. 6 17
      app/Models/Tag.php
  23. 6 9
      app/Models/Themes.php
  24. 12 24
      app/Models/UserQuery.php
  25. 65 110
      app/Models/View.php
  26. 5 7
      app/Models/ViewJavascript.php
  27. 23 30
      app/Models/ViewStats.php
  28. 6 10
      app/Services/ExportService.php
  29. 5 6
      app/Services/ImportService.php
  30. 5 8
      cli/i18n/I18nCompletionValidator.php
  31. 1 1
      cli/i18n/I18nData.php
  32. 5 8
      cli/i18n/I18nUsageValidator.php
  33. 2 4
      cli/i18n/I18nValue.php
  34. 1 1
      composer.json
  35. 2 2
      composer.lock
  36. 1 1
      constants.php
  37. 1 1
      docs/en/admins/02_Prerequisites.md
  38. 1 1
      docs/en/admins/10_ServerConfig.md
  39. 2 2
      docs/fr/users/01_Installation.md
  40. 3 3
      lib/Minz/ActionController.php
  41. 6 9
      lib/Minz/Configuration.php
  42. 4 7
      lib/Minz/Dispatcher.php
  43. 2 2
      lib/Minz/Extension.php
  44. 8 9
      lib/Minz/ExtensionManager.php
  45. 1 2
      lib/Minz/FrontController.php
  46. 3 5
      lib/Minz/Mailer.php
  47. 3 3
      lib/Minz/Migrator.php
  48. 1 2
      lib/Minz/ModelArray.php
  49. 5 18
      lib/Minz/ModelPdo.php
  50. 9 9
      lib/Minz/Paginator.php
  51. 1 2
      lib/Minz/Pdo.php
  52. 7 10
      lib/Minz/Request.php
  53. 3 6
      lib/Minz/Session.php
  54. 4 5
      lib/Minz/Translate.php
  55. 7 10
      lib/Minz/View.php
  56. 2 4
      p/api/fever.php
  57. 2 5
      tests/app/Models/LogDAOTest.php
  58. 1 1
      tests/cli/i18n/I18nDataTest.php
  59. 2 2
      tests/cli/i18n/I18nUsageValidatorTest.php

+ 2 - 2
Docker/Dockerfile-Oldest

@@ -1,4 +1,4 @@
-FROM alpine:3.8
+FROM alpine:3.13
 
 ENV TZ UTC
 SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
@@ -7,7 +7,7 @@ RUN apk add --no-cache \
 	tzdata \
 	apache2 php7-apache2 \
 	php7 php7-curl php7-gmp php7-intl php7-mbstring php7-xml php7-zip \
-	php7-ctype php7-dom php7-iconv php7-json php7-opcache php7-openssl php7-phar php7-session php7-xmlreader php7-xml php7-zlib \
+	php7-ctype php7-dom php7-iconv php7-json php7-opcache php7-openssl php7-phar php7-session php7-simplexml php7-xmlreader php7-xmlwriter php7-xml php7-tokenizer php7-zlib \
 	php7-pdo_sqlite php7-pdo_mysql php7-pdo_pgsql
 
 RUN mkdir -p /var/www/FreshRSS /run/apache2/

+ 1 - 1
README.fr.md

@@ -58,7 +58,7 @@ FreshRSS n’est fourni avec aucune garantie.
 * Serveur modeste, par exemple sous Linux ou Windows
 	* Fonctionne même sur un Raspberry Pi 1 avec des temps de réponse < 1s (testé sur 150 flux, 22k articles)
 * Serveur Web Apache2 (recommandé), ou nginx, lighttpd (non testé sur les autres)
-* PHP 7.2+
+* PHP 7.4+
 	* Requis : [cURL](https://www.php.net/curl), [DOM](https://www.php.net/dom), [JSON](https://www.php.net/json), [XML](https://www.php.net/xml), [session](https://www.php.net/session), [ctype](https://www.php.net/ctype), et [PDO_MySQL](https://www.php.net/pdo-mysql) ou [PDO_SQLite](https://www.php.net/pdo-sqlite) ou [PDO_PGSQL](https://www.php.net/pdo-pgsql)
 	* Recommandés : [GMP](https://www.php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](https://www.php.net/intl.idn) (pour les noms de domaines internationalisés), [mbstring](https://www.php.net/mbstring) (pour le texte Unicode), [iconv](https://www.php.net/iconv) (pour conversion d’encodages), [ZIP](https://www.php.net/zip) (pour import/export), [zlib](https://www.php.net/zlib) (pour les flux compressés)
 * MySQL 5.5.3+ ou équivalent MariaDB, ou SQLite 3.7.4+, ou PostgreSQL 9.5+

+ 1 - 1
README.md

@@ -58,7 +58,7 @@ FreshRSS comes with absolutely no warranty.
 * Light server running Linux or Windows
 	* It even works on Raspberry Pi 1 with response time under a second (tested with 150 feeds, 22k articles)
 * A web server: Apache2 (recommended), nginx, lighttpd (not tested on others)
-* PHP 7.2+
+* PHP 7.4+
 	* Required extensions: [cURL](https://www.php.net/curl), [DOM](https://www.php.net/dom), [JSON](https://www.php.net/json), [XML](https://www.php.net/xml), [session](https://www.php.net/session), [ctype](https://www.php.net/ctype), and [PDO_MySQL](https://www.php.net/pdo-mysql) or [PDO_SQLite](https://www.php.net/pdo-sqlite) or [PDO_PGSQL](https://www.php.net/pdo-pgsql)
 	* Recommended extensions: [GMP](https://www.php.net/gmp) (for API access on 32-bit platforms), [IDN](https://www.php.net/intl.idn) (for Internationalized Domain Names), [mbstring](https://www.php.net/mbstring) (for Unicode strings), [iconv](https://www.php.net/iconv) (for charset conversion), [ZIP](https://www.php.net/zip) (for import/export), [zlib](https://www.php.net/zlib) (for compressed feeds)
 * MySQL 5.5.3+ or MariaDB equivalent, or SQLite 3.7.4+, or PostgreSQL 9.5+

+ 1 - 2
app/Controllers/entryController.php

@@ -7,9 +7,8 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
 
 	/**
 	 * JavaScript request or not.
-	 * @var bool
 	 */
-	private $ajax = false;
+	private bool $ajax = false;
 
 	/**
 	 * This action is called before every other action in that class. It is

+ 2 - 4
app/Controllers/importExportController.php

@@ -5,11 +5,9 @@
  */
 class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 
-	/** @var FreshRSS_EntryDAO */
-	private $entryDAO;
+	private FreshRSS_EntryDAO $entryDAO;
 
-	/** @var FreshRSS_FeedDAO */
-	private $feedDAO;
+	private FreshRSS_FeedDAO $feedDAO;
 
 	/**
 	 * This action is called before every other action in that class. It is

+ 1 - 2
app/Controllers/tagController.php

@@ -7,9 +7,8 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 
 	/**
 	 * JavaScript request or not.
-	 * @var bool|mixed
 	 */
-	private $ajax = false;
+	private bool $ajax = false;
 
 	/**
 	 * This action is called before every other action in that class. It is

+ 1 - 2
app/Exceptions/AlreadySubscribedException.php

@@ -2,8 +2,7 @@
 
 class FreshRSS_AlreadySubscribed_Exception extends Exception {
 
-	/** @var string */
-	private $feedName = '';
+	private string $feedName = '';
 
 	public function __construct(string $url, string $feedName) {
 		parent::__construct('Already subscribed! ' . $url, 2135);

+ 1 - 2
app/Exceptions/FeedNotAddedException.php

@@ -2,8 +2,7 @@
 
 class FreshRSS_FeedNotAdded_Exception extends Exception {
 
-	/** @var string */
-	private $url = '';
+	private string $url = '';
 
 	public function __construct(string $url) {
 		parent::__construct('Feed not added! ' . $url, 2147);

+ 1 - 2
app/Exceptions/ZipException.php

@@ -2,8 +2,7 @@
 
 class FreshRSS_Zip_Exception extends Exception {
 
-	/** @var int */
-	private $zipErrorCode = 0;
+	private int $zipErrorCode = 0;
 
 	public function __construct(int $zipErrorCode) {
 		parent::__construct('ZIP error!', 2141);

+ 1 - 2
app/Models/Auth.php

@@ -9,8 +9,7 @@ class FreshRSS_Auth {
 	 */
 	public const DEFAULT_COOKIE_DURATION = 7776000;
 
-	/** @var bool */
-	private static $login_ok = false;
+	private static bool $login_ok = false;
 
 	/**
 	 * This method initializes authentication system.

+ 3 - 5
app/Models/BooleanSearch.php

@@ -5,16 +5,14 @@
  */
 class FreshRSS_BooleanSearch {
 
-	/** @var string */
-	private $raw_input = '';
+	private string $raw_input = '';
 	/** @var array<FreshRSS_BooleanSearch|FreshRSS_Search> */
-	private $searches = [];
+	private array $searches = [];
 
 	/**
 	 * @phpstan-var 'AND'|'OR'|'AND NOT'
-	 * @var string
 	 */
-	private $operator;
+	private string $operator;
 
 	/** @param 'AND'|'OR'|'AND NOT' $operator */
 	public function __construct(string $input, int $level = 0, string $operator = 'AND') {

+ 9 - 16
app/Models/Category.php

@@ -12,26 +12,19 @@ class FreshRSS_Category extends Minz_Model {
 	 */
 	public const KIND_DYNAMIC_OPML = 2;
 
-	/** @var int */
-	private $id = 0;
-	/** @var int */
-	private $kind = 0;
-	/** @var string */
-	private $name;
-	/** @var int */
-	private $nbFeeds = -1;
-	/** @var int */
-	private $nbNotRead = -1;
+	private int $id = 0;
+	private int $kind = 0;
+	private string $name;
+	private int $nbFeeds = -1;
+	private int $nbNotRead = -1;
 	/** @var array<FreshRSS_Feed>|null */
-	private $feeds;
+	private ?array $feeds = null;
 	/** @var bool|int */
 	private $hasFeedsWithError = false;
 	/** @var array<string,mixed> */
-	private $attributes = [];
-	/** @var int */
-	private $lastUpdate = 0;
-	/** @var bool */
-	private $error = false;
+	private array $attributes = [];
+	private int $lastUpdate = 0;
+	private bool $error = false;
 
 	/**
 	 * @param array<FreshRSS_Feed>|null $feeds

+ 20 - 65
app/Models/Context.php

@@ -6,50 +6,31 @@
  */
 final class FreshRSS_Context {
 
-	/**
-	 * @var FreshRSS_UserConfiguration|null
-	 */
-	public static $user_conf;
-
-	/**
-	 * @var FreshRSS_SystemConfiguration|null
-	 */
-	public static $system_conf;
+	public static ?FreshRSS_UserConfiguration $user_conf = null;
+	public static ?FreshRSS_SystemConfiguration $system_conf = null;
 	/**
 	 * @var array<int,FreshRSS_Category>
 	 */
-	public static $categories = [];
+	public static array $categories = [];
 	/**
 	 * @var array<int,FreshRSS_Tag>
 	 */
-	public static $tags = [];
-	/**
-	 * @var string
-	 */
-	public static $name = '';
-	/**
-	 * @var string
-	 */
-	public static $description = '';
-	/**
-	 * @var int
-	 */
-	public static $total_unread = 0;
+	public static array $tags = [];
+	public static string $name = '';
+	public static string $description = '';
+	public static int $total_unread = 0;
 
 	/** @var array{'all':int,'read':int,'unread':int} */
-	public static $total_starred = [
+	public static array $total_starred = [
 		'all' => 0,
 		'read' => 0,
 		'unread' => 0,
 	];
 
-	/**
-	 * @var int
-	 */
-	public static $get_unread = 0;
+	public static int $get_unread = 0;
 
 	/** @var array{'all':bool,'starred':bool,'feed':int|false,'category':int|false,'tag':int|false,'tags':bool} */
-	public static $current_get = [
+	public static array $current_get = [
 		'all' => false,
 		'starred' => false,
 		'feed' => false,
@@ -58,45 +39,19 @@ final class FreshRSS_Context {
 		'tags' => false,
 	];
 
-	/**
-	 * @var string
-	 */
-	public static $next_get = 'a';
-	/**
-	 * @var int
-	 */
-	public static $state = 0;
+	public static string $next_get = 'a';
+	public static int $state = 0;
 	/**
 	 * @phpstan-var 'ASC'|'DESC'
-	 * @var string
-	 */
-	public static $order = 'DESC';
-	/**
-	 * @var int
-	 */
-	public static $number = 0;
-	/** @var FreshRSS_BooleanSearch */
-	public static $search;
-	/**
-	 * @var string
-	 */
-	public static $first_id = '';
-	/**
-	 * @var string
-	 */
-	public static $next_id = '';
-	/**
-	 * @var string
-	 */
-	public static $id_max = '';
-	/**
-	 * @var int
-	 */
-	public static $sinceHours = 0;
-	/**
-	 * @var bool
 	 */
-	public static $isCli = false;
+	public static string $order = 'DESC';
+	public static int $number = 0;
+	public static FreshRSS_BooleanSearch $search;
+	public static string $first_id = '';
+	public static string $next_id = '';
+	public static string $id_max = '';
+	public static int $sinceHours = 0;
+	public static bool $isCli = false;
 
 	/**
 	 * Initialize the context for the global system.

+ 17 - 29
app/Models/Entry.php

@@ -7,38 +7,26 @@ class FreshRSS_Entry extends Minz_Model {
 	public const STATE_FAVORITE = 4;
 	public const STATE_NOT_FAVORITE = 8;
 
-	/** @var string */
-	private $id = '0';
-	/** @var string */
-	private $guid;
-	/** @var string */
-	private $title;
+	private string $id = '0';
+	private string $guid;
+	private string $title;
 	/** @var array<string> */
-	private $authors;
-	/** @var string */
-	private $content;
-	/** @var string */
-	private $link;
-	/** @var int */
-	private $date;
-	/** @var int */
-	private $lastSeen = 0;
-	/** @var string In microseconds */
-	private $date_added = '0';
-	/** @var string */
-	private $hash = '';
-	/** @var bool|null */
-	private $is_read;
-	/** @var bool|null */
-	private $is_favorite;
-	/** @var int */
-	private $feedId;
-	/** @var FreshRSS_Feed|null */
-	private $feed;
+	private array $authors;
+	private string $content;
+	private string $link;
+	private int $date;
+	private int $lastSeen = 0;
+	/** In microseconds */
+	private string $date_added = '0';
+	private string $hash = '';
+	private ?bool $is_read;
+	private ?bool $is_favorite;
+	private int $feedId;
+	private ?FreshRSS_Feed $feed;
 	/** @var array<string> */
-	private $tags = [];
+	private array $tags = [];
 	/** @var array<string,mixed> */
-	private $attributes = [];
+	private array $attributes = [];
 
 	/**
 	 * @param int|string $pubdate

+ 1 - 2
app/Models/EntryDAO.php

@@ -190,8 +190,7 @@ SQL;
 		return $result;
 	}
 
-	/** @var PDOStatement|null */
-	private $updateEntryPrepared = null;
+	private ?PDOStatement $updateEntryPrepared = null;
 
 	/** @param array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'lastSeen':int,'hash':string,
 	 *		'is_read':bool|int|null,'is_favorite':bool|int|null,'id_feed':int,'tags':string,'attributes':array<string,mixed>} $valuesTmp */

+ 24 - 46
app/Models/Feed.php

@@ -37,54 +37,32 @@ class FreshRSS_Feed extends Minz_Model {
 	public const ARCHIVING_RETENTION_COUNT_LIMIT = 10000;
 	public const ARCHIVING_RETENTION_PERIOD = 'P3M';
 
-	/** @var int */
-	private $id = 0;
-	/** @var string */
-	private $url = '';
-	/** @var int */
-	private $kind = 0;
-	/** @var int */
-	private $categoryId = 1;
-	/** @var FreshRSS_Category|null */
-	private $category;
-	/** @var int */
-	private $nbEntries = -1;
-	/** @var int */
-	private $nbNotRead = -1;
-	/** @var int */
-	private $nbPendingNotRead = 0;
-	/** @var string */
-	private $name = '';
-	/** @var string */
-	private $website = '';
-	/** @var string */
-	private $description = '';
-	/** @var int */
-	private $lastUpdate = 0;
-	/** @var int */
-	private $priority = self::PRIORITY_MAIN_STREAM;
-	/** @var string */
-	private $pathEntries = '';
-	/** @var string */
-	private $httpAuth = '';
-	/** @var bool */
-	private $error = false;
-	/** @var int */
-	private $ttl = self::TTL_DEFAULT;
+	private int $id = 0;
+	private string $url = '';
+	private int $kind = 0;
+	private int $categoryId = 1;
+	private ?FreshRSS_Category $category;
+	private int $nbEntries = -1;
+	private int $nbNotRead = -1;
+	private int $nbPendingNotRead = 0;
+	private string $name = '';
+	private string $website = '';
+	private string $description = '';
+	private int $lastUpdate = 0;
+	private int $priority = self::PRIORITY_MAIN_STREAM;
+	private string $pathEntries = '';
+	private string $httpAuth = '';
+	private bool $error = false;
+	private int $ttl = self::TTL_DEFAULT;
 	/** @var array<string,mixed> */
-	private $attributes = [];
-	/** @var bool */
-	private $mute = false;
-	/** @var string */
-	private $hash = '';
-	/** @var string */
-	private $lockPath = '';
-	/** @var string */
-	private $hubUrl = '';
-	/** @var string */
-	private $selfUrl = '';
+	private array $attributes = [];
+	private bool $mute = false;
+	private string $hash = '';
+	private string $lockPath = '';
+	private string $hubUrl = '';
+	private string $selfUrl = '';
 	/** @var array<FreshRSS_FilterAction> $filterActions */
-	private $filterActions = null;
+	private array $filterActions = [];
 
 	public function __construct(string $url, bool $validate = true) {
 		if ($validate) {

+ 2 - 3
app/Models/FilterAction.php

@@ -2,10 +2,9 @@
 
 class FreshRSS_FilterAction {
 
-	/** @var FreshRSS_BooleanSearch */
-	private $booleanSearch = null;
+	private FreshRSS_BooleanSearch $booleanSearch;
 	/** @var array<string>|null */
-	private $actions = null;
+	private ?array $actions = null;
 
 	/** @param array<string> $actions */
 	private function __construct(FreshRSS_BooleanSearch $booleanSearch, array $actions) {

+ 4 - 6
app/Models/Log.php

@@ -3,12 +3,10 @@
 declare(strict_types=1);
 
 class FreshRSS_Log extends Minz_Model {
-	/** @var string */
-	private $date;
-	/** @var string */
-	private $level;
-	/** @var string */
-	private $information;
+
+	private string $date;
+	private string $level;
+	private string $information;
 
 	public function date(): string {
 		return $this->date;

+ 5 - 17
app/Models/ReadingMode.php

@@ -5,24 +5,12 @@
  */
 class FreshRSS_ReadingMode {
 
-	/**
-	 * @var string
-	 */
-	protected $id;
-	/**
-	 * @var string
-	 */
-	protected $name;
-	/**
-	 * @var string
-	 */
-	protected $title;
+	protected string $id;
+	protected string $name;
+	protected string $title;
 	/** @var array{'c':string,'a':string,'params':array<string,mixed>} */
-	protected $urlParams;
-	/**
-	 * @var bool
-	 */
-	protected $isActive = false;
+	protected array $urlParams;
+	protected bool $isActive = false;
 
 	/**
 	 * ReadingMode constructor.

+ 27 - 28
app/Models/Search.php

@@ -12,64 +12,63 @@ class FreshRSS_Search {
 
 	/**
 	 * This contains the user input string
-	 * @var string
 	 */
-	private $raw_input = '';
+	private string $raw_input = '';
 
 	// The following properties are extracted from the raw input
 	/** @var array<string>|null */
-	private $entry_ids;
+	private ?array $entry_ids = null;
 	/** @var array<int>|null */
-	private $feed_ids;
+	private ?array $feed_ids = null;
 	/** @var array<int>|'*'|null */
-	private $label_ids;
+	private $label_ids = null;
 	/** @var array<string>|null */
-	private $label_names;
+	private ?array $label_names = null;
 	/** @var array<string>|null */
-	private $intitle;
+	private ?array $intitle = null;
 	/** @var int|false|null */
-	private $min_date;
+	private $min_date = null;
 	/** @var int|false|null */
-	private $max_date;
+	private $max_date = null;
 	/** @var int|false|null */
-	private $min_pubdate;
+	private $min_pubdate = null;
 	/** @var int|false|null */
-	private $max_pubdate;
+	private $max_pubdate = null;
 	/** @var array<string>|null */
-	private $inurl;
+	private ?array $inurl = null;
 	/** @var array<string>|null */
-	private $author;
+	private ?array $author = null;
 	/** @var array<string>|null */
-	private $tags;
+	private ?array $tags = null;
 	/** @var array<string>|null */
-	private $search;
+	private ?array $search = null;
 
 	/** @var array<string>|null */
-	private $not_entry_ids;
+	private ?array $not_entry_ids = null;
 	/** @var array<int>|null */
-	private $not_feed_ids;
+	private ?array $not_feed_ids = null;
 	/** @var array<int>|'*'|null */
-	private $not_label_ids;
+	private $not_label_ids = null;
 	/** @var array<string>|null */
-	private $not_label_names;
+	private ?array $not_label_names = null;
 	/** @var array<string>|null */
-	private $not_intitle;
+	private ?array $not_intitle = null;
 	/** @var int|false|null */
-	private $not_min_date;
+	private $not_min_date = null;
 	/** @var int|false|null */
-	private $not_max_date;
+	private $not_max_date = null;
 	/** @var int|false|null */
-	private $not_min_pubdate;
+	private $not_min_pubdate = null;
 	/** @var int|false|null */
-	private $not_max_pubdate;
+	private $not_max_pubdate = null;
 	/** @var array<string>|null */
-	private $not_inurl;
+	private ?array $not_inurl = null;
 	/** @var array<string>|null */
-	private $not_author;
+	private ?array $not_author = null;
 	/** @var array<string>|null */
-	private $not_tags;
+	private ?array $not_tags = null;
 	/** @var array<string>|null */
-	private $not_search;
+	private ?array $not_search = null;
 
 	public function __construct(string $input) {
 		$input = self::cleanSearch($input);

+ 16 - 30
app/Models/Share.php

@@ -8,7 +8,7 @@ class FreshRSS_Share {
 	 * The list of available sharing options.
 	 * @var array<string,FreshRSS_Share>
 	 */
-	private static $list_sharing = [];
+	private static array $list_sharing = [];
 
 	/**
 	 * Register a new sharing option.
@@ -71,45 +71,31 @@ class FreshRSS_Share {
 	}
 
 
-	/** @var string */
-	private $type;
-	/** @var string */
-	private $name;
-	/** @var string */
-	private $url_transform;
+	private string $type;
+	private string $name;
+	private string $url_transform;
 	/** @var array<callable>|array<string,array<callable>> */
-	private $transforms;
+	private array $transforms;
 	/**
 	 * @phpstan-var 'simple'|'advanced'
-	 * @var string
 	 */
-	private $form_type;
-	/** @var string */
-	private $help_url;
-	/** @var string|null */
-	private $custom_name = null;
-	/** @var string|null */
-	private $base_url = null;
-	/** @var string|null */
-	private $id = null;
-	/** @var string|null */
-	private $title = null;
-	/** @var string|null */
-	private $link = null;
-	/** @var bool */
-	private $isDeprecated;
+	private string $form_type;
+	private string $help_url;
+	private ?string $custom_name = null;
+	private ?string $base_url = null;
+	private ?string $id = null;
+	private ?string $title = null;
+	private ?string $link = null;
+	private bool $isDeprecated;
 	/**
 	 * @phpstan-var 'GET'|'POST'
-	 * @var string
 	 */
-	private $method;
-	/** @var string|null */
-	private $field;
+	private string $method;
+	private ?string $field;
 	/**
 	 * @phpstan-var 'button'|null
-	 * @var string
 	 */
-	private $HTMLtag;
+	private ?string $HTMLtag;
 
 	/**
 	 * Create a FreshRSS_Share object.

+ 6 - 17
app/Models/Tag.php

@@ -1,26 +1,15 @@
 <?php
 
 class FreshRSS_Tag extends Minz_Model {
-	/**
-	 * @var int
-	 */
-	private $id = 0;
-	/**
-	 * @var string
-	 */
-	private $name;
+
+	private int $id = 0;
+	private string $name;
 	/**
 	 * @var array<string,mixed>
 	 */
-	private $attributes = [];
-	/**
-	 * @var int
-	 */
-	private $nbEntries = -1;
-	/**
-	 * @var int
-	 */
-	private $nbUnread = -1;
+	private array $attributes = [];
+	private int $nbEntries = -1;
+	private int $nbUnread = -1;
 
 	public function __construct(string $name = '') {
 		$this->_name($name);

+ 6 - 9
app/Models/Themes.php

@@ -1,12 +1,10 @@
 <?php
 
 class FreshRSS_Themes extends Minz_Model {
-	/** @var string */
-	private static $themesUrl = '/themes/';
-	/** @var string */
-	private static $defaultIconsUrl = '/themes/icons/';
-	/** @var string */
-	public static $defaultTheme = 'Origine';
+
+	private static string $themesUrl = '/themes/';
+	private static string $defaultIconsUrl = '/themes/icons/';
+	public static string $defaultTheme = 'Origine';
 
 	/** @return array<string> */
 	public static function getList(): array {
@@ -51,10 +49,9 @@ class FreshRSS_Themes extends Minz_Model {
 		return false;
 	}
 
-	/** @var string */
-	private static $themeIconsUrl;
+	private static string $themeIconsUrl;
 	/** @var array<string,int> */
-	private static $themeIcons;
+	private static array $themeIcons;
 
 	/**
 	 * @return false|array{'id':string,'name':string,'author':string,'description':string,'version':float|string,'files':array<string>,'theme-color'?:string|array{'dark'?:string,'light'?:string,'default'?:string}}

+ 12 - 24
app/Models/UserQuery.php

@@ -8,30 +8,18 @@
  */
 class FreshRSS_UserQuery {
 
-	/** @var bool */
-	private $deprecated = false;
-	/** @var string */
-	private $get = '';
-	/** @var string */
-	private $get_name = '';
-	/** @var string */
-	private $get_type = '';
-	/** @var string */
-	private $name = '';
-	/** @var string */
-	private $order = '';
-	/** @var FreshRSS_BooleanSearch */
-	private $search;
-	/** @var int */
-	private $state = 0;
-	/** @var string */
-	private $url = '';
-	/** @var FreshRSS_FeedDAO|null */
-	private $feed_dao;
-	/** @var FreshRSS_CategoryDAO|null */
-	private $category_dao;
-	/** @var FreshRSS_TagDAO|null */
-	private $tag_dao;
+	private bool $deprecated = false;
+	private string $get = '';
+	private string $get_name = '';
+	private string $get_type = '';
+	private string $name = '';
+	private string $order = '';
+	private FreshRSS_BooleanSearch $search;
+	private int $state = 0;
+	private string $url = '';
+	private ?FreshRSS_FeedDAO $feed_dao;
+	private ?FreshRSS_CategoryDAO $category_dao;
+	private ?FreshRSS_TagDAO $tag_dao;
 
 	/**
 	 * @param array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string} $query

+ 65 - 110
app/Models/View.php

@@ -10,166 +10,121 @@ class FreshRSS_View extends Minz_View {
 	/** @var callable */
 	public $callbackBeforePagination;
 	/** @var array<FreshRSS_Category> */
-	public $categories;
-	/** @var FreshRSS_Category|null */
-	public $category;
-	/** @var string */
-	public $current_user;
+	public array $categories;
+	public ?FreshRSS_Category $category;
+	public string $current_user;
 	/** @var iterable<FreshRSS_Entry> */
 	public $entries;
-	/** @var FreshRSS_Entry */
-	public $entry;
-	/** @var FreshRSS_Feed|null */
-	public $feed;
+	public FreshRSS_Entry $entry;
+	public ?FreshRSS_Feed $feed;
 	/** @var array<FreshRSS_Feed> */
-	public $feeds;
-	/** @var int */
-	public $nbUnreadTags;
+	public array $feeds;
+	public int $nbUnreadTags;
 	/** @var array<FreshRSS_Tag> */
-	public $tags;
+	public array $tags;
 	/** @var array<int,array{'id':int,'name':string,'id_entry':string,'checked':bool}> */
-	public $tagsForEntry;
+	public array $tagsForEntry;
 	/** @var array<string,array<string>> */
-	public $tagsForEntries;
+	public array $tagsForEntries;
 	/** @var array<string,string> */
-	public $notification;
-	/** @var bool */
-	public $excludeMutedFeeds;
+	public array $notification;
+	public bool $excludeMutedFeeds;
 
 	// Substriptions
-	/** @var FreshRSS_Category|null */
-	public $default_category;
-	/** @var bool */
-	public $displaySlider;
-	/** @var bool */
-	public $load_ok;
-	/** @var bool */
-	public $onlyFeedsWithError;
-	/** @var bool */
-	public $signalError;
+	public ?FreshRSS_Category $default_category;
+	public bool $displaySlider = false;
+	public bool $load_ok;
+	public bool $onlyFeedsWithError;
+	public bool $signalError;
 
 	// Manage users
 	/** @var array{'feed_count':int,'article_count':int,'database_size':int,'language':string,'mail_login':string,'enabled':bool,'is_admin':bool,'last_user_activity':string,'is_default':bool} */
-	public $details;
-	/** @var bool */
-	public $disable_aside;
-	/** @var bool */
-	public $show_email_field;
-	/** @var string */
-	public $username;
+	public array $details;
+	public bool $disable_aside;
+	public bool $show_email_field;
+	public string $username;
 	/** @var array<array{'language':string,'enabled':bool,'is_admin':bool,'enabled':bool,'article_count':int,'database_size':int,'last_user_activity':string,'mail_login':string,'feed_count':int,'is_default':bool}> */
-	public $users;
+	public array $users;
 
 	// Updates
-	/** @var string */
-	public $last_update_time;
+	public string $last_update_time;
 	/** @var array<string,bool> */
-	public $status_files;
+	public array $status_files;
 	/** @var array<string,bool> */
-	public $status_php;
-	/** @var bool */
-	public $update_to_apply;
+	public array $status_php;
+	public bool $update_to_apply;
 	/** @var array<string,bool> */
-	public $status_database;
-	/** @var bool */
-	public $is_release_channel_stable;
+	public array $status_database;
+	public bool $is_release_channel_stable;
 
 	// Archiving
-	/** @var int */
-	public $nb_total;
-	/** @var int */
-	public $size_total;
-	/** @var int */
-	public $size_user;
+	public int $nb_total;
+	public int $size_total;
+	public int $size_user;
 
 	// Display
 	/** @var array<string,array{'id':string,'name':string,'author':string,'description':string,'version':float|string,'files':array<string>,'theme-color'?:string|array{'dark'?:string,'light'?:string,'default'?:string}}> */
-	public $themes;
+	public array $themes;
 
 	// Shortcuts
 	/** @var array<int, string> */
-	public $list_keys;
+	public array $list_keys;
 
 	// User queries
 	/** @var array<int,FreshRSS_UserQuery> */
-	public $queries;
+	public array $queries;
 	/**  @var FreshRSS_UserQuery|null */
-	public $query;
+	public ?FreshRSS_UserQuery $query = null;
 
 	// Export / Import
-	/** @var string */
-	public $content;
+	public string $content;
 	/** @var array<string,array<string>> */
-	public $entryIdsTagNames;
-	/** @var string */
-	public $list_title;
-	/** @var int */
-	public $queryId;
-	/** @var string */
-	public $type;
+	public array $entryIdsTagNames;
+	public string $list_title;
+	public int $queryId;
+	public string $type;
 
 	// Form login
-	/** @var int */
-	public $cookie_days;
+	public int $cookie_days;
 
 	// Registration
-	/** @var bool */
-	public $can_register;
-	/** @var string */
-	public $preferred_language;
-	/** @var bool */
-	public $show_tos_checkbox;
-	/** @var string */
-	public $terms_of_service;
-	/** @var string */
-	public $site_title;
-	/** @var string */
-	public $validation_url;
+	public bool $can_register;
+	public string $preferred_language;
+	public bool $show_tos_checkbox;
+	public string $terms_of_service;
+	public string $site_title;
+	public string $validation_url;
 
 	// Logs
-	/** @var int */
-	public $currentPage;
-	/** @var Minz_Paginator */
-	public $logsPaginator;
-	/** @var int */
-	public $nbPage;
+	public int $currentPage;
+	public Minz_Paginator $logsPaginator;
+	public int $nbPage;
 
 	// RSS view
-	/** @var string */
-	public $rss_title = '';
-	/** @var string */
-	public $rss_url = '';
-	/** @var string */
-	public $rss_base = '';
-	/** @var bool */
-	public $internal_rendering = false;
+	public string $rss_title = '';
+	public string $rss_url = '';
+	public string $rss_base = '';
+	public bool $internal_rendering = false;
 
 	// Content preview
-	/** @var string */
-	public $fatalError;
-	/** @var string */
-	public $htmlContent;
-	/** @var bool */
-	public $selectorSuccess;
+	public string $fatalError;
+	public string $htmlContent;
+	public bool $selectorSuccess;
 
 	// Extensions
 	/** @var array<string,array{'name':string,'author':string,'description':string,'version':string,'entrypoint':string,'type':'system'|'user','url':string,'method':string,'directory':string}> */
-	public $available_extensions;
-	/** @var ?Minz_Extension */
-	public $ext_details;
+	public array $available_extensions;
+	public ?Minz_Extension $ext_details;
 	/** @var array{'system':array<Minz_Extension>,'user':array<Minz_Extension>} */
-	public $extension_list;
-	/** @var ?Minz_Extension */
-	public $extension;
+	public array $extension_list;
+	public ?Minz_Extension $extension;
 	/** @var array<string,string> */
-	public $extensions_installed;
+	public array $extensions_installed;
 
 	// Errors
-	/** @var string */
-	public $code;
-	/** @var string */
-	public $errorMessage;
+	public string $code;
+	public string $errorMessage;
 	/** @var array<string,string> */
-	public $message;
+	public array $message;
 
 }

+ 5 - 7
app/Models/ViewJavascript.php

@@ -5,14 +5,12 @@ declare(strict_types=1);
 final class FreshRSS_ViewJavascript extends FreshRSS_View {
 
 	/** @var array<FreshRSS_Category> */
-	public $categories;
+	public array $categories;
 	/** @var array<FreshRSS_Feed> */
-	public $feeds;
+	public array $feeds;
 	/** @var array<FreshRSS_Tag> */
-	public $tags;
+	public array $tags;
 
-	/** @var string */
-	public $nonce;
-	/** @var string */
-	public $salt1;
+	public string $nonce;
+	public string $salt1;
 }

+ 23 - 30
app/Models/ViewStats.php

@@ -4,54 +4,47 @@ declare(strict_types=1);
 
 final class FreshRSS_ViewStats extends FreshRSS_View {
 
-	/** @var FreshRSS_Category|null */
-	public $default_category;
+	public ?FreshRSS_Category $default_category;
 	/** @var array<FreshRSS_Category> */
-	public $categories;
-	/** @var FreshRSS_Feed|null */
-	public $feed;
+	public array $categories;
+	public ?FreshRSS_Feed $feed;
 	/** @var array<FreshRSS_Feed> */
-	public $feeds;
-	/** @var bool */
-	public $displaySlider;
+	public array $feeds;
+	public bool $displaySlider = false;
 
-	/** @var float */
-	public $average;
-	/** @var float */
-	public $averageDayOfWeek;
-	/** @var float */
-	public $averageHour;
-	/** @var float */
-	public $averageMonth;
+	public float $average;
+	public float $averageDayOfWeek;
+	public float $averageHour;
+	public float $averageMonth;
 	/** @var array<string> */
-	public $days;
+	public array $days;
 	/** @var array<string,array<int,int|string>> */
-	public $entryByCategory;
+	public array $entryByCategory;
 	/** @var array<int,int> */
-	public $entryCount;
+	public array $entryCount;
 	/** @var array<string,array<int,int|string>> */
-	public $feedByCategory;
+	public array $feedByCategory;
 	/** @var array<int, string> */
-	public $hours24Labels;
+	public array $hours24Labels;
 	/** @var array<string,array<int,array<string,int|string>>> */
-	public $idleFeeds;
+	public array $idleFeeds;
 	/** @var array<int,string> */
-	public $last30DaysLabel;
+	public array $last30DaysLabel;
 	/** @var array<int,string> */
-	public $last30DaysLabels;
+	public array $last30DaysLabels;
 	/** @var array<string,string> */
-	public $months;
+	public array $months;
 	/** @var array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false */
 	public $repartition;
 	/** @var array{'main_stream':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false,'all_feeds':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false} */
-	public $repartitions;
+	public array $repartitions;
 	/** @var array<int,int> */
-	public $repartitionDayOfWeek;
+	public array $repartitionDayOfWeek;
 	/** @var array<string,int>|array<int,int> */
-	public $repartitionHour;
+	public array $repartitionHour;
 	/** @var array<int,int> */
-	public $repartitionMonth;
+	public array $repartitionMonth;
 	/** @var array<array{'id':int,'name':string,'category':string,'count':int}> */
-	public $topFeed;
+	public array $topFeed;
 
 }

+ 6 - 10
app/Services/ExportService.php

@@ -4,20 +4,16 @@
  * Provide useful methods to generate files to export.
  */
 class FreshRSS_Export_Service {
-	/** @var string */
-	private $username;
 
-	/** @var FreshRSS_CategoryDAO */
-	private $category_dao;
+	private string $username;
 
-	/** @var FreshRSS_FeedDAO */
-	private $feed_dao;
+	private FreshRSS_CategoryDAO $category_dao;
 
-	/** @var FreshRSS_EntryDAO */
-	private $entry_dao;
+	private FreshRSS_FeedDAO $feed_dao;
 
-	/** @var FreshRSS_TagDAO */
-	private $tag_dao;
+	private FreshRSS_EntryDAO $entry_dao;
+
+	private FreshRSS_TagDAO $tag_dao;
 
 	public const FRSS_NAMESPACE = 'https://freshrss.org/opml';
 	public const TYPE_HTML_XPATH = 'HTML+XPath';

+ 5 - 6
app/Services/ImportService.php

@@ -4,14 +4,13 @@
  * Provide methods to import files.
  */
 class FreshRSS_Import_Service {
-	/** @var FreshRSS_CategoryDAO */
-	private $catDAO;
 
-	/** @var FreshRSS_FeedDAO */
-	private $feedDAO;
+	private FreshRSS_CategoryDAO $catDAO;
 
-	/** @var bool true if success, false otherwise */
-	private $lastStatus;
+	private FreshRSS_FeedDAO $feedDAO;
+
+	/** true if success, false otherwise */
+	private bool $lastStatus;
 
 	/**
 	 * Initialize the service for the given user.

+ 5 - 8
cli/i18n/I18nCompletionValidator.php

@@ -5,15 +5,12 @@ require_once __DIR__ . '/I18nValidatorInterface.php';
 class I18nCompletionValidator implements I18nValidatorInterface {
 
 	/** @var array<string,array<string,I18nValue>> */
-	private $reference;
+	private array $reference;
 	/** @var array<string,array<string,I18nValue>> */
-	private $language;
-	/** @var int */
-	private $totalEntries = 0;
-	/** @var int */
-	private $passEntries = 0;
-	/** @var string */
-	private $result = '';
+	private array $language;
+	private int $totalEntries = 0;
+	private int $passEntries = 0;
+	private string $result = '';
 
 	/**
 	 * @param array<string,array<string,I18nValue>> $reference

+ 1 - 1
cli/i18n/I18nData.php

@@ -5,7 +5,7 @@ class I18nData {
 	public const REFERENCE_LANGUAGE = 'en';
 
 	/** @var array<string,array<string,array<string,I18nValue>>> */
-	private $data;
+	private array $data;
 
 	/** @param array<string,array<string,array<string,I18nValue>>> $data */
 	public function __construct(array $data) {

+ 5 - 8
cli/i18n/I18nUsageValidator.php

@@ -5,15 +5,12 @@ require_once __DIR__ . '/I18nValidatorInterface.php';
 class I18nUsageValidator implements I18nValidatorInterface {
 
 	/** @var array<string> */
-	private $code;
+	private array $code;
 	/** @var array<string,array<string,string>> */
-	private $reference;
-	/** @var int */
-	private $totalEntries = 0;
-	/** @var int */
-	private $failedEntries = 0;
-	/** @var string */
-	private $result = '';
+	private array $reference;
+	private int $totalEntries = 0;
+	private int $failedEntries = 0;
+	private string $result = '';
 
 	/**
 	 * @param array<string,array<string,string>> $reference

+ 2 - 4
cli/i18n/I18nValue.php

@@ -10,10 +10,8 @@ class I18nValue {
 		self::STATE_TODO,
 	];
 
-	/** @var string */
-	private $value;
-	/**	@var string|null */
-	private $state;
+	private string $value;
+	private ?string $state = null;
 
 	public function __construct(string $data) {
 		$data = explode(' -> ', $data);

+ 1 - 1
composer.json

@@ -17,7 +17,7 @@
         "WebSub"
     ],
     "require": {
-        "php": ">=7.2",
+        "php": ">=7.4",
         "ext-ctype": "*",
         "ext-curl": "*",
         "ext-dom": "*",

+ 2 - 2
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "280bec285929f5841748be3d4e256011",
+    "content-hash": "1f09a030b755d6dd8104c9f7a5035f47",
     "packages": [],
     "packages-dev": [
         {
@@ -1966,7 +1966,7 @@
     "prefer-stable": false,
     "prefer-lowest": false,
     "platform": {
-        "php": ">=7.2",
+        "php": ">=7.4",
         "ext-ctype": "*",
         "ext-curl": "*",
         "ext-dom": "*",

+ 1 - 1
constants.php

@@ -2,7 +2,7 @@
 //NB: Do not edit; use ./constants.local.php instead.
 
 //<Not customisable>
-const FRESHRSS_MIN_PHP_VERSION = '7.2.0';
+const FRESHRSS_MIN_PHP_VERSION = '7.4.0';
 const FRESHRSS_VERSION = '1.22.2-dev';
 const FRESHRSS_WEBSITE = 'https://freshrss.org';
 const FRESHRSS_WIKI = 'https://freshrss.github.io/FreshRSS/';

+ 1 - 1
docs/en/admins/02_Prerequisites.md

@@ -7,7 +7,7 @@ You need to verify that your server can run FreshRSS before installing it. If yo
 | Software      | Recommended             | Also Works With         |
 | ------------- | ----------------------- | ----------------------- |
 | Web server    | **Apache 2**            | Nginx, lighttpd         |
-| PHP           | **PHP 7.2+**            |                         |
+| PHP           | **PHP 7.4+**            |                         |
 | PHP modules   | Required: libxml, cURL, JSON, PDO_MySQL, PCRE and ctype.<br />Required (32-bit only): GMP <br />Recommended: Zlib, mbstring, iconv, ZipArchive<br />*For the whole modules list see [Dockerfile](https://github.com/FreshRSS/FreshRSS/blob/edge/Docker/Dockerfile-Alpine#L7-L9)* | |
 | Database      | **MySQL 5.5.3+**        | SQLite 3.7.4+, PostgreSQL 9.5+          |
 | Browser       | **Firefox**             | Chrome, Opera, Safari, or Edge          |

+ 1 - 1
docs/en/admins/10_ServerConfig.md

@@ -95,7 +95,7 @@ server {
 	# php files handling
 	# this regex is mandatory because of the API
 	location ~ ^.+?\.php(/.*)?$ {
-		fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
+		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
 		fastcgi_split_path_info ^(.+\.php)(/.*)$;
 		# By default, the variable PATH_INFO is not set under PHP-FPM
 		# But FreshRSS API greader.php need it. If you have a “Bad Request” error, double check this var!

+ 2 - 2
docs/fr/users/01_Installation.md

@@ -7,7 +7,7 @@ Il est toutefois de votre responsabilité de vérifier que votre hébergement pe
 | Logiciel         | Recommandé                                                                                                     | Fonctionne aussi avec          |
 | --------         | -----------                                                                                                    | ---------------------          |
 | Serveur web      | **Apache 2**                                                                                                   | Nginx                          |
-| PHP              | **PHP 7.2+**                                                                                                   |                                |
+| PHP              | **PHP 7.4+**                                                                                                   |                                |
 | Modules PHP      | Requis : libxml, cURL, JSON, PDO_MySQL, PCRE et ctype<br />Requis (32 bits seulement) : GMP<br />Recommandé : Zlib, mbstring et iconv, ZipArchive<br />*Pour une liste complète des modules nécessaires voir le [Dockerfile](https://github.com/FreshRSS/FreshRSS/blob/edge/Docker/Dockerfile-Alpine#L7-L9)* |                                |
 | Base de données  | **MySQL 5.5.3+**                                                                                               | SQLite 3.7.4+, PostgreSQL 9.5+   |
 | Navigateur       | **Firefox**                                                                                                    | Chrome, Opera, Safari, or Edge   |
@@ -115,7 +115,7 @@ server {
 	# gestion des fichiers php
 	# il est nécessaire d’utiliser cette expression régulière pour le bon fonctionnement de l’API
 	location ~ ^.+?\.php(/.*)?$ {
-		fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
+		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
 		fastcgi_split_path_info ^(.+\.php)(/.*)$;
 		# Par défaut la variable PATH_INFO n’est pas définie sous PHP-FPM
 		# or l’API FreshRSS greader.php en a besoin. Si vous avez un “Bad Request”, vérifiez bien cette dernière !

+ 3 - 3
lib/Minz/ActionController.php

@@ -10,12 +10,12 @@
 class Minz_ActionController {
 
 	/** @var array<string,string> */
-	private static $csp_default = [
+	private static array $csp_default = [
 		'default-src' => "'self'",
 	];
 
 	/** @var array<string,string> */
-	private $csp_policies;
+	private array $csp_policies;
 
 	/** @var Minz_View */
 	protected $view;
@@ -25,7 +25,7 @@ class Minz_ActionController {
 	 * @var class-string
 	 * @deprecated Use constructor with view type instead
 	 */
-	public static $defaultViewType = Minz_View::class;
+	public static string $defaultViewType = Minz_View::class;
 
 	/**
 	 * @phpstan-param class-string|'' $viewType

+ 6 - 9
lib/Minz/Configuration.php

@@ -17,7 +17,7 @@ class Minz_Configuration {
 	 * The list of configurations.
 	 * @var array<string,static>
 	 */
-	private static $config_list = array();
+	private static array $config_list = array();
 
 	/**
 	 * Add a new configuration to the list of configuration.
@@ -72,31 +72,28 @@ class Minz_Configuration {
 	 * Unused.
 	 * @phpstan-ignore-next-line
 	 */
-	private $namespace = '';
+	private string $namespace = '';
 
 	/**
 	 * The filename for the current configuration.
-	 * @var string
 	 */
-	private $config_filename = '';
+	private string $config_filename = '';
 
 	/**
 	 * The filename for the current default values, null by default.
-	 * @var string|null
 	 */
-	private $default_filename = null;
+	private ?string $default_filename = null;
 
 	/**
 	 * The configuration values, an empty array by default.
 	 * @var array<string,mixed>
 	 */
-	private $data = array();
+	private array $data = [];
 
 	/**
 	 * An object which help to set good values in configuration.
-	 * @var Minz_ConfigurationSetterInterface|null
 	 */
-	private $configuration_setter;
+	private ?Minz_ConfigurationSetterInterface $configuration_setter = null;
 
 	/**
 	 * Create a new Minz_Configuration object.

+ 4 - 7
lib/Minz/Dispatcher.php

@@ -12,15 +12,12 @@ class Minz_Dispatcher {
 
 	/**
 	 * Singleton
-	 * @var Minz_Dispatcher|null
 	 */
-	private static $instance;
-	/** @var bool */
-	private static $needsReset;
+	private static ?Minz_Dispatcher $instance = null;
+	private static bool $needsReset;
 	/** @var array<string,string> */
-	private static $registrations = [];
-	/** @var Minz_ActionController */
-	private $controller;
+	private static array $registrations = [];
+	private Minz_ActionController $controller;
 
 	/**
 	 * Retrieves the Dispatcher instance

+ 2 - 2
lib/Minz/Extension.php

@@ -26,10 +26,10 @@ abstract class Minz_Extension {
 	private $system_configuration;
 
 	/** @var array{0:'system',1:'user'} */
-	public static $authorized_types = array(
+	public static array $authorized_types = [
 		'system',
 		'user',
-	);
+	];
 
 	/** @var bool */
 	private $is_enabled;

+ 8 - 9
lib/Minz/ExtensionManager.php

@@ -6,22 +6,21 @@
  * @todo see coding style for methods!!
  */
 final class Minz_ExtensionManager {
-	/** @var string */
-	private static $ext_metaname = 'metadata.json';
-	/** @var string */
-	private static $ext_entry_point = 'extension.php';
+
+	private static string $ext_metaname = 'metadata.json';
+	private static string $ext_entry_point = 'extension.php';
 	/** @var array<string,Minz_Extension> */
-	private static $ext_list = array();
+	private static array $ext_list = [];
 	/** @var array<string,Minz_Extension> */
-	private static $ext_list_enabled = array();
+	private static array $ext_list_enabled = [];
 	/** @var array<string,bool> */
-	private static $ext_auto_enabled = array();
+	private static array $ext_auto_enabled = [];
 
 	/**
 	 * List of available hooks. Please keep this list sorted.
 	 * @var array<string,array{'list':array<callable>,'signature':'NoneToNone'|'NoneToString'|'OneToOne'|'PassArguments'}>
 	 */
-	private static $hook_list = array(
+	private static array $hook_list = [
 		'check_url_before_add' => array(	// function($url) -> Url | null
 			'list' => array(),
 			'signature' => 'OneToOne',
@@ -90,7 +89,7 @@ final class Minz_ExtensionManager {
 			'list' => array(),
 			'signature' => 'PassArguments',
 		),
-	);
+	];
 
 	/** Remove extensions and hooks from a previous initialisation */
 	private static function reset(): void {

+ 1 - 2
lib/Minz/FrontController.php

@@ -25,8 +25,7 @@
  */
 class Minz_FrontController {
 
-	/** @var Minz_Dispatcher */
-	protected $dispatcher;
+	protected Minz_Dispatcher $dispatcher;
 
 	/**
 	 * Constructeur

+ 3 - 5
lib/Minz/Mailer.php

@@ -32,12 +32,10 @@ class Minz_Mailer {
 	 */
 	protected $view;
 
-	/** @var string */
-	private $mailer;
+	private string $mailer;
 	/** @var array{'hostname':string,'host':string,'auth':bool,'username':string,'password':string,'secure':string,'port':int,'from':string} */
-	private $smtp_config;
-	/** @var int */
-	private $debug_level;
+	private array $smtp_config;
+	private int $debug_level;
 
 	/**
 	 * Constructor.

+ 3 - 3
lib/Minz/Migrator.php

@@ -9,11 +9,11 @@
  */
 class Minz_Migrator
 {
-	/** @var string[] */
-	private $applied_versions;
+	/** @var array<string> */
+	private array $applied_versions;
 
 	/** @var array<callable> */
-	private $migrations = [];
+	private array $migrations = [];
 
 	/**
 	 * Execute a list of migrations, skipping versions indicated in a file

+ 1 - 2
lib/Minz/ModelArray.php

@@ -10,9 +10,8 @@
 class Minz_ModelArray {
 	/**
 	 * $filename est le nom du fichier
-	 * @var string
 	 */
-	protected $filename;
+	protected string $filename;
 
 	/**
 	 * Ouvre le fichier indiqué, charge le tableau dans $array et le $filename

+ 5 - 18
lib/Minz/ModelPdo.php

@@ -12,29 +12,16 @@ class Minz_ModelPdo {
 
 	/**
 	 * Shares the connection to the database between all instances.
-	 * @var bool
 	 */
-	public static $usesSharedPdo = true;
+	public static bool $usesSharedPdo = true;
 
-	/**
-	 * @var Minz_Pdo|null
-	 */
-	private static $sharedPdo;
+	private static ?Minz_Pdo $sharedPdo = null;
 
-	/**
-	 * @var string|null
-	 */
-	private static $sharedCurrentUser;
+	private static ?string $sharedCurrentUser;
 
-	/**
-	 * @var Minz_Pdo
-	 */
-	protected $pdo;
+	protected Minz_Pdo $pdo;
 
-	/**
-	 * @var string|null
-	 */
-	protected $current_user;
+	protected ?string $current_user;
 
 	/**
 	 * @throws Minz_ConfigurationNamespaceException

+ 9 - 9
lib/Minz/Paginator.php

@@ -11,27 +11,27 @@ class Minz_Paginator {
 	/**
 	 * @var array<Minz_Model> tableau des éléments à afficher/gérer
 	 */
-	private $items = array ();
+	private array $items = [];
 
 	/**
-	 * @var int le nombre d'éléments par page
+	 * le nombre d'éléments par page
 	 */
-	private $nbItemsPerPage = 10;
+	private int $nbItemsPerPage = 10;
 
 	/**
-	 * @var int page actuelle à gérer
+	 * page actuelle à gérer
 	 */
-	private $currentPage = 1;
+	private int $currentPage = 1;
 
 	/**
-	 * @var int le nombre de pages de pagination
+	 * le nombre de pages de pagination
 	 */
-	private $nbPage = 1;
+	private int $nbPage = 1;
 
 	/**
-	 * @var int le nombre d'éléments
+	 * le nombre d'éléments
 	 */
-	private $nbItems = 0;
+	private int $nbItems = 0;
 
 	/**
 	 * Constructeur

+ 1 - 2
lib/Minz/Pdo.php

@@ -14,8 +14,7 @@ abstract class Minz_Pdo extends PDO {
 
 	abstract public function dbType(): string;
 
-	/** @var string */
-	private $prefix = '';
+	private string $prefix = '';
 	public function prefix(): string {
 		return $this->prefix;
 	}

+ 7 - 10
lib/Minz/Request.php

@@ -8,20 +8,17 @@
  * Request représente la requête http
  */
 class Minz_Request {
-	/** @var string */
-	private static $controller_name = '';
-	/** @var string */
-	private static $action_name = '';
+
+	private static string $controller_name = '';
+	private static string $action_name = '';
 	/** @var array<string,mixed> */
-	private static $params = array();
+	private static array $params = [];
 
-	/** @var string */
-	private static $default_controller_name = 'index';
-	/** @var string */
-	private static $default_action_name = 'index';
+	private static string $default_controller_name = 'index';
+	private static string $default_action_name = 'index';
 
 	/** @var array{'c'?:string,'a'?:string,'params'?:array<string,mixed>} */
-	private static $originalRequest = [];
+	private static array $originalRequest = [];
 
 	/**
 	 * Getteurs

+ 3 - 6
lib/Minz/Session.php

@@ -4,16 +4,13 @@
  * The Minz_Session class handles user’s session
  */
 class Minz_Session {
-	/**
-	 * @var bool
-	 */
-	private static $volatile = false;
+
+	private static bool $volatile = false;
 
 	/**
 	 * For mutual exclusion.
-	 * @var bool
 	 */
-	private static $locked = false;
+	private static bool $locked = false;
 
 	public static function lock(): bool {
 		if (!self::$volatile && !self::$locked) {

+ 4 - 5
lib/Minz/Translate.php

@@ -13,25 +13,24 @@ class Minz_Translate {
 	 * $path_list is the list of registered base path to search translations.
 	 * @var array<string>
 	 */
-	private static $path_list = array();
+	private static array $path_list = [];
 
 	/**
 	 * $lang_name is the name of the current language to use.
-	 * @var string
 	 */
-	private static $lang_name;
+	private static string $lang_name;
 
 	/**
 	 * $lang_files is a list of registered i18n files.
 	 * @var array<string,array<string>>
 	 */
-	private static $lang_files = array();
+	private static array $lang_files = [];
 
 	/**
 	 * $translates is a cache for i18n translation.
 	 * @var array<string,mixed>
 	 */
-	private static $translates = array();
+	private static array $translates = [];
 
 	/**
 	 * Init the translation object.

+ 7 - 10
lib/Minz/View.php

@@ -12,22 +12,19 @@ class Minz_View {
 	private const LAYOUT_PATH_NAME = '/layout/';
 	private const LAYOUT_DEFAULT = 'layout';
 
-	/** @var string */
-	private $view_filename = '';
-	/** @var string */
-	private $layout_filename = '';
+	private string $view_filename = '';
+	private string $layout_filename = '';
 	/** @var array<string> */
-	private static $base_pathnames = array(APP_PATH);
-	/** @var string */
-	private static $title = '';
+	private static array $base_pathnames = [APP_PATH];
+	private static string $title = '';
 	/** @var array<array{'media':string,'url':string}> */
-	private static $styles = [];
+	private static array $styles = [];
 	/** @var array<array{'url':string,'id':string,'defer':bool,'async':bool}> */
-	private static $scripts = [];
+	private static array $scripts = [];
 	/** @var string|array{'dark'?:string,'light'?:string,'default'?:string} */
 	private static $themeColors;
 	/** @var array<string,mixed> */
-	private static $params = [];
+	private static array $params = [];
 
 	/**
 	 * Determines if a layout is used or not

+ 2 - 4
p/api/fever.php

@@ -136,11 +136,9 @@ final class FeverAPI
 	const STATUS_OK = 1;
 	const STATUS_ERR = 0;
 
-	/** @var FreshRSS_EntryDAO */
-	private $entryDAO;
+	private FreshRSS_EntryDAO $entryDAO;
 
-	/** @var FreshRSS_FeedDAO */
-	private $feedDAO;
+	private FreshRSS_FeedDAO $feedDAO;
 
 	/**
 	 * Authenticate the user

+ 2 - 5
tests/app/Models/LogDAOTest.php

@@ -6,11 +6,9 @@ use PHPUnit\Framework\TestCase;
 class LogDAOTest extends TestCase {
 	private const LOG_FILE_TEST = 'logFileTest.txt';
 
-	/** @var FreshRSS_LogDAO */
-	private $logDAO;
+	private FreshRSS_LogDAO $logDAO;
 
-	/** @var string */
-	private $logPath;
+	private string $logPath;
 
 	protected function setUp(): void {
 		$this->logDAO = new FreshRSS_LogDAO();
@@ -27,7 +25,6 @@ class LogDAOTest extends TestCase {
 
 		$line = $this->logDAO::lines(self::LOG_FILE_TEST);
 
-		self::assertIsArray($line);
 		self::assertCount(1, $line);
 		self::assertInstanceOf(FreshRSS_Log::class, $line[0]);
 		self::assertEquals('Wed, 08 Feb 2023 15:35:05 +0000', $line[0]->date());

+ 1 - 1
tests/cli/i18n/I18nDataTest.php

@@ -5,7 +5,7 @@ require_once __DIR__ . '/../../../cli/i18n/I18nValue.php';
 
 class I18nDataTest extends PHPUnit\Framework\TestCase {
 	/** @var array<string,array<string,array<string,I18nValue>>> */
-	private $referenceData;
+	private array $referenceData;
 	/** @var I18nValue&PHPUnit\Framework\MockObject\MockObject */
 	private $value;
 

+ 2 - 2
tests/cli/i18n/I18nUsageValidatorTest.php

@@ -4,8 +4,8 @@ require_once __DIR__ . '/../../../cli/i18n/I18nValue.php';
 require_once __DIR__ . '/../../../cli/i18n/I18nUsageValidator.php';
 
 class I18nUsageValidatorTest extends PHPUnit\Framework\TestCase {
-	/** @var I18nValue */
-	private $value;
+
+	private I18nValue $value;
 
 	public function setUp(): void {
 		$this->value = $this->getMockBuilder(I18nValue::class)