mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-18 21:34:35 -04:00
PHPStan Level 7 for more DAO PDO (#5328)
* PHPStan Level 7 for more DAO PDO With new function to address common type and check problems * A bit more * PHPStan Level 7 for FreshRSS_Entry
This commit is contained in:
committed by
GitHub
parent
26e2a70312
commit
c72914bba2
@@ -79,6 +79,8 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
|
||||
|
||||
/**
|
||||
* This action updates the given category.
|
||||
* @todo Check whether this function is used at all
|
||||
* @see FreshRSS_subscription_Controller::categoryAction() (consider merging)
|
||||
*
|
||||
* Request parameters are:
|
||||
* - id
|
||||
@@ -97,14 +99,16 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController {
|
||||
Minz_Request::bad(_t('feedback.sub.category.no_name'), $url_redirect);
|
||||
}
|
||||
|
||||
if ($catDAO->searchById($id) == null) {
|
||||
$cat = $catDAO->searchById($id);
|
||||
if ($cat === null) {
|
||||
Minz_Request::bad(_t('feedback.sub.category.not_exist'), $url_redirect);
|
||||
}
|
||||
|
||||
$cat = new FreshRSS_Category($name);
|
||||
$values = array(
|
||||
$values = [
|
||||
'name' => $cat->name(),
|
||||
);
|
||||
'kind' => $cat->kind(),
|
||||
'attributes' => $cat->attributes(),
|
||||
];
|
||||
|
||||
if ($catDAO->updateCategory($id, $values)) {
|
||||
Minz_Request::good(_t('feedback.sub.category.updated'), $url_redirect);
|
||||
|
||||
@@ -47,25 +47,34 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController {
|
||||
$statsDAO = FreshRSS_Factory::createStatsDAO();
|
||||
FreshRSS_View::appendScript(Minz_Url::display('/scripts/vendor/chart.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.min.js')));
|
||||
|
||||
$this->view->repartition = $statsDAO->calculateEntryRepartition();
|
||||
$this->view->repartitions = $statsDAO->calculateEntryRepartition();
|
||||
|
||||
$entryCount = $statsDAO->calculateEntryCount();
|
||||
$this->view->entryCount = $entryCount;
|
||||
$this->view->average = round(array_sum(array_values($entryCount)) / count($entryCount), 2);
|
||||
if (is_array($entryCount) && count($entryCount) > 0) {
|
||||
$this->view->entryCount = $entryCount;
|
||||
$this->view->average = round(array_sum(array_values($entryCount)) / count($entryCount), 2);
|
||||
} else {
|
||||
$this->view->entryCount = [];
|
||||
$this->view->average = -1.0;
|
||||
}
|
||||
|
||||
$feedByCategory_calculated = $statsDAO->calculateFeedByCategory();
|
||||
$feedByCategory = [];
|
||||
for ($i = 0; $i < count($feedByCategory_calculated); $i++) {
|
||||
$feedByCategory['label'][$i] = $feedByCategory_calculated[$i]['label'];
|
||||
$feedByCategory['data'][$i] = $feedByCategory_calculated[$i]['data'];
|
||||
$feedByCategory_calculated = $statsDAO->calculateFeedByCategory();
|
||||
if (is_array($feedByCategory_calculated)) {
|
||||
for ($i = 0; $i < count($feedByCategory_calculated); $i++) {
|
||||
$feedByCategory['label'][$i] = $feedByCategory_calculated[$i]['label'];
|
||||
$feedByCategory['data'][$i] = $feedByCategory_calculated[$i]['data'];
|
||||
}
|
||||
}
|
||||
$this->view->feedByCategory = $feedByCategory;
|
||||
|
||||
$entryByCategory_calculated = $statsDAO->calculateEntryByCategory();
|
||||
$entryByCategory = [];
|
||||
for ($i = 0; $i < count($entryByCategory_calculated); $i++) {
|
||||
$entryByCategory['label'][$i] = $entryByCategory_calculated[$i]['label'];
|
||||
$entryByCategory['data'][$i] = $entryByCategory_calculated[$i]['data'];
|
||||
$entryByCategory_calculated = $statsDAO->calculateEntryByCategory();
|
||||
if (is_array($entryByCategory_calculated)) {
|
||||
for ($i = 0; $i < count($entryByCategory_calculated); $i++) {
|
||||
$entryByCategory['label'][$i] = $entryByCategory_calculated[$i]['label'];
|
||||
$entryByCategory['data'][$i] = $entryByCategory_calculated[$i]['data'];
|
||||
}
|
||||
}
|
||||
$this->view->entryByCategory = $entryByCategory;
|
||||
|
||||
@@ -114,7 +123,7 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController {
|
||||
FreshRSS_View::appendScript(Minz_Url::display('/scripts/feed.js?' . @filemtime(PUBLIC_PATH . '/scripts/feed.js')));
|
||||
$feed_dao = FreshRSS_Factory::createFeedDao();
|
||||
$statsDAO = FreshRSS_Factory::createStatsDAO();
|
||||
$feeds = $statsDAO->calculateFeedLastDate();
|
||||
$feeds = $statsDAO->calculateFeedLastDate() ?: [];
|
||||
$idleFeeds = array(
|
||||
'last_5_year' => array(),
|
||||
'last_3_year' => array(),
|
||||
|
||||
@@ -132,7 +132,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
|
||||
$targetTag = $tagDAO->searchByName($targetName);
|
||||
if ($targetTag === null) {
|
||||
// There is no existing tag with the same target name
|
||||
$tagDAO->updateTag($sourceId, ['name' => $targetName]);
|
||||
$tagDAO->updateTagName($sourceId, $targetName);
|
||||
} else {
|
||||
// There is an existing tag with the same target name
|
||||
$tagDAO->updateEntryTag($sourceId, $targetTag->id());
|
||||
|
||||
@@ -7,7 +7,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
|
||||
public function resetDefaultCategoryName(): bool {
|
||||
//FreshRSS 1.15.1
|
||||
$stm = $this->pdo->prepare('UPDATE `_category` SET name = :name WHERE id = :id');
|
||||
if ($stm) {
|
||||
if ($stm !== false) {
|
||||
$stm->bindValue(':id', self::DEFAULTCATEGORYID, PDO::PARAM_INT);
|
||||
$stm->bindValue(':name', 'Uncategorized');
|
||||
}
|
||||
@@ -115,14 +115,14 @@ SQL;
|
||||
$valuesTmp['name'],
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values) && $stm->rowCount() > 0) {
|
||||
if ($stm !== false && $stm->execute($values) && $stm->rowCount() > 0) {
|
||||
return $this->pdo->lastInsertId('`_category_id_seq`');
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->addCategory($valuesTmp);
|
||||
}
|
||||
Minz_Log::error('SQL error addCategory: ' . json_encode($info));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ SQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $valuesTmp
|
||||
* @param array{'name':string,'kind':int,'attributes':array<string,mixed>} $valuesTmp
|
||||
* @return int|false
|
||||
*/
|
||||
public function updateCategory(int $id, array $valuesTmp) {
|
||||
@@ -155,7 +155,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
|
||||
$valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8');
|
||||
if (!isset($valuesTmp['attributes'])) {
|
||||
if (empty($valuesTmp['attributes'])) {
|
||||
$valuesTmp['attributes'] = [];
|
||||
}
|
||||
$values = array(
|
||||
@@ -166,14 +166,14 @@ SQL;
|
||||
$valuesTmp['name'],
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->updateCategory($id, $valuesTmp);
|
||||
}
|
||||
Minz_Log::error('SQL error updateCategory: ' . json_encode($info));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -188,11 +188,11 @@ SQL;
|
||||
];
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::warning(__METHOD__ . ' error: ' . $sql . ' : ' . json_encode($info));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -205,20 +205,20 @@ SQL;
|
||||
$sql = 'DELETE FROM `_category` WHERE id=:id';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
if ($stm && $stm->execute()) {
|
||||
if ($stm !== false && $stm->execute()) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error deleteCategory: ' . json_encode($info));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Traversable<array<string,string|int>> */
|
||||
/** @return Traversable<array{'id':int,'name':string,'kind':int,'lastUpdate':int,'error':int,'attributes'?:array<string,mixed>}> */
|
||||
public function selectAll(): Traversable {
|
||||
$sql = 'SELECT id, name, kind, `lastUpdate`, error, attributes FROM `_category`';
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm != false) {
|
||||
if ($stm !== false) {
|
||||
while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
|
||||
yield $row;
|
||||
}
|
||||
@@ -235,7 +235,7 @@ SQL;
|
||||
public function searchById(int $id): ?FreshRSS_Category {
|
||||
$sql = 'SELECT * FROM `_category` WHERE id=:id';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm &&
|
||||
if ($stm !== false &&
|
||||
$stm->bindParam(':id', $id, PDO::PARAM_INT) &&
|
||||
$stm->execute()) {
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
@@ -250,19 +250,12 @@ SQL;
|
||||
/** @return FreshRSS_Category|null|false */
|
||||
public function searchByName(string $name) {
|
||||
$sql = 'SELECT * FROM `_category` WHERE name=:name';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm == false) {
|
||||
$res = $this->fetchAssoc($sql, ['name' => $name]);
|
||||
if ($res == null) {
|
||||
return false;
|
||||
}
|
||||
$stm->bindParam(':name', $name);
|
||||
$stm->execute();
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$cat = self::daoToCategory($res);
|
||||
if (isset($cat[0])) {
|
||||
return $cat[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return $cat[0] ?? null;
|
||||
}
|
||||
|
||||
/** @return array<FreshRSS_Category>|false */
|
||||
@@ -300,20 +293,19 @@ SQL;
|
||||
. 'ORDER BY c.name, f.name';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$values = [ ':priority_normal' => FreshRSS_Feed::PRIORITY_NORMAL ];
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return self::daoToCategoryPrepopulated($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->listCategories($prePopulateFeeds, $details);
|
||||
}
|
||||
Minz_Log::error('SQL error listCategories: ' . json_encode($info));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$sql = 'SELECT * FROM `_category` ORDER BY name';
|
||||
$stm = $this->pdo->query($sql);
|
||||
return self::daoToCategory($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
$res = $this->fetchAssoc('SELECT * FROM `_category` ORDER BY name');
|
||||
return $res == null ? false : self::daoToCategory($res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +314,7 @@ SQL;
|
||||
$sql = 'SELECT * FROM `_category` WHERE kind = :kind AND `lastUpdate` < :lu ORDER BY `lastUpdate`'
|
||||
. ($limit < 1 ? '' : ' LIMIT ' . $limit);
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm &&
|
||||
if ($stm !== false &&
|
||||
$stm->bindValue(':kind', FreshRSS_Category::KIND_DYNAMIC_OPML, PDO::PARAM_INT) &&
|
||||
$stm->bindValue(':lu', time() - $defaultCacheDuration, PDO::PARAM_INT) &&
|
||||
$stm->execute()) {
|
||||
@@ -376,11 +368,11 @@ SQL;
|
||||
$cat->name(),
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $this->pdo->lastInsertId('`_category_id_seq`');
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error check default category: ' . json_encode($info));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,11 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
|
||||
try {
|
||||
$sql = 'SELECT 1';
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm === false) {
|
||||
return 'Error during SQL connection test!';
|
||||
}
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
return $res == false ? 'Error during SQL connection test!' : '';
|
||||
return $res == false ? 'Error during SQL connection fetch test!' : '';
|
||||
} catch (Exception $e) {
|
||||
syslog(LOG_DEBUG, __method__ . ' warning: ' . $e->getMessage());
|
||||
return $e->getMessage();
|
||||
@@ -45,8 +48,10 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
|
||||
}
|
||||
|
||||
public function tablesAreCorrect(): bool {
|
||||
$stm = $this->pdo->query('SHOW TABLES');
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$res = $this->fetchAssoc('SHOW TABLES');
|
||||
if ($res == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tables = array(
|
||||
$this->pdo->prefix() . 'category' => false,
|
||||
@@ -60,21 +65,23 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
|
||||
$tables[array_pop($value)] = true;
|
||||
}
|
||||
|
||||
return count(array_keys($tables, true, true)) == count($tables);
|
||||
return count(array_keys($tables, true, true)) === count($tables);
|
||||
}
|
||||
|
||||
/** @return array<array<string,string|bool>> */
|
||||
/** @return array<array<string,string|int|bool|null>> */
|
||||
public function getSchema(string $table): array {
|
||||
$sql = 'DESC `_' . $table . '`';
|
||||
$stm = $this->pdo->query($sql);
|
||||
return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
$res = $this->fetchAssoc('DESC `_' . $table . '`');
|
||||
return $res == null ? [] : $this->listDaoToSchema($res);
|
||||
}
|
||||
|
||||
/** @param array<string> $schema */
|
||||
public function checkTable(string $table, array $schema): bool {
|
||||
$columns = $this->getSchema($table);
|
||||
if (count($columns) === 0 || count($schema) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ok = (count($columns) == count($schema));
|
||||
$ok = count($columns) === count($schema);
|
||||
foreach ($columns as $c) {
|
||||
$ok &= in_array($c['name'], $schema);
|
||||
}
|
||||
@@ -123,21 +130,21 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string> $dao
|
||||
* @return array<string,string|bool>
|
||||
* @param array<string,string|int|bool|null> $dao
|
||||
* @return array<string,string|int|bool|null>
|
||||
*/
|
||||
public function daoToSchema(array $dao): array {
|
||||
return array(
|
||||
'name' => $dao['Field'],
|
||||
'type' => strtolower($dao['Type']),
|
||||
return [
|
||||
'name' => (string)($dao['Field']),
|
||||
'type' => strtolower((string)($dao['Type'])),
|
||||
'notnull' => (bool)$dao['Null'],
|
||||
'default' => $dao['Default'],
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array<string,string>> $listDAO
|
||||
* @return array<array<string,string|bool>>
|
||||
* @param array<array<string,string|int|bool|null>> $listDAO
|
||||
* @return array<array<string,string|int|bool|null>>
|
||||
*/
|
||||
public function listDaoToSchema(array $listDAO): array {
|
||||
$list = array();
|
||||
@@ -151,16 +158,18 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
|
||||
|
||||
public function size(bool $all = false): int {
|
||||
$db = FreshRSS_Context::$system_conf->db;
|
||||
$sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?'; //MySQL
|
||||
$values = array($db['base']);
|
||||
//MySQL:
|
||||
$sql = <<<'SQL'
|
||||
SELECT SUM(data_length + index_length)
|
||||
FROM information_schema.TABLES WHERE table_schema=:table_schema
|
||||
SQL;
|
||||
$values = [':table_schema' => $db['base']];
|
||||
if (!$all) {
|
||||
$sql .= ' AND table_name LIKE ?';
|
||||
$values[] = $this->pdo->prefix() . '%';
|
||||
$sql .= ' AND table_name LIKE :table_name';
|
||||
$values[':table_name'] = $this->pdo->prefix() . '%';
|
||||
}
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->execute($values);
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
return intval($res[0]);
|
||||
$res = $this->fetchColumn($sql, 0, $values);
|
||||
return isset($res[0]) ? (int)($res[0]) : -1;
|
||||
}
|
||||
|
||||
public function optimize(): bool {
|
||||
|
||||
@@ -11,56 +11,54 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite {
|
||||
|
||||
public function tablesAreCorrect(): bool {
|
||||
$db = FreshRSS_Context::$system_conf->db;
|
||||
$dbowner = $db['user'];
|
||||
$sql = 'SELECT * FROM pg_catalog.pg_tables where tableowner=?';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$values = array($dbowner);
|
||||
$stm->execute($values);
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$sql = 'SELECT * FROM pg_catalog.pg_tables where tableowner=:tableowner';
|
||||
$res = $this->fetchAssoc($sql, [':tableowner' => $db['user']]);
|
||||
if ($res == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tables = array(
|
||||
$tables = [
|
||||
$this->pdo->prefix() . 'category' => false,
|
||||
$this->pdo->prefix() . 'feed' => false,
|
||||
$this->pdo->prefix() . 'entry' => false,
|
||||
$this->pdo->prefix() . 'entrytmp' => false,
|
||||
$this->pdo->prefix() . 'tag' => false,
|
||||
$this->pdo->prefix() . 'entrytag' => false,
|
||||
);
|
||||
];
|
||||
foreach ($res as $value) {
|
||||
$tables[array_pop($value)] = true;
|
||||
}
|
||||
|
||||
return count(array_keys($tables, true, true)) == count($tables);
|
||||
return count(array_keys($tables, true, true)) === count($tables);
|
||||
}
|
||||
|
||||
/** @return array<array<string,string|bool>> */
|
||||
/** @return array<array<string,string|int|bool|null>> */
|
||||
public function getSchema(string $table): array {
|
||||
$sql = 'select column_name as field, data_type as type, column_default as default, is_nullable as null from INFORMATION_SCHEMA.COLUMNS where table_name = ?';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->execute(array($this->pdo->prefix() . $table));
|
||||
return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
$sql = <<<'SQL'
|
||||
SELECT column_name AS field, data_type AS type, column_default AS default, is_nullable AS null
|
||||
FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name
|
||||
SQL;
|
||||
$res = $this->fetchAssoc($sql, [':table_name' => $this->pdo->prefix() . $table]);
|
||||
return $res == null ? [] : $this->listDaoToSchema($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string> $dao
|
||||
* @return array<string,string|bool>
|
||||
* @param array<string,string|int|bool|null> $dao
|
||||
* @return array{'name':string,'type':string,'notnull':bool,'default':mixed}
|
||||
*/
|
||||
public function daoToSchema(array $dao): array {
|
||||
return array(
|
||||
'name' => $dao['field'],
|
||||
'type' => strtolower($dao['type']),
|
||||
return [
|
||||
'name' => (string)($dao['field']),
|
||||
'type' => strtolower((string)($dao['type'])),
|
||||
'notnull' => (bool)$dao['null'],
|
||||
'default' => $dao['default'],
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
public function size(bool $all = false): int {
|
||||
if ($all) {
|
||||
$db = FreshRSS_Context::$system_conf->db;
|
||||
$sql = 'SELECT pg_database_size(:base)';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->bindParam(':base', $db['base']);
|
||||
$stm->execute();
|
||||
$res = $this->fetchColumn('SELECT pg_database_size(:base)', 0, [':base' => $db['base']]);
|
||||
} else {
|
||||
$sql = <<<SQL
|
||||
SELECT
|
||||
@@ -71,13 +69,9 @@ pg_total_relation_size('`{$this->pdo->prefix()}entrytmp`') +
|
||||
pg_total_relation_size('`{$this->pdo->prefix()}tag`') +
|
||||
pg_total_relation_size('`{$this->pdo->prefix()}entrytag`')
|
||||
SQL;
|
||||
$stm = $this->pdo->query($sql);
|
||||
$res = $this->fetchColumn($sql, 0);
|
||||
}
|
||||
if ($stm == false) {
|
||||
return 0;
|
||||
}
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
return intval($res[0]);
|
||||
return intval($res[0] ?? -1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO {
|
||||
return count(array_keys($tables, true, true)) == count($tables);
|
||||
}
|
||||
|
||||
/** @return array<array<string,string|bool>> */
|
||||
/** @return array<array<string,string|int|bool|null>> */
|
||||
public function getSchema(string $table): array {
|
||||
$sql = 'PRAGMA table_info(' . $table . ')';
|
||||
$stm = $this->pdo->query($sql);
|
||||
|
||||
@@ -40,9 +40,11 @@ class FreshRSS_Entry extends Minz_Model {
|
||||
|
||||
/**
|
||||
* @param int|string $pubdate
|
||||
* @param bool|int|null $is_read
|
||||
* @param bool|int|null $is_favorite
|
||||
*/
|
||||
public function __construct(int $feedId = 0, string $guid = '', string $title = '', string $authors = '', string $content = '',
|
||||
string $link = '', $pubdate = 0, ?bool $is_read = false, ?bool $is_favorite = false, string $tags = '') {
|
||||
string $link = '', $pubdate = 0, $is_read = false, $is_favorite = false, string $tags = '') {
|
||||
$this->_title($title);
|
||||
$this->_authors($authors);
|
||||
$this->_content($content);
|
||||
@@ -55,11 +57,18 @@ class FreshRSS_Entry extends Minz_Model {
|
||||
$this->_guid($guid);
|
||||
}
|
||||
|
||||
/** @param array<string,string|int> $dao */
|
||||
/** @param array{'id'?:string,'id_feed'?:int,'guid'?:string,'title'?:string,'author'?:string,'content'?:string,'link'?:string,'date'?:int|string,
|
||||
* 'is_read'?:bool|int,'is_favorite'?:bool|int,'tags'?:string,'attributes'?:string,'thumbnail'?:string,'timestamp'?:string,'categories'?:string} $dao */
|
||||
public static function fromArray(array $dao): FreshRSS_Entry {
|
||||
if (empty($dao['content'])) {
|
||||
$dao['content'] = '';
|
||||
}
|
||||
|
||||
$dao['attributes'] = empty($dao['attributes']) ? [] : json_decode($dao['attributes'], true);
|
||||
if (!is_array($dao['attributes'])) {
|
||||
$dao['attributes'] = [];
|
||||
}
|
||||
|
||||
if (!empty($dao['thumbnail'])) {
|
||||
$dao['attributes']['thumbnail'] = [
|
||||
'url' => $dao['thumbnail'],
|
||||
@@ -81,7 +90,7 @@ class FreshRSS_Entry extends Minz_Model {
|
||||
$entry->_id($dao['id']);
|
||||
}
|
||||
if (!empty($dao['timestamp'])) {
|
||||
$entry->_date(strtotime($dao['timestamp']));
|
||||
$entry->_date(strtotime($dao['timestamp']) ?: 0);
|
||||
}
|
||||
if (!empty($dao['categories'])) {
|
||||
$entry->_tags($dao['categories']);
|
||||
@@ -283,7 +292,7 @@ HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,string>|null
|
||||
* @return array{'url':string,'type'?:string,'medium'?:string,'length'?:int,'title'?:string,'description'?:string,'credit'?:string,'height'?:int,'width'?:int,'thumbnails'?:array<string>}|null
|
||||
*/
|
||||
public function thumbnail(bool $searchEnclosures = true): ?array {
|
||||
$thumbnail = $this->attributes('thumbnail');
|
||||
@@ -317,7 +326,11 @@ HTML;
|
||||
public function machineReadableDate(): string {
|
||||
return @date (DATE_ATOM, $this->date);
|
||||
}
|
||||
/** @return int|string */
|
||||
|
||||
/**
|
||||
* @phpstan-return ($raw is false ? string : ($microsecond is true ? string : int))
|
||||
* @return int|string
|
||||
*/
|
||||
public function dateAdded(bool $raw = false, bool $microsecond = false) {
|
||||
if ($raw) {
|
||||
if ($microsecond) {
|
||||
@@ -437,10 +450,10 @@ HTML;
|
||||
if (!is_array($value)) {
|
||||
if (strpos($value, ';') !== false) {
|
||||
$value = htmlspecialchars_decode($value, ENT_QUOTES);
|
||||
$value = preg_split('/\s*[;]\s*/', $value, -1, PREG_SPLIT_NO_EMPTY) ?: '';
|
||||
$value = preg_split('/\s*[;]\s*/', $value, -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
||||
$value = Minz_Helper::htmlspecialchars_utf8($value);
|
||||
} else {
|
||||
$value = preg_split('/\s*[,]\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$value = preg_split('/\s*[,]\s*/', $value, -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
||||
}
|
||||
}
|
||||
$this->authors = $value;
|
||||
@@ -462,15 +475,17 @@ HTML;
|
||||
/** @param int|string $value */
|
||||
public function _dateAdded($value, bool $microsecond = false): void {
|
||||
if ($microsecond) {
|
||||
$this->date_added = $value;
|
||||
$this->date_added = (string)($value);
|
||||
} else {
|
||||
$this->date_added = $value . '000000';
|
||||
}
|
||||
}
|
||||
public function _isRead(?bool $value): void {
|
||||
/** @param bool|int|null $value */
|
||||
public function _isRead($value): void {
|
||||
$this->is_read = $value === null ? null : (bool)$value;
|
||||
}
|
||||
public function _isFavorite(?bool $value): void {
|
||||
/** @param bool|int|null $value */
|
||||
public function _isFavorite($value): void {
|
||||
$this->is_favorite = $value === null ? null : (bool)$value;
|
||||
}
|
||||
|
||||
@@ -702,7 +717,7 @@ HTML;
|
||||
if ($nodes != false) {
|
||||
foreach ($nodes as $node) {
|
||||
if (!empty($attributes['path_entries_filter'])) {
|
||||
$filterednodes = $xpath->query(new Gt\CssXPath\Translator($attributes['path_entries_filter']), $node);
|
||||
$filterednodes = $xpath->query(new Gt\CssXPath\Translator($attributes['path_entries_filter']), $node) ?: [];
|
||||
foreach ($filterednodes as $filterednode) {
|
||||
$filterednode->parentNode->removeChild($filterednode);
|
||||
}
|
||||
@@ -790,7 +805,7 @@ HTML;
|
||||
private static function dec2hex($dec): string {
|
||||
return PHP_INT_SIZE < 8 ? // 32-bit ?
|
||||
str_pad(gmp_strval(gmp_init($dec, 10), 16), 16, '0', STR_PAD_LEFT) :
|
||||
str_pad(dechex($dec), 16, '0', STR_PAD_LEFT);
|
||||
str_pad(dechex((int)($dec)), 16, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -185,7 +185,7 @@ SQL;
|
||||
$this->addEntryPrepared = null;
|
||||
return $this->addEntry($valuesTmp);
|
||||
} elseif ((int)((int)$info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries
|
||||
Minz_Log::error('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)
|
||||
. ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title']);
|
||||
}
|
||||
return false;
|
||||
@@ -295,7 +295,7 @@ SQL;
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->updateEntry($valuesTmp);
|
||||
}
|
||||
Minz_Log::error('SQL error updateEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)
|
||||
. ' while updating entry with GUID ' . $valuesTmp['guid'] . ' in feed ' . $valuesTmp['id_feed']);
|
||||
return false;
|
||||
}
|
||||
@@ -333,11 +333,11 @@ SQL;
|
||||
$values = array($is_favorite ? 1 : 0);
|
||||
$values = array_merge($values, $ids);
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markFavorite: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -350,15 +350,15 @@ SQL;
|
||||
* @todo It can use the query builder refactoring to build that query
|
||||
*/
|
||||
protected function updateCacheUnreads(?int $catId = null, ?int $feedId = null): bool {
|
||||
$sql = 'UPDATE `_feed` f '
|
||||
. 'LEFT OUTER JOIN ('
|
||||
. 'SELECT e.id_feed, '
|
||||
. 'COUNT(*) AS nbUnreads '
|
||||
. 'FROM `_entry` e '
|
||||
. 'WHERE e.is_read=0 '
|
||||
. 'GROUP BY e.id_feed'
|
||||
. ') x ON x.id_feed=f.id '
|
||||
. 'SET f.`cache_nbUnreads`=COALESCE(x.nbUnreads, 0)';
|
||||
$sql = <<<'SQL'
|
||||
UPDATE `_feed` f LEFT OUTER JOIN (
|
||||
SELECT e.id_feed, COUNT(*) AS nbUnreads
|
||||
FROM `_entry` e
|
||||
WHERE e.is_read = 0
|
||||
GROUP BY e.id_feed
|
||||
) x ON x.id_feed = f.id
|
||||
SET f.`cache_nbUnreads` = COALESCE(x.nbUnreads, 0)
|
||||
SQL;
|
||||
$hasWhere = false;
|
||||
$values = array();
|
||||
if ($feedId != null) {
|
||||
@@ -374,11 +374,11 @@ SQL;
|
||||
$values[] = $catId;
|
||||
}
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return true;
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error updateCacheUnreads: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -423,7 +423,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if (!($stm && $stm->execute($values))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markRead: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' A ' . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
$affected = $stm->rowCount();
|
||||
@@ -438,11 +438,11 @@ SQL;
|
||||
. 'WHERE e.id=? AND e.is_read=?';
|
||||
$values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1);
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markRead: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' B ' . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -490,7 +490,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql . $search);
|
||||
if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markReadEntries: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
$affected = $stm->rowCount();
|
||||
@@ -528,7 +528,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql . $search);
|
||||
if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markReadCat: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
$affected = $stm->rowCount();
|
||||
@@ -567,7 +567,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql . $search);
|
||||
if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markReadFeed: ' . $info[2] . ' with SQL: ' . $sql . $search);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info) . ' with SQL: ' . $sql . $search);
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
@@ -581,7 +581,7 @@ SQL;
|
||||
$stm->bindParam(':id', $id_feed, PDO::PARAM_INT);
|
||||
if (!($stm && $stm->execute())) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markReadFeed cache: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
@@ -622,7 +622,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql . $search);
|
||||
if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markReadTag: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
$affected = $stm->rowCount();
|
||||
@@ -684,7 +684,7 @@ SQL;
|
||||
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
|
||||
if ($stm && $stm->execute($params)) {
|
||||
if ($stm !== false && $stm->execute($params)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
@@ -718,39 +718,33 @@ SQL;
|
||||
}
|
||||
|
||||
public function searchByGuid(int $id_feed, string $guid): ?FreshRSS_Entry {
|
||||
// un guid est unique pour un flux donné
|
||||
$sql = 'SELECT id, guid, title, author, '
|
||||
. (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
||||
. ', link, date, is_read, is_favorite, id_feed, tags, attributes '
|
||||
. 'FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->bindParam(':id_feed', $id_feed, PDO::PARAM_INT);
|
||||
$stm->bindParam(':guid', $guid);
|
||||
$stm->execute();
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content';
|
||||
$sql = <<<SQL
|
||||
SELECT id, guid, title, author, link, date, is_read, is_favorite, id_feed, tags, attributes, {$content}
|
||||
FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid
|
||||
SQL;
|
||||
$res = $this->fetchAssoc($sql, [':id_feed' => $id_feed, ':guid' => $guid]);
|
||||
/** @var array<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}> $res */
|
||||
return isset($res[0]) ? FreshRSS_Entry::fromArray($res[0]) : null;
|
||||
}
|
||||
|
||||
public function searchById(string $id): ?FreshRSS_Entry {
|
||||
$sql = 'SELECT id, guid, title, author, '
|
||||
. (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
||||
. ', link, date, is_read, is_favorite, id_feed, tags, attributes '
|
||||
. 'FROM `_entry` WHERE id=:id';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stm->execute();
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content';
|
||||
$sql = <<<SQL
|
||||
SELECT id, guid, title, author, link, date, is_read, is_favorite, id_feed, tags, attributes, {$content}
|
||||
FROM `_entry` WHERE id=:id
|
||||
SQL;
|
||||
$res = $this->fetchAssoc($sql, [':id' => $id]);
|
||||
/** @var array<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}> $res */
|
||||
return isset($res[0]) ? FreshRSS_Entry::fromArray($res[0]) : null;
|
||||
}
|
||||
|
||||
public function searchIdByGuid(int $id_feed, string $guid): ?string {
|
||||
$sql = 'SELECT id FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->bindParam(':id_feed', $id_feed, PDO::PARAM_INT);
|
||||
$stm->bindParam(':guid', $guid);
|
||||
$stm->execute();
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
return isset($res[0]) ? $res[0] : null;
|
||||
$res = $this->fetchColumn($sql, 0, [':id_feed' => $id_feed, ':guid' => $guid]);
|
||||
return empty($res[0]) ? null : (string)($res[0]);
|
||||
}
|
||||
|
||||
/** @return array{0:array<int|string>,1:string} */
|
||||
@@ -1135,14 +1129,14 @@ SQL;
|
||||
. 'ORDER BY e0.id ' . $order;
|
||||
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm;
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->listWhereRaw($type, $id, $state, $order, $limit, $firstId, $filters, $date_min);
|
||||
}
|
||||
Minz_Log::error('SQL error listWhereRaw: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1157,6 +1151,8 @@ SQL;
|
||||
$stm = $this->listWhereRaw($type, $id, $state, $order, $limit, $firstId, $filters, $date_min);
|
||||
if ($stm) {
|
||||
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 */
|
||||
yield FreshRSS_Entry::fromArray($row);
|
||||
}
|
||||
}
|
||||
@@ -1180,17 +1176,24 @@ SQL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($order !== 'DESC' && $order !== 'ASC') {
|
||||
$order = 'DESC';
|
||||
}
|
||||
|
||||
$sql = 'SELECT id, guid, title, author, '
|
||||
. (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
||||
. ', link, date, is_read, is_favorite, id_feed, tags, attributes '
|
||||
. 'FROM `_entry` '
|
||||
. 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?) '
|
||||
. 'ORDER BY id ' . $order;
|
||||
$content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content';
|
||||
$repeats = str_repeat('?,', count($ids) - 1) . '?';
|
||||
$sql = <<<SQL
|
||||
SELECT id, guid, title, author, link, date, is_read, is_favorite, id_feed, tags, attributes, {$content}
|
||||
FROM `_entry`
|
||||
WHERE id IN ({$repeats})
|
||||
ORDER BY id {$order}
|
||||
SQL;
|
||||
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->execute($ids);
|
||||
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 */
|
||||
yield FreshRSS_Entry::fromArray($row);
|
||||
}
|
||||
}
|
||||
@@ -1198,16 +1201,12 @@ SQL;
|
||||
/**
|
||||
* @phpstan-param 'a'|'A'|'s'|'S'|'c'|'f'|'t'|'T'|'ST' $type
|
||||
* @param int $id category/feed/tag ID
|
||||
* @return array<numeric-string>|false
|
||||
* @return array<numeric-string>
|
||||
*/
|
||||
public function listIdsWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL,
|
||||
string $order = 'DESC', int $limit = 1, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null) {
|
||||
string $order = 'DESC', int $limit = 1, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null): ?array {
|
||||
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters);
|
||||
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->execute($values);
|
||||
|
||||
return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
return $this->fetchColumn($sql, 0, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1232,7 +1231,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$values = array($id_feed);
|
||||
$values = array_merge($values, $guids);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
$rows = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($rows as $row) {
|
||||
$result[$row['guid']] = $row['hex_hash'];
|
||||
@@ -1243,7 +1242,7 @@ SQL;
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->listHashForFeedGuids($id_feed, $guids);
|
||||
}
|
||||
Minz_Log::error('SQL error listHashForFeedGuids: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)
|
||||
. ' while querying feed ' . $id_feed);
|
||||
return false;
|
||||
}
|
||||
@@ -1272,14 +1271,14 @@ SQL;
|
||||
}
|
||||
$values = array($mtime, $id_feed);
|
||||
$values = array_merge($values, $guids);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->updateLastSeen($id_feed, $guids);
|
||||
}
|
||||
Minz_Log::error('SQL error updateLastSeen: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)
|
||||
. ' while updating feed ' . $id_feed);
|
||||
return false;
|
||||
}
|
||||
@@ -1287,32 +1286,33 @@ SQL;
|
||||
|
||||
/** @return array<string,int>|false */
|
||||
public function countUnreadRead() {
|
||||
$sql = 'SELECT COUNT(e.id) AS count FROM `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id WHERE f.priority > 0'
|
||||
. ' UNION SELECT COUNT(e.id) AS count FROM `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id WHERE f.priority > 0 AND e.is_read=0';
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm === false) {
|
||||
$sql = <<<'SQL'
|
||||
SELECT COUNT(e.id) AS count FROM `_entry` e
|
||||
INNER JOIN `_feed` f ON e.id_feed=f.id
|
||||
WHERE f.priority > 0
|
||||
UNION
|
||||
SELECT COUNT(e.id) AS count FROM `_entry` e
|
||||
INNER JOIN `_feed` f ON e.id_feed=f.id
|
||||
WHERE f.priority > 0 AND e.is_read=0
|
||||
SQL;
|
||||
$res = $this->fetchColumn($sql, 0);
|
||||
if ($res == null) {
|
||||
return false;
|
||||
}
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
rsort($res);
|
||||
$all = empty($res[0]) ? 0 : (int)$res[0];
|
||||
$unread = empty($res[1]) ? 0 : (int)$res[1];
|
||||
return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
|
||||
$all = (int)($res[0] ?? 0);
|
||||
$unread = (int)($res[1] ?? 0);
|
||||
return ['all' => $all, 'unread' => $unread, 'read' => $all - $unread];
|
||||
}
|
||||
|
||||
/** @return int|false */
|
||||
public function count(?int $minPriority = null) {
|
||||
public function count(?int $minPriority = null): int {
|
||||
$sql = 'SELECT COUNT(e.id) AS count FROM `_entry` e';
|
||||
if ($minPriority !== null) {
|
||||
$sql .= ' INNER JOIN `_feed` f ON e.id_feed=f.id';
|
||||
$sql .= ' WHERE f.priority > ' . $minPriority;
|
||||
$sql .= ' WHERE f.priority > :priority';
|
||||
}
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm == false) {
|
||||
return false;
|
||||
}
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
return isset($res[0]) ? intval($res[0]) : 0;
|
||||
$res = $this->fetchColumn($sql, 0, [':priority' => $minPriority]);
|
||||
return isset($res[0]) ? (int)($res[0]) : -1;
|
||||
}
|
||||
|
||||
public function countNotRead(?int $minPriority = null): int {
|
||||
@@ -1322,11 +1322,10 @@ SQL;
|
||||
}
|
||||
$sql .= ' WHERE e.is_read=0';
|
||||
if ($minPriority !== null) {
|
||||
$sql .= ' AND f.priority > ' . $minPriority;
|
||||
$sql .= ' AND f.priority > :priority';
|
||||
}
|
||||
$stm = $this->pdo->query($sql);
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
return isset($res[0]) ? intval($res[0]) : 0;
|
||||
$res = $this->fetchColumn($sql, 0, [':priority' => $minPriority]);
|
||||
return isset($res[0]) ? (int)($res[0]) : -1;
|
||||
}
|
||||
|
||||
/** @return array<string,int>|false */
|
||||
@@ -1334,33 +1333,31 @@ SQL;
|
||||
$sql = <<<'SQL'
|
||||
SELECT c FROM (
|
||||
SELECT COUNT(e1.id) AS c, 1 AS o
|
||||
FROM `_entry` AS e1
|
||||
JOIN `_feed` AS f1 ON e1.id_feed = f1.id
|
||||
FROM `_entry` AS e1
|
||||
JOIN `_feed` AS f1 ON e1.id_feed = f1.id
|
||||
WHERE e1.is_favorite = 1
|
||||
AND f1.priority >= :priority_normal1
|
||||
AND f1.priority >= :priority_normal1
|
||||
UNION
|
||||
SELECT COUNT(e2.id) AS c, 2 AS o
|
||||
FROM `_entry` AS e2
|
||||
JOIN `_feed` AS f2 ON e2.id_feed = f2.id
|
||||
FROM `_entry` AS e2
|
||||
JOIN `_feed` AS f2 ON e2.id_feed = f2.id
|
||||
WHERE e2.is_favorite = 1
|
||||
AND e2.is_read = 0
|
||||
AND f2.priority >= :priority_normal2
|
||||
AND e2.is_read = 0 AND f2.priority >= :priority_normal2
|
||||
) u
|
||||
ORDER BY o
|
||||
SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if (!$stm) {
|
||||
Minz_Log::error('SQL error in ' . __method__ . ' ' . json_encode($this->pdo->errorInfo()));
|
||||
//Binding a value more than once is not standard and does not work with native prepared statements (e.g. MySQL) https://bugs.php.net/bug.php?id=40417
|
||||
$res = $this->fetchColumn($sql, 0, [
|
||||
':priority_normal1' => FreshRSS_Feed::PRIORITY_NORMAL,
|
||||
':priority_normal2' => FreshRSS_Feed::PRIORITY_NORMAL,
|
||||
]);
|
||||
if ($res == null) {
|
||||
return false;
|
||||
}
|
||||
//Binding a value more than once is not standard and does not work with native prepared statements (e.g. MySQL) https://bugs.php.net/bug.php?id=40417
|
||||
$stm->bindValue(':priority_normal1', FreshRSS_Feed::PRIORITY_NORMAL, PDO::PARAM_INT);
|
||||
$stm->bindValue(':priority_normal2', FreshRSS_Feed::PRIORITY_NORMAL, PDO::PARAM_INT);
|
||||
$stm->execute();
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
|
||||
rsort($res);
|
||||
$all = empty($res[0]) ? 0 : intval($res[0]);
|
||||
$unread = empty($res[1]) ? 0 : intval($res[1]);
|
||||
return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
|
||||
$all = (int)($res[0] ?? 0);
|
||||
$unread = (int)($res[1] ?? 0);
|
||||
return ['all' => $all, 'unread' => $unread, 'read' => $all - $unread];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
|
||||
}
|
||||
|
||||
public function commitNewEntries(): bool {
|
||||
$sql = '
|
||||
$sql = <<<'SQL'
|
||||
DROP TABLE IF EXISTS `tmp`;
|
||||
CREATE TEMP TABLE `tmp` AS
|
||||
SELECT id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags, attributes
|
||||
@@ -63,14 +63,14 @@ INSERT OR IGNORE INTO `_entry`
|
||||
ORDER BY date, id;
|
||||
DELETE FROM `_entrytmp` WHERE id <= (SELECT MAX(id) FROM `tmp`);
|
||||
DROP TABLE IF EXISTS `tmp`;
|
||||
';
|
||||
SQL;
|
||||
$hadTransaction = $this->pdo->inTransaction();
|
||||
if (!$hadTransaction) {
|
||||
$this->pdo->beginTransaction();
|
||||
}
|
||||
$result = $this->pdo->exec($sql) !== false;
|
||||
if (!$result) {
|
||||
Minz_Log::error('SQL error commitNewEntries: ' . json_encode($this->pdo->errorInfo()));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($this->pdo->errorInfo()));
|
||||
}
|
||||
if (!$hadTransaction) {
|
||||
$this->pdo->commit();
|
||||
@@ -79,10 +79,12 @@ DROP TABLE IF EXISTS `tmp`;
|
||||
}
|
||||
|
||||
protected function updateCacheUnreads(?int $catId = null, ?int $feedId = null): bool {
|
||||
$sql = 'UPDATE `_feed` '
|
||||
. 'SET `cache_nbUnreads`=('
|
||||
. 'SELECT COUNT(*) AS nbUnreads FROM `_entry` e '
|
||||
. 'WHERE e.id_feed=`_feed`.id AND e.is_read=0)';
|
||||
$sql = <<<'SQL'
|
||||
UPDATE `_feed`
|
||||
SET `cache_nbUnreads`=(
|
||||
SELECT COUNT(*) AS nbUnreads FROM `_entry` e
|
||||
WHERE e.id_feed=`_feed`.id AND e.is_read=0)
|
||||
SQL;
|
||||
$hasWhere = false;
|
||||
$values = array();
|
||||
if ($feedId != null) {
|
||||
@@ -98,11 +100,11 @@ DROP TABLE IF EXISTS `tmp`;
|
||||
$values[] = $catId;
|
||||
}
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return true;
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error updateCacheUnreads: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +139,7 @@ DROP TABLE IF EXISTS `tmp`;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if (!($stm && $stm->execute($values))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markRead 1: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' A ' . json_encode($info));
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
@@ -149,7 +151,7 @@ DROP TABLE IF EXISTS `tmp`;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if (!($stm && $stm->execute($values))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markRead 2: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' B ' . json_encode($info));
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
@@ -201,7 +203,7 @@ DROP TABLE IF EXISTS `tmp`;
|
||||
$stm = $this->pdo->prepare($sql . $search);
|
||||
if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markReadEntries: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
$affected = $stm->rowCount();
|
||||
@@ -240,7 +242,7 @@ DROP TABLE IF EXISTS `tmp`;
|
||||
$stm = $this->pdo->prepare($sql . $search);
|
||||
if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markReadCat: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
$affected = $stm->rowCount();
|
||||
@@ -279,7 +281,7 @@ DROP TABLE IF EXISTS `tmp`;
|
||||
$stm = $this->pdo->prepare($sql . $search);
|
||||
if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markReadTag: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
$affected = $stm->rowCount();
|
||||
|
||||
@@ -68,14 +68,14 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
|
||||
is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES),
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return (int)($this->pdo->lastInsertId('`_feed_id_seq`'));
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->addFeed($valuesTmp);
|
||||
}
|
||||
Minz_Log::error('SQL error addFeed: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -171,14 +171,14 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
|
||||
}
|
||||
$values[] = $id;
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->updateFeed($id, $valuesTmp);
|
||||
}
|
||||
Minz_Log::error('SQL error updateFeed: ' . $info[2] . ' for feed ' . $id);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info) . ' for feed ' . $id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
|
||||
);
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
@@ -239,11 +239,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
|
||||
$idOldCat
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error changeCategory: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -255,11 +255,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
|
||||
|
||||
$values = array($id);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error deleteFeed: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -277,11 +277,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
|
||||
|
||||
$values = array($id);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error deleteFeedByCategory: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -301,10 +301,10 @@ SQL;
|
||||
|
||||
public function searchById(int $id): ?FreshRSS_Feed {
|
||||
$sql = 'SELECT * FROM `_feed` WHERE id=:id';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stm->execute();
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$res = $this->fetchAssoc($sql, [':id' => $id]);
|
||||
if ($res == null) {
|
||||
return null;
|
||||
}
|
||||
$feed = self::daoToFeed($res);
|
||||
return $feed[$id] ?? null;
|
||||
}
|
||||
@@ -375,7 +375,7 @@ SQL;
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->listFeedsOrderUpdate($defaultCacheDuration, $limit);
|
||||
}
|
||||
Minz_Log::error('SQL error listFeedsOrderUpdate: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return array();
|
||||
}
|
||||
}
|
||||
@@ -388,7 +388,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->bindParam(':id_feed', $id, PDO::PARAM_INT);
|
||||
|
||||
if ($stm && $stm->execute()) {
|
||||
if ($stm !== false && $stm->execute()) {
|
||||
return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
}
|
||||
return false;
|
||||
@@ -446,15 +446,15 @@ SQL;
|
||||
. '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `_entry` e2 WHERE e2.id_feed=`_feed`.id AND e2.is_read=0)'
|
||||
. ($id != 0 ? ' WHERE id=:id' : '');
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm && $id != 0) {
|
||||
if ($stm !== false && $id != 0) {
|
||||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
if ($stm && $stm->execute()) {
|
||||
if ($stm !== false && $stm->execute()) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error updateCachedValue: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -483,7 +483,7 @@ SQL;
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error keepMaxUnread: ' . json_encode($info));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -514,7 +514,7 @@ SQL;
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error markAsReadUponGone: ' . json_encode($info));
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -529,7 +529,7 @@ SQL;
|
||||
$this->pdo->beginTransaction();
|
||||
if (!($stm && $stm->execute())) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error truncate: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
@@ -541,7 +541,7 @@ SQL;
|
||||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
if (!($stm && $stm->execute())) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error truncate: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
@@ -556,7 +556,7 @@ SQL;
|
||||
$this->pdo->beginTransaction();
|
||||
if (!($stm && $stm->execute())) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error truncate: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' A ' . json_encode($info));
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
@@ -565,7 +565,7 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if (!($stm && $stm->execute())) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error truncate: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' B ' . json_encode($info));
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
@@ -575,7 +575,7 @@ SQL;
|
||||
|
||||
/**
|
||||
* @param array<int,array<string,string|int>>|array<string,string|int> $listDAO
|
||||
* @return array<FreshRSS_Feed>
|
||||
* @return array<int,FreshRSS_Feed>
|
||||
*/
|
||||
public static function daoToFeed(array $listDAO, ?int $catID = null): array {
|
||||
$list = array();
|
||||
@@ -626,13 +626,13 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if (!($stm && $stm->execute(array(':new_value' => FreshRSS_Feed::TTL_DEFAULT, ':old_value' => -2)))) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL warning updateTTL 1: ' . $info[2] . ' ' . $sql);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' A ' . json_encode($info));
|
||||
|
||||
$sql2 = 'ALTER TABLE `_feed` ADD COLUMN ttl INT NOT NULL DEFAULT ' . FreshRSS_Feed::TTL_DEFAULT; //v0.7.3
|
||||
$stm = $this->pdo->query($sql2);
|
||||
if ($stm === false) {
|
||||
$info = $this->pdo->errorInfo();
|
||||
Minz_Log::error('SQL error updateTTL 2: ' . $info[2] . ' ' . $sql2);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' B ' . json_encode($info));
|
||||
}
|
||||
} else {
|
||||
$stm->execute(array(':new_value' => -3600, ':old_value' => -1));
|
||||
|
||||
@@ -11,7 +11,7 @@ class FreshRSS_StatsDAO extends Minz_ModelPdo {
|
||||
/**
|
||||
* Calculates entry repartition for all feeds and for main stream.
|
||||
*
|
||||
* @return array{'main_stream':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int},'all_feeds':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}}
|
||||
* @return array{'main_stream':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false,'all_feeds':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false}
|
||||
*/
|
||||
public function calculateEntryRepartition(): array {
|
||||
return array(
|
||||
@@ -28,9 +28,9 @@ class FreshRSS_StatsDAO extends Minz_ModelPdo {
|
||||
* - unread entries
|
||||
* - favorite entries
|
||||
*
|
||||
* @return array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}
|
||||
* @return array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false
|
||||
*/
|
||||
public function calculateEntryRepartitionPerFeed(?int $feed = null, bool $only_main = false): array {
|
||||
public function calculateEntryRepartitionPerFeed(?int $feed = null, bool $only_main = false) {
|
||||
$filter = '';
|
||||
if ($only_main) {
|
||||
$filter .= 'AND f.priority = 10';
|
||||
@@ -47,10 +47,9 @@ FROM `_entry` AS e, `_feed` AS f
|
||||
WHERE e.id_feed = f.id
|
||||
{$filter}
|
||||
SQL;
|
||||
$stm = $this->pdo->query($sql);
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return $res[0];
|
||||
$res = $this->fetchAssoc($sql);
|
||||
/** @var array<array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}>|null $res */
|
||||
return $res[0] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,14 +71,14 @@ WHERE date >= {$oldest} AND date < {$midnight}
|
||||
GROUP BY day
|
||||
ORDER BY day ASC
|
||||
SQL;
|
||||
$stm = $this->pdo->query($sql);
|
||||
/** @var array<array{'day':int,'count':int}> */
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($res as $value) {
|
||||
$count[(int)($value['day'])] = (int) $value['count'];
|
||||
$res = $this->fetchAssoc($sql);
|
||||
if ($res == false) {
|
||||
return [];
|
||||
}
|
||||
/** @var array<array{'day':int,'count':int}> $res */
|
||||
foreach ($res as $value) {
|
||||
$count[(int)($value['day'])] = (int)($value['count']);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
@@ -138,9 +137,10 @@ GROUP BY period
|
||||
ORDER BY period ASC
|
||||
SQL;
|
||||
|
||||
$stm = $this->pdo->query($sql);
|
||||
$res = $stm->fetchAll(PDO::FETCH_NAMED);
|
||||
|
||||
$res = $this->fetchAssoc($sql);
|
||||
if ($res == false) {
|
||||
return [];
|
||||
}
|
||||
switch ($period) {
|
||||
case '%H':
|
||||
$periodMax = 24;
|
||||
@@ -152,12 +152,12 @@ SQL;
|
||||
$periodMax = 12;
|
||||
break;
|
||||
default:
|
||||
$periodMax = 30;
|
||||
$periodMax = 30;
|
||||
}
|
||||
|
||||
$repartition = array_fill(0, $periodMax, 0);
|
||||
foreach ($res as $value) {
|
||||
$repartition[(int) $value['period']] = (int) $value['count'];
|
||||
$repartition[(int)$value['period']] = (int)$value['count'];
|
||||
}
|
||||
|
||||
return $repartition;
|
||||
@@ -200,12 +200,14 @@ SELECT COUNT(1) AS count
|
||||
FROM `_entry` AS e
|
||||
{$restrict}
|
||||
SQL;
|
||||
$stm = $this->pdo->query($sql);
|
||||
$res = $stm->fetch(PDO::FETCH_NAMED);
|
||||
$res = $this->fetchAssoc($sql);
|
||||
if ($res == null || empty($res[0])) {
|
||||
return -1.0;
|
||||
}
|
||||
$date_min = new \DateTime();
|
||||
$date_min->setTimestamp($res['date_min']);
|
||||
$date_min->setTimestamp((int)($res[0]['date_min']));
|
||||
$date_max = new \DateTime();
|
||||
$date_max->setTimestamp($res['date_max']);
|
||||
$date_max->setTimestamp((int)($res[0]['date_max']));
|
||||
$interval = $date_max->diff($date_min, true);
|
||||
$interval_in_days = (float)($interval->format('%a'));
|
||||
if ($interval_in_days <= 0) {
|
||||
@@ -214,7 +216,7 @@ SQL;
|
||||
$interval_in_days = $period;
|
||||
}
|
||||
|
||||
return intval($res['count']) / ($interval_in_days / $period);
|
||||
return intval($res[0]['count']) / ($interval_in_days / $period);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,10 +242,9 @@ WHERE c.id = f.category
|
||||
GROUP BY label
|
||||
ORDER BY data DESC
|
||||
SQL;
|
||||
$stm = $this->pdo->query($sql);
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return $res;
|
||||
$res = $this->fetchAssoc($sql);
|
||||
/** @var array<array{'label':string,'data':int}>|null @res */
|
||||
return $res == null ? [] : $res;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,10 +261,9 @@ AND f.id = e.id_feed
|
||||
GROUP BY label
|
||||
ORDER BY data DESC
|
||||
SQL;
|
||||
$stm = $this->pdo->query($sql);
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return $res;
|
||||
$res = $this->fetchAssoc($sql);
|
||||
/** @var array<array{'label':string,'data':int}>|null $res */
|
||||
return $res == null ? [] : $res;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,8 +283,9 @@ GROUP BY f.id
|
||||
ORDER BY count DESC
|
||||
LIMIT 10
|
||||
SQL;
|
||||
$stm = $this->pdo->query($sql);
|
||||
return $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$res = $this->fetchAssoc($sql);
|
||||
/** @var array<array{'id':int,'name':string,'category':string,'count':int}>|null $res */
|
||||
return $res == null ? [] : $res;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,8 +303,9 @@ WHERE f.id = e.id_feed
|
||||
GROUP BY f.id
|
||||
ORDER BY name
|
||||
SQL;
|
||||
$stm = $this->pdo->query($sql);
|
||||
return $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$res = $this->fetchAssoc($sql);
|
||||
/** @var array<array{'id':int,'name':string,'last_date':int,'nb_articles':int}>|null $res */
|
||||
return $res == null ? [] : $res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,11 +47,10 @@ GROUP BY period
|
||||
ORDER BY period ASC
|
||||
SQL;
|
||||
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm === false) {
|
||||
$res = $this->fetchAssoc($sql);
|
||||
if ($res == null) {
|
||||
return [];
|
||||
}
|
||||
$res = $stm->fetchAll(PDO::FETCH_NAMED);
|
||||
|
||||
switch ($period) {
|
||||
case 'hour':
|
||||
@@ -69,7 +68,7 @@ SQL;
|
||||
|
||||
$repartition = array_fill(0, $periodMax, 0);
|
||||
foreach ($res as $value) {
|
||||
$repartition[(int) $value['period']] = (int) $value['count'];
|
||||
$repartition[(int)$value['period']] = (int)$value['count'];
|
||||
}
|
||||
|
||||
return $repartition;
|
||||
|
||||
@@ -24,11 +24,10 @@ GROUP BY period
|
||||
ORDER BY period ASC
|
||||
SQL;
|
||||
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm === false) {
|
||||
$res = $this->fetchAssoc($sql);
|
||||
if ($res == null) {
|
||||
return [];
|
||||
}
|
||||
$res = $stm->fetchAll(PDO::FETCH_NAMED);
|
||||
|
||||
switch ($period) {
|
||||
case '%H':
|
||||
@@ -46,7 +45,7 @@ SQL;
|
||||
|
||||
$repartition = array_fill(0, $periodMax, 0);
|
||||
foreach ($res as $value) {
|
||||
$repartition[(int) $value['period']] = (int) $value['count'];
|
||||
$repartition[(int)$value['period']] = (int)$value['count'];
|
||||
}
|
||||
|
||||
return $repartition;
|
||||
|
||||
@@ -43,7 +43,7 @@ class FreshRSS_TagDAO extends Minz_ModelPdo {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string|array<string,mixed>> $valuesTmp
|
||||
* @param array{'id'?:int,'name':string,'attributes'?:array<string,mixed>} $valuesTmp
|
||||
* @return int|false
|
||||
*/
|
||||
public function addTag(array $valuesTmp) {
|
||||
@@ -66,16 +66,17 @@ SQL;
|
||||
$valuesTmp['name'],
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values) && $stm->rowCount() > 0) {
|
||||
if ($stm !== false && $stm->execute($values) && $stm->rowCount() > 0) {
|
||||
return (int)($this->pdo->lastInsertId('`_tag_id_seq`'));
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error addTag: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function addTagObject(FreshRSS_Tag $tag): int {
|
||||
/** @return int|false */
|
||||
public function addTagObject(FreshRSS_Tag $tag) {
|
||||
$tag0 = $this->searchByName($tag->name());
|
||||
if (!$tag0) {
|
||||
$values = array(
|
||||
@@ -87,49 +88,53 @@ SQL;
|
||||
return $tag->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string|int|array<string,mixed>> $valuesTmp
|
||||
* @return int|false
|
||||
*/
|
||||
public function updateTag(int $id, array $valuesTmp) {
|
||||
/** @return int|false */
|
||||
public function updateTagName(int $id, string $name) {
|
||||
// No category of the same name
|
||||
$sql = <<<'SQL'
|
||||
UPDATE `_tag` SET name=?, attributes=? WHERE id=?
|
||||
UPDATE `_tag` SET name=? WHERE id=?
|
||||
AND NOT EXISTS (SELECT 1 FROM `_category` WHERE name = ?)
|
||||
SQL;
|
||||
|
||||
$name = mb_strcut(trim($name), 0, 63, 'UTF-8');
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
|
||||
$valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, 63, 'UTF-8');
|
||||
if (!isset($valuesTmp['attributes'])) {
|
||||
$valuesTmp['attributes'] = [];
|
||||
}
|
||||
$values = array(
|
||||
$valuesTmp['name'],
|
||||
is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES),
|
||||
$id,
|
||||
$valuesTmp['name'],
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false &&
|
||||
$stm->bindValue(':id', $id, PDO::PARAM_INT) &&
|
||||
$stm->bindValue(':name', $name, PDO::PARAM_STR) &&
|
||||
$stm->execute()) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error updateTag: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $attributes
|
||||
* @return int|false
|
||||
*/
|
||||
public function updateTagAttributes(int $id, array $attributes) {
|
||||
$sql = 'UPDATE `_tag` SET attributes=:attributes WHERE id=:id';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm !== false &&
|
||||
$stm->bindValue(':id', $id, PDO::PARAM_INT) &&
|
||||
$stm->bindValue(':attributes', json_encode($attributes, JSON_UNESCAPED_SLASHES), PDO::PARAM_STR) &&
|
||||
$stm->execute()) {
|
||||
return $stm->rowCount();
|
||||
}
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return int|false
|
||||
*/
|
||||
public function updateTagAttribute(FreshRSS_Tag $tag, string $key, $value) {
|
||||
$tag->_attributes($key, $value);
|
||||
return $this->updateTag(
|
||||
$tag->id(),
|
||||
[ 'attributes' => $tag->attributes() ]
|
||||
);
|
||||
return $this->updateTagAttributes($tag->id(), $tag->attributes());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,19 +149,23 @@ SQL;
|
||||
|
||||
$values = array($id);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error deleteTag: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Traversable<array{'id':int,'name':string,'attributes':string}> */
|
||||
/** @return Traversable<array{'id':int,'name':string,'attributes'?:array<string,mixed>}> */
|
||||
public function selectAll(): Traversable {
|
||||
$sql = 'SELECT id, name, attributes FROM `_tag`';
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm === false) {
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($this->pdo->errorInfo()));
|
||||
return;
|
||||
}
|
||||
while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
|
||||
yield $row;
|
||||
}
|
||||
@@ -166,6 +175,10 @@ SQL;
|
||||
public function selectEntryTag(): Traversable {
|
||||
$sql = 'SELECT id_tag, id_entry FROM `_entrytag`';
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm === false) {
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($this->pdo->errorInfo()));
|
||||
return;
|
||||
}
|
||||
while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
|
||||
yield $row;
|
||||
}
|
||||
@@ -180,53 +193,44 @@ DELETE FROM `_entrytag` WHERE EXISTS (
|
||||
SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
|
||||
if (!($stm && $stm->execute([$newTagId, $oldTagId]))) {
|
||||
if ($stm === false || !$stm->execute([$newTagId, $oldTagId])) {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error moveTag: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' A ' . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql = 'UPDATE `_entrytag` SET id_tag = ? WHERE id_tag = ?';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
|
||||
if ($stm && $stm->execute([$newTagId, $oldTagId])) {
|
||||
if ($stm !== false && $stm->execute([$newTagId, $oldTagId])) {
|
||||
return $stm->rowCount();
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error moveTag: ' . $info[2]);
|
||||
return false;
|
||||
}
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . ' B ' . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
|
||||
public function searchById(int $id): ?FreshRSS_Tag {
|
||||
$sql = 'SELECT * FROM `_tag` WHERE id=?';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$values = array($id);
|
||||
$stm->execute($values);
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$tag = self::daoToTag($res);
|
||||
return isset($tag[0]) ? $tag[0] : null;
|
||||
$res = $this->fetchAssoc('SELECT * FROM `_tag` WHERE id=:id', [':id' => $id]);
|
||||
return $res === null ? null : self::daoToTag($res)[0] ?? null;
|
||||
}
|
||||
|
||||
public function searchByName(string $name): ?FreshRSS_Tag {
|
||||
$sql = 'SELECT * FROM `_tag` WHERE name=?';
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$values = array($name);
|
||||
$stm->execute($values);
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$tag = self::daoToTag($res);
|
||||
return isset($tag[0]) ? $tag[0] : null;
|
||||
$res = $this->fetchAssoc('SELECT * FROM `_tag` WHERE name=:name', [':name' => $name]);
|
||||
return $res === null ? null : self::daoToTag($res)[0] ?? null;
|
||||
}
|
||||
|
||||
/** @return array<FreshRSS_Tag>|false */
|
||||
public function listTags(bool $precounts = false) {
|
||||
if ($precounts) {
|
||||
$sql = 'SELECT t.id, t.name, count(e.id) AS unreads '
|
||||
. 'FROM `_tag` t '
|
||||
. 'LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id '
|
||||
. 'LEFT OUTER JOIN `_entry` e ON et.id_entry = e.id AND e.is_read = 0 '
|
||||
. 'GROUP BY t.id '
|
||||
. 'ORDER BY t.name';
|
||||
$sql = <<<'SQL'
|
||||
SELECT t.id, t.name, count(e.id) AS unreads
|
||||
FROM `_tag` t
|
||||
LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id
|
||||
LEFT OUTER JOIN `_entry` e ON et.id_entry = e.id AND e.is_read = 0
|
||||
GROUP BY t.id
|
||||
ORDER BY t.name
|
||||
SQL;
|
||||
} else {
|
||||
$sql = 'SELECT * FROM `_tag` ORDER BY name';
|
||||
}
|
||||
@@ -239,27 +243,31 @@ SQL;
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->listTags($precounts);
|
||||
}
|
||||
Minz_Log::error('SQL error listTags: ' . $info[2]);
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<string,string> */
|
||||
public function listTagsNewestItemUsec(?int $id_tag = null): array {
|
||||
$sql = 'SELECT t.id AS id_tag, MAX(e.id) AS newest_item_us '
|
||||
. 'FROM `_tag` t '
|
||||
. 'LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id '
|
||||
. 'LEFT OUTER JOIN `_entry` e ON et.id_entry = e.id ';
|
||||
$sql = <<<'SQL'
|
||||
SELECT t.id AS id_tag, MAX(e.id) AS newest_item_us
|
||||
FROM `_tag` t
|
||||
LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id
|
||||
LEFT OUTER JOIN `_entry` e ON et.id_entry = e.id
|
||||
SQL;
|
||||
if ($id_tag === null) {
|
||||
$sql .= 'GROUP BY t.id';
|
||||
$sql .= ' GROUP BY t.id';
|
||||
} else {
|
||||
$sql .= 'WHERE t.id=' . intval($id_tag);
|
||||
$sql .= ' WHERE t.id=' . intval($id_tag);
|
||||
}
|
||||
$res = $this->fetchAssoc($sql);
|
||||
if ($res == null) {
|
||||
return [];
|
||||
}
|
||||
$stm = $this->pdo->query($sql);
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
$newestItemUsec = [];
|
||||
foreach ($res as $line) {
|
||||
$newestItemUsec['t_' . $line['id_tag']] = $line['newest_item_us'];
|
||||
$newestItemUsec['t_' . $line['id_tag']] = (string)($line['newest_item_us']);
|
||||
}
|
||||
return $newestItemUsec;
|
||||
}
|
||||
@@ -271,56 +279,47 @@ SQL;
|
||||
if ($stm !== false) {
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
return (int)$res[0]['count'];
|
||||
} else {
|
||||
$info = $this->pdo->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->count();
|
||||
}
|
||||
Minz_Log::error('SQL error TagDAO::count: ' . $info[2]);
|
||||
return false;
|
||||
}
|
||||
$info = $this->pdo->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->count();
|
||||
}
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|false
|
||||
*/
|
||||
public function countEntries(int $id) {
|
||||
$sql = 'SELECT COUNT(*) AS count FROM `_entrytag` WHERE id_tag=?';
|
||||
$values = array($id);
|
||||
if (($stm = $this->pdo->prepare($sql)) !== false &&
|
||||
$stm->execute($values) &&
|
||||
($res = $stm->fetchAll(PDO::FETCH_ASSOC)) !== false) {
|
||||
return (int)$res[0]['count'];
|
||||
} else {
|
||||
$info = is_object($stm) ? $stm->errorInfo() : $this->pdo->errorInfo();
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
$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 (int)$res[0]['count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|false
|
||||
*/
|
||||
public function countNotRead(?int $id = null) {
|
||||
$sql = 'SELECT COUNT(*) AS count FROM `_entrytag` et '
|
||||
. 'INNER JOIN `_entry` e ON et.id_entry=e.id '
|
||||
. 'WHERE e.is_read=0';
|
||||
$values = null;
|
||||
|
||||
$sql = <<<'SQL'
|
||||
SELECT COUNT(*) AS count FROM `_entrytag` et
|
||||
INNER JOIN `_entry` e ON et.id_entry=e.id
|
||||
WHERE e.is_read=0
|
||||
SQL;
|
||||
$values = [];
|
||||
if (null !== $id) {
|
||||
$sql .= ' AND et.id_tag=?';
|
||||
$values = [$id];
|
||||
$sql .= ' AND et.id_tag=:id_tag';
|
||||
$values[':id_tag'] = $id;
|
||||
}
|
||||
|
||||
if (($stm = $this->pdo->prepare($sql)) !== false &&
|
||||
$stm->execute($values) &&
|
||||
($res = $stm->fetchAll(PDO::FETCH_ASSOC)) !== false) {
|
||||
return (int)$res[0]['count'];
|
||||
} else {
|
||||
$info = is_object($stm) ? $stm->errorInfo() : $this->pdo->errorInfo();
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
$res = $this->fetchAssoc($sql, $values);
|
||||
if ($res == null || !isset($res[0]['count'])) {
|
||||
return false;
|
||||
}
|
||||
return (int)$res[0]['count'];
|
||||
}
|
||||
|
||||
public function tagEntry(int $id_tag, string $id_entry, bool $checked = true): bool {
|
||||
@@ -332,42 +331,42 @@ SQL;
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$values = array($id_tag, $id_entry);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return true;
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error tagEntry: ' . $info[2]);
|
||||
return false;
|
||||
}
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,array{'id':int,'name':string,'id_entry':string,'checked':bool}>|false
|
||||
*/
|
||||
public function getTagsForEntry(string $id_entry) {
|
||||
$sql = 'SELECT t.id, t.name, et.id_entry IS NOT NULL as checked '
|
||||
. 'FROM `_tag` t '
|
||||
. 'LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id AND et.id_entry=? '
|
||||
. 'ORDER BY t.name';
|
||||
$sql = <<<'SQL'
|
||||
SELECT t.id, t.name, et.id_entry IS NOT NULL as checked
|
||||
FROM `_tag` t
|
||||
LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id AND et.id_entry=?
|
||||
ORDER BY t.name
|
||||
SQL;
|
||||
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$values = array($id_entry);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
$lines = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
for ($i = count($lines) - 1; $i >= 0; $i--) {
|
||||
$lines[$i]['id'] = intval($lines[$i]['id']);
|
||||
$lines[$i]['checked'] = !empty($lines[$i]['checked']);
|
||||
}
|
||||
return $lines;
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->getTagsForEntry($id_entry);
|
||||
}
|
||||
Minz_Log::error('SQL error getTagsForEntry: ' . $info[2]);
|
||||
return false;
|
||||
}
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->getTagsForEntry($id_entry);
|
||||
}
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,9 +374,11 @@ SQL;
|
||||
* @return array<array{'id_entry':string,'id_tag':int,'name':string}>|false
|
||||
*/
|
||||
public function getTagsForEntries(array $entries) {
|
||||
$sql = 'SELECT et.id_entry, et.id_tag, t.name '
|
||||
. 'FROM `_tag` t '
|
||||
. 'INNER JOIN `_entrytag` et ON et.id_tag = t.id';
|
||||
$sql = <<<'SQL'
|
||||
SELECT et.id_entry, et.id_tag, t.name
|
||||
FROM `_tag` t
|
||||
INNER JOIN `_entrytag` et ON et.id_tag = t.id
|
||||
SQL;
|
||||
|
||||
$values = array();
|
||||
if (is_array($entries) && count($entries) > 0) {
|
||||
@@ -392,9 +393,12 @@ SQL;
|
||||
$sql .= ' AND et.id_entry IN (' . str_repeat('?,', count($entries) - 1). '?)';
|
||||
if (is_array($entries[0])) {
|
||||
foreach ($entries as $entry) {
|
||||
$values[] = $entry['id'];
|
||||
if (!empty($entry['id'])) {
|
||||
$values[] = $entry['id'];
|
||||
}
|
||||
}
|
||||
} elseif (is_object($entries[0])) {
|
||||
/** @var array<FreshRSS_Entry> $entries */
|
||||
foreach ($entries as $entry) {
|
||||
$values[] = $entry->id();
|
||||
}
|
||||
@@ -406,16 +410,15 @@ SQL;
|
||||
}
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
return $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->getTagsForEntries($entries);
|
||||
}
|
||||
Minz_Log::error('SQL error getTagsForEntries: ' . $info[2]);
|
||||
return false;
|
||||
}
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->getTagsForEntries($entries);
|
||||
}
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -425,7 +428,7 @@ SQL;
|
||||
*/
|
||||
public function getEntryIdsTagNames(array $entries): array {
|
||||
$result = array();
|
||||
foreach ($this->getTagsForEntries($entries) as $line) {
|
||||
foreach ($this->getTagsForEntries($entries) ?: [] as $line) {
|
||||
$entryId = 'e_' . $line['id_entry'];
|
||||
$tagName = $line['name'];
|
||||
if (empty($result[$entryId])) {
|
||||
@@ -437,18 +440,16 @@ SQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array<string,string|int>>|array<string,string|int> $listDAO
|
||||
* @param iterable<array<string,int|string|null>> $listDAO
|
||||
* @return array<FreshRSS_Tag>
|
||||
*/
|
||||
private static function daoToTag(array $listDAO): array {
|
||||
$list = array();
|
||||
if (!is_array($listDAO)) {
|
||||
$listDAO = array($listDAO);
|
||||
}
|
||||
private static function daoToTag(iterable $listDAO): array {
|
||||
$list = [];
|
||||
foreach ($listDAO as $dao) {
|
||||
$tag = new FreshRSS_Tag(
|
||||
$dao['name']
|
||||
);
|
||||
if (empty($dao['id']) || !is_int($dao['id']) || empty($dao['name']) || !is_string($dao['name'])) {
|
||||
continue;
|
||||
}
|
||||
$tag = new FreshRSS_Tag($dao['name']);
|
||||
$tag->_id($dao['id']);
|
||||
if (!empty($dao['attributes'])) {
|
||||
$tag->_attributes('', $dao['attributes']);
|
||||
|
||||
@@ -201,15 +201,17 @@ class FreshRSS_View extends Minz_View {
|
||||
public $last30DaysLabels;
|
||||
/** @var array<string,string> */
|
||||
public $months;
|
||||
/** @var array<string,array<string,int>>|array<string,int> */
|
||||
/** @var array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false */
|
||||
public $repartition;
|
||||
/** @var array{'main_stream':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false,'all_feeds':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false} */
|
||||
public $repartitions;
|
||||
/** @var array<int,int> */
|
||||
public $repartitionDayOfWeek;
|
||||
/** @var array<string,int>|array<int,int> */
|
||||
public $repartitionHour;
|
||||
/** @var array<int,int> */
|
||||
public $repartitionMonth;
|
||||
/** @var array<array<string,int|string>> */
|
||||
/** @var array<array{'id':int,'name':string,'category':string,'count':int}> */
|
||||
public $topFeed;
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class FreshRSS_Export_Service {
|
||||
|
||||
$view->list_title = _t('sub.import_export.starred_list');
|
||||
$view->type = 'starred';
|
||||
$entriesId = $this->entry_dao->listIdsWhere($type, 0, FreshRSS_Entry::STATE_ALL, 'ASC', -1) ?: [];
|
||||
$entriesId = $this->entry_dao->listIdsWhere($type, 0, FreshRSS_Entry::STATE_ALL, 'ASC', -1) ?? [];
|
||||
$view->entryIdsTagNames = $this->tag_dao->getEntryIdsTagNames($entriesId);
|
||||
// The following is a streamable query, i.e. must be last
|
||||
$view->entries = $this->entry_dao->listWhere(
|
||||
@@ -108,7 +108,7 @@ class FreshRSS_Export_Service {
|
||||
$view->type = 'feed/' . $feed->id();
|
||||
$entriesId = $this->entry_dao->listIdsWhere(
|
||||
'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', $max_number_entries
|
||||
) ?: [];
|
||||
) ?? [];
|
||||
$view->entryIdsTagNames = $this->tag_dao->getEntryIdsTagNames($entriesId);
|
||||
// The following is a streamable query, i.e. must be last
|
||||
$view->entries = $this->entry_dao->listWhere(
|
||||
|
||||
@@ -24,29 +24,29 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><?= _t('admin.stats.status_total') ?></th>
|
||||
<td class="numeric"><?= format_number($this->repartition['main_stream']['total']) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartition['all_feeds']['total']) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartitions['main_stream']['total'] ?? -1) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartitions['all_feeds']['total'] ?? -1) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= _t('admin.stats.status_read') ?></th>
|
||||
<td class="numeric"><?= format_number($this->repartition['main_stream']['count_reads']) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartition['all_feeds']['count_reads']) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartitions['main_stream']['count_reads'] ?? -1) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartitions['all_feeds']['count_reads'] ?? -1) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= _t('admin.stats.status_unread') ?></th>
|
||||
<td class="numeric"><?= format_number($this->repartition['main_stream']['count_unreads']) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartition['all_feeds']['count_unreads']) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartitions['main_stream']['count_unreads'] ?? -1) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartitions['all_feeds']['count_unreads'] ?? -1) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= _t('admin.stats.status_favorites') ?></th>
|
||||
<td class="numeric"><?= format_number($this->repartition['main_stream']['count_favorites']) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartition['all_feeds']['count_favorites']) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartitions['main_stream']['count_favorites'] ?? -1) ?></td>
|
||||
<td class="numeric"><?= format_number($this->repartitions['all_feeds']['count_favorites'] ?? -1) ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div><!--
|
||||
</div>
|
||||
|
||||
--><div class="stat half">
|
||||
<div class="stat half">
|
||||
<h2><?= _t('admin.stats.top_feed') ?></h2>
|
||||
<table>
|
||||
<thead>
|
||||
@@ -58,14 +58,18 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($this->topFeed as $feed) { ?>
|
||||
<?php foreach ($this->topFeed as $feed): ?>
|
||||
<tr>
|
||||
<td><a href="<?= _url('stats', 'repartition', 'id', $feed['id']) ?>"><?= $feed['name'] ?></a></td>
|
||||
<td><?= $feed['category'] ?></td>
|
||||
<td class="numeric"><?= format_number($feed['count']) ?></td>
|
||||
<td class="numeric"><?= format_number($feed['count'] / $this->repartition['all_feeds']['total'] * 100, 1) ?></td>
|
||||
<td class="numeric"><?php
|
||||
if (!empty($this->repartitions['all_feeds']['total'])) {
|
||||
echo format_number($feed['count'] / $this->repartitions['all_feeds']['total'] * 100, 1);
|
||||
}
|
||||
?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -46,10 +46,10 @@
|
||||
<th><?= _t('admin.stats.status_favorites') ?></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="numeric"><?= $this->repartition['total'] ?></td>
|
||||
<td class="numeric"><?= $this->repartition['count_reads'] ?></td>
|
||||
<td class="numeric"><?= $this->repartition['count_unreads'] ?></td>
|
||||
<td class="numeric"><?= $this->repartition['count_favorites'] ?></td>
|
||||
<td class="numeric"><?= $this->repartition['total'] ?? -1 ?></td>
|
||||
<td class="numeric"><?= $this->repartition['count_reads'] ?? -1 ?></td>
|
||||
<td class="numeric"><?= $this->repartition['count_unreads'] ?? -1 ?></td>
|
||||
<td class="numeric"><?= $this->repartition['count_favorites'] ?? -1 ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -66,7 +66,7 @@ foreach ($users as $username) {
|
||||
|
||||
if ($nbFavorites === false) {
|
||||
$nbFavorites = [
|
||||
'all' => 0,
|
||||
'all' => -1,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ foreach ($users as $username) {
|
||||
|
||||
if ($nbEntries === false) {
|
||||
$nbEntries = [
|
||||
'read' => 0,
|
||||
'unread' => 0,
|
||||
'read' => -1,
|
||||
'unread' => -1,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
/**
|
||||
* Manage configuration for the application.
|
||||
* @property-read string $base_url
|
||||
* @property array{'type'?:string,'host'?:string,'user'?:string,'password'?:string,'base'?:string,'prefix'?:string,
|
||||
* 'connection_uri_params'?:string,'pdo_options'?:array<string|int,string|int|bool>} $db
|
||||
* @property array{'type':string,'host':string,'user':string,'password':string,'base':string,'prefix':string,
|
||||
* 'connection_uri_params':string,'pdo_options':array<int,int|string|bool>} $db
|
||||
* @property-read string $disable_update
|
||||
* @property-read string $environment
|
||||
* @property array<string,bool> $extensions_enabled
|
||||
|
||||
@@ -179,4 +179,64 @@ class Minz_ModelPdo {
|
||||
self::$sharedPdo = null;
|
||||
self::$sharedCurrentUser = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,int|string|null> $values
|
||||
* @phpstan-return ($mode is PDO::FETCH_ASSOC ? array<array<string,int|string|null>>|null : array<int|string|null>|null)
|
||||
* @return array<array<string,int|string|null>>|array<int|string|null>|null
|
||||
*/
|
||||
private function fetchAny(string $sql, array $values, int $mode, int $column = 0): ?array {
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$ok = $stm !== false;
|
||||
if ($ok && !empty($values)) {
|
||||
foreach ($values as $name => $value) {
|
||||
if (is_int($value)) $type = PDO::PARAM_INT;
|
||||
elseif (is_string($value)) $type = PDO::PARAM_STR;
|
||||
elseif (is_null($value)) $type = PDO::PARAM_NULL;
|
||||
else {
|
||||
$ok = false;
|
||||
break;
|
||||
}
|
||||
if (!$stm->bindValue($name, $value, $type)) {
|
||||
$ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($ok && $stm !== false && $stm->execute()) {
|
||||
switch ($mode) {
|
||||
case PDO::FETCH_COLUMN:
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, $column);
|
||||
break;
|
||||
case PDO::FETCH_ASSOC:
|
||||
default:
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
break;
|
||||
}
|
||||
if ($res !== false) {
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
||||
$callingFunction = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['function'] ?? '??';
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error ' . $callingFunction . ' ' . json_encode($info));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,int|string|null> $values
|
||||
* @return array<array<string,int|string|null>>|null
|
||||
*/
|
||||
public function fetchAssoc(string $sql, array $values = []): ?array {
|
||||
return $this->fetchAny($sql, $values, PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,int|string|null> $values
|
||||
* @return array<int|string|null>|null
|
||||
*/
|
||||
public function fetchColumn(string $sql, int $column, array $values = []): ?array {
|
||||
return $this->fetchAny($sql, $values, PDO::FETCH_COLUMN, $column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
abstract class Minz_Pdo extends PDO {
|
||||
/** @param array<int,int|string>|null $options */
|
||||
/** @param array<int,int|string|bool>|null $options */
|
||||
public function __construct(string $dsn, ?string $username = null, ?string $passwd = null, ?array $options = null) {
|
||||
parent::__construct($dsn, $username, $passwd, $options);
|
||||
$this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
class Minz_PdoMysql extends Minz_Pdo {
|
||||
/** @param array<int,int|string>|null $options */
|
||||
/** @param array<int,int|string|bool>|null $options */
|
||||
public function __construct(string $dsn, ?string $username = null, ?string $passwd = null, ?array $options = null) {
|
||||
parent::__construct($dsn, $username, $passwd, $options);
|
||||
$this->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
class Minz_PdoPgsql extends Minz_Pdo {
|
||||
/** @param array<int,int|string>|null $options */
|
||||
/** @param array<int,int|string|bool>|null $options */
|
||||
public function __construct(string $dsn, ?string $username = null, ?string $passwd = null, ?array $options = null) {
|
||||
parent::__construct($dsn, $username, $passwd, $options);
|
||||
$this->exec("SET NAMES 'UTF8';");
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
class Minz_PdoSqlite extends Minz_Pdo {
|
||||
/** @param array<int,int|string>|null $options */
|
||||
/** @param array<int,int|string|bool>|null $options */
|
||||
public function __construct(string $dsn, ?string $username = null, ?string $passwd = null, ?array $options = null) {
|
||||
parent::__construct($dsn, $username, $passwd, $options);
|
||||
$this->exec('PRAGMA foreign_keys = ON;');
|
||||
|
||||
@@ -113,7 +113,7 @@ final class FeverDAO extends Minz_ModelPdo
|
||||
$sql .= ' LIMIT 50';
|
||||
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
if ($stm !== false && $stm->execute($values)) {
|
||||
$result = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$entries = array();
|
||||
@@ -374,10 +374,7 @@ final class FeverAPI
|
||||
return $favicons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|false
|
||||
*/
|
||||
private function getTotalItems() {
|
||||
private function getTotalItems(): int {
|
||||
return $this->entryDAO->count();
|
||||
}
|
||||
|
||||
@@ -419,12 +416,12 @@ final class FeverAPI
|
||||
}
|
||||
|
||||
private function getUnreadItemIds(): string {
|
||||
$entries = $this->entryDAO->listIdsWhere('a', 0, FreshRSS_Entry::STATE_NOT_READ, 'ASC', 0) ?: [];
|
||||
$entries = $this->entryDAO->listIdsWhere('a', 0, FreshRSS_Entry::STATE_NOT_READ, 'ASC', 0) ?? [];
|
||||
return $this->entriesToIdList($entries);
|
||||
}
|
||||
|
||||
private function getSavedItemIds(): string {
|
||||
$entries = $this->entryDAO->listIdsWhere('a', 0, FreshRSS_Entry::STATE_FAVORITE, 'ASC', 0) ?: [];
|
||||
$entries = $this->entryDAO->listIdsWhere('a', 0, FreshRSS_Entry::STATE_FAVORITE, 'ASC', 0) ?? [];
|
||||
return $this->entriesToIdList($entries);
|
||||
}
|
||||
|
||||
|
||||
@@ -746,7 +746,7 @@ final class GReaderAPI {
|
||||
|
||||
$entryDAO = FreshRSS_Factory::createEntryDao();
|
||||
$ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, $searches);
|
||||
if ($ids === false) {
|
||||
if ($ids == null) {
|
||||
self::internalServerError();
|
||||
}
|
||||
|
||||
@@ -898,13 +898,15 @@ final class GReaderAPI {
|
||||
$categoryDAO = FreshRSS_Factory::createCategoryDao();
|
||||
$cat = $categoryDAO->searchByName($s);
|
||||
if ($cat != null) {
|
||||
$categoryDAO->updateCategory($cat->id(), array('name' => $dest));
|
||||
$categoryDAO->updateCategory($cat->id(), [
|
||||
'name' => $dest, 'kind' => $cat->kind(), 'attributes' => $cat->attributes()
|
||||
]);
|
||||
exit('OK');
|
||||
} else {
|
||||
$tagDAO = FreshRSS_Factory::createTagDao();
|
||||
$tag = $tagDAO->searchByName($s);
|
||||
if ($tag != null) {
|
||||
$tagDAO->updateTag($tag->id(), array('name' => $dest));
|
||||
$tagDAO->updateTagName($tag->id(), $dest);
|
||||
exit('OK');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,23 +10,15 @@
|
||||
./app/Controllers/userController.php
|
||||
./app/Models/CategoryDAO.php
|
||||
./app/Models/Context.php
|
||||
./app/Models/DatabaseDAO.php
|
||||
./app/Models/DatabaseDAOPGSQL.php
|
||||
./app/Models/Entry.php
|
||||
./app/Models/EntryDAO.php
|
||||
./app/Models/Feed.php
|
||||
./app/Models/FeedDAO.php
|
||||
./app/Models/Search.php
|
||||
./app/Models/Share.php
|
||||
./app/Models/StatsDAO.php
|
||||
./app/Models/TagDAO.php
|
||||
./app/views/helpers/logs_pagination.phtml
|
||||
./app/views/stats/index.phtml
|
||||
./app/views/stats/repartition.phtml
|
||||
./cli/check.translation.php
|
||||
./cli/manipulate.translation.php
|
||||
./lib/Minz/Error.php
|
||||
./lib/Minz/Mailer.php
|
||||
./lib/Minz/Migrator.php
|
||||
./lib/Minz/ModelPdo.php
|
||||
./lib/Minz/Request.php
|
||||
|
||||
Reference in New Issue
Block a user