diff options
author | Alexandre Alapetite <alexandre@alapetite.fr> | 2023-05-02 14:38:32 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-02 14:38:32 +0200 |
commit | bd9fa803f1f0c23face77fa1bc550d1198ce5ad6 (patch) | |
tree | edba662e84e70a6b0f23c8379d4ef174f714e999 | |
parent | 4de1d5efea128e6cc70c71bad9be28d01e851f81 (diff) | |
download | freshrss-bd9fa803f1f0c23face77fa1bc550d1198ce5ad6.tar.gz freshrss-bd9fa803f1f0c23face77fa1bc550d1198ce5ad6.zip |
PHPStan Level 7 complete DAOs (#5354)
* PHPStan Level 7 complete DAOs
* Finalise PHPStan Level 7 for CategoryDAO
* PHPStan Level 7 for Context and Search
* Apply suggestions from code review
Co-authored-by: Luc SANCHEZ <4697568+ColonelMoutarde@users.noreply.github.com>
-rw-r--r-- | app/Controllers/feedController.php | 6 | ||||
-rw-r--r-- | app/Controllers/indexController.php | 7 | ||||
-rw-r--r-- | app/Controllers/statsController.php | 5 | ||||
-rw-r--r-- | app/Models/CategoryDAO.php | 132 | ||||
-rw-r--r-- | app/Models/Context.php | 18 | ||||
-rw-r--r-- | app/Models/Entry.php | 10 | ||||
-rw-r--r-- | app/Models/EntryDAO.php | 86 | ||||
-rw-r--r-- | app/Models/Feed.php | 5 | ||||
-rw-r--r-- | app/Models/FeedDAO.php | 129 | ||||
-rw-r--r-- | app/Models/Search.php | 24 | ||||
-rw-r--r-- | app/Models/TagDAO.php | 22 | ||||
-rw-r--r-- | app/Models/UserQuery.php | 6 | ||||
-rw-r--r-- | app/Services/ExportService.php | 4 | ||||
-rwxr-xr-x | cli/user-info.php | 16 | ||||
-rw-r--r-- | tests/phpstan-next.txt | 5 |
15 files changed, 231 insertions, 244 deletions
diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 76cd3ad83..1974751d0 100644 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -823,8 +823,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { $feedDAO = FreshRSS_Factory::createFeedDao(); $feed = $feedDAO->searchById($id); - - if (!$feed) { + if ($feed === null) { Minz_Request::bad(_t('feedback.sub.feed.not_found'), array()); return; } @@ -854,8 +853,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { $entryDAO = FreshRSS_Factory::createEntryDao(); $feed = $feedDAO->searchById($feed_id); - - if (!$feed) { + if ($feed === null) { Minz_Request::bad(_t('feedback.sub.feed.not_found'), array()); return; } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 21a94d53d..3f5c419f1 100644 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -194,7 +194,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { $get = FreshRSS_Context::currentGet(true); if (is_array($get)) { $type = $get[0]; - $id = $get[1]; + $id = (int)$get[1]; } else { $type = $get; $id = 0; @@ -219,7 +219,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { case 'f': // We most likely already have the feed object in cache $feed = FreshRSS_CategoryDAO::findFeed($categories, $id); - if ($feed == null) { + if ($feed === null) { $feedDAO = FreshRSS_Factory::createFeedDao(); $feed = $feedDAO->searchById($id); if ($feed == null) { @@ -290,8 +290,9 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { */ public function tosAction(): void { $terms_of_service = file_get_contents(TOS_FILENAME); - if (!$terms_of_service) { + if ($terms_of_service === false) { Minz_Error::error(404); + return; } $this->view->terms_of_service = $terms_of_service; diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index afbaccfc5..79facbbf6 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -155,7 +155,10 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController { foreach ($feeds as $feed) { $feedDAO = FreshRSS_Factory::createFeedDao(); - $feed['favicon'] = $feedDAO->searchById($feed['id'])->favicon(); + $feedObject = $feedDAO->searchById($feed['id']); + if ($feedObject !== null) { + $feed['favicon'] = $feedObject->favicon(); + } $feedDate->setTimestamp($feed['last_date']); if ($feedDate >= $lastWeek) { diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index ec7bb45cb..98d45733c 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -29,10 +29,15 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { } elseif ('attributes' === $name) { //v1.15.0 $ok = $this->pdo->exec('ALTER TABLE `_category` ADD COLUMN attributes TEXT') !== false; - $stm = $this->pdo->query('SELECT * FROM `_feed`'); - $feeds = $stm->fetchAll(PDO::FETCH_ASSOC); + /** @var array<array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int, + * 'priority':int,'pathEntries':string,'httpAuth':string,'error':int,'ttl':int,'attributes':string}> $feeds */ + $feeds = $this->fetchAssoc('SELECT * FROM `_feed`') ?? []; $stm = $this->pdo->prepare('UPDATE `_feed` SET attributes = :attributes WHERE id = :id'); + if ($stm === false) { + Minz_Log::error('SQL error ' . __METHOD__ . json_encode($this->pdo->errorInfo())); + return false; + } foreach ($feeds as $feed) { if (empty($feed['keep_history']) || empty($feed['id'])) { continue; @@ -54,9 +59,11 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { } else { continue; } - $stm->bindValue(':id', $feed['id'], PDO::PARAM_INT); - $stm->bindValue(':attributes', json_encode($attributes, JSON_UNESCAPED_SLASHES)); - $stm->execute(); + if (!($stm->bindValue(':id', $feed['id'], PDO::PARAM_INT) && + $stm->bindValue(':attributes', json_encode($attributes, JSON_UNESCAPED_SLASHES)) && + $stm->execute())) { + Minz_Log::error('SQL error ' . __METHOD__ . json_encode($stm->errorInfo())); + } } if ($this->pdo->dbType() !== 'sqlite') { //SQLite does not support DROP COLUMN @@ -91,7 +98,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { } /** - * @param array<string,mixed> $valuesTmp + * @param array{'name':string,'id'?:int,'kind'?:int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string|array<string,mixed>} $valuesTmp * @return int|false */ public function addCategory(array $valuesTmp) { @@ -116,7 +123,8 @@ SQL; ); if ($stm !== false && $stm->execute($values) && $stm->rowCount() > 0) { - return $this->pdo->lastInsertId('`_category_id_seq`'); + $catId = $this->pdo->lastInsertId('`_category_id_seq`'); + return $catId === false ? false : (int)$catId; } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { @@ -143,7 +151,7 @@ SQL; } /** - * @param array{'name':string,'kind':int,'attributes':array<string,mixed>} $valuesTmp + * @param array{'name':string,'kind':int,'attributes'?:string|array<string,mixed>} $valuesTmp * @return int|false */ public function updateCategory(int $id, array $valuesTmp) { @@ -204,8 +212,7 @@ SQL; } $sql = 'DELETE FROM `_category` WHERE id=:id'; $stm = $this->pdo->prepare($sql); - $stm->bindParam(':id', $id, PDO::PARAM_INT); - if ($stm !== false && $stm->execute()) { + if ($stm !== false && $stm->bindParam(':id', $id, PDO::PARAM_INT) && $stm->execute()) { return $stm->rowCount(); } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); @@ -234,36 +241,23 @@ SQL; public function searchById(int $id): ?FreshRSS_Category { $sql = 'SELECT * FROM `_category` WHERE id=:id'; - $stm = $this->pdo->prepare($sql); - if ($stm !== false && - $stm->bindParam(':id', $id, PDO::PARAM_INT) && - $stm->execute()) { - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - $cat = self::daoToCategory($res); - if (isset($cat[0])) { - return $cat[0]; - } - } - return null; + $res = $this->fetchAssoc($sql, ['id' => $id]) ?? []; + /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */ + $cat = self::daoToCategory($res); + return $cat[0] ?? null; } - /** @return FreshRSS_Category|null|false */ - public function searchByName(string $name) { + public function searchByName(string $name): ?FreshRSS_Category { $sql = 'SELECT * FROM `_category` WHERE name=:name'; - $res = $this->fetchAssoc($sql, ['name' => $name]); - if ($res == null) { - return false; - } + $res = $this->fetchAssoc($sql, ['name' => $name]) ?? []; + /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */ $cat = self::daoToCategory($res); return $cat[0] ?? null; } - /** @return array<FreshRSS_Category>|false */ - public function listSortedCategories(bool $prePopulateFeeds = true, bool $details = false) { + /** @return array<FreshRSS_Category> */ + public function listSortedCategories(bool $prePopulateFeeds = true, bool $details = false): array { $categories = $this->listCategories($prePopulateFeeds, $details); - if ($categories === false) { - return false; - } uasort($categories, static function (FreshRSS_Category $a, FreshRSS_Category $b) { $aPosition = $a->attributes('position'); @@ -281,11 +275,11 @@ SQL; return $categories; } - /** @return array<FreshRSS_Category>|false */ - public function listCategories(bool $prePopulateFeeds = true, bool $details = false) { + /** @return array<FreshRSS_Category> */ + public function listCategories(bool $prePopulateFeeds = true, bool $details = false): array { if ($prePopulateFeeds) { $sql = 'SELECT c.id AS c_id, c.name AS c_name, c.kind AS c_kind, c.`lastUpdate` AS c_last_update, c.error AS c_error, c.attributes AS c_attributes, ' - . ($details ? 'f.* ' : 'f.id, f.name, f.url, f.website, f.priority, f.error, f.`cache_nbEntries`, f.`cache_nbUnreads`, f.ttl ') + . ($details ? 'f.* ' : 'f.id, f.name, f.url, f.kind, f.website, f.priority, f.error, f.`cache_nbEntries`, f.`cache_nbUnreads`, f.ttl ') . 'FROM `_category` c ' . 'LEFT OUTER JOIN `_feed` f ON f.category=c.id ' . 'WHERE f.priority >= :priority_normal ' @@ -294,18 +288,22 @@ SQL; $stm = $this->pdo->prepare($sql); $values = [ ':priority_normal' => FreshRSS_Feed::PRIORITY_NORMAL ]; if ($stm !== false && $stm->execute($values)) { - return self::daoToCategoryPrepopulated($stm->fetchAll(PDO::FETCH_ASSOC)); + $res = $stm->fetchAll(PDO::FETCH_ASSOC) ?: []; + /** @var array<array{'c_name':string,'c_id':int,'c_kind':int,'c_last_update':int,'c_error':int|bool,'c_attributes'?:string, + * 'id'?:int,'name'?:string,'url'?:string,'kind'?:int,'category'?:int,'website'?:string,'priority'?:int,'error'?:int|bool,'cache_nbEntries'?:int,'cache_nbUnreads'?:int,'ttl'?:int}> $res */ + return self::daoToCategoryPrepopulated($res); } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->listCategories($prePopulateFeeds, $details); } Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); - return false; + return []; } } else { $res = $this->fetchAssoc('SELECT * FROM `_category` ORDER BY name'); - return $res == null ? false : self::daoToCategory($res); + /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */ + return $res == null ? [] : self::daoToCategory($res); } } @@ -331,12 +329,9 @@ SQL; public function getDefault(): ?FreshRSS_Category { $sql = 'SELECT * FROM `_category` WHERE id=:id'; - $stm = $this->pdo->prepare($sql); - $stm->bindValue(':id', self::DEFAULTCATEGORYID, PDO::PARAM_INT); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - $cat = self::daoToCategory($res); - + $res = $this->fetchAssoc($sql, [':id' => self::DEFAULTCATEGORYID]); + /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */ + $cat = self::daoToCategory($res ?? []); if (isset($cat[0])) { return $cat[0]; } else { @@ -369,7 +364,8 @@ SQL; ); if ($stm !== false && $stm->execute($values)) { - return $this->pdo->lastInsertId('`_category_id_seq`'); + $catId = $this->pdo->lastInsertId('`_category_id_seq`'); + return $catId === false ? false : (int)$catId; } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); @@ -381,27 +377,20 @@ SQL; public function count(): int { $sql = 'SELECT COUNT(*) AS count FROM `_category`'; - $stm = $this->pdo->query($sql); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - return $res[0]['count']; + $res = $this->fetchColumn($sql, 0); + return isset($res[0]) ? (int)$res[0] : -1; } public function countFeed(int $id): int { $sql = 'SELECT COUNT(*) AS count FROM `_feed` WHERE category=:id'; - $stm = $this->pdo->prepare($sql); - $stm->bindParam(':id', $id, PDO::PARAM_INT); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - return $res[0]['count']; + $res = $this->fetchColumn($sql, 0, [':id' => $id]); + return isset($res[0]) ? (int)$res[0] : -1; } public function countNotRead(int $id): int { $sql = 'SELECT COUNT(*) AS count FROM `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id WHERE category=:id AND e.is_read=0'; - $stm = $this->pdo->prepare($sql); - $stm->bindParam(':id', $id, PDO::PARAM_INT); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - return $res[0]['count']; + $res = $this->fetchColumn($sql, 0, [':id' => $id]); + return isset($res[0]) ? (int)$res[0] : -1; } /** @param array<FreshRSS_Category> $categories */ @@ -432,14 +421,15 @@ SQL; } /** - * @param array<string,array<string,string|int>> $listDAO + * @param array<array{'c_name':string,'c_id':int,'c_kind':int,'c_last_update':int,'c_error':int|bool,'c_attributes'?:string, + * 'id'?:int,'name'?:string,'url'?:string,'kind'?:int,'website'?:string,'priority'?:int, + * 'error'?:int|bool,'cache_nbEntries'?:int,'cache_nbUnreads'?:int,'ttl'?:int}> $listDAO * @return array<int,FreshRSS_Category> */ private static function daoToCategoryPrepopulated(array $listDAO) { - $list = array(); - $previousLine = null; - /** @var array<string,string|int> */ - $feedsDao = array(); + $list = []; + $previousLine = []; + $feedsDao = []; $feedDao = FreshRSS_Factory::createFeedDAO(); foreach ($listDAO as $line) { if (!empty($previousLine['c_id']) && $line['c_id'] !== $previousLine['c_id']) { @@ -450,10 +440,10 @@ SQL; ); $cat->_id($previousLine['c_id']); $cat->_kind($previousLine['c_kind']); - $cat->_attributes('', $previousLine['c_attributes']); + $cat->_attributes('', $previousLine['c_attributes'] ?? '[]'); $list[$previousLine['c_id']] = $cat; - $feedsDao = array(); //Prepare for next category + $feedsDao = []; //Prepare for next category } $previousLine = $line; @@ -470,7 +460,7 @@ SQL; $cat->_kind($previousLine['c_kind']); $cat->_lastUpdate($previousLine['c_last_update'] ?? 0); $cat->_error($previousLine['c_error'] ?? 0); - $cat->_attributes('', $previousLine['c_attributes']); + $cat->_attributes('', $previousLine['c_attributes'] ?? []); $list[$previousLine['c_id']] = $cat; } @@ -478,15 +468,11 @@ SQL; } /** - * @param array<array<string,string|int>>|array<string,string|int> $listDAO + * @param array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $listDAO * @return array<FreshRSS_Category> */ - private static function daoToCategory($listDAO): array { - $list = array(); - - if (!is_array($listDAO)) { - $listDAO = array($listDAO); - } + private static function daoToCategory(array $listDAO): array { + $list = []; foreach ($listDAO as $dao) { $cat = new FreshRSS_Category( diff --git a/app/Models/Context.php b/app/Models/Context.php index 03006cbbf..ce29ebd5c 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -16,11 +16,11 @@ final class FreshRSS_Context { */ public static $system_conf; /** - * @var array<FreshRSS_Category> + * @var array<int,FreshRSS_Category> */ public static $categories = array(); /** - * @var array<string> + * @var array<int,FreshRSS_Tag> */ public static $tags = array(); /** @@ -67,6 +67,7 @@ final class FreshRSS_Context { */ public static $state = 0; /** + * @phpstan-var 'ASC'|'DESC' * @var string */ public static $order = 'DESC'; @@ -217,7 +218,8 @@ final class FreshRSS_Context { } self::$search = new FreshRSS_BooleanSearch(Minz_Request::paramString('search')); - self::$order = Minz_Request::paramString('order') ?: self::$user_conf->sort_order; + $order = Minz_Request::paramString('order') ?: self::$user_conf->sort_order; + self::$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC'; self::$number = Minz_Request::paramInt('nb') ?: self::$user_conf->posts_per_page; if (self::$number > self::$user_conf->max_posts_per_rss) { self::$number = max( @@ -381,7 +383,7 @@ final class FreshRSS_Context { if ($feed === null) { $feedDAO = FreshRSS_Factory::createFeedDao(); $feed = $feedDAO->searchById($id); - if (!$feed) { + if ($feed === null) { throw new FreshRSS_Context_Exception('Invalid feed: ' . $id); } } @@ -397,9 +399,10 @@ final class FreshRSS_Context { if (!isset(self::$categories[$id])) { $catDAO = FreshRSS_Factory::createCategoryDao(); $cat = $catDAO->searchById($id); - if (!$cat) { + if ($cat === null) { throw new FreshRSS_Context_Exception('Invalid category: ' . $id); } + //self::$categories[$id] = $cat; } else { $cat = self::$categories[$id]; } @@ -412,9 +415,10 @@ final class FreshRSS_Context { if (!isset(self::$tags[$id])) { $tagDAO = FreshRSS_Factory::createTagDao(); $tag = $tagDAO->searchById($id); - if (!$tag) { + if ($tag === null) { throw new FreshRSS_Context_Exception('Invalid tag: ' . $id); } + //self::$tags[$id] = $tag; } else { $tag = self::$tags[$id]; } @@ -541,6 +545,6 @@ final class FreshRSS_Context { public static function defaultTimeZone(): string { $timezone = ini_get('date.timezone'); - return $timezone != '' ? $timezone : 'UTC'; + return $timezone != false ? $timezone : 'UTC'; } } diff --git a/app/Models/Entry.php b/app/Models/Entry.php index c29f84172..8f2d66882 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -350,10 +350,7 @@ HTML; return $this->is_favorite; } - /** - * @return FreshRSS_Feed|null|false - */ - public function feed() { + public function feed(): ?FreshRSS_Feed { if ($this->feed === null) { $feedDAO = FreshRSS_Factory::createFeedDao(); $this->feed = $feedDAO->searchById($this->feedId); @@ -778,7 +775,10 @@ HTML; return false; } - /** @return array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'hash':string,'is_read':?bool,'is_favorite':?bool,'id_feed':int,'tags':string,'attributes':array<string,mixed>} */ + /** + * @return array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, + * 'hash':string,'is_read':?bool,'is_favorite':?bool,'id_feed':int,'tags':string,'attributes':array<string,mixed>} + */ public function toArray(): array { return array( 'id' => $this->id(), diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index af89e9c52..8d32e86f9 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -120,7 +120,8 @@ SQL; */ private $addEntryPrepared = false; - /** @param array<string,string|int> $valuesTmp */ + /** @param array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'hash':string, + * 'is_read':bool|int|null,'is_favorite':bool|int|null,'id_feed':int,'tags':string,'attributes':array<string,mixed>} $valuesTmp */ public function addEntry(array $valuesTmp, bool $useTmpTable = true): bool { if ($this->addEntryPrepared == null) { $sql = static::sqlIgnoreConflict( @@ -220,7 +221,8 @@ SQL; /** @var PDOStatement|null */ private $updateEntryPrepared = null; - /** @param array<string,string|int> $valuesTmp */ + /** @param array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'hash':string, + * 'is_read':bool|int|null,'is_favorite':bool|int|null,'id_feed':int,'tags':string,'attributes':array<string,mixed>} $valuesTmp */ public function updateEntry(array $valuesTmp): bool { if (!isset($valuesTmp['is_read'])) { $valuesTmp['is_read'] = null; @@ -239,7 +241,7 @@ SQL; . ', is_favorite=COALESCE(:is_favorite, is_favorite)' . ', tags=:tags, attributes=:attributes ' . 'WHERE id_feed=:id_feed AND guid=:guid'; - $this->updateEntryPrepared = $this->pdo->prepare($sql); + $this->updateEntryPrepared = $this->pdo->prepare($sql) ?: null; } if ($this->updateEntryPrepared) { $valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760); @@ -578,8 +580,9 @@ SQL; . 'SET `cache_nbUnreads`=`cache_nbUnreads`-' . $affected . ' WHERE id=:id'; $stm = $this->pdo->prepare($sql); - $stm->bindParam(':id', $id_feed, PDO::PARAM_INT); - if (!($stm && $stm->execute())) { + if (!($stm !== false && + $stm->bindParam(':id', $id_feed, PDO::PARAM_INT) && + $stm->execute())) { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); $this->pdo->rollBack(); @@ -667,7 +670,7 @@ SQL; //==Inclusions== $sql .= ' AND (1=0'; - if (!empty($options['keep_period'])) { + if (!empty($options['keep_period']) && is_string($options['keep_period'])) { $sql .= ' OR `lastSeen` < :max_last_seen'; $now = new DateTime('now'); $now->sub(new DateInterval($options['keep_period'])); @@ -696,12 +699,15 @@ SQL; } } - /** @return Traversable<array<string,string|int>> */ + /** @return Traversable<array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, + * 'hash':string,'is_read':?bool,'is_favorite':?bool,'id_feed':int,'tags':string,'attributes':array<string,mixed>}> */ public function selectAll(): Traversable { - $sql = 'SELECT id, guid, title, author, ' - . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') - . ', link, date, `lastSeen`, ' . static::sqlHexEncode('hash') . ' AS hash, is_read, is_favorite, id_feed, tags, attributes ' - . 'FROM `_entry`'; + $content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content'; + $hash = static::sqlHexEncode('hash'); + $sql = <<<SQL +SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes +FROM `_entry` +SQL; $stm = $this->pdo->query($sql); if ($stm != false) { while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { @@ -998,7 +1004,10 @@ SQL; return [ $values, $search ]; } - /** @return array{0:array<int|string>,1:string} */ + /** + * @param 'ASC'|'DESC' $order + * @return array{0:array<int|string>,1:string} + */ protected function sqlListEntriesWhere(string $alias = '', ?FreshRSS_BooleanSearch $filters = null, int $state = FreshRSS_Entry::STATE_ALL, string $order = 'DESC', string $firstId = '', int $date_min = 0) { @@ -1048,6 +1057,7 @@ SQL; /** * @phpstan-param 'a'|'A'|'s'|'S'|'c'|'f'|'t'|'T'|'ST' $type * @param int $id category/feed/tag ID + * @param 'ASC'|'DESC' $order * @return array{0:array<int|string>,1:string} */ private function sqlListWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL, @@ -1111,23 +1121,25 @@ SQL; /** * @phpstan-param 'a'|'A'|'s'|'S'|'c'|'f'|'t'|'T'|'ST' $type + * @param 'ASC'|'DESC' $order * @param int $id category/feed/tag ID * @return PDOStatement|false */ private function listWhereRaw(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL, string $order = 'DESC', int $limit = 1, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null, int $date_min = 0) { - list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters, $date_min); - - $sql = 'SELECT e0.id, e0.guid, e0.title, e0.author, ' - . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') - . ', e0.link, e0.date, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags, e0.attributes ' - . 'FROM `_entry` e0 ' - . 'INNER JOIN (' - . $sql - . ') e2 ON e2.id=e0.id ' - . 'ORDER BY e0.id ' . $order; + [$values, $sql] = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters, $date_min); + if ($order !== 'DESC' && $order !== 'ASC') { + $order = 'DESC'; + } + $content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content'; + $sql = <<<SQL +SELECT e0.id, e0.guid, e0.title, e0.author, {$content}, e0.link, e0.date, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags, e0.attributes +FROM `_entry` e0 +INNER JOIN ({$sql}) e2 ON e2.id=e0.id +ORDER BY e0.id {$order} +SQL; $stm = $this->pdo->prepare($sql); if ($stm !== false && $stm->execute($values)) { return $stm; @@ -1142,7 +1154,9 @@ SQL; } /** + * @phpstan-param 'a'|'A'|'s'|'S'|'c'|'f'|'t'|'T'|'ST' $type * @param int $id category/feed/tag ID + * @param 'ASC'|'DESC' $order * @return Traversable<FreshRSS_Entry> */ public function listWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL, @@ -1160,6 +1174,7 @@ SQL; /** * @param array<string> $ids + * @param 'ASC'|'DESC' $order * @return Traversable<FreshRSS_Entry> */ public function listByIds(array $ids, string $order = 'DESC'): Traversable { @@ -1179,7 +1194,6 @@ SQL; if ($order !== 'DESC' && $order !== 'ASC') { $order = 'DESC'; } - $content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content'; $repeats = str_repeat('?,', count($ids) - 1) . '?'; $sql = <<<SQL @@ -1188,9 +1202,10 @@ FROM `_entry` WHERE id IN ({$repeats}) ORDER BY id {$order} SQL; - $stm = $this->pdo->prepare($sql); - $stm->execute($ids); + if ($stm === false || !$stm->execute($ids)) { + return; + } while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { /** @var array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, * 'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string} $row */ @@ -1201,6 +1216,7 @@ SQL; /** * @phpstan-param 'a'|'A'|'s'|'S'|'c'|'f'|'t'|'T'|'ST' $type * @param int $id category/feed/tag ID + * @param 'ASC'|'DESC' $order * @return array<numeric-string>|null */ public function listIdsWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL, @@ -1292,8 +1308,8 @@ SQL; } } - /** @return array<string,int>|false */ - public function countUnreadRead() { + /** @return array<string,int> */ + public function countUnreadRead(): array { $sql = <<<'SQL' SELECT COUNT(e.id) AS count FROM `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id @@ -1304,8 +1320,8 @@ SELECT COUNT(e.id) AS count FROM `_entry` e WHERE f.priority > 0 AND e.is_read=0 SQL; $res = $this->fetchColumn($sql, 0); - if ($res == null) { - return false; + if ($res === null) { + return ['all' => -1, 'unread' => -1, 'read' => -1]; } rsort($res); $all = (int)($res[0] ?? 0); @@ -1329,15 +1345,17 @@ SQL; $sql .= ' INNER JOIN `_feed` f ON e.id_feed=f.id'; } $sql .= ' WHERE e.is_read=0'; + $values = []; if ($minPriority !== null) { $sql .= ' AND f.priority > :priority'; + $values[':priority'] = $minPriority; } - $res = $this->fetchColumn($sql, 0, [':priority' => $minPriority]); + $res = $this->fetchColumn($sql, 0, $values); return isset($res[0]) ? (int)($res[0]) : -1; } - /** @return array<string,int>|false */ - public function countUnreadReadFavorites() { + /** @return array{'all':int,'read':int,'unread':int} */ + public function countUnreadReadFavorites(): array { $sql = <<<'SQL' SELECT c FROM ( SELECT COUNT(e1.id) AS c, 1 AS o @@ -1359,8 +1377,8 @@ SQL; ':priority_normal1' => FreshRSS_Feed::PRIORITY_NORMAL, ':priority_normal2' => FreshRSS_Feed::PRIORITY_NORMAL, ]); - if ($res == null) { - return false; + if ($res === null) { + return ['all' => -1, 'unread' => -1, 'read' => -1]; } rsort($res); diff --git a/app/Models/Feed.php b/app/Models/Feed.php index c0fce6e4a..6c840942b 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -125,10 +125,7 @@ class FreshRSS_Feed extends Minz_Model { return $this->hubUrl; } - /** - * @return FreshRSS_Category|null|false - */ - public function category() { + public function category(): ?FreshRSS_Category { if ($this->category === null) { $catDAO = FreshRSS_Factory::createCategoryDao(); $this->category = $catDAO->searchById($this->categoryId); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 98bcef379..7819bf9b5 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -35,7 +35,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } /** - * @param array<string,mixed> $valuesTmp + * @param array{'url':string,'kind':int,'category':int,'name':string,'website':string,'description':string,'lastUpdate':int,'priority'?:int, + * 'pathEntries'?:string,'httpAuth':string,'error':int|bool,'ttl'?:int,'attributes'?:string|array<string|mixed>} $valuesTmp * @return int|false */ public function addFeed(array $valuesTmp) { @@ -69,7 +70,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { ); if ($stm !== false && $stm->execute($values)) { - return (int)($this->pdo->lastInsertId('`_feed_id_seq`')); + $feedId = $this->pdo->lastInsertId('`_feed_id_seq`'); + return $feedId === false ? false : (int)$feedId; } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { @@ -94,6 +96,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { 'website' => $feed->website(), 'description' => $feed->description(), 'lastUpdate' => 0, + 'error' => false, 'pathEntries' => $feed->pathEntries(), 'httpAuth' => $feed->httpAuth(), 'ttl' => $feed->ttl(true), @@ -137,10 +140,12 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } /** - * @param array<string,mixed> $valuesTmp + * @param array{'url'?:string,'kind'?:int,'category'?:int,'name'?:string,'website'?:string,'description'?:string,'lastUpdate'?:int,'priority'?:int, + * 'pathEntries'?:string,'httpAuth'?:string,'error'?:int,'ttl'?:int,'attributes'?:string|array<string,mixed>} $valuesTmp $valuesTmp * @return int|false */ public function updateFeed(int $id, array $valuesTmp) { + $originalValues = $valuesTmp; if (isset($valuesTmp['name'])) { $valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8'); } @@ -176,7 +181,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { - return $this->updateFeed($id, $valuesTmp); + return $this->updateFeed($id, $originalValues); } Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info) . ' for feed ' . $id); return false; @@ -227,7 +232,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { public function changeCategory(int $idOldCat, int $idNewCat) { $catDAO = FreshRSS_Factory::createCategoryDao(); $newCat = $catDAO->searchById($idNewCat); - if (!$newCat) { + if ($newCat === null) { $newCat = $catDAO->getDefault(); } @@ -286,7 +291,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } - /** @return Traversable<array<string,string|int>> */ + /** @return Traversable<array{'id':int,'url':string,'kind':int,'category':int,'name':string,'website':string,'description':string,'lastUpdate':int,'priority'?:int, + * 'pathEntries'?:string,'httpAuth':string,'error':int|bool,'ttl'?:int,'attributes'?:string}> */ public function selectAll(): Traversable { $sql = <<<'SQL' SELECT id, url, kind, category, name, website, description, `lastUpdate`, @@ -294,6 +300,9 @@ SELECT id, url, kind, category, name, website, description, `lastUpdate`, FROM `_feed` SQL; $stm = $this->pdo->query($sql); + if ($stm === false) { + return; + } while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { yield $row; } @@ -305,27 +314,26 @@ SQL; if ($res == null) { return null; } - $feed = self::daoToFeed($res); - return $feed[$id] ?? null; + /** @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int, + * 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res */ + $feeds = self::daoToFeed($res); + return $feeds[$id] ?? null; } public function searchByUrl(string $url): ?FreshRSS_Feed { - $sql = 'SELECT * FROM `_feed` WHERE url=?'; - $stm = $this->pdo->prepare($sql); - - $values = array($url); - - $stm->execute($values); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - $feed = current(self::daoToFeed($res)); - return $feed == false ? null : $feed; + $sql = 'SELECT * FROM `_feed` WHERE url=:url'; + $res = $this->fetchAssoc($sql, [':url' => $url]); + /** @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int, + * 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res */ + return empty($res[0]) ? null : (current(self::daoToFeed($res)) ?: null); } - /** @return array<int>|false */ - public function listFeedsIds() { + /** @return array<int> */ + public function listFeedsIds(): array { $sql = 'SELECT id FROM `_feed`'; - $stm = $this->pdo->query($sql); - return $stm ? $stm->fetchAll(PDO::FETCH_COLUMN, 0) : false; + /** @var array<int> $res */ + $res = $this->fetchColumn($sql, 0) ?? []; + return $res; } /** @@ -333,8 +341,10 @@ SQL; */ public function listFeeds(): array { $sql = 'SELECT * FROM `_feed` ORDER BY name'; - $stm = $this->pdo->query($sql); - return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); + $res = $this->fetchAssoc($sql); + /** @var array<array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int, + * 'priority':int,'pathEntries':string,'httpAuth':string,'error':int,'ttl':int,'attributes':string}>|null $res */ + return $res == null ? [] : self::daoToFeed($res); } /** @return array<string,string> */ @@ -345,8 +355,11 @@ SQL; } else { $sql .= 'WHERE id_feed=' . intval($id_feed); } - $stm = $this->pdo->query($sql); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $res = $this->fetchAssoc($sql); + /** @var array<array{'id_feed':int,'newest_item_us':string}>|null $res */ + if ($res == null) { + return []; + } $newestItemUsec = []; foreach ($res as $line) { $newestItemUsec['f_' . $line['id_feed']] = $line['newest_item_us']; @@ -380,18 +393,13 @@ SQL; } } - /** @return array<string>|false */ - public function listTitles(int $id, int $limit = 0) { + /** @return array<string> */ + public function listTitles(int $id, int $limit = 0): array { $sql = 'SELECT title FROM `_entry` WHERE id_feed=:id_feed ORDER BY id DESC' . ($limit < 1 ? '' : ' LIMIT ' . intval($limit)); - - $stm = $this->pdo->prepare($sql); - $stm->bindParam(':id_feed', $id, PDO::PARAM_INT); - - if ($stm !== false && $stm->execute()) { - return $stm->fetchAll(PDO::FETCH_COLUMN, 0); - } - return false; + $res = $this->fetchColumn($sql, 0, [':id_feed' => $id]) ?? []; + /** @var array<string> $res */ + return $res; } /** @@ -399,15 +407,18 @@ SQL; * @return array<FreshRSS_Feed> */ public function listByCategory(int $cat, ?bool $muted = null): array { - $sql = 'SELECT * FROM `_feed` WHERE category=?'; + $sql = 'SELECT * FROM `_feed` WHERE category=:category'; if ($muted) { $sql .= ' AND ttl < 0'; } - $stm = $this->pdo->prepare($sql); - - $stm->execute(array($cat)); + $res = $this->fetchAssoc($sql, [':category' => $cat]); + if ($res == null) { + return []; + } - $feeds = self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); + /** @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int, + * 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res */ + $feeds = self::daoToFeed($res); usort($feeds, static function (FreshRSS_Feed $a, FreshRSS_Feed $b) { return strnatcasecmp($a->name(), $b->name()); @@ -417,23 +428,15 @@ SQL; } public function countEntries(int $id): int { - $sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=?'; - $stm = $this->pdo->prepare($sql); - $values = array($id); - $stm->execute($values); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - - return $res[0]['count']; + $sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=:id_feed'; + $res = $this->fetchColumn($sql, 0, ['id_feed' => $id]); + return isset($res[0]) ? (int)($res[0]) : -1; } public function countNotRead(int $id): int { - $sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=? AND is_read=0'; - $stm = $this->pdo->prepare($sql); - $values = array($id); - $stm->execute($values); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - - return $res[0]['count']; + $sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=:id_feed AND is_read=0'; + $res = $this->fetchColumn($sql, 0, ['id_feed' => $id]); + return isset($res[0]) ? (int)($res[0]) : -1; } /** @@ -525,9 +528,10 @@ SQL; public function truncate(int $id) { $sql = 'DELETE FROM `_entry` WHERE id_feed=:id'; $stm = $this->pdo->prepare($sql); - $stm->bindParam(':id', $id, PDO::PARAM_INT); $this->pdo->beginTransaction(); - if (!($stm && $stm->execute())) { + if (!($stm !== false && + $stm->bindParam(':id', $id, PDO::PARAM_INT) && + $stm->execute())) { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); $this->pdo->rollBack(); @@ -535,11 +539,11 @@ SQL; } $affected = $stm->rowCount(); - $sql = 'UPDATE `_feed` ' - . 'SET `cache_nbEntries`=0, `cache_nbUnreads`=0, `lastUpdate`=0 WHERE id=:id'; + $sql = 'UPDATE `_feed` SET `cache_nbEntries`=0, `cache_nbUnreads`=0, `lastUpdate`=0 WHERE id=:id'; $stm = $this->pdo->prepare($sql); - $stm->bindParam(':id', $id, PDO::PARAM_INT); - if (!($stm && $stm->execute())) { + if (!($stm !== false && + $stm->bindParam(':id', $id, PDO::PARAM_INT) && + $stm->execute())) { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); $this->pdo->rollBack(); @@ -574,7 +578,8 @@ SQL; } /** - * @param array<int,array<string,string|int>>|array<string,string|int> $listDAO + * @param array<int,array{'id'?:int,'url'?:string,'kind'?:int,'category'?:int,'name'?:string,'website'?:string,'description'?:string,'lastUpdate'?:int,'priority'?:int, + * 'pathEntries'?:string,'httpAuth'?:string,'error'?:int|bool,'ttl'?:int,'attributes'?:string,'cache_nbUnreads'?:int,'cache_nbEntries'?:int}> $listDAO * @return array<int,FreshRSS_Feed> */ public static function daoToFeed(array $listDAO, ?int $catID = null): array { @@ -589,7 +594,7 @@ SQL; continue; } if (isset($dao['id'])) { - $key = $dao['id']; + $key = (int)$dao['id']; } if ($catID === null) { $category = $dao['category'] ?? 0; diff --git a/app/Models/Search.php b/app/Models/Search.php index f5b061512..d165b05f0 100644 --- a/app/Models/Search.php +++ b/app/Models/Search.php @@ -164,37 +164,37 @@ class FreshRSS_Search { } public function getMinDate(): ?int { - return $this->min_date; + return $this->min_date ?: null; } public function getNotMinDate(): ?int { - return $this->not_min_date; + return $this->not_min_date ?: null; } public function setMinDate(int $value): void { $this->min_date = $value; } public function getMaxDate(): ?int { - return $this->max_date; + return $this->max_date ?: null; } public function getNotMaxDate(): ?int { - return $this->not_max_date; + return $this->not_max_date ?: null; } public function setMaxDate(int $value): void { $this->max_date = $value; } public function getMinPubdate(): ?int { - return $this->min_pubdate; + return $this->min_pubdate ?: null; } public function getNotMinPubdate(): ?int { - return $this->not_min_pubdate; + return $this->not_min_pubdate ?: null; } public function getMaxPubdate(): ?int { - return $this->max_pubdate; + return $this->max_pubdate ?: null; } public function getNotMaxPubdate(): ?int { - return $this->not_max_pubdate; + return $this->not_max_pubdate ?: null; } /** @return array<string>|null */ @@ -518,7 +518,7 @@ class FreshRSS_Search { $input = str_replace($matches[0], '', $input); $dates = self::removeEmptyValues($matches['search']); if (!empty($dates[0])) { - list($this->min_date, $this->max_date) = parseDateInterval($dates[0]); + [$this->min_date, $this->max_date] = parseDateInterval($dates[0]); } } return $input; @@ -529,7 +529,7 @@ class FreshRSS_Search { $input = str_replace($matches[0], '', $input); $dates = self::removeEmptyValues($matches['search']); if (!empty($dates[0])) { - list($this->not_min_date, $this->not_max_date) = parseDateInterval($dates[0]); + [$this->not_min_date, $this->not_max_date] = parseDateInterval($dates[0]); } } return $input; @@ -545,7 +545,7 @@ class FreshRSS_Search { $input = str_replace($matches[0], '', $input); $dates = self::removeEmptyValues($matches['search']); if (!empty($dates[0])) { - list($this->min_pubdate, $this->max_pubdate) = parseDateInterval($dates[0]); + [$this->min_pubdate, $this->max_pubdate] = parseDateInterval($dates[0]); } } return $input; @@ -556,7 +556,7 @@ class FreshRSS_Search { $input = str_replace($matches[0], '', $input); $dates = self::removeEmptyValues($matches['search']); if (!empty($dates[0])) { - list($this->not_min_pubdate, $this->not_max_pubdate) = parseDateInterval($dates[0]); + [$this->not_min_pubdate, $this->not_max_pubdate] = parseDateInterval($dates[0]); } } return $input; diff --git a/app/Models/TagDAO.php b/app/Models/TagDAO.php index a675c7e04..133907a3a 100644 --- a/app/Models/TagDAO.php +++ b/app/Models/TagDAO.php @@ -67,7 +67,8 @@ SQL; ); if ($stm !== false && $stm->execute($values) && $stm->rowCount() > 0) { - return (int)($this->pdo->lastInsertId('`_tag_id_seq`')); + $tagId = $this->pdo->lastInsertId('`_tag_id_seq`'); + return $tagId === false ? false : (int)$tagId; } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); @@ -275,8 +276,7 @@ SQL; return $newestItemUsec; } - /** @return int|false */ - public function count() { + public function count(): int { $sql = 'SELECT COUNT(*) AS count FROM `_tag`'; $stm = $this->pdo->query($sql); if ($stm !== false) { @@ -288,25 +288,19 @@ SQL; return $this->count(); } Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); - return false; + return -1; } - /** - * @return int|false - */ - public function countEntries(int $id) { + public function countEntries(int $id): int { $sql = 'SELECT COUNT(*) AS count FROM `_entrytag` WHERE id_tag=:id_tag'; $res = $this->fetchAssoc($sql, [':id_tag' => $id]); if ($res == null || !isset($res[0]['count'])) { - return false; + return -1; } return (int)$res[0]['count']; } - /** - * @return int|false - */ - public function countNotRead(?int $id = null) { + public function countNotRead(?int $id = null): int { $sql = <<<'SQL' SELECT COUNT(*) AS count FROM `_entrytag` et INNER JOIN `_entry` e ON et.id_entry=e.id @@ -320,7 +314,7 @@ SQL; $res = $this->fetchAssoc($sql, $values); if ($res == null || !isset($res[0]['count'])) { - return false; + return -1; } return (int)$res[0]['count']; } diff --git a/app/Models/UserQuery.php b/app/Models/UserQuery.php index f2301ff26..a4cb469fe 100644 --- a/app/Models/UserQuery.php +++ b/app/Models/UserQuery.php @@ -128,7 +128,7 @@ class FreshRSS_UserQuery { throw new FreshRSS_DAO_Exception('Category DAO is not loaded in UserQuery'); } $category = $this->category_dao->searchById($id); - if ($category) { + if ($category !== null) { $this->get_name = $category->name(); } else { $this->deprecated = true; @@ -146,7 +146,7 @@ class FreshRSS_UserQuery { throw new FreshRSS_DAO_Exception('Feed DAO is not loaded in UserQuery'); } $feed = $this->feed_dao->searchById($id); - if ($feed) { + if ($feed !== null) { $this->get_name = $feed->name(); } else { $this->deprecated = true; @@ -164,7 +164,7 @@ class FreshRSS_UserQuery { throw new FreshRSS_DAO_Exception('Tag DAO is not loaded in UserQuery'); } $tag = $this->tag_dao->searchById($id); - if ($tag) { + if ($tag !== null) { $this->get_name = $tag->name(); } else { $this->deprecated = true; diff --git a/app/Services/ExportService.php b/app/Services/ExportService.php index 0d45ba548..d3a3373a5 100644 --- a/app/Services/ExportService.php +++ b/app/Services/ExportService.php @@ -94,7 +94,7 @@ class FreshRSS_Export_Service { */ public function generateFeedEntries(int $feed_id, int $max_number_entries): ?array { $feed = $this->feed_dao->searchById($feed_id); - if (!$feed) { + if ($feed === null) { return null; } @@ -127,7 +127,7 @@ class FreshRSS_Export_Service { * @return array<string,string> Keys are filenames and values are contents. */ public function generateAllFeedEntries(int $max_number_entries): array { - $feed_ids = $this->feed_dao->listFeedsIds() ?: []; + $feed_ids = $this->feed_dao->listFeedsIds(); $exported_files = []; foreach ($feed_ids as $feed_id) { diff --git a/cli/user-info.php b/cli/user-info.php index d8b498cc3..a320a4d19 100755 --- a/cli/user-info.php +++ b/cli/user-info.php @@ -63,22 +63,8 @@ foreach ($users as $username) { $nbEntries = $entryDAO->countUnreadRead(); $nbFavorites = $entryDAO->countUnreadReadFavorites(); - - if ($nbFavorites === false) { - $nbFavorites = [ - 'all' => -1, - ]; - } - $feedList = $feedDAO->listFeedsIds(); - if ($nbEntries === false) { - $nbEntries = [ - 'read' => -1, - 'unread' => -1, - ]; - } - $data = array( 'default' => $username === FreshRSS_Context::$system_conf->default_user ? '*' : '', 'user' => $username, @@ -87,7 +73,7 @@ foreach ($users as $username) { 'last_user_activity' => FreshRSS_UserDAO::mtime($username), 'database_size' => $databaseDAO->size(), 'categories' => $catDAO->count(), - 'feeds' => count($feedList === false ? [] : $feedList), + 'feeds' => count($feedList), 'reads' => (int)$nbEntries['read'], 'unreads' => (int)$nbEntries['unread'], 'favourites' => (int)$nbFavorites['all'], diff --git a/tests/phpstan-next.txt b/tests/phpstan-next.txt index 723ef4ef2..c1c73d43e 100644 --- a/tests/phpstan-next.txt +++ b/tests/phpstan-next.txt @@ -8,12 +8,7 @@ ./app/Controllers/indexController.php ./app/Controllers/updateController.php ./app/Controllers/userController.php -./app/Models/CategoryDAO.php -./app/Models/Context.php -./app/Models/EntryDAO.php ./app/Models/Feed.php -./app/Models/FeedDAO.php -./app/Models/Search.php ./app/Models/Share.php ./app/views/helpers/logs_pagination.phtml ./lib/Minz/Error.php |