mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-13 17:53:56 -04:00
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>
This commit is contained in:
committed by
GitHub
parent
4de1d5efea
commit
bd9fa803f1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/** @return FreshRSS_Category|null|false */
|
||||
public function searchByName(string $name) {
|
||||
$sql = 'SELECT * FROM `_category` WHERE name=:name';
|
||||
$res = $this->fetchAssoc($sql, ['name' => $name]);
|
||||
if ($res == null) {
|
||||
return false;
|
||||
}
|
||||
$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 array<FreshRSS_Category>|false */
|
||||
public function listSortedCategories(bool $prePopulateFeeds = true, bool $details = false) {
|
||||
public function searchByName(string $name): ?FreshRSS_Category {
|
||||
$sql = 'SELECT * FROM `_category` WHERE name=:name';
|
||||
$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> */
|
||||
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(
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
$res = $this->fetchAssoc($sql, [':category' => $cat]);
|
||||
if ($res == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$stm->execute(array($cat));
|
||||
|
||||
$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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user