aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2024-04-10 15:33:43 +0200
committerGravatar GitHub <noreply@github.com> 2024-04-10 15:33:43 +0200
commit350edf398c55b472e19a3017de9b4d2d3420b9e4 (patch)
tree00672f4cba0830e4b39f778e3a36de6b961fc5bb
parent8280e3d88edb93211fcf2aec15a7b4c1ae4d3813 (diff)
PHP 8.3 #[\Override] (#6273)
* PHP 8.3 #[\Override] https://php.watch/versions/8.3/override-attr With PHPStan `checkMissingOverrideMethodAttribute` https://phpstan.org/config-reference#checkmissingoverridemethodattribute And modified the call to phpstan-next on the model of https://github.com/FreshRSS/Extensions/pull/228 (more robust than the find method, which gave some strange errors) * Update extension example accordingly
-rw-r--r--app/Controllers/categoryController.php1
-rw-r--r--app/Controllers/configureController.php1
-rw-r--r--app/Controllers/entryController.php1
-rw-r--r--app/Controllers/extensionController.php1
-rw-r--r--app/Controllers/feedController.php1
-rw-r--r--app/Controllers/importExportController.php1
-rw-r--r--app/Controllers/indexController.php1
-rw-r--r--app/Controllers/javascriptController.php1
-rw-r--r--app/Controllers/statsController.php1
-rw-r--r--app/Controllers/subscriptionController.php1
-rw-r--r--app/Controllers/tagController.php1
-rw-r--r--app/Controllers/updateController.php1
-rw-r--r--app/Models/BooleanSearch.php1
-rw-r--r--app/Models/CategoryDAOSQLite.php1
-rw-r--r--app/Models/DatabaseDAOPGSQL.php6
-rw-r--r--app/Models/DatabaseDAOSQLite.php7
-rw-r--r--app/Models/EntryDAOPGSQL.php6
-rw-r--r--app/Models/EntryDAOSQLite.php9
-rw-r--r--app/Models/FeedDAOSQLite.php1
-rw-r--r--app/Models/Search.php1
-rw-r--r--app/Models/StatsDAOPGSQL.php4
-rw-r--r--app/Models/StatsDAOSQLite.php2
-rw-r--r--app/Models/TagDAOPGSQL.php1
-rw-r--r--app/Models/TagDAOSQLite.php1
-rw-r--r--cli/i18n/I18nCompletionValidator.php3
-rw-r--r--cli/i18n/I18nUsageValidator.php3
-rw-r--r--cli/i18n/I18nValue.php1
-rw-r--r--composer.json2
-rw-r--r--composer.lock14
-rw-r--r--docs/en/developers/03_Backend/05_Extensions.md15
-rw-r--r--docs/fr/developers/03_Backend/05_Extensions.md15
-rw-r--r--lib/Minz/Pdo.php4
-rw-r--r--lib/Minz/PdoMysql.php2
-rw-r--r--lib/Minz/PdoPgsql.php2
-rw-r--r--lib/Minz/PdoSqlite.php2
-rw-r--r--package-lock.json2
-rw-r--r--package.json2
-rw-r--r--phpstan-next.neon18
-rw-r--r--phpstan.neon1
-rw-r--r--tests/app/Models/LogDAOTest.php2
-rw-r--r--tests/cli/i18n/I18nCompletionValidatorTest.php1
-rw-r--r--tests/cli/i18n/I18nDataTest.php1
-rw-r--r--tests/cli/i18n/I18nUsageValidatorTest.php1
-rw-r--r--tests/phpstan-next.txt15
44 files changed, 128 insertions, 30 deletions
diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php
index 780f03f02..410147b4e 100644
--- a/app/Controllers/categoryController.php
+++ b/app/Controllers/categoryController.php
@@ -12,6 +12,7 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
* underlying framework.
*
*/
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php
index e7f877428..4fd9a2f28 100644
--- a/app/Controllers/configureController.php
+++ b/app/Controllers/configureController.php
@@ -10,6 +10,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
* the common boilerplate for every action. It is triggered by the
* underlying framework.
*/
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php
index c30c6b6fa..38dbf8317 100644
--- a/app/Controllers/entryController.php
+++ b/app/Controllers/entryController.php
@@ -16,6 +16,7 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
* the common boilerplate for every action. It is triggered by the
* underlying framework.
*/
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
diff --git a/app/Controllers/extensionController.php b/app/Controllers/extensionController.php
index 8b01b5823..0029d5e52 100644
--- a/app/Controllers/extensionController.php
+++ b/app/Controllers/extensionController.php
@@ -10,6 +10,7 @@ class FreshRSS_extension_Controller extends FreshRSS_ActionController {
* the common boiler plate for every action. It is triggered by the
* underlying framework.
*/
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php
index 122807cf0..d1d506c0d 100644
--- a/app/Controllers/feedController.php
+++ b/app/Controllers/feedController.php
@@ -10,6 +10,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
* the common boiler plate for every action. It is triggered by the
* underlying framework.
*/
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess()) {
// Token is useful in the case that anonymous refresh is forbidden
diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php
index 2a437e34c..f92abf69d 100644
--- a/app/Controllers/importExportController.php
+++ b/app/Controllers/importExportController.php
@@ -15,6 +15,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
* the common boilerplate for every action. It is triggered by the
* underlying framework.
*/
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php
index 17439fe27..65e30c4bd 100644
--- a/app/Controllers/indexController.php
+++ b/app/Controllers/indexController.php
@@ -6,6 +6,7 @@ declare(strict_types=1);
*/
class FreshRSS_index_Controller extends FreshRSS_ActionController {
+ #[\Override]
public function firstAction(): void {
$this->view->html_url = Minz_Url::display(['c' => 'index', 'a' => 'index'], 'html', 'root');
}
diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php
index f7ac5a852..2cdf75d03 100644
--- a/app/Controllers/javascriptController.php
+++ b/app/Controllers/javascriptController.php
@@ -12,6 +12,7 @@ class FreshRSS_javascript_Controller extends FreshRSS_ActionController {
parent::__construct(FreshRSS_ViewJavascript::class);
}
+ #[\Override]
public function firstAction(): void {
$this->view->_layout(null);
}
diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php
index 8ff2744ae..062603930 100644
--- a/app/Controllers/statsController.php
+++ b/app/Controllers/statsController.php
@@ -20,6 +20,7 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController {
* the common boilerplate for every action. It is triggered by the
* underlying framework.
*/
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php
index 554243725..39836d1e4 100644
--- a/app/Controllers/subscriptionController.php
+++ b/app/Controllers/subscriptionController.php
@@ -10,6 +10,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
* the common boilerplate for every action. It is triggered by the
* underlying framework.
*/
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(403);
diff --git a/app/Controllers/tagController.php b/app/Controllers/tagController.php
index d998045a4..02bb930ee 100644
--- a/app/Controllers/tagController.php
+++ b/app/Controllers/tagController.php
@@ -16,6 +16,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
* the common boilerplate for every action. It is triggered by the
* underlying framework.
*/
+ #[\Override]
public function firstAction(): void {
// If ajax request, we do not print layout
$this->ajax = Minz_Request::paramBoolean('ajax');
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index eeac7eb09..3f4a46e6a 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -113,6 +113,7 @@ class FreshRSS_update_Controller extends FreshRSS_ActionController {
return $return == 0 ? true : 'Git error: ' . $line;
}
+ #[\Override]
public function firstAction(): void {
if (!FreshRSS_Auth::hasAccess('admin')) {
Minz_Error::error(403);
diff --git a/app/Models/BooleanSearch.php b/app/Models/BooleanSearch.php
index dd8b95efb..898ffa3bc 100644
--- a/app/Models/BooleanSearch.php
+++ b/app/Models/BooleanSearch.php
@@ -291,6 +291,7 @@ class FreshRSS_BooleanSearch {
$this->searches[] = $search;
}
+ #[\Override]
public function __toString(): string {
return $this->getRawInput();
}
diff --git a/app/Models/CategoryDAOSQLite.php b/app/Models/CategoryDAOSQLite.php
index 268d579b1..5fb0da31d 100644
--- a/app/Models/CategoryDAOSQLite.php
+++ b/app/Models/CategoryDAOSQLite.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
class FreshRSS_CategoryDAOSQLite extends FreshRSS_CategoryDAO {
/** @param array<int|string> $errorInfo */
+ #[\Override]
protected function autoUpdateDb(array $errorInfo): bool {
if ($tableInfo = $this->pdo->query("PRAGMA table_info('category')")) {
$columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1);
diff --git a/app/Models/DatabaseDAOPGSQL.php b/app/Models/DatabaseDAOPGSQL.php
index fe3d6149d..e6895e6f1 100644
--- a/app/Models/DatabaseDAOPGSQL.php
+++ b/app/Models/DatabaseDAOPGSQL.php
@@ -10,6 +10,7 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite {
public const UNDEFINED_COLUMN = '42703';
public const UNDEFINED_TABLE = '42P01';
+ #[\Override]
public function tablesAreCorrect(): bool {
$db = FreshRSS_Context::systemConf()->db;
$sql = 'SELECT * FROM pg_catalog.pg_tables where tableowner=:tableowner';
@@ -34,6 +35,7 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite {
}
/** @return array<array<string,string|int|bool|null>> */
+ #[\Override]
public function getSchema(string $table): array {
$sql = <<<'SQL'
SELECT column_name AS field, data_type AS type, column_default AS default, is_nullable AS null
@@ -47,6 +49,7 @@ SQL;
* @param array<string,string|int|bool|null> $dao
* @return array{'name':string,'type':string,'notnull':bool,'default':mixed}
*/
+ #[\Override]
public function daoToSchema(array $dao): array {
return [
'name' => (string)($dao['field']),
@@ -56,6 +59,7 @@ SQL;
];
}
+ #[\Override]
public function size(bool $all = false): int {
if ($all) {
$db = FreshRSS_Context::systemConf()->db;
@@ -75,7 +79,7 @@ SQL;
return (int)($res[0] ?? -1);
}
-
+ #[\Override]
public function optimize(): bool {
$ok = true;
$tables = ['category', 'feed', 'entry', 'entrytmp', 'tag', 'entrytag'];
diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php
index e72cc74e8..0ac6ee8f5 100644
--- a/app/Models/DatabaseDAOSQLite.php
+++ b/app/Models/DatabaseDAOSQLite.php
@@ -6,6 +6,7 @@ declare(strict_types=1);
*/
class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
+ #[\Override]
public function tablesAreCorrect(): bool {
$sql = 'SELECT name FROM sqlite_master WHERE type="table"';
$stm = $this->pdo->query($sql);
@@ -30,18 +31,21 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
}
/** @return array<array<string,string|int|bool|null>> */
+ #[\Override]
public function getSchema(string $table): array {
$sql = 'PRAGMA table_info(' . $table . ')';
$stm = $this->pdo->query($sql);
return $stm ? $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC) ?: []) : [];
}
+ #[\Override]
public function entryIsCorrect(): bool {
return $this->checkTable('entry', [
'id', 'guid', 'title', 'author', 'content', 'link', 'date', 'lastSeen', 'hash', 'is_read', 'is_favorite', 'id_feed', 'tags',
]);
}
+ #[\Override]
public function entrytmpIsCorrect(): bool {
return $this->checkTable('entrytmp', [
'id', 'guid', 'title', 'author', 'content', 'link', 'date', 'lastSeen', 'hash', 'is_read', 'is_favorite', 'id_feed', 'tags'
@@ -52,6 +56,7 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
* @param array<string,string|int|bool|null> $dao
* @return array{'name':string,'type':string,'notnull':bool,'default':mixed}
*/
+ #[\Override]
public function daoToSchema(array $dao): array {
return [
'name' => (string)$dao['name'],
@@ -61,6 +66,7 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
];
}
+ #[\Override]
public function size(bool $all = false): int {
$sum = 0;
if ($all) {
@@ -73,6 +79,7 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
return $sum;
}
+ #[\Override]
public function optimize(): bool {
$ok = $this->pdo->exec('VACUUM') !== false;
if (!$ok) {
diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php
index 39e86384d..8adeffe9e 100644
--- a/app/Models/EntryDAOPGSQL.php
+++ b/app/Models/EntryDAOPGSQL.php
@@ -3,23 +3,28 @@ declare(strict_types=1);
class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite {
+ #[\Override]
public static function hasNativeHex(): bool {
return true;
}
+ #[\Override]
public static function sqlHexDecode(string $x): string {
return 'decode(' . $x . ", 'hex')";
}
+ #[\Override]
public static function sqlHexEncode(string $x): string {
return 'encode(' . $x . ", 'hex')";
}
+ #[\Override]
public static function sqlIgnoreConflict(string $sql): string {
return rtrim($sql, ' ;') . ' ON CONFLICT DO NOTHING';
}
/** @param array<string|int> $errorInfo */
+ #[\Override]
protected function autoUpdateDb(array $errorInfo): bool {
if (isset($errorInfo[0])) {
if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_COLUMN) {
@@ -34,6 +39,7 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite {
return false;
}
+ #[\Override]
public function commitNewEntries(): bool {
//TODO: Update to PostgreSQL 9.5+ syntax with ON CONFLICT DO NOTHING
$sql = 'DO $$
diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php
index 1a87d03be..91894b8ac 100644
--- a/app/Models/EntryDAOSQLite.php
+++ b/app/Models/EntryDAOSQLite.php
@@ -3,27 +3,33 @@ declare(strict_types=1);
class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
+ #[\Override]
public static function isCompressed(): bool {
return false;
}
+ #[\Override]
public static function hasNativeHex(): bool {
return false;
}
+ #[\Override]
protected static function sqlConcat(string $s1, string $s2): string {
return $s1 . '||' . $s2;
}
+ #[\Override]
public static function sqlHexDecode(string $x): string {
return $x;
}
+ #[\Override]
public static function sqlIgnoreConflict(string $sql): string {
return str_replace('INSERT INTO ', 'INSERT OR IGNORE INTO ', $sql);
}
/** @param array<string|int> $errorInfo */
+ #[\Override]
protected function autoUpdateDb(array $errorInfo): bool {
if ($tableInfo = $this->pdo->query("PRAGMA table_info('entry')")) {
$columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1) ?: [];
@@ -36,6 +42,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
return false;
}
+ #[\Override]
public function commitNewEntries(): bool {
$sql = <<<'SQL'
DROP TABLE IF EXISTS `tmp`;
@@ -74,6 +81,7 @@ SQL;
* @param bool $is_read
* @return int|false affected rows
*/
+ #[\Override]
public function markRead($ids, bool $is_read = true) {
FreshRSS_UserDAO::touch();
if (is_array($ids)) { //Many IDs at once (used by API)
@@ -119,6 +127,7 @@ SQL;
* @param string $idMax max article ID
* @return int|false affected rows
*/
+ #[\Override]
public function markReadTag($id = 0, string $idMax = '0', ?FreshRSS_BooleanSearch $filters = null, int $state = 0, bool $is_read = true) {
FreshRSS_UserDAO::touch();
if ($idMax == 0) {
diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php
index c6bf9c8ae..69b859b79 100644
--- a/app/Models/FeedDAOSQLite.php
+++ b/app/Models/FeedDAOSQLite.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO {
/** @param array<int|string> $errorInfo */
+ #[\Override]
protected function autoUpdateDb(array $errorInfo): bool {
if ($tableInfo = $this->pdo->query("PRAGMA table_info('feed')")) {
$columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1);
diff --git a/app/Models/Search.php b/app/Models/Search.php
index d5dd701c3..00cb408f9 100644
--- a/app/Models/Search.php
+++ b/app/Models/Search.php
@@ -107,6 +107,7 @@ class FreshRSS_Search {
$this->parseSearch($input);
}
+ #[\Override]
public function __toString(): string {
return $this->getRawInput();
}
diff --git a/app/Models/StatsDAOPGSQL.php b/app/Models/StatsDAOPGSQL.php
index 5204b04e3..4fd00c29d 100644
--- a/app/Models/StatsDAOPGSQL.php
+++ b/app/Models/StatsDAOPGSQL.php
@@ -9,6 +9,7 @@ class FreshRSS_StatsDAOPGSQL extends FreshRSS_StatsDAO {
* @param int $feed id
* @return array<int,int>
*/
+ #[\Override]
public function calculateEntryRepartitionPerFeedPerHour(?int $feed = null): array {
return $this->calculateEntryRepartitionPerFeedPerPeriod('hour', $feed);
}
@@ -17,6 +18,7 @@ class FreshRSS_StatsDAOPGSQL extends FreshRSS_StatsDAO {
* Calculates the number of article per day of week per feed
* @return array<int,int>
*/
+ #[\Override]
public function calculateEntryRepartitionPerFeedPerDayOfWeek(?int $feed = null): array {
return $this->calculateEntryRepartitionPerFeedPerPeriod('day', $feed);
}
@@ -25,6 +27,7 @@ class FreshRSS_StatsDAOPGSQL extends FreshRSS_StatsDAO {
* Calculates the number of article per month per feed
* @return array<int,int>
*/
+ #[\Override]
public function calculateEntryRepartitionPerFeedPerMonth(?int $feed = null): array {
return $this->calculateEntryRepartitionPerFeedPerPeriod('month', $feed);
}
@@ -34,6 +37,7 @@ class FreshRSS_StatsDAOPGSQL extends FreshRSS_StatsDAO {
* @param string $period format string to use for grouping
* @return array<int,int>
*/
+ #[\Override]
protected function calculateEntryRepartitionPerFeedPerPeriod(string $period, ?int $feed = null): array {
$restrict = '';
if ($feed) {
diff --git a/app/Models/StatsDAOSQLite.php b/app/Models/StatsDAOSQLite.php
index 2aa86f0da..8997f3abe 100644
--- a/app/Models/StatsDAOSQLite.php
+++ b/app/Models/StatsDAOSQLite.php
@@ -3,6 +3,7 @@ declare(strict_types=1);
class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO {
+ #[\Override]
protected function sqlFloor(string $s): string {
return "CAST(($s) AS INT)";
}
@@ -10,6 +11,7 @@ class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO {
/**
* @return array<int,int>
*/
+ #[\Override]
protected function calculateEntryRepartitionPerFeedPerPeriod(string $period, ?int $feed = null): array {
if ($feed) {
$restrict = "WHERE e.id_feed = {$feed}";
diff --git a/app/Models/TagDAOPGSQL.php b/app/Models/TagDAOPGSQL.php
index de3b20f92..107ab6d08 100644
--- a/app/Models/TagDAOPGSQL.php
+++ b/app/Models/TagDAOPGSQL.php
@@ -3,6 +3,7 @@ declare(strict_types=1);
class FreshRSS_TagDAOPGSQL extends FreshRSS_TagDAO {
+ #[\Override]
public function sqlIgnore(): string {
return ''; //TODO
}
diff --git a/app/Models/TagDAOSQLite.php b/app/Models/TagDAOSQLite.php
index efa52807a..2ecda7735 100644
--- a/app/Models/TagDAOSQLite.php
+++ b/app/Models/TagDAOSQLite.php
@@ -3,6 +3,7 @@ declare(strict_types=1);
class FreshRSS_TagDAOSQLite extends FreshRSS_TagDAO {
+ #[\Override]
public function sqlIgnore(): string {
return 'OR IGNORE';
}
diff --git a/cli/i18n/I18nCompletionValidator.php b/cli/i18n/I18nCompletionValidator.php
index 4a8e54759..2a42dc13f 100644
--- a/cli/i18n/I18nCompletionValidator.php
+++ b/cli/i18n/I18nCompletionValidator.php
@@ -22,6 +22,7 @@ class I18nCompletionValidator implements I18nValidatorInterface {
$this->language = $language;
}
+ #[\Override]
public function displayReport(): string {
if ($this->passEntries > $this->totalEntries) {
throw new \RuntimeException('The number of translated strings cannot be higher than the number of strings');
@@ -32,10 +33,12 @@ class I18nCompletionValidator implements I18nValidatorInterface {
return sprintf('Translation is %5.1f%% complete.', $this->passEntries / $this->totalEntries * 100) . PHP_EOL;
}
+ #[\Override]
public function displayResult(): string {
return $this->result;
}
+ #[\Override]
public function validate(): bool {
foreach ($this->reference as $file => $data) {
foreach ($data as $refKey => $refValue) {
diff --git a/cli/i18n/I18nUsageValidator.php b/cli/i18n/I18nUsageValidator.php
index 1267cd66d..dd514236b 100644
--- a/cli/i18n/I18nUsageValidator.php
+++ b/cli/i18n/I18nUsageValidator.php
@@ -22,6 +22,7 @@ class I18nUsageValidator implements I18nValidatorInterface {
$this->reference = $reference;
}
+ #[\Override]
public function displayReport(): string {
if ($this->failedEntries > $this->totalEntries) {
throw new \RuntimeException('The number of unused strings cannot be higher than the number of strings');
@@ -32,10 +33,12 @@ class I18nUsageValidator implements I18nValidatorInterface {
return sprintf('%5.1f%% of translation keys are unused.', $this->failedEntries / $this->totalEntries * 100) . PHP_EOL;
}
+ #[\Override]
public function displayResult(): string {
return $this->result;
}
+ #[\Override]
public function validate(): bool {
foreach ($this->reference as $file => $data) {
foreach ($data as $key => $value) {
diff --git a/cli/i18n/I18nValue.php b/cli/i18n/I18nValue.php
index 88d0ea494..03e7676ae 100644
--- a/cli/i18n/I18nValue.php
+++ b/cli/i18n/I18nValue.php
@@ -66,6 +66,7 @@ class I18nValue {
}
}
+ #[\Override]
public function __toString(): string {
if ($this->state === null) {
return $this->value;
diff --git a/composer.json b/composer.json
index 1d7e33c91..416a4ba12 100644
--- a/composer.json
+++ b/composer.json
@@ -69,7 +69,7 @@
"phpcs": "phpcs . -s",
"phpcbf": "phpcbf . -p -s",
"phpstan": "phpstan analyse --memory-limit 512M .",
- "phpstan-next": "phpstan analyse --level 9 --memory-limit 512M $(find . -type d -name 'vendor' -prune -o -name '*.php' -o -name '*.phtml' | grep -Fxvf ./tests/phpstan-next.txt | sort | paste -s -)",
+ "phpstan-next": "phpstan analyse --memory-limit 512M -c phpstan-next.neon .",
"phpunit": "phpunit --bootstrap ./tests/bootstrap.php --verbose ./tests",
"translations": "cli/manipulate.translation.php -a format",
"test": [
diff --git a/composer.lock b/composer.lock
index 8b2575e31..113295443 100644
--- a/composer.lock
+++ b/composer.lock
@@ -428,21 +428,21 @@
},
{
"name": "phpstan/phpstan-strict-rules",
- "version": "1.5.2",
+ "version": "1.5.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
- "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542"
+ "reference": "568210bd301f94a0d4b1e5a0808c374c1b9cf11b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542",
- "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/568210bd301f94a0d4b1e5a0808c374c1b9cf11b",
+ "reference": "568210bd301f94a0d4b1e5a0808c374c1b9cf11b",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
- "phpstan/phpstan": "^1.10.34"
+ "phpstan/phpstan": "^1.10.60"
},
"require-dev": {
"nikic/php-parser": "^4.13.0",
@@ -471,9 +471,9 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
- "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2"
+ "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.3"
},
- "time": "2023-10-30T14:35:06+00:00"
+ "time": "2024-04-06T07:43:25+00:00"
},
{
"name": "phpunit/php-code-coverage",
diff --git a/docs/en/developers/03_Backend/05_Extensions.md b/docs/en/developers/03_Backend/05_Extensions.md
index 770ea29cc..602625cf8 100644
--- a/docs/en/developers/03_Backend/05_Extensions.md
+++ b/docs/en/developers/03_Backend/05_Extensions.md
@@ -132,15 +132,26 @@ The `Minz_Extension` abstract class defines another set of methods that should n
You can register at the FreshRSS event system in an extensions `init()` method, to manipulate data when some of the core functions are executed.
```php
-class HelloWorldExtension extends Minz_Extension
+final class HelloWorldExtension extends Minz_Extension
{
+ #[\Override]
public function init(): void {
$this->registerHook('entry_before_display', [$this, 'renderEntry']);
+ $this->registerHook('check_url_before_add', [self::class, 'checkUrl']);
}
+
public function renderEntry(FreshRSS_Entry $entry): FreshRSS_Entry {
- $entry->_content('<h1>Hello World</h1>' . $entry->content());
+ $message = $this->getUserConfigurationValue('message');
+ $entry->_content("<h1>{$message}</h1>" . $entry->content());
return $entry;
}
+
+ public static function checkUrlBeforeAdd(string $url): string {
+ if (str_starts_with($url, 'https://')) {
+ return $url;
+ }
+ return null;
+ }
}
```
diff --git a/docs/fr/developers/03_Backend/05_Extensions.md b/docs/fr/developers/03_Backend/05_Extensions.md
index 14a774704..a8662ffa1 100644
--- a/docs/fr/developers/03_Backend/05_Extensions.md
+++ b/docs/fr/developers/03_Backend/05_Extensions.md
@@ -188,15 +188,26 @@ You can register at the FreshRSS event system in an extensions `init()`
method, to manipulate data when some of the core functions are executed.
```php
-class HelloWorldExtension extends Minz_Extension
+final class HelloWorldExtension extends Minz_Extension
{
+ #[\Override]
public function init(): void {
$this->registerHook('entry_before_display', [$this, 'renderEntry']);
+ $this->registerHook('check_url_before_add', [self::class, 'checkUrl']);
}
+
public function renderEntry(FreshRSS_Entry $entry): FreshRSS_Entry {
- $entry->_content('<h1>Hello World</h1>' . $entry->content());
+ $message = $this->getUserConfigurationValue('message');
+ $entry->_content("<h1>{$message}</h1>" . $entry->content());
return $entry;
}
+
+ public static function checkUrlBeforeAdd(string $url): string {
+ if (str_starts_with($url, 'https://')) {
+ return $url;
+ }
+ return null;
+ }
}
```
diff --git a/lib/Minz/Pdo.php b/lib/Minz/Pdo.php
index 2f053c2af..bb07de488 100644
--- a/lib/Minz/Pdo.php
+++ b/lib/Minz/Pdo.php
@@ -43,6 +43,7 @@ abstract class Minz_Pdo extends PDO {
* @return string|false
* @throws PDOException if the attribute `PDO::ATTR_ERRMODE` is set to `PDO::ERRMODE_EXCEPTION`
*/
+ #[\Override]
#[\ReturnTypeWillChange]
public function lastInsertId($name = null) {
if ($name != null) {
@@ -59,6 +60,7 @@ abstract class Minz_Pdo extends PDO {
* @throws PDOException if the attribute `PDO::ATTR_ERRMODE` is set to `PDO::ERRMODE_EXCEPTION`
* @phpstan-ignore-next-line
*/
+ #[\Override]
#[\ReturnTypeWillChange]
public function prepare($query, $options = []) {
$query = $this->preSql($query);
@@ -72,6 +74,7 @@ abstract class Minz_Pdo extends PDO {
* @throws PDOException if the attribute `PDO::ATTR_ERRMODE` is set to `PDO::ERRMODE_EXCEPTION`
* @phpstan-ignore-next-line
*/
+ #[\Override]
#[\ReturnTypeWillChange]
public function exec($statement) {
$statement = $this->preSql($statement);
@@ -83,6 +86,7 @@ abstract class Minz_Pdo extends PDO {
* @throws PDOException if the attribute `PDO::ATTR_ERRMODE` is set to `PDO::ERRMODE_EXCEPTION`
* @phpstan-ignore-next-line
*/
+ #[\Override]
#[\ReturnTypeWillChange]
public function query(string $query, ?int $fetch_mode = null, ...$fetch_mode_args) {
$query = $this->preSql($query);
diff --git a/lib/Minz/PdoMysql.php b/lib/Minz/PdoMysql.php
index a6d927308..3f7a804a3 100644
--- a/lib/Minz/PdoMysql.php
+++ b/lib/Minz/PdoMysql.php
@@ -16,6 +16,7 @@ class Minz_PdoMysql extends Minz_Pdo {
$this->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
}
+ #[\Override]
public function dbType(): string {
return 'mysql';
}
@@ -25,6 +26,7 @@ class Minz_PdoMysql extends Minz_Pdo {
* @return string|false
* @throws PDOException if the attribute `PDO::ATTR_ERRMODE` is set to `PDO::ERRMODE_EXCEPTION`
*/
+ #[\Override]
#[\ReturnTypeWillChange]
public function lastInsertId($name = null) {
return parent::lastInsertId(); //We discard the name, only used by PostgreSQL
diff --git a/lib/Minz/PdoPgsql.php b/lib/Minz/PdoPgsql.php
index f60dd695f..d075c396f 100644
--- a/lib/Minz/PdoPgsql.php
+++ b/lib/Minz/PdoPgsql.php
@@ -16,10 +16,12 @@ class Minz_PdoPgsql extends Minz_Pdo {
$this->exec("SET NAMES 'UTF8';");
}
+ #[\Override]
public function dbType(): string {
return 'pgsql';
}
+ #[\Override]
protected function preSql(string $statement): string {
$statement = parent::preSql($statement);
return str_replace(array('`', ' LIKE '), array('"', ' ILIKE '), $statement);
diff --git a/lib/Minz/PdoSqlite.php b/lib/Minz/PdoSqlite.php
index cecc68a64..537b6cdc6 100644
--- a/lib/Minz/PdoSqlite.php
+++ b/lib/Minz/PdoSqlite.php
@@ -16,6 +16,7 @@ class Minz_PdoSqlite extends Minz_Pdo {
$this->exec('PRAGMA foreign_keys = ON;');
}
+ #[\Override]
public function dbType(): string {
return 'sqlite';
}
@@ -25,6 +26,7 @@ class Minz_PdoSqlite extends Minz_Pdo {
* @return string|false
* @throws PDOException if the attribute `PDO::ATTR_ERRMODE` is set to `PDO::ERRMODE_EXCEPTION`
*/
+ #[\Override]
#[\ReturnTypeWillChange]
public function lastInsertId($name = null) {
return parent::lastInsertId(); //We discard the name, only used by PostgreSQL
diff --git a/package-lock.json b/package-lock.json
index 33ab85f79..e11bb29c4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,7 @@
"eslint-plugin-promise": "^6.1.1",
"markdownlint-cli": "^0.39.0",
"rtlcss": "^4.1.1",
- "sass": "^1.72.0",
+ "sass": "^1.74.1",
"stylelint": "^15.11.0",
"stylelint-config-recommended-scss": "^13.1.0",
"stylelint-order": "^6.0.4",
diff --git a/package.json b/package.json
index d99c56c8f..44cdb376d 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"eslint-plugin-promise": "^6.1.1",
"markdownlint-cli": "^0.39.0",
"rtlcss": "^4.1.1",
- "sass": "^1.72.0",
+ "sass": "^1.74.1",
"stylelint": "^15.11.0",
"stylelint-config-recommended-scss": "^13.1.0",
"stylelint-order": "^6.0.4",
diff --git a/phpstan-next.neon b/phpstan-next.neon
new file mode 100644
index 000000000..0cc7f5eac
--- /dev/null
+++ b/phpstan-next.neon
@@ -0,0 +1,18 @@
+includes:
+ - phpstan.neon
+
+parameters:
+ level: 9
+ excludePaths:
+ analyse:
+ # TODO: Update files below and remove them from this list
+ - app/Controllers/configureController.php
+ - app/Controllers/importExportController.php
+ - app/Models/DatabaseDAO.php
+ - app/Models/Entry.php
+ - app/Models/Feed.php
+ - app/Services/ImportService.php
+ - app/views/configure/archiving.phtml
+ - app/views/helpers/feed/update.phtml
+ - lib/Minz/Helper.php
+ - lib/Minz/Request.php
diff --git a/phpstan.neon b/phpstan.neon
index a0f7fe8ef..3097afd9c 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -35,6 +35,7 @@ parameters:
- STDOUT
- TMP_PATH
- USERS_PATH
+ checkMissingOverrideMethodAttribute: true
reportMaybesInPropertyPhpDocTypes: false
treatPhpDocTypesAsCertain: false
strictRules:
diff --git a/tests/app/Models/LogDAOTest.php b/tests/app/Models/LogDAOTest.php
index 4d9ec0e7d..90261ae55 100644
--- a/tests/app/Models/LogDAOTest.php
+++ b/tests/app/Models/LogDAOTest.php
@@ -10,6 +10,7 @@ class LogDAOTest extends TestCase {
private string $logPath;
+ #[\Override]
protected function setUp(): void {
$this->logDAO = new FreshRSS_LogDAO();
$this->logPath = FreshRSS_LogDAO::logPath(self::LOG_FILE_TEST);
@@ -36,6 +37,7 @@ class LogDAOTest extends TestCase {
self::assertStringContainsString('', file_get_contents($this->logPath) ?: '');
}
+ #[\Override]
protected function tearDown(): void {
unlink($this->logPath);
}
diff --git a/tests/cli/i18n/I18nCompletionValidatorTest.php b/tests/cli/i18n/I18nCompletionValidatorTest.php
index 03896c11b..bc992edbe 100644
--- a/tests/cli/i18n/I18nCompletionValidatorTest.php
+++ b/tests/cli/i18n/I18nCompletionValidatorTest.php
@@ -7,6 +7,7 @@ class I18nCompletionValidatorTest extends PHPUnit\Framework\TestCase {
/** @var I18nValue&PHPUnit\Framework\MockObject\MockObject */
private $value;
+ #[\Override]
public function setUp(): void {
$this->value = $this->getMockBuilder(I18nValue::class)
->disableOriginalConstructor()
diff --git a/tests/cli/i18n/I18nDataTest.php b/tests/cli/i18n/I18nDataTest.php
index 5823d55b8..bbfc92872 100644
--- a/tests/cli/i18n/I18nDataTest.php
+++ b/tests/cli/i18n/I18nDataTest.php
@@ -9,6 +9,7 @@ class I18nDataTest extends PHPUnit\Framework\TestCase {
/** @var I18nValue&PHPUnit\Framework\MockObject\MockObject */
private $value;
+ #[\Override]
public function setUp(): void {
$this->value = $this->getMockBuilder(I18nValue::class)
->disableOriginalConstructor()
diff --git a/tests/cli/i18n/I18nUsageValidatorTest.php b/tests/cli/i18n/I18nUsageValidatorTest.php
index 720cab32e..3135cef22 100644
--- a/tests/cli/i18n/I18nUsageValidatorTest.php
+++ b/tests/cli/i18n/I18nUsageValidatorTest.php
@@ -7,6 +7,7 @@ class I18nUsageValidatorTest extends PHPUnit\Framework\TestCase {
private I18nValue $value;
+ #[\Override]
public function setUp(): void {
$this->value = $this->getMockBuilder(I18nValue::class)
->disableOriginalConstructor()
diff --git a/tests/phpstan-next.txt b/tests/phpstan-next.txt
deleted file mode 100644
index 8e28e66a8..000000000
--- a/tests/phpstan-next.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-# List of files, which are not yet passing PHPStan level 9 https://phpstan.org/user-guide/rule-levels
-# Used for automated tests to avoid regressions in files already passing that level.
-# Can be regenerated with something like:
-# find . -type d -name 'vendor' -prune -o -name '*.php' -exec sh -c 'vendor/bin/phpstan analyse --level 9 --memory-limit 512M {} >/dev/null 2>/dev/null || echo {}' \;
-
-./app/Controllers/configureController.php
-./app/Controllers/importExportController.php
-./app/Models/DatabaseDAO.php
-./app/Models/Entry.php
-./app/Models/Feed.php
-./app/Services/ImportService.php
-./app/views/configure/archiving.phtml
-./app/views/helpers/feed/update.phtml
-./lib/Minz/Helper.php
-./lib/Minz/Request.php