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 $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> */ + #[\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 $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> */ + #[\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 $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 $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 $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 $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 */ + #[\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 */ + #[\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 */ + #[\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 */ + #[\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 */ + #[\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('

Hello World

' . $entry->content()); + $message = $this->getUserConfigurationValue('message'); + $entry->_content("

{$message}

" . $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('

Hello World

' . $entry->content()); + $message = $this->getUserConfigurationValue('message'); + $entry->_content("

{$message}

" . $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