mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-10-20 10:14:33 -04:00
PHPStan level 9 for ./p/ and lib_rss.php (#5049)
And app/FreshRSS.php Contributes to https://github.com/FreshRSS/FreshRSS/issues/4112
This commit is contained in:
1 parent
2303b29e68
commit
4f316b2ed3
12 files changed
+1292
-1196
No files matched your search
+109
-85
@@ -17,7 +17,7 @@ require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
|
||||
FreshRSS_Context::initSystem();
|
||||
|
||||
// check if API is enabled globally
|
||||
if (!FreshRSS_Context::$system_conf->api_enabled) {
|
||||
if (FreshRSS_Context::$system_conf == null || !FreshRSS_Context::$system_conf->api_enabled) {
|
||||
Minz_Log::warning('Fever API: service unavailable!');
|
||||
Minz_Log::debug('Fever API: serviceUnavailable() ' . debugInfo(), API_LOG);
|
||||
header('HTTP/1.1 503 Service Unavailable');
|
||||
@@ -29,12 +29,9 @@ Minz_Session::init('FreshRSS', true);
|
||||
// ================================================================================================
|
||||
|
||||
// <Debug>
|
||||
$ORIGINAL_INPUT = file_get_contents('php://input', false, null, 0, 1048576);
|
||||
$ORIGINAL_INPUT = file_get_contents('php://input', false, null, 0, 1048576) ?: '';;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function debugInfo() {
|
||||
function debugInfo(): string {
|
||||
if (function_exists('getallheaders')) {
|
||||
$ALL_HEADERS = getallheaders();
|
||||
} else { //nginx http://php.net/getallheaders#84262
|
||||
@@ -62,8 +59,12 @@ function debugInfo() {
|
||||
//Minz_Log::debug(debugInfo(), API_LOG);
|
||||
// </Debug>
|
||||
|
||||
class FeverDAO extends Minz_ModelPdo
|
||||
final class FeverDAO extends Minz_ModelPdo
|
||||
{
|
||||
/**
|
||||
* @param array<string|int> $values
|
||||
* @param array<string,string|int> $bindArray
|
||||
*/
|
||||
protected function bindParamArray(string $prefix, array $values, array &$bindArray): string {
|
||||
$str = '';
|
||||
for ($i = 0; $i < count($values); $i++) {
|
||||
@@ -74,9 +75,11 @@ class FeverDAO extends Minz_ModelPdo
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string|int> $feed_ids
|
||||
* @param array<string> $entry_ids
|
||||
* @return FreshRSS_Entry[]
|
||||
*/
|
||||
public function findEntries(array $feed_ids, array $entry_ids, string $max_id, string $since_id) {
|
||||
public function findEntries(array $feed_ids, array $entry_ids, string $max_id, string $since_id): array {
|
||||
$values = array();
|
||||
$order = '';
|
||||
$entryDAO = FreshRSS_Factory::createEntryDao();
|
||||
@@ -110,36 +113,34 @@ class FeverDAO extends Minz_ModelPdo
|
||||
$sql .= ' LIMIT 50';
|
||||
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
$stm->execute($values);
|
||||
$result = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
if ($stm && $stm->execute($values)) {
|
||||
$result = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$entries = array();
|
||||
foreach ($result as $dao) {
|
||||
$entries[] = FreshRSS_Entry::fromArray($dao);
|
||||
$entries = array();
|
||||
foreach ($result as $dao) {
|
||||
$entries[] = FreshRSS_Entry::fromArray($dao);
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
return $entries;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class FeverAPI
|
||||
*/
|
||||
class FeverAPI
|
||||
final class FeverAPI
|
||||
{
|
||||
const API_LEVEL = 3;
|
||||
const STATUS_OK = 1;
|
||||
const STATUS_ERR = 0;
|
||||
|
||||
/**
|
||||
* @var FreshRSS_EntryDAO|null
|
||||
*/
|
||||
private $entryDAO = null;
|
||||
/** @var FreshRSS_EntryDAO */
|
||||
private $entryDAO;
|
||||
|
||||
/**
|
||||
* @var FreshRSS_FeedDAO|null
|
||||
*/
|
||||
private $feedDAO = null;
|
||||
/** @var FreshRSS_FeedDAO */
|
||||
private $feedDAO;
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
@@ -148,6 +149,9 @@ class FeverAPI
|
||||
* your FreshRSS "username:your-api-password" combination
|
||||
*/
|
||||
private function authenticate(): bool {
|
||||
if (FreshRSS_Context::$system_conf === null) {
|
||||
throw new FreshRSS_Context_Exception('System configuration not initialised!');
|
||||
}
|
||||
FreshRSS_Context::$user_conf = null;
|
||||
Minz_Session::_param('currentUser');
|
||||
$feverKey = empty($_POST['api_key']) ? '' : substr(trim($_POST['api_key']), 0, 128);
|
||||
@@ -176,16 +180,12 @@ class FeverAPI
|
||||
|
||||
public function isAuthenticatedApiUser(): bool {
|
||||
$this->authenticate();
|
||||
|
||||
if (FreshRSS_Context::$user_conf !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return FreshRSS_Context::$user_conf !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This does all the processing, since the fever api does not have a specific variable that specifies the operation
|
||||
* @return array<string,mixed>
|
||||
* @throws Exception
|
||||
*/
|
||||
public function process(): array {
|
||||
@@ -226,37 +226,54 @@ class FeverAPI
|
||||
$response_arr['saved_item_ids'] = $this->getSavedItemIds();
|
||||
}
|
||||
|
||||
$id = isset($_REQUEST['id']) ? '' . $_REQUEST['id'] : '';
|
||||
if (isset($_REQUEST['mark'], $_REQUEST['as'], $_REQUEST['id']) && ctype_digit($id)) {
|
||||
$method_name = 'set' . ucfirst($_REQUEST['mark']) . 'As' . ucfirst($_REQUEST['as']);
|
||||
$allowedMethods = array(
|
||||
'setFeedAsRead', 'setGroupAsRead', 'setItemAsRead',
|
||||
'setItemAsSaved', 'setItemAsUnread', 'setItemAsUnsaved'
|
||||
);
|
||||
if (in_array($method_name, $allowedMethods)) {
|
||||
switch (strtolower($_REQUEST['mark'])) {
|
||||
case 'item':
|
||||
$this->{$method_name}($id);
|
||||
break;
|
||||
case 'feed':
|
||||
case 'group':
|
||||
$before = $_REQUEST['before'] ?? '';
|
||||
$this->{$method_name}($id, $before);
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($_REQUEST['as']) {
|
||||
case 'read':
|
||||
case 'unread':
|
||||
$response_arr['unread_item_ids'] = $this->getUnreadItemIds();
|
||||
break;
|
||||
|
||||
case 'saved':
|
||||
case 'unsaved':
|
||||
$response_arr['saved_item_ids'] = $this->getSavedItemIds();
|
||||
break;
|
||||
}
|
||||
if (isset($_REQUEST['mark'], $_REQUEST['as'], $_REQUEST['id']) && ctype_digit($_REQUEST['id'])) {
|
||||
$id = intval($_REQUEST['id']);
|
||||
$before = intval($_REQUEST['before'] ?? '0');
|
||||
switch (strtolower($_REQUEST['mark'])) {
|
||||
case 'item':
|
||||
switch ($_REQUEST['as']) {
|
||||
case 'read':
|
||||
$this->setItemAsRead($id);
|
||||
break;
|
||||
case 'saved':
|
||||
$this->setItemAsSaved($id);
|
||||
break;
|
||||
case 'unread':
|
||||
$this->setItemAsUnread($id);
|
||||
break;
|
||||
case 'unsaved':
|
||||
$this->setItemAsUnsaved($id);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'feed':
|
||||
switch ($_REQUEST['as']) {
|
||||
case 'read':
|
||||
$this->setFeedAsRead($id, $before);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'group':
|
||||
switch ($_REQUEST['as']) {
|
||||
case 'read':
|
||||
$this->setFeedAsRead($id, $before);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($_REQUEST['as']) {
|
||||
case 'read':
|
||||
case 'unread':
|
||||
$response_arr['unread_item_ids'] = $this->getUnreadItemIds();
|
||||
break;
|
||||
|
||||
case 'saved':
|
||||
case 'unsaved':
|
||||
$response_arr['saved_item_ids'] = $this->getSavedItemIds();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $response_arr;
|
||||
@@ -264,6 +281,7 @@ class FeverAPI
|
||||
|
||||
/**
|
||||
* Returns the complete JSON, with 'api_version' and status as 'auth'.
|
||||
* @param array<string,mixed> $reply
|
||||
*/
|
||||
public function wrap(int $status, array $reply = array()): string {
|
||||
$arr = array('api_version' => self::API_LEVEL, 'auth' => $status);
|
||||
@@ -273,7 +291,7 @@ class FeverAPI
|
||||
$arr = array_merge($arr, $reply);
|
||||
}
|
||||
|
||||
return json_encode($arr);
|
||||
return json_encode($arr) ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,6 +310,7 @@ class FeverAPI
|
||||
return $lastUpdate;
|
||||
}
|
||||
|
||||
/** @return array<array<string,string|int>> */
|
||||
protected function getFeeds(): array {
|
||||
$feeds = array();
|
||||
$myFeeds = $this->feedDAO->listFeeds();
|
||||
@@ -312,6 +331,7 @@ class FeverAPI
|
||||
return $feeds;
|
||||
}
|
||||
|
||||
/** @return array<array<string,int|string>> */
|
||||
protected function getGroups(): array {
|
||||
$groups = array();
|
||||
|
||||
@@ -329,12 +349,15 @@ class FeverAPI
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/** @return array<array<string,int|string>> */
|
||||
protected function getFavicons(): array {
|
||||
if (FreshRSS_Context::$system_conf == null) {
|
||||
return [];
|
||||
}
|
||||
$favicons = array();
|
||||
$salt = FreshRSS_Context::$system_conf->salt;
|
||||
$myFeeds = $this->feedDAO->listFeeds();
|
||||
|
||||
/** @var FreshRSS_Feed $feed */
|
||||
foreach ($myFeeds as $feed) {
|
||||
|
||||
$id = hash('crc32b', $salt . $feed->url());
|
||||
@@ -345,7 +368,7 @@ class FeverAPI
|
||||
|
||||
$favicons[] = array(
|
||||
'id' => $feed->id(),
|
||||
'data' => image_type_to_mime_type(exif_imagetype($filename)) . ';base64,' . base64_encode(file_get_contents($filename))
|
||||
'data' => image_type_to_mime_type(exif_imagetype($filename) ?: 0) . ';base64,' . base64_encode(file_get_contents($filename) ?: '')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -359,17 +382,19 @@ class FeverAPI
|
||||
return $this->entryDAO->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array<string,int|string>>
|
||||
*/
|
||||
protected function getFeedsGroup(): array {
|
||||
$groups = array();
|
||||
$ids = array();
|
||||
$myFeeds = $this->feedDAO->listFeeds();
|
||||
|
||||
/** @var FreshRSS_Feed $feed */
|
||||
foreach ($myFeeds as $feed) {
|
||||
$ids[$feed->categoryId()][] = $feed->id();
|
||||
}
|
||||
|
||||
foreach($ids as $category => $feedIds) {
|
||||
foreach ($ids as $category => $feedIds) {
|
||||
$groups[] = array(
|
||||
'group_id' => $category,
|
||||
'feed_ids' => implode(',', $feedIds)
|
||||
@@ -381,13 +406,14 @@ class FeverAPI
|
||||
|
||||
/**
|
||||
* AFAIK there is no 'hot links' alternative in FreshRSS
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function getLinks(): array {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $ids
|
||||
* @param array<string> $ids
|
||||
*/
|
||||
protected function entriesToIdList(array $ids = array()): string {
|
||||
return implode(',', array_values($ids));
|
||||
@@ -398,10 +424,7 @@ class FeverAPI
|
||||
return $this->entriesToIdList($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getSavedItemIds() {
|
||||
protected function getSavedItemIds(): string {
|
||||
$entries = $this->entryDAO->listIdsWhere('a', '', FreshRSS_Entry::STATE_FAVORITE, 'ASC', 0);
|
||||
return $this->entriesToIdList($entries);
|
||||
}
|
||||
@@ -409,31 +432,32 @@ class FeverAPI
|
||||
/**
|
||||
* @return integer|false
|
||||
*/
|
||||
protected function setItemAsRead($id) {
|
||||
protected function setItemAsRead(int $id) {
|
||||
return $this->entryDAO->markRead($id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer|false
|
||||
*/
|
||||
protected function setItemAsUnread($id) {
|
||||
protected function setItemAsUnread(int $id) {
|
||||
return $this->entryDAO->markRead($id, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer|false
|
||||
*/
|
||||
protected function setItemAsSaved($id) {
|
||||
protected function setItemAsSaved(int $id) {
|
||||
return $this->entryDAO->markFavorite($id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer|false
|
||||
*/
|
||||
protected function setItemAsUnsaved($id) {
|
||||
protected function setItemAsUnsaved(int $id) {
|
||||
return $this->entryDAO->markFavorite($id, false);
|
||||
}
|
||||
|
||||
/** @return array<array<string,string|int>> */
|
||||
protected function getItems(): array {
|
||||
$feed_ids = array();
|
||||
$entry_ids = array();
|
||||
@@ -448,16 +472,16 @@ class FeverAPI
|
||||
if (isset($_REQUEST['group_ids'])) {
|
||||
$categoryDAO = FreshRSS_Factory::createCategoryDao();
|
||||
$group_ids = explode(',', $_REQUEST['group_ids']);
|
||||
$feeds = [];
|
||||
foreach ($group_ids as $id) {
|
||||
/** @var FreshRSS_Category $category */
|
||||
$category = $categoryDAO->searchById($id); //TODO: Transform to SQL query without loop! Consider FreshRSS_CategoryDAO::listCategories(true)
|
||||
/** @var FreshRSS_Feed $feed */
|
||||
$feeds = [];
|
||||
if ($category == null) {
|
||||
continue;
|
||||
}
|
||||
foreach ($category->feeds() as $feed) {
|
||||
$feeds[] = $feed->id();
|
||||
}
|
||||
}
|
||||
|
||||
$feed_ids = array_unique($feeds);
|
||||
}
|
||||
}
|
||||
@@ -511,30 +535,30 @@ class FeverAPI
|
||||
/**
|
||||
* TODO replace by a dynamic fetch for id <= $before timestamp
|
||||
*/
|
||||
protected function convertBeforeToId(string $beforeTimestamp): string {
|
||||
return $beforeTimestamp == '0' ? '0' : $beforeTimestamp . '000000';
|
||||
protected function convertBeforeToId(int $beforeTimestamp): string {
|
||||
return $beforeTimestamp == 0 ? '0' : $beforeTimestamp . '000000';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer|false
|
||||
*/
|
||||
protected function setFeedAsRead(string $id, string $before) {
|
||||
protected function setFeedAsRead(int $id, int $before) {
|
||||
$before = $this->convertBeforeToId($before);
|
||||
return $this->entryDAO->markReadFeed(intval($id), $before);
|
||||
return $this->entryDAO->markReadFeed($id, $before);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer|false
|
||||
*/
|
||||
protected function setGroupAsRead(string $id, string $before) {
|
||||
protected function setGroupAsRead(int $id, int $before) {
|
||||
$before = $this->convertBeforeToId($before);
|
||||
|
||||
// special case to mark all items as read
|
||||
if ($id == '0') {
|
||||
if ($id == 0) {
|
||||
return $this->entryDAO->markReadEntries($before);
|
||||
}
|
||||
|
||||
return $this->entryDAO->markReadCat(intval($id), $before);
|
||||
return $this->entryDAO->markReadCat($id, $before);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user