aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2024-06-09 20:32:12 +0200
committerGravatar GitHub <noreply@github.com> 2024-06-09 20:32:12 +0200
commit5b28a35003a015e29770094932157f13a3f7f5c0 (patch)
tree4cbe4100379ca0d148115ad31f5a1c0c95ff7c80 /app
parente98c57841b843ed881f06ce6ed1c9c89942c27b8 (diff)
Pass PHPStan level 9 (#6544)
* More PHPStan * More, passing * 4 more files * Update to PHPStan 1.11.4 Needed for fixed bug: Consider numeric-string types after string concat https://github.com/phpstan/phpstan/releases/tag/1.11.4 * Pass PHPStan level 9 Start tracking booleansInConditions * Fix mark as read * Fix doctype * ctype_digit
Diffstat (limited to 'app')
-rw-r--r--app/Controllers/apiController.php4
-rw-r--r--app/Controllers/authController.php2
-rw-r--r--app/Controllers/configureController.php19
-rw-r--r--app/Controllers/entryController.php23
-rw-r--r--app/Controllers/feedController.php3
-rw-r--r--app/Controllers/importExportController.php2
-rw-r--r--app/Models/DatabaseDAO.php6
-rw-r--r--app/Models/DatabaseDAOPGSQL.php2
-rw-r--r--app/Models/DatabaseDAOSQLite.php2
-rw-r--r--app/Models/Entry.php22
-rw-r--r--app/Models/EntryDAO.php14
-rw-r--r--app/Models/Feed.php30
-rw-r--r--app/Models/ReadingMode.php8
-rw-r--r--app/Models/UserConfiguration.php2
-rw-r--r--app/Models/UserQuery.php12
-rw-r--r--app/Services/ImportService.php18
-rw-r--r--app/layout/layout.phtml8
-rw-r--r--app/views/helpers/feed/update.phtml26
-rw-r--r--app/views/helpers/index/article.phtml4
-rw-r--r--app/views/helpers/index/normal/entry_bottom.phtml8
-rw-r--r--app/views/helpers/index/normal/entry_header.phtml8
-rw-r--r--app/views/helpers/stream-footer.phtml12
-rw-r--r--app/views/javascript/actualize.phtml14
-rw-r--r--app/views/user/profile.phtml4
24 files changed, 140 insertions, 113 deletions
diff --git a/app/Controllers/apiController.php b/app/Controllers/apiController.php
index 2d9fad535..7c20b630b 100644
--- a/app/Controllers/apiController.php
+++ b/app/Controllers/apiController.php
@@ -21,7 +21,7 @@ class FreshRSS_api_Controller extends FreshRSS_ActionController {
FreshRSS_Context::userConf()->apiPasswordHash = $apiPasswordHash;
$feverKey = FreshRSS_fever_Util::updateKey($username, $apiPasswordPlain);
- if (!$feverKey) {
+ if ($feverKey == false) {
return _t('feedback.api.password.failed');
}
@@ -56,7 +56,7 @@ class FreshRSS_api_Controller extends FreshRSS_ActionController {
}
$error = self::updatePassword($apiPasswordPlain);
- if ($error) {
+ if (is_string($error)) {
Minz_Request::bad($error, $return_url);
} else {
Minz_Request::good(_t('feedback.api.password.updated'), $return_url);
diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php
index 7b0462888..3fc7036fa 100644
--- a/app/Controllers/authController.php
+++ b/app/Controllers/authController.php
@@ -191,7 +191,7 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController {
$password = Minz_Request::paramString('p');
Minz_Request::_param('p');
- if (!$username) {
+ if ($username === '') {
return;
}
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php
index f28369477..343623d75 100644
--- a/app/Controllers/configureController.php
+++ b/app/Controllers/configureController.php
@@ -204,6 +204,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
$default = Minz_Configuration::load(FRESHRSS_PATH . '/config-user.default.php');
$shortcuts = $default['shortcuts'];
}
+ /** @var array<string,string> $shortcuts */
FreshRSS_Context::userConf()->shortcuts = array_map('trim', $shortcuts);
FreshRSS_Context::userConf()->save();
invalidateHttpCache();
@@ -384,27 +385,27 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
$queryParams['search'] = htmlspecialchars_decode($params['search'], ENT_QUOTES);
}
if (!empty($params['state']) && is_array($params['state'])) {
- $queryParams['state'] = (int)(array_sum($params['state']));
+ $queryParams['state'] = (int)array_sum($params['state']);
}
if (empty($params['token']) || !is_string($params['token'])) {
$queryParams['token'] = FreshRSS_UserQuery::generateToken($name);
} else {
$queryParams['token'] = $params['token'];
}
- if (!empty($params['shareRss']) && ctype_digit($params['shareRss'])) {
- $queryParams['shareRss'] = (bool)$params['shareRss'];
- }
- if (!empty($params['shareOpml']) && ctype_digit($params['shareOpml'])) {
- $queryParams['shareOpml'] = (bool)$params['shareOpml'];
- }
+ $queryParams['url'] = Minz_Url::display(['params' => $queryParams]);
+ $queryParams['name'] = $name;
if (!empty($params['description']) && is_string($params['description'])) {
$queryParams['description'] = htmlspecialchars_decode($params['description'], ENT_QUOTES);
}
if (!empty($params['imageUrl']) && is_string($params['imageUrl'])) {
$queryParams['imageUrl'] = $params['imageUrl'];
}
- $queryParams['url'] = Minz_Url::display(['params' => $queryParams]);
- $queryParams['name'] = $name;
+ if (!empty($params['shareOpml']) && ctype_digit($params['shareOpml'])) {
+ $queryParams['shareOpml'] = (bool)$params['shareOpml'];
+ }
+ if (!empty($params['shareRss']) && ctype_digit($params['shareRss'])) {
+ $queryParams['shareRss'] = (bool)$params['shareRss'];
+ }
$queries = FreshRSS_Context::userConf()->queries;
$queries[$id] = (new FreshRSS_UserQuery($queryParams, FreshRSS_Context::categories(), FreshRSS_Context::labels()))->toArray();
diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php
index 38dbf8317..9104cefc4 100644
--- a/app/Controllers/entryController.php
+++ b/app/Controllers/entryController.php
@@ -44,10 +44,12 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
* - is_read (default: true)
*/
public function readAction(): void {
- $id = Minz_Request::param('id');
$get = Minz_Request::paramString('get');
$next_get = Minz_Request::paramString('nextGet') ?: $get;
$id_max = Minz_Request::paramString('idMax') ?: '0';
+ if (!ctype_digit($id_max)) {
+ $id_max = '0';
+ }
$is_read = Minz_Request::paramTernary('is_read') ?? true;
FreshRSS_Context::$search = new FreshRSS_BooleanSearch(Minz_Request::paramString('search'));
@@ -64,14 +66,14 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
$this->view->tagsForEntries = [];
$entryDAO = FreshRSS_Factory::createEntryDao();
- if ($id == false) {
- // id is false? It MUST be a POST request!
+ if (!Minz_Request::hasParam('id')) {
+ // No id, then it MUST be a POST request
if (!Minz_Request::isPost()) {
Minz_Request::bad(_t('feedback.access.not_found'), ['c' => 'index', 'a' => 'index']);
return;
}
- if (!$get) {
+ if ($get === '') {
// No get? Mark all entries as read (from $id_max)
$entryDAO->markReadEntries($id_max, false, FreshRSS_Feed::PRIORITY_MAIN_STREAM, FreshRSS_Feed::PRIORITY_IMPORTANT, null, 0, $is_read);
} else {
@@ -111,7 +113,16 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
}
}
} else {
- $ids = is_array($id) ? $id : [$id];
+ /** @var array<numeric-string> $idArray */
+ $idArray = Minz_Request::paramArray('id');
+ $idString = Minz_Request::paramString('id');
+ if (count($idArray) > 0) {
+ $ids = $idArray;
+ } elseif (ctype_digit($idString)) {
+ $ids = [$idString];
+ } else {
+ $ids = [];
+ }
$entryDAO->markRead($ids, $is_read);
$tagDAO = FreshRSS_Factory::createTagDao();
$tagsForEntries = $tagDAO->getTagsForEntries($ids) ?: [];
@@ -145,7 +156,7 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController {
public function bookmarkAction(): void {
$id = Minz_Request::paramString('id');
$is_favourite = Minz_Request::paramTernary('is_favorite') ?? true;
- if ($id != '') {
+ if ($id != '' && ctype_digit($id)) {
$entryDAO = FreshRSS_Factory::createEntryDao();
$entryDAO->markFavorite($id, $is_favourite);
}
diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php
index 131d58d5e..2ecf6c374 100644
--- a/app/Controllers/feedController.php
+++ b/app/Controllers/feedController.php
@@ -439,9 +439,8 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
$nb_new_articles = 0;
foreach ($feeds as $feed) {
- /** @var FreshRSS_Feed|null $feed */
$feed = Minz_ExtensionManager::callHook('feed_before_actualize', $feed);
- if (null === $feed) {
+ if (!($feed instanceof FreshRSS_Feed)) {
continue;
}
diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php
index b4be5dd73..52c70ffe0 100644
--- a/app/Controllers/importExportController.php
+++ b/app/Controllers/importExportController.php
@@ -563,7 +563,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
// Call the extension hook
$feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed);
- if ($feed != null) {
+ if ($feed instanceof FreshRSS_Feed) {
// addFeedObject checks if feed is already in DB so nothing else to
// check here.
$id = $this->feedDAO->addFeedObject($feed);
diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php
index d0938ca62..b57138093 100644
--- a/app/Models/DatabaseDAO.php
+++ b/app/Models/DatabaseDAO.php
@@ -81,7 +81,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
return count(array_keys($tables, true, true)) === count($tables);
}
- /** @return array<array<string,string|int|bool|null>> */
+ /** @return array<array{name:string,type:string,notnull:bool,default:mixed}> */
public function getSchema(string $table): array {
$res = $this->fetchAssoc('DESC `_' . $table . '`');
return $res == null ? [] : $this->listDaoToSchema($res);
@@ -160,7 +160,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
/**
* @param array<string,string|int|bool|null> $dao
- * @return array{'name':string,'type':string,'notnull':bool,'default':mixed}
+ * @return array{name:string,type:string,notnull:bool,default:mixed}
*/
public function daoToSchema(array $dao): array {
return [
@@ -173,7 +173,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
/**
* @param array<array<string,string|int|bool|null>> $listDAO
- * @return array<array<string,string|int|bool|null>>
+ * @return array<array{name:string,type:string,notnull:bool,default:mixed}>
*/
public function listDaoToSchema(array $listDAO): array {
$list = [];
diff --git a/app/Models/DatabaseDAOPGSQL.php b/app/Models/DatabaseDAOPGSQL.php
index e6895e6f1..3cce4b062 100644
--- a/app/Models/DatabaseDAOPGSQL.php
+++ b/app/Models/DatabaseDAOPGSQL.php
@@ -34,7 +34,7 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite {
return count(array_keys($tables, true, true)) === count($tables);
}
- /** @return array<array<string,string|int|bool|null>> */
+ /** @return array<array{name:string,type:string,notnull:bool,default:mixed}> */
#[\Override]
public function getSchema(string $table): array {
$sql = <<<'SQL'
diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php
index 0ac6ee8f5..3e15578e8 100644
--- a/app/Models/DatabaseDAOSQLite.php
+++ b/app/Models/DatabaseDAOSQLite.php
@@ -30,7 +30,7 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
return count(array_keys($tables, true, true)) == count($tables);
}
- /** @return array<array<string,string|int|bool|null>> */
+ /** @return array<array{name:string,type:string,notnull:bool,default:mixed}> */
#[\Override]
public function getSchema(string $table): array {
$sql = 'PRAGMA table_info(' . $table . ')';
diff --git a/app/Models/Entry.php b/app/Models/Entry.php
index 2325252f7..e102c5c21 100644
--- a/app/Models/Entry.php
+++ b/app/Models/Entry.php
@@ -10,6 +10,7 @@ class FreshRSS_Entry extends Minz_Model {
public const STATE_FAVORITE = 4;
public const STATE_NOT_FAVORITE = 8;
+ /** @var numeric-string */
private string $id = '0';
private string $guid;
private string $title;
@@ -110,6 +111,7 @@ class FreshRSS_Entry extends Minz_Model {
}
}
+ /** @return numeric-string */
public function id(): string {
return $this->id;
}
@@ -195,8 +197,8 @@ class FreshRSS_Entry extends Minz_Model {
$thumbnailAttribute = $this->attributeArray('thumbnail') ?? [];
if (!empty($thumbnailAttribute['url'])) {
$elink = $thumbnailAttribute['url'];
- if ($allowDuplicateEnclosures || !self::containsLink($content, $elink)) {
- $content .= <<<HTML
+ if (is_string($elink) && ($allowDuplicateEnclosures || !self::containsLink($content, $elink))) {
+ $content .= <<<HTML
<figure class="enclosure">
<p class="enclosure-content">
<img class="enclosure-thumbnail" src="{$elink}" alt="" />
@@ -216,7 +218,7 @@ HTML;
continue;
}
$elink = $enclosure['url'] ?? '';
- if ($elink == '') {
+ if ($elink == '' || !is_string($elink)) {
continue;
}
if (!$allowDuplicateEnclosures && self::containsLink($content, $elink)) {
@@ -281,6 +283,7 @@ HTML;
$attributeEnclosures = $this->attributeArray('enclosures');
if (is_iterable($attributeEnclosures)) {
// FreshRSS 1.20.1+: The enclosures are saved as attributes
+ /** @var iterable<array{'url':string,'type'?:string,'medium'?:string,'length'?:int,'title'?:string,'description'?:string,'credit'?:string|array<string>,'height'?:int,'width'?:int,'thumbnails'?:array<string>}> $attributeEnclosures */
yield from $attributeEnclosures;
}
try {
@@ -296,8 +299,10 @@ HTML;
// Legacy code for database entries < FreshRSS 1.20.1
$enclosures = $xpath->query('//div[@class="enclosure"]/p[@class="enclosure-content"]/*[@src]');
if (!empty($enclosures)) {
- /** @var DOMElement $enclosure */
foreach ($enclosures as $enclosure) {
+ if (!($enclosure instanceof DOMElement)) {
+ continue;
+ }
$result = [
'url' => $enclosure->getAttribute('src'),
'type' => $enclosure->getAttribute('data-type'),
@@ -318,8 +323,10 @@ HTML;
if ($searchBodyImages && $xpath !== null) {
$images = $xpath->query('//img');
if (!empty($images)) {
- /** @var DOMElement $img */
foreach ($images as $img) {
+ if (!($img instanceof DOMElement)) {
+ continue;
+ }
$src = $img->getAttribute('src');
if ($src == null) {
$src = $img->getAttribute('data-src');
@@ -346,6 +353,7 @@ HTML;
$thumbnail = $this->attributeArray('thumbnail') ?? [];
// First, use the provided thumbnail, if any
if (!empty($thumbnail['url'])) {
+ /** @var array{'url':string,'height'?:int,'width'?:int,'time'?:string} $thumbnail */
return $thumbnail;
}
if ($searchEnclosures) {
@@ -467,7 +475,7 @@ HTML;
return $this->hash;
}
- /** @param int|string $value String is for compatibility with 32-bit platforms */
+ /** @param int|numeric-string $value String is for compatibility with 32-bit platforms */
public function _id($value): void {
if (is_int($value)) {
$value = (string)$value;
@@ -882,7 +890,7 @@ HTML;
/**
* Integer format conversion for Google Reader API format
- * @param string|int $dec Decimal number
+ * @param numeric-string|int $dec Decimal number
* @return string 64-bit hexa http://code.google.com/p/google-reader-api/wiki/ItemId
*/
private static function dec2hex($dec): string {
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index bb0b9af43..0b289eb41 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -321,7 +321,7 @@ SQL;
* @todo simplify the query by removing the str_repeat. I am pretty sure
* there is an other way to do that.
*
- * @param string|array<string> $ids
+ * @param numeric-string|array<numeric-string> $ids
* @return int|false
*/
public function markFavorite($ids, bool $is_favorite = true) {
@@ -399,7 +399,7 @@ SQL;
* Toggle the read marker on one or more article.
* Then the cache is updated.
*
- * @param string|array<string> $ids
+ * @param numeric-string|array<numeric-string> $ids
* @param bool $is_read
* @return int|false affected rows
*/
@@ -465,7 +465,7 @@ SQL;
*
* If $idMax equals 0, a deprecated debug message is logged
*
- * @param string $idMax fail safe article ID
+ * @param numeric-string $idMax fail safe article ID
* @return int|false affected rows
*/
public function markReadEntries(string $idMax = '0', bool $onlyFavorites = false, ?int $priorityMin = null, ?int $prioritMax = null,
@@ -517,7 +517,7 @@ SQL;
* If $idMax equals 0, a deprecated debug message is logged
*
* @param int $id category ID
- * @param string $idMax fail safe article ID
+ * @param numeric-string $idMax fail safe article ID
* @return int|false affected rows
*/
public function markReadCat(int $id, string $idMax = '0', ?FreshRSS_BooleanSearch $filters = null, int $state = 0, bool $is_read = true) {
@@ -558,7 +558,7 @@ SQL;
* If $idMax equals 0, a deprecated debug message is logged
*
* @param int $id_feed feed ID
- * @param string $idMax fail safe article ID
+ * @param numeric-string $idMax fail safe article ID
* @return int|false affected rows
*/
public function markReadFeed(int $id_feed, string $idMax = '0', ?FreshRSS_BooleanSearch $filters = null, int $state = 0, bool $is_read = true) {
@@ -612,7 +612,7 @@ SQL;
/**
* Mark all the articles in a tag as read.
* @param int $id tag ID, or empty for targeting any tag
- * @param string $idMax max article ID
+ * @param numeric-string $idMax max article ID
* @return int|false affected rows
*/
public function markReadTag(int $id = 0, string $idMax = '0', ?FreshRSS_BooleanSearch $filters = null,
@@ -1206,7 +1206,7 @@ SQL;
}
/**
- * @param array<string> $ids
+ * @param array<numeric-string> $ids
* @param 'ASC'|'DESC' $order
* @return Traversable<FreshRSS_Entry>
*/
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
index ebe69a84c..0274ded0a 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -658,7 +658,7 @@ class FreshRSS_Feed extends Minz_Model {
//check if the content is actual JSON
$jf = json_decode($json, true);
- if (json_last_error() !== JSON_ERROR_NONE) {
+ if (json_last_error() !== JSON_ERROR_NONE || !is_array($jf)) {
return null;
}
@@ -734,9 +734,14 @@ class FreshRSS_Feed extends Minz_Model {
}
$xpath = new DOMXPath($doc);
+ $xpathEvaluateString = function (string $expression, ?DOMNode $contextNode = null) use ($xpath): string {
+ $result = @$xpath->evaluate('normalize-space(' . $expression . ')', $contextNode);
+ return is_string($result) ? $result : '';
+ };
+
$view->rss_title = $xPathFeedTitle == '' ? $this->name() :
- htmlspecialchars(@$xpath->evaluate('normalize-space(' . $xPathFeedTitle . ')'), ENT_COMPAT, 'UTF-8');
- $view->rss_base = htmlspecialchars(trim($xpath->evaluate('normalize-space(//base/@href)')), ENT_COMPAT, 'UTF-8');
+ htmlspecialchars($xpathEvaluateString($xPathFeedTitle), ENT_COMPAT, 'UTF-8');
+ $view->rss_base = htmlspecialchars(trim($xpathEvaluateString('//base/@href')), ENT_COMPAT, 'UTF-8');
$nodes = $xpath->query($xPathItem);
if ($nodes === false || $nodes->length === 0) {
return null;
@@ -744,7 +749,7 @@ class FreshRSS_Feed extends Minz_Model {
foreach ($nodes as $node) {
$item = [];
- $item['title'] = $xPathItemTitle == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemTitle . ')', $node);
+ $item['title'] = $xPathItemTitle == '' ? '' : $xpathEvaluateString($xPathItemTitle, $node);
$item['content'] = '';
if ($xPathItemContent != '') {
@@ -756,36 +761,35 @@ class FreshRSS_Feed extends Minz_Model {
$content .= $doc->saveHTML($child) . "\n";
}
$item['content'] = $content;
- } else {
+ } elseif (is_string($result) || is_int($result) || is_bool($result)) {
// Typed expression, save as-is
$item['content'] = (string)$result;
}
}
- $item['link'] = $xPathItemUri == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemUri . ')', $node);
- $item['author'] = $xPathItemAuthor == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemAuthor . ')', $node);
- $item['timestamp'] = $xPathItemTimestamp == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemTimestamp . ')', $node);
+ $item['link'] = $xPathItemUri == '' ? '' : $xpathEvaluateString($xPathItemUri, $node);
+ $item['author'] = $xPathItemAuthor == '' ? '' : $xpathEvaluateString($xPathItemAuthor, $node);
+ $item['timestamp'] = $xPathItemTimestamp == '' ? '' : $xpathEvaluateString($xPathItemTimestamp, $node);
if ($xPathItemTimeFormat != '') {
- $dateTime = DateTime::createFromFormat($xPathItemTimeFormat, $item['timestamp'] ?? '');
+ $dateTime = DateTime::createFromFormat($xPathItemTimeFormat, $item['timestamp']);
if ($dateTime != false) {
$item['timestamp'] = $dateTime->format(DateTime::ATOM);
}
}
- $item['thumbnail'] = $xPathItemThumbnail == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemThumbnail . ')', $node);
+ $item['thumbnail'] = $xPathItemThumbnail == '' ? '' : $xpathEvaluateString($xPathItemThumbnail, $node);
if ($xPathItemCategories != '') {
$itemCategories = @$xpath->evaluate($xPathItemCategories, $node);
if (is_string($itemCategories) && $itemCategories !== '') {
$item['tags'] = [$itemCategories];
} elseif ($itemCategories instanceof DOMNodeList && $itemCategories->length > 0) {
$item['tags'] = [];
- /** @var DOMNode $itemCategory */
foreach ($itemCategories as $itemCategory) {
$item['tags'][] = $itemCategory->textContent;
}
}
}
if ($xPathItemUid != '') {
- $item['guid'] = @$xpath->evaluate('normalize-space(' . $xPathItemUid . ')', $node);
+ $item['guid'] = $xpathEvaluateString($xPathItemUid, $node);
}
if (empty($item['guid'])) {
$item['guid'] = 'urn:sha1:' . sha1($item['title'] . $item['content'] . $item['link']);
@@ -958,7 +962,7 @@ class FreshRSS_Feed extends Minz_Model {
$hubFilename = PSHB_PATH . '/feeds/' . sha1($url) . '/!hub.json';
$hubFile = @file_get_contents($hubFilename);
$hubJson = is_string($hubFile) ? json_decode($hubFile, true) : null;
- if (is_array($hubJson) && !isset($hubJson['error']) || $hubJson['error'] !== $error) {
+ if (is_array($hubJson) && (!isset($hubJson['error']) || $hubJson['error'] !== $error)) {
$hubJson['error'] = $error;
file_put_contents($hubFilename, json_encode($hubJson));
Minz_Log::warning('Set error to ' . ($error ? 1 : 0) . ' for ' . $url, PSHB_LOG);
diff --git a/app/Models/ReadingMode.php b/app/Models/ReadingMode.php
index c8ebe66f3..035b22114 100644
--- a/app/Models/ReadingMode.php
+++ b/app/Models/ReadingMode.php
@@ -9,13 +9,13 @@ class FreshRSS_ReadingMode {
protected string $id;
protected string $name;
protected string $title;
- /** @var array{'c':string,'a':string,'params':array<string,mixed>} */
+ /** @var array{c:string,a:string,params:array<string,mixed>} */
protected array $urlParams;
protected bool $isActive = false;
/**
* ReadingMode constructor.
- * @param array{'c':string,'a':string,'params':array<string,mixed>} $urlParams
+ * @param array{c:string,a:string,params:array<string,mixed>} $urlParams
*/
public function __construct(string $id, string $title, array $urlParams, bool $active) {
$this->id = $id;
@@ -47,12 +47,12 @@ class FreshRSS_ReadingMode {
return $this;
}
- /** @return array{'c':string,'a':string,'params':array<string,mixed>} */
+ /** @return array{c:string,a:string,params:array<string,mixed>} */
public function getUrlParams(): array {
return $this->urlParams;
}
- /** @param array{'c':string,'a':string,'params':array<string,mixed>} $urlParams */
+ /** @param array{c:string,a:string,params:array<string,mixed>} $urlParams */
public function setUrlParams(array $urlParams): FreshRSS_ReadingMode {
$this->urlParams = $urlParams;
return $this;
diff --git a/app/Models/UserConfiguration.php b/app/Models/UserConfiguration.php
index 0b02960c4..c9d5b07af 100644
--- a/app/Models/UserConfiguration.php
+++ b/app/Models/UserConfiguration.php
@@ -69,7 +69,7 @@ declare(strict_types=1);
* @property int $dynamic_opml_ttl_default
* @property-read bool $unsafe_autologin_enabled
* @property string $view_mode
- * @property array<string,mixed> $volatile
+ * @property array<string,bool|int|string> $volatile
* @property array<string,array<string,mixed>> $extensions
*/
final class FreshRSS_UserConfiguration extends Minz_Configuration {
diff --git a/app/Models/UserQuery.php b/app/Models/UserQuery.php
index 73bf52ef8..3058483d3 100644
--- a/app/Models/UserQuery.php
+++ b/app/Models/UserQuery.php
@@ -41,7 +41,8 @@ class FreshRSS_UserQuery {
}
/**
- * @param array{get?:string,name?:string,order?:string,search?:string,state?:int,url?:string,token?:string,shareRss?:bool,shareOpml?:bool} $query
+ * @param array{get?:string,name?:string,order?:string,search?:string,state?:int,url?:string,token?:string,
+ * shareRss?:bool,shareOpml?:bool,description?:string,imageUrl?:string} $query
* @param array<int,FreshRSS_Category> $categories
* @param array<int,FreshRSS_Tag> $labels
*/
@@ -61,8 +62,13 @@ class FreshRSS_UserQuery {
}
if (empty($query['url'])) {
if (!empty($query)) {
- unset($query['name']);
- $this->url = Minz_Url::display(['params' => $query]);
+ $link = $query;
+ unset($link['description']);
+ unset($link['imageUrl']);
+ unset($link['name']);
+ unset($link['shareOpml']);
+ unset($link['shareRss']);
+ $this->url = Minz_Url::display(['params' => $link]);
}
} else {
$this->url = $query['url'];
diff --git a/app/Services/ImportService.php b/app/Services/ImportService.php
index 2c23e5d4f..11da49217 100644
--- a/app/Services/ImportService.php
+++ b/app/Services/ImportService.php
@@ -78,13 +78,13 @@ class FreshRSS_Import_Service {
$category_element = $categories_elements[$category_name] ?? null;
$category = null;
- if ($forced_category) {
+ if ($forced_category !== null) {
// If the category is forced, ignore the actual category name
$category = $forced_category;
} elseif (isset($categories_by_names[$category_name])) {
// If the category already exists, get it from $categories_by_names
$category = $categories_by_names[$category_name];
- } elseif ($category_element) {
+ } elseif (is_array($category_element)) {
// Otherwise, create the category (if possible)
$limit_reached = $nb_categories >= $limits['max_categories'];
$can_create_category = FreshRSS_Context::$isCli || !$limit_reached;
@@ -362,11 +362,11 @@ class FreshRSS_Import_Service {
* This method is applied to a list of outlines. It merges the different
* list of feeds from several outlines into one array.
*
- * @param array<mixed> $outlines
+ * @param array<array<mixed>> $outlines
* The outlines from which to extract the outlines.
* @param string $parent_category_name
* The name of the parent category of the current outlines.
- * @return array{0:array<mixed>,1:array<mixed>}
+ * @return array{0:array<string,array<string,string>>,1:array<string,array<array<string,string>>>}
*/
private function loadFromOutlines(array $outlines, string $parent_category_name): array {
$categories_elements = [];
@@ -410,13 +410,13 @@ class FreshRSS_Import_Service {
* @param string $parent_category_name
* The name of the parent category of the current outline.
*
- * @return array{0:array<string,mixed>,1:array<string,mixed>}
+ * @return array{0:array<string,array<string,string>>,1:array<array<string,array<string,string>>>}
*/
- private function loadFromOutline($outline, $parent_category_name): array {
+ private function loadFromOutline(array $outline, string $parent_category_name): array {
$categories_elements = [];
$categories_to_feeds = [];
- if ($parent_category_name === '' && isset($outline['category'])) {
+ if ($parent_category_name === '' && isset($outline['category']) && is_array($outline['category'])) {
// The outline has no parent category, but its OPML category
// attribute is set, so we use it as the category name.
// lib_opml parses this attribute as an array of strings, so we
@@ -429,9 +429,9 @@ class FreshRSS_Import_Service {
if (isset($outline['@outlines'])) {
// The outline has children, it’s probably a category
- if (!empty($outline['text'])) {
+ if (!empty($outline['text']) && is_string($outline['text'])) {
$category_name = $outline['text'];
- } elseif (!empty($outline['title'])) {
+ } elseif (!empty($outline['title']) && is_string($outline['title'])) {
$category_name = $outline['title'];
} else {
$category_name = $parent_category_name;
diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml
index e581850bd..685d2d48f 100644
--- a/app/layout/layout.phtml
+++ b/app/layout/layout.phtml
@@ -41,8 +41,8 @@
if ($this->rss_title != '') {
$url_rss = $url_base;
$url_rss['a'] = 'rss';
- $url_rss['params']['user'] = Minz_User::name();
- $url_rss['params']['token'] = FreshRSS_Context::userConf()->token ?: null;
+ $url_rss['params']['user'] = Minz_User::name() ?? '';
+ $url_rss['params']['token'] = FreshRSS_Context::userConf()->token;
unset($url_rss['params']['rid']);
if (FreshRSS_Context::userConf()->since_hours_posts_per_rss) {
$url_rss['params']['hours'] = FreshRSS_Context::userConf()->since_hours_posts_per_rss;
@@ -52,8 +52,8 @@
<?php } if (FreshRSS_Context::isAll() || FreshRSS_Context::isCategory() || FreshRSS_Context::isFeed()) {
$opml_rss = $url_base;
$opml_rss['a'] = 'opml';
- $opml_rss['params']['user'] = Minz_User::name();
- $opml_rss['params']['token'] = FreshRSS_Context::userConf()->token ?: null;
+ $opml_rss['params']['user'] = Minz_User::name() ?? '';
+ $opml_rss['params']['token'] = FreshRSS_Context::userConf()->token;
unset($opml_rss['params']['rid']);
?>
<link rel="outline" type="text/x-opml" title="OPML" href="<?= Minz_Url::display($opml_rss) ?>" />
diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml
index 0d9252910..8390fa6bf 100644
--- a/app/views/helpers/feed/update.phtml
+++ b/app/views/helpers/feed/update.phtml
@@ -421,6 +421,7 @@
<fieldset id="html_xpath">
<?php
+ /** @var array<string> $xpath */
$xpath = Minz_Helper::htmlspecialchars_utf8($this->feed->attributeArray('xpath') ?? []);
?>
<p class="help"><?= _i('help') ?> <?= _t('sub.feed.kind.html_xpath.help') ?></p>
@@ -516,6 +517,7 @@
<fieldset id="json_dotnotation">
<?php
+ /** @var array<string,string> $jsonSettings */
$jsonSettings = Minz_Helper::htmlspecialchars_utf8($this->feed->attributeArray('json_dotnotation') ?? []);
?>
<p class="help"><?= _i('help') ?> <?= _t('sub.feed.kind.json_dotnotation.help') ?></p>
@@ -634,17 +636,19 @@
</div>
<div class="form-group">
+ <?php
+ /** @var array<int,int|string> $curlParams */
+ $curlParams = $this->feed->attributeArray('curl_params') ?? [];
+ ?>
<label class="group-name" for="curl_params_cookie"><?= _t('sub.feed.css_cookie') ?></label>
<div class="group-controls">
<input type="text" name="curl_params_cookie" id="curl_params_cookie" class="w100" value="<?=
- $this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_COOKIE]) ?
- $this->feed->attributeArray('curl_params')[CURLOPT_COOKIE] : ''
+ !empty($curlParams[CURLOPT_COOKIE]) ? $curlParams[CURLOPT_COOKIE] : ''
?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" />
<p class="help"><?= _i('help') ?> <?= _t('sub.feed.css_cookie_help') ?></p>
<label for="curl_params_cookiefile">
<input type="checkbox" name="curl_params_cookiefile" id="curl_params_cookiefile" value="1"<?=
- $this->feed->attributeArray('curl_params') !== null && isset($this->feed->attributeArray('curl_params')[CURLOPT_COOKIEFILE]) ?
- ' checked="checked"' : ''
+ isset($curlParams[CURLOPT_COOKIEFILE]) ? ' checked="checked"' : ''
?> />
<?= _t('sub.feed.accept_cookies') ?>
</label>
@@ -656,8 +660,7 @@
<label class="group-name" for="curl_params_redirects"><?= _t('sub.feed.max_http_redir') ?></label>
<div class="group-controls">
<input type="number" name="curl_params_redirects" id="curl_params_redirects" class="w50" min="-1" value="<?=
- $this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_MAXREDIRS]) ?
- $this->feed->attributeArray('curl_params')[CURLOPT_MAXREDIRS] : ''
+ !empty($curlParams[CURLOPT_MAXREDIRS]) ? $curlParams[CURLOPT_MAXREDIRS] : ''
?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" />
<p class="help"><?= _i('help') ?> <?= _t('sub.feed.max_http_redir_help') ?></p>
</div>
@@ -678,8 +681,7 @@
<label class="group-name" for="curl_params_useragent"><?= _t('sub.feed.useragent') ?></label>
<div class="group-controls">
<input type="text" name="curl_params_useragent" id="curl_params_useragent" class="w100" value="<?=
- $this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_USERAGENT]) ?
- $this->feed->attributeArray('curl_params')[CURLOPT_USERAGENT] : ''
+ !empty($curlParams[CURLOPT_USERAGENT]) ? $curlParams[CURLOPT_USERAGENT] : ''
?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" />
<p class="help"><?= _i('help') ?> <?= _t('sub.feed.useragent_help') ?></p>
</div>
@@ -689,18 +691,14 @@
<label class="group-name" for="proxy_type"><?= _t('sub.feed.proxy') ?></label>
<div class="group-controls">
<select name="proxy_type" id="proxy_type"><?php
- $type = '';
- if ($this->feed->attributeArray('curl_params') !== null && isset($this->feed->attributeArray('curl_params')[CURLOPT_PROXYTYPE])) {
- $type = $this->feed->attributeArray('curl_params')[CURLOPT_PROXYTYPE];
- }
+ $type = $curlParams[CURLOPT_PROXYTYPE] ?? '';
foreach(['' => '', 3 => 'NONE', 0 => 'HTTP', 2 => 'HTTPS', 4 => 'SOCKS4', 6 => 'SOCKS4A', 5 => 'SOCKS5', 7 => 'SOCKS5H'] as $k => $v) {
echo '<option value="' . $k . ($type === $k ? '" selected="selected' : '' ) . '">' . $v . '</option>';
}
?>
</select>
<input type="text" name="curl_params" id="curl_params" value="<?=
- $this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_PROXY]) ?
- $this->feed->attributeArray('curl_params')[CURLOPT_PROXY] : ''
+ !empty($curlParams[CURLOPT_PROXY]) ? $curlParams[CURLOPT_PROXY] : ''
?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" />
<p class="help"><?= _i('help') ?> <?= _t('sub.feed.proxy_help') ?></p>
</div>
diff --git a/app/views/helpers/index/article.phtml b/app/views/helpers/index/article.phtml
index 56fd06b4f..bfdfdb8d1 100644
--- a/app/views/helpers/index/article.phtml
+++ b/app/views/helpers/index/article.phtml
@@ -10,11 +10,11 @@
<?php
$favoriteUrl = ['c' => 'entry', 'a' => 'bookmark', 'params' => ['id' => $entry->id()]];
if ($entry->isFavorite()) {
- $favoriteUrl['params']['is_favorite'] = 0;
+ $favoriteUrl['params']['is_favorite'] = '0';
}
$readUrl = ['c' => 'entry', 'a' => 'read', 'params' => ['id' => $entry->id()]];
if ($entry->isRead()) {
- $readUrl['params']['is_read'] = 0;
+ $readUrl['params']['is_read'] = '0';
}
?>
<div class="article-header-topline">
diff --git a/app/views/helpers/index/normal/entry_bottom.phtml b/app/views/helpers/index/normal/entry_bottom.phtml
index e5bfd7fd0..21bd1d355 100644
--- a/app/views/helpers/index/normal/entry_bottom.phtml
+++ b/app/views/helpers/index/normal/entry_bottom.phtml
@@ -12,9 +12,9 @@
if (FreshRSS_Auth::hasAccess()) {
if ($bottomline_read) {
?><li class="item manage"><?php
- $arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('id' => $this->entry->id()));
+ $arUrl = ['c' => 'entry', 'a' => 'read', 'params' => ['id' => $this->entry->id()]];
if ($this->entry->isRead()) {
- $arUrl['params']['is_read'] = 0;
+ $arUrl['params']['is_read'] = '0';
}
?><a class="item-element read" href="<?= Minz_Url::display($arUrl) ?>" title="<?= _t('conf.shortcut.mark_read') ?>"><?php
echo _i($this->entry->isRead() ? 'read' : 'unread'); ?></a><?php
@@ -22,9 +22,9 @@
}
if ($bottomline_favorite) {
?><li class="item manage"><?php
- $arUrl = array('c' => 'entry', 'a' => 'bookmark', 'params' => array('id' => $this->entry->id()));
+ $arUrl = ['c' => 'entry', 'a' => 'bookmark', 'params' => ['id' => $this->entry->id()]];
if ($this->entry->isFavorite()) {
- $arUrl['params']['is_favorite'] = 0;
+ $arUrl['params']['is_favorite'] = '0';
}
?><a class="item-element bookmark" href="<?= Minz_Url::display($arUrl) ?>" title="<?= _t('conf.shortcut.mark_favorite') ?>"><?php
echo _i($this->entry->isFavorite() ? 'starred' : 'non-starred'); ?></a><?php
diff --git a/app/views/helpers/index/normal/entry_header.phtml b/app/views/helpers/index/normal/entry_header.phtml
index 0db375309..e4befb346 100644
--- a/app/views/helpers/index/normal/entry_header.phtml
+++ b/app/views/helpers/index/normal/entry_header.phtml
@@ -14,9 +14,9 @@
if (FreshRSS_Auth::hasAccess()) {
if ($topline_read) {
?><li class="item manage"><?php
- $arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('id' => $this->entry->id()));
+ $arUrl = ['c' => 'entry', 'a' => 'read', 'params' => ['id' => $this->entry->id()]];
if ($this->entry->isRead()) {
- $arUrl['params']['is_read'] = 0;
+ $arUrl['params']['is_read'] = '0';
}
?><a class="item-element read" href="<?= Minz_Url::display($arUrl) ?>" title="<?= _t('conf.shortcut.mark_read') ?>"><?php
echo _i($this->entry->isRead() ? 'read' : 'unread'); ?></a><?php
@@ -24,9 +24,9 @@
}
if ($topline_favorite) {
?><li class="item manage"><?php
- $arUrl = array('c' => 'entry', 'a' => 'bookmark', 'params' => array('id' => $this->entry->id()));
+ $arUrl = ['c' => 'entry', 'a' => 'bookmark', 'params' => ['id' => $this->entry->id()]];
if ($this->entry->isFavorite()) {
- $arUrl['params']['is_favorite'] = 0;
+ $arUrl['params']['is_favorite'] = '0';
}
?><a class="item-element bookmark" href="<?= Minz_Url::display($arUrl) ?>" title="<?= _t('conf.shortcut.mark_favorite') ?>"><?php
echo _i($this->entry->isFavorite() ? 'starred' : 'non-starred'); ?></a><?php
diff --git a/app/views/helpers/stream-footer.phtml b/app/views/helpers/stream-footer.phtml
index 0cbab601a..9681cff02 100644
--- a/app/views/helpers/stream-footer.phtml
+++ b/app/views/helpers/stream-footer.phtml
@@ -3,20 +3,20 @@
/** @var FreshRSS_View $this */
$url_next = Minz_Request::currentRequest();
$url_next['params']['next'] = FreshRSS_Context::$next_id;
- $url_next['params']['state'] = FreshRSS_Context::$state;
- $url_next['params']['ajax'] = 1;
+ $url_next['params']['state'] = (string)FreshRSS_Context::$state;
+ $url_next['params']['ajax'] = '1';
- $url_mark_read = array(
+ $url_mark_read = [
'c' => 'entry',
'a' => 'read',
- 'params' => array(
+ 'params' => [
'get' => FreshRSS_Context::currentGet(),
'nextGet' => FreshRSS_Context::$next_get,
'idMax' => FreshRSS_Context::$id_max,
'search' => htmlspecialchars_decode(FreshRSS_Context::$search->getRawInput(), ENT_QUOTES),
'state' => FreshRSS_Context::$state,
- )
- );
+ ],
+ ];
$hasAccess = FreshRSS_Auth::hasAccess();
if ($hasAccess) {
diff --git a/app/views/javascript/actualize.phtml b/app/views/javascript/actualize.phtml
index bce316d85..314f83012 100644
--- a/app/views/javascript/actualize.phtml
+++ b/app/views/javascript/actualize.phtml
@@ -5,21 +5,21 @@ declare(strict_types=1);
$categories = [];
foreach ($this->categories as $category) {
$categories[] = [
- 'url' => Minz_Url::display(array('c' => 'category', 'a' => 'refreshOpml', 'params' => array('id' => $category->id(), 'ajax' => '1')), 'php'),
+ 'url' => Minz_Url::display(['c' => 'category', 'a' => 'refreshOpml', 'params' => ['id' => $category->id(), 'ajax' => '1']], 'php'),
'title' => $category->name(),
];
}
-$feeds = array();
+$feeds = [];
foreach ($this->feeds as $feed) {
- $feeds[] = array(
- 'url' => Minz_Url::display(array('c' => 'feed', 'a' => 'actualize', 'params' => array('id' => $feed->id(), 'ajax' => '1')), 'php'),
+ $feeds[] = [
+ 'url' => Minz_Url::display(['c' => 'feed', 'a' => 'actualize', 'params' => ['id' => $feed->id(), 'ajax' => '1']], 'php'),
'title' => $feed->name(),
- );
+ ];
}
-echo json_encode(array(
+echo json_encode([
'categories' => $categories,
'feeds' => $feeds,
'feedback_no_refresh' => _t('feedback.sub.feed.no_refresh'),
'feedback_actualize' => _t('feedback.sub.actualize'),
-));
+]);
diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml
index 3a80065ef..b2e8a7d7b 100644
--- a/app/views/user/profile.phtml
+++ b/app/views/user/profile.phtml
@@ -60,8 +60,8 @@
<div class="group-controls">
<input type="text" id="token" name="token" value="<?= $token ?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" data-leave-validation="<?= $token ?>"/>
<p class="help"><?= _i('help') ?> <?= _t('admin.auth.token_help') ?></p>
- <kbd><?= Minz_Url::display(array('a' => 'rss', 'params' => array('user' => Minz_User::name(),
- 'token' => $token, 'hours' => FreshRSS_Context::userConf()->since_hours_posts_per_rss)), 'html', true) ?></kbd>
+ <kbd><?= Minz_Url::display(['a' => 'rss', 'params' => ['user' => Minz_User::name() ?? '',
+ 'token' => $token, 'hours' => FreshRSS_Context::userConf()->since_hours_posts_per_rss]], 'html', true) ?></kbd>
<p class="help"><?= _i('help') ?> <?= _t('conf.query.help') ?></a></p>
</div>
</div>