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:
Alexandre Alapetite
2023-04-28 14:01:11 +02:00
committed by GitHub
parent 26e2a70312
commit c72914bba2
29 changed files with 575 additions and 495 deletions

View File

@@ -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);

View File

@@ -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(),

View File

@@ -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());

View File

@@ -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;
}
}

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}
/**

View File

@@ -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];
}
}

View File

@@ -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();

View File

@@ -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));

View File

@@ -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;
}
/**

View File

@@ -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;

View File

@@ -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;

View File

@@ -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']);

View File

@@ -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;
}

View File

@@ -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(

View File

@@ -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>

View File

@@ -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>

View File

@@ -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,
];
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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';");

View File

@@ -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;');

View File

@@ -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);
}

View File

@@ -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');
}
}

View File

@@ -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