diff options
| author | 2023-04-28 14:01:11 +0200 | |
|---|---|---|
| committer | 2023-04-28 14:01:11 +0200 | |
| commit | c72914bba2363e436574204b3d6093a6f3cfce89 (patch) | |
| tree | 377008a7393e4d80e4c8659f27dd42c0ccbab382 /app/Models/EntryDAO.php | |
| parent | 26e2a703125ffe1d0d2746b0e5ea3491b627832c (diff) | |
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
Diffstat (limited to 'app/Models/EntryDAO.php')
| -rw-r--r-- | app/Models/EntryDAO.php | 209 |
1 files changed, 103 insertions, 106 deletions
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 19547ac31..4c6bf0641 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -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; - } - $stm = $this->pdo->query($sql); - if ($stm == false) { - return false; + $sql .= ' WHERE f.priority > :priority'; } - $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]; } } |
