Jelajahi Sumber

Fix Fever 32 bit ID issue + more PHP type hints (#4201)

* Fix Fever 32 bit ID issue + more PHP type hints
#fix https://github.com/FreshRSS/FreshRSS/issues/4200
Follow up and fix regression from https://github.com/FreshRSS/FreshRSS/pull/4110

* More PHP type hints with PHPStan

* Fix pull problem

* Avoid more nulls
Alexandre Alapetite 4 tahun lalu
induk
melakukan
1c5cf71859

+ 1 - 1
app/Controllers/feedController.php

@@ -207,7 +207,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 
 			// Entries are in DB, we redirect to feed configuration page.
 			$url_redirect['a'] = 'index';
-			$url_redirect['params']['id'] = $feed->id();
+			$url_redirect['params']['id'] = '' . $feed->id();
 			Minz_Request::good(_t('feedback.sub.feed.added', $feed->name()), $url_redirect);
 		} else {
 			// GET request: we must ask confirmation to user before adding feed.

+ 10 - 7
app/Models/Category.php

@@ -1,6 +1,9 @@
 <?php
 
 class FreshRSS_Category extends Minz_Model {
+	/**
+	 * @var int
+	 */
 	private $id = 0;
 	private $name;
 	private $nbFeed = -1;
@@ -10,7 +13,7 @@ class FreshRSS_Category extends Minz_Model {
 	private $isDefault = false;
 	private $attributes = [];
 
-	public function __construct($name = '', $feeds = null) {
+	public function __construct(string $name = '', $feeds = null) {
 		$this->_name($name);
 		if (isset($feeds)) {
 			$this->_feeds($feeds);
@@ -24,16 +27,16 @@ class FreshRSS_Category extends Minz_Model {
 		}
 	}
 
-	public function id() {
+	public function id(): int {
 		return $this->id;
 	}
-	public function name() {
+	public function name(): string {
 		return $this->name;
 	}
-	public function isDefault() {
+	public function isDefault(): bool {
 		return $this->isDefault;
 	}
-	public function nbFeed() {
+	public function nbFeed(): int {
 		if ($this->nbFeed < 0) {
 			$catDAO = FreshRSS_Factory::createCategoryDao();
 			$this->nbFeed = $catDAO->countFeed($this->id());
@@ -41,7 +44,7 @@ class FreshRSS_Category extends Minz_Model {
 
 		return $this->nbFeed;
 	}
-	public function nbNotRead() {
+	public function nbNotRead(): int {
 		if ($this->nbNotRead < 0) {
 			$catDAO = FreshRSS_Factory::createCategoryDao();
 			$this->nbNotRead = $catDAO->countNotRead($this->id());
@@ -49,7 +52,7 @@ class FreshRSS_Category extends Minz_Model {
 
 		return $this->nbNotRead;
 	}
-	public function feeds() {
+	public function feeds(): array {
 		if ($this->feeds === null) {
 			$feedDAO = FreshRSS_Factory::createFeedDao();
 			$this->feeds = $feedDAO->listByCategory($this->id());

+ 1 - 1
app/Models/CategoryDAO.php

@@ -66,7 +66,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
 		return false;
 	}
 
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if (isset($errorInfo[0])) {
 			if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_COLUMN) {
 				foreach (['attributes'] as $column) {

+ 1 - 1
app/Models/CategoryDAOSQLite.php

@@ -2,7 +2,7 @@
 
 class FreshRSS_CategoryDAOSQLite extends FreshRSS_CategoryDAO {
 
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if ($tableInfo = $this->pdo->query("PRAGMA table_info('category')")) {
 			$columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1);
 			foreach (['attributes'] as $column) {

+ 63 - 40
app/Models/Entry.php

@@ -7,23 +7,46 @@ class FreshRSS_Entry extends Minz_Model {
 	const STATE_FAVORITE = 4;
 	const STATE_NOT_FAVORITE = 8;
 
-	private $id = 0;
+	/**
+	 * @var string
+	 */
+	private $id = '0';
+
+	/**
+	 * @var string
+	 */
 	private $guid;
+
 	private $title;
 	private $authors;
 	private $content;
 	private $link;
 	private $date;
 	private $date_added = 0; //In microseconds
-	private $hash = null;
-	private $is_read;	//Nullable boolean
+	/**
+	 * @var string
+	 */
+	private $hash = '';
+	/**
+	 * @var bool|null
+	 */
+	private $is_read;
 	private $is_favorite;
+
+	/**
+	 * @var int
+	 */
 	private $feedId;
+
+	/**
+	 * @var FreshRSS_Feed|null
+	 */
 	private $feed;
+
 	private $tags;
 
-	public function __construct($feedId = '', $guid = '', $title = '', $authors = '', $content = '',
-			$link = '', $pubdate = 0, $is_read = false, $is_favorite = false, $tags = '') {
+	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 = '') {
 		$this->_title($title);
 		$this->_authors($authors);
 		$this->_content($content);
@@ -36,31 +59,31 @@ class FreshRSS_Entry extends Minz_Model {
 		$this->_guid($guid);
 	}
 
-	public function id() {
+	public function id(): string {
 		return $this->id;
 	}
-	public function guid() {
+	public function guid(): string {
 		return $this->guid;
 	}
-	public function title() {
+	public function title(): string {
 		return $this->title == '' ? $this->guid() : $this->title;
 	}
-	public function author() {
+	public function author(): string {
 		//Deprecated
 		return $this->authors(true);
 	}
-	public function authors($asString = false) {
+	public function authors(bool $asString = false) {
 		if ($asString) {
 			return $this->authors == null ? '' : ';' . implode('; ', $this->authors);
 		} else {
 			return $this->authors;
 		}
 	}
-	public function content() {
+	public function content(): string {
 		return $this->content;
 	}
 
-	public function enclosures($searchBodyImages = false) {
+	public function enclosures(bool $searchBodyImages = false): array {
 		$results = [];
 		try {
 			$searchEnclosures = strpos($this->content, '<p class="enclosure-content') !== false;
@@ -102,28 +125,28 @@ class FreshRSS_Entry extends Minz_Model {
 		}
 	}
 
-	public function thumbnail() {
+	public function thumbnail(): string {
 		foreach ($this->enclosures(true) as $enclosure) {
 			if (!empty($enclosure['url']) && empty($enclosure['type'])) {
 				return $enclosure;
 			}
 		}
-		return null;
+		return '';
 	}
 
-	public function link() {
+	public function link(): string {
 		return $this->link;
 	}
-	public function date($raw = false) {
+	public function date(bool $raw = false) {
 		if ($raw) {
 			return $this->date;
 		}
 		return timestamptodate($this->date);
 	}
-	public function machineReadableDate() {
+	public function machineReadableDate(): string {
 		return @date (DATE_ATOM, $this->date);
 	}
-	public function dateAdded($raw = false, $microsecond = false) {
+	public function dateAdded(bool $raw = false, bool $microsecond = false) {
 		if ($raw) {
 			if ($microsecond) {
 				return $this->date_added;
@@ -160,15 +183,15 @@ class FreshRSS_Entry extends Minz_Model {
 		}
 	}
 
-	public function hash() {
-		if ($this->hash === null) {
+	public function hash(): string {
+		if ($this->hash == '') {
 			//Do not include $this->date because it may be automatically generated when lacking
 			$this->hash = md5($this->link . $this->title . $this->authors(true) . $this->content . $this->tags(true));
 		}
 		return $this->hash;
 	}
 
-	public function _hash($value) {
+	public function _hash(string $value) {
 		$value = trim($value);
 		if (ctype_xdigit($value)) {
 			$this->hash = substr($value, 0, 32);
@@ -182,7 +205,7 @@ class FreshRSS_Entry extends Minz_Model {
 			$this->date_added = $value;
 		}
 	}
-	public function _guid($value) {
+	public function _guid(string $value) {
 		if ($value == '') {
 			$value = $this->link;
 			if ($value == '') {
@@ -191,16 +214,16 @@ class FreshRSS_Entry extends Minz_Model {
 		}
 		$this->guid = $value;
 	}
-	public function _title($value) {
-		$this->hash = null;
+	public function _title(string $value) {
+		$this->hash = '';
 		$this->title = trim($value);
 	}
-	public function _author($value) {
+	public function _author(string $value) {
 		//Deprecated
 		$this->_authors($value);
 	}
 	public function _authors($value) {
-		$this->hash = null;
+		$this->hash = '';
 		if (!is_array($value)) {
 			if (strpos($value, ';') !== false) {
 				$value = preg_split('/\s*[;]\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
@@ -210,20 +233,20 @@ class FreshRSS_Entry extends Minz_Model {
 		}
 		$this->authors = $value;
 	}
-	public function _content($value) {
-		$this->hash = null;
+	public function _content(string $value) {
+		$this->hash = '';
 		$this->content = $value;
 	}
-	public function _link($value) {
-		$this->hash = null;
+	public function _link(string $value) {
+		$this->hash = '';
 		$this->link = $value;
 	}
 	public function _date($value) {
-		$this->hash = null;
+		$this->hash = '';
 		$value = intval($value);
 		$this->date = $value > 1 ? $value : time();
 	}
-	public function _dateAdded($value, $microsecond = false) {
+	public function _dateAdded($value, bool $microsecond = false) {
 		if ($microsecond) {
 			$this->date_added = $value;
 		} else {
@@ -247,15 +270,15 @@ class FreshRSS_Entry extends Minz_Model {
 		$this->feedId = intval($value);
 	}
 	public function _tags($value) {
-		$this->hash = null;
+		$this->hash = '';
 		if (!is_array($value)) {
 			$value = preg_split('/\s*[#,]\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
 		}
 		$this->tags = $value;
 	}
 
-	public function matches($booleanSearch) {
-		if (!$booleanSearch || count($booleanSearch->searches()) <= 0) {
+	public function matches(FreshRSS_BooleanSearch $booleanSearch): bool {
+		if (count($booleanSearch->searches()) <= 0) {
 			return true;
 		}
 		foreach ($booleanSearch->searches() as $filter) {
@@ -359,7 +382,7 @@ class FreshRSS_Entry extends Minz_Model {
 		return false;
 	}
 
-	public function applyFilterActions($titlesAsRead = []) {
+	public function applyFilterActions(array $titlesAsRead = []) {
 		if ($this->feed != null) {
 			if ($this->feed->attributes('read_upon_reception') ||
 				($this->feed->attributes('read_upon_reception') === null && FreshRSS_Context::$user_conf->mark_when['reception'])) {
@@ -389,7 +412,7 @@ class FreshRSS_Entry extends Minz_Model {
 		}
 	}
 
-	public function isDay($day, $today) {
+	public function isDay(int $day, int $today): bool {
 		$date = $this->dateAdded(true);
 		switch ($day) {
 		case FreshRSS_Days::TODAY:
@@ -406,7 +429,7 @@ class FreshRSS_Entry extends Minz_Model {
 		}
 	}
 
-	public static function getContentByParsing($url, $path, $attributes = array(), $maxRedirs = 3) {
+	public static function getContentByParsing(string $url, string $path, array $attributes = array(), int $maxRedirs = 3): string {
 		$limits = FreshRSS_Context::$system_conf->limits;
 		$feed_timeout = empty($attributes['timeout']) ? 0 : intval($attributes['timeout']);
 
@@ -488,7 +511,7 @@ class FreshRSS_Entry extends Minz_Model {
 		}
 	}
 
-	public function loadCompleteContent($force = false) {
+	public function loadCompleteContent(bool $force = false): bool {
 		// Gestion du contenu
 		// Trying to fetch full article content even when feeds do not propose it
 		$feed = $this->feed(true);
@@ -534,7 +557,7 @@ class FreshRSS_Entry extends Minz_Model {
 		return false;
 	}
 
-	public function toArray() {
+	public function toArray(): array {
 		return array(
 			'id' => $this->id(),
 			'guid' => $this->guid(),

+ 14 - 15
app/Models/EntryDAO.php

@@ -2,23 +2,23 @@
 
 class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 
-	public function isCompressed() {
+	public function isCompressed(): bool {
 		return true;
 	}
 
-	public function hasNativeHex() {
+	public function hasNativeHex(): bool {
 		return true;
 	}
 
-	public function sqlHexDecode($x) {
+	public function sqlHexDecode(string $x): string {
 		return 'unhex(' . $x . ')';
 	}
 
-	public function sqlHexEncode($x) {
+	public function sqlHexEncode(string $x): string {
 		return 'hex(' . $x . ')';
 	}
 
-	public function sqlIgnoreConflict($sql) {
+	public function sqlIgnoreConflict(string $sql): string {
 		return str_replace('INSERT INTO ', 'INSERT IGNORE INTO ', $sql);
 	}
 
@@ -62,7 +62,7 @@ SQL;
 	}
 
 	//TODO: Move the database auto-updates to DatabaseDAO
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if (isset($errorInfo[0])) {
 			if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_TABLE_ERROR) {
 				if (stripos($errorInfo[2], 'tag') !== false) {
@@ -85,7 +85,7 @@ SQL;
 
 	private $addEntryPrepared = null;
 
-	public function addEntry($valuesTmp, $useTmpTable = true) {
+	public function addEntry(array $valuesTmp, bool $useTmpTable = true) {
 		if ($this->addEntryPrepared == null) {
 			$sql = $this->sqlIgnoreConflict(
 				'INSERT INTO `_' . ($useTmpTable ? 'entrytmp' : 'entry') . '` (id, guid, title, author, '
@@ -251,10 +251,9 @@ SQL;
 	 * there is an other way to do that.
 	 *
 	 * @param integer|array $ids
-	 * @param boolean $is_favorite
 	 * @return false|integer
 	 */
-	public function markFavorite($ids, $is_favorite = true) {
+	public function markFavorite($ids, bool $is_favorite = true) {
 		if (!is_array($ids)) {
 			$ids = array($ids);
 		}
@@ -495,12 +494,12 @@ SQL;
 	 * If $idMax equals 0, a deprecated debug message is logged
 	 *
 	 * @param integer $id_feed feed ID
-	 * @param integer $idMax fail safe article ID
+	 * @param string $idMax fail safe article ID
 	 * @return integer|false affected rows
 	 */
-	public function markReadFeed($id_feed, $idMax = 0, $filters = null, $state = 0, $is_read = true) {
+	public function markReadFeed($id_feed, $idMax = '0', $filters = null, $state = 0, $is_read = true) {
 		FreshRSS_UserDAO::touch();
-		if ($idMax == 0) {
+		if ($idMax == '0') {
 			$idMax = time() . '000000';
 			Minz_Log::debug('Calling markReadFeed(0) is deprecated!');
 		}
@@ -543,12 +542,12 @@ SQL;
 	/**
 	 * Mark all the articles in a tag as read.
 	 * @param integer $id tag ID, or empty for targeting any tag
-	 * @param integer $idMax max article ID
+	 * @param string $idMax max article ID
 	 * @return integer|false affected rows
 	 */
-	public function markReadTag($id = 0, $idMax = 0, $filters = null, $state = 0, $is_read = true) {
+	public function markReadTag($id = 0, string $idMax = '0', $filters = null, int $state = 0, bool $is_read = true) {
 		FreshRSS_UserDAO::touch();
-		if ($idMax == 0) {
+		if ($idMax == '0') {
 			$idMax = time() . '000000';
 			Minz_Log::debug('Calling markReadTag(0) is deprecated!');
 		}

+ 6 - 6
app/Models/EntryDAOPGSQL.php

@@ -2,23 +2,23 @@
 
 class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite {
 
-	public function hasNativeHex() {
+	public function hasNativeHex(): bool {
 		return true;
 	}
 
-	public function sqlHexDecode($x) {
+	public function sqlHexDecode(string $x): string {
 		return 'decode(' . $x . ", 'hex')";
 	}
 
-	public function sqlHexEncode($x) {
+	public function sqlHexEncode(string $x): string {
 		return 'encode(' . $x . ", 'hex')";
 	}
 
-	public function sqlIgnoreConflict($sql) {
+	public function sqlIgnoreConflict(string $sql): string {
 		return rtrim($sql, ' ;') . ' ON CONFLICT DO NOTHING';
 	}
 
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if (isset($errorInfo[0])) {
 			if ($errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_TABLE) {
 				if (stripos($errorInfo[2], 'tag') !== false) {
@@ -32,7 +32,7 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite {
 		return false;
 	}
 
-	protected function addColumn($name) {
+	protected function addColumn(string $name) {
 		return false;
 	}
 

+ 7 - 7
app/Models/EntryDAOSQLite.php

@@ -2,23 +2,23 @@
 
 class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
 
-	public function isCompressed() {
+	public function isCompressed(): bool {
 		return false;
 	}
 
-	public function hasNativeHex() {
+	public function hasNativeHex(): bool {
 		return false;
 	}
 
-	public function sqlHexDecode($x) {
+	public function sqlHexDecode(string $x): string {
 		return $x;
 	}
 
-	public function sqlIgnoreConflict($sql) {
+	public function sqlIgnoreConflict(string $sql): string {
 		return str_replace('INSERT INTO ', 'INSERT OR IGNORE INTO ', $sql);
 	}
 
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if ($tableInfo = $this->pdo->query("SELECT sql FROM sqlite_master where name='tag'")) {
 			$showCreate = $tableInfo->fetchColumn();
 			if (stripos($showCreate, 'tag') === false) {
@@ -243,10 +243,10 @@ DROP TABLE IF EXISTS `tmp`;
 	/**
 	 * Mark all the articles in a tag as read.
 	 * @param integer $id tag ID, or empty for targeting any tag
-	 * @param integer $idMax max article ID
+	 * @param string $idMax max article ID
 	 * @return integer|false affected rows
 	 */
-	public function markReadTag($id = 0, $idMax = 0, $filters = null, $state = 0, $is_read = true) {
+	public function markReadTag($id = 0, string $idMax = '0', $filters = null, int $state = 0, bool $is_read = true) {
 		FreshRSS_UserDAO::touch();
 		if ($idMax == 0) {
 			$idMax = time() . '000000';

+ 64 - 47
app/Models/Feed.php

@@ -10,8 +10,14 @@ class FreshRSS_Feed extends Minz_Model {
 	const ARCHIVING_RETENTION_COUNT_LIMIT = 10000;
 	const ARCHIVING_RETENTION_PERIOD = 'P3M';
 
+	/**
+	 * @var int
+	 */
 	private $id = 0;
 	private $url;
+	/**
+	 * @var int
+	 */
 	private $category = 1;
 	private $nbEntries = -1;
 	private $nbNotRead = -1;
@@ -33,7 +39,7 @@ class FreshRSS_Feed extends Minz_Model {
 	private $selfUrl = '';
 	private $filterActions = null;
 
-	public function __construct($url, $validate = true) {
+	public function __construct(string $url, bool $validate = true) {
 		if ($validate) {
 			$this->_url($url);
 		} else {
@@ -47,11 +53,11 @@ class FreshRSS_Feed extends Minz_Model {
 		return $f;
 	}
 
-	public function id() {
+	public function id(): int {
 		return $this->id;
 	}
 
-	public function hash() {
+	public function hash(): string {
 		if ($this->hash === null) {
 			$salt = FreshRSS_Context::$system_conf->salt;
 			$this->hash = hash('crc32b', $salt . $this->url);
@@ -59,16 +65,16 @@ class FreshRSS_Feed extends Minz_Model {
 		return $this->hash;
 	}
 
-	public function url($includeCredentials = true) {
+	public function url(bool $includeCredentials = true): string {
 		return $includeCredentials ? $this->url : SimplePie_Misc::url_remove_credentials($this->url);
 	}
-	public function selfUrl() {
+	public function selfUrl(): string {
 		return $this->selfUrl;
 	}
-	public function hubUrl() {
+	public function hubUrl(): string {
 		return $this->hubUrl;
 	}
-	public function category() {
+	public function category(): int {
 		return $this->category;
 	}
 	public function entries() {
@@ -76,22 +82,22 @@ class FreshRSS_Feed extends Minz_Model {
 		$simplePie = $this->load(false, true);
 		return $simplePie == null ? [] : iterator_to_array($this->loadEntries($simplePie));
 	}
-	public function name($raw = false) {
+	public function name($raw = false): string {
 		return $raw || $this->name != '' ? $this->name : preg_replace('%^https?://(www[.])?%i', '', $this->url);
 	}
-	public function website() {
+	public function website(): string {
 		return $this->website;
 	}
-	public function description() {
+	public function description(): string {
 		return $this->description;
 	}
-	public function lastUpdate() {
+	public function lastUpdate(): int {
 		return $this->lastUpdate;
 	}
-	public function priority() {
+	public function priority(): int {
 		return $this->priority;
 	}
-	public function pathEntries() {
+	public function pathEntries(): string {
 		return $this->pathEntries;
 	}
 	public function httpAuth($raw = true) {
@@ -108,10 +114,10 @@ class FreshRSS_Feed extends Minz_Model {
 			);
 		}
 	}
-	public function inError() {
+	public function inError(): bool {
 		return $this->error;
 	}
-	public function ttl() {
+	public function ttl(): int {
 		return $this->ttl;
 	}
 	public function attributes($key = '') {
@@ -121,7 +127,7 @@ class FreshRSS_Feed extends Minz_Model {
 			return isset($this->attributes[$key]) ? $this->attributes[$key] : null;
 		}
 	}
-	public function mute() {
+	public function mute(): bool {
 		return $this->mute;
 	}
 	// public function ttlExpire() {
@@ -134,7 +140,7 @@ class FreshRSS_Feed extends Minz_Model {
 		// }
 		// return $this->lastUpdate + $ttl;
 	// }
-	public function nbEntries() {
+	public function nbEntries(): int {
 		if ($this->nbEntries < 0) {
 			$feedDAO = FreshRSS_Factory::createFeedDao();
 			$this->nbEntries = $feedDAO->countEntries($this->id());
@@ -142,7 +148,7 @@ class FreshRSS_Feed extends Minz_Model {
 
 		return $this->nbEntries;
 	}
-	public function nbNotRead($includePending = false) {
+	public function nbNotRead($includePending = false): int {
 		if ($this->nbNotRead < 0) {
 			$feedDAO = FreshRSS_Factory::createFeedDao();
 			$this->nbNotRead = $feedDAO->countNotRead($this->id());
@@ -177,14 +183,14 @@ class FreshRSS_Feed extends Minz_Model {
 		@unlink($path . '.ico');
 		@unlink($path . '.txt');
 	}
-	public function favicon() {
+	public function favicon(): string {
 		return Minz_Url::display('/f.php?' . $this->hash());
 	}
 
 	public function _id($value) {
 		$this->id = intval($value);
 	}
-	public function _url($value, $validate = true) {
+	public function _url(string $value, bool $validate = true) {
 		$this->hash = null;
 		if ($validate) {
 			$value = checkUrl($value);
@@ -198,10 +204,10 @@ class FreshRSS_Feed extends Minz_Model {
 		$value = intval($value);
 		$this->category = $value >= 0 ? $value : 0;
 	}
-	public function _name($value) {
-		$this->name = $value === null ? '' : trim($value);
+	public function _name(string $value) {
+		$this->name = $value == '' ? '' : trim($value);
 	}
-	public function _website($value, $validate = true) {
+	public function _website(string $value, bool $validate = true) {
 		if ($validate) {
 			$value = checkUrl($value);
 		}
@@ -210,8 +216,8 @@ class FreshRSS_Feed extends Minz_Model {
 		}
 		$this->website = $value;
 	}
-	public function _description($value) {
-		$this->description = $value === null ? '' : $value;
+	public function _description(string $value) {
+		$this->description = $value == '' ? '' : $value;
 	}
 	public function _lastUpdate($value) {
 		$this->lastUpdate = intval($value);
@@ -219,10 +225,10 @@ class FreshRSS_Feed extends Minz_Model {
 	public function _priority($value) {
 		$this->priority = intval($value);
 	}
-	public function _pathEntries($value) {
+	public function _pathEntries(string $value) {
 		$this->pathEntries = $value;
 	}
-	public function _httpAuth($value) {
+	public function _httpAuth(string $value) {
 		$this->httpAuth = $value;
 	}
 	public function _error($value) {
@@ -235,7 +241,7 @@ class FreshRSS_Feed extends Minz_Model {
 		$this->mute = $value < self::TTL_DEFAULT;
 	}
 
-	public function _attributes($key, $value) {
+	public function _attributes(string $key, $value) {
 		if ($key == '') {
 			if (is_string($value)) {
 				$value = json_decode($value, true);
@@ -257,7 +263,10 @@ class FreshRSS_Feed extends Minz_Model {
 		$this->nbEntries = intval($value);
 	}
 
-	public function load($loadDetails = false, $noCache = false) {
+	/**
+	 * @return SimplePie|null
+	 */
+	public function load(bool $loadDetails = false, bool $noCache = false) {
 		if ($this->url !== null) {
 			// @phpstan-ignore-next-line
 			if (CACHE_PATH === false) {
@@ -322,15 +331,17 @@ class FreshRSS_Feed extends Minz_Model {
 				if (($mtime === true) || ($mtime > $this->lastUpdate) || $noCache) {
 					//Minz_Log::debug('FreshRSS no cache ' . $mtime . ' > ' . $this->lastUpdate . ' for ' . $clean_url);
 					return $simplePie;
-				} else {
-					//Minz_Log::debug('FreshRSS use cache for ' . $clean_url);
-					return null;
 				}
+				//Minz_Log::debug('FreshRSS use cache for ' . $clean_url);
 			}
 		}
+		return null;
 	}
 
-	public function loadGuids($simplePie) {
+	/**
+	 * @return array<string>
+	 */
+	public function loadGuids(SimplePie $simplePie) {
 		$hasUniqueGuids = true;
 		$testGuids = [];
 		$guids = [];
@@ -360,7 +371,7 @@ class FreshRSS_Feed extends Minz_Model {
 		return $guids;
 	}
 
-	public function loadEntries($simplePie) {
+	public function loadEntries(SimplePie $simplePie) {
 		$hasBadGuids = $this->attributes('hasBadGuids');
 
 		// We want chronological order and SimplePie uses reverse order.
@@ -481,7 +492,7 @@ class FreshRSS_Feed extends Minz_Model {
 	/**
 	 * To keep track of some new potentially unread articles since last commit+fetch from database
 	 */
-	public function incPendingUnread($n = 1) {
+	public function incPendingUnread(int $n = 1) {
 		$this->nbPendingNotRead += $n;
 	}
 
@@ -521,13 +532,13 @@ class FreshRSS_Feed extends Minz_Model {
 		return false;
 	}
 
-	protected function cacheFilename() {
+	protected function cacheFilename(): string {
 		$simplePie = customSimplePie($this->attributes());
 		$filename = $simplePie->get_cache_filename($this->url);
 		return CACHE_PATH . '/' . $filename . '.spc';
 	}
 
-	public function clearCache() {
+	public function clearCache(): bool {
 		return @unlink($this->cacheFilename());
 	}
 
@@ -535,7 +546,7 @@ class FreshRSS_Feed extends Minz_Model {
 		return @filemtime($this->cacheFilename());
 	}
 
-	public function lock() {
+	public function lock(): bool {
 		$this->lockPath = TMP_PATH . '/' . $this->hash() . '.freshrss.lock';
 		if (file_exists($this->lockPath) && ((time() - @filemtime($this->lockPath)) > 3600)) {
 			@unlink($this->lockPath);
@@ -548,11 +559,14 @@ class FreshRSS_Feed extends Minz_Model {
 		return true;
 	}
 
-	public function unlock() {
-		@unlink($this->lockPath);
+	public function unlock(): bool {
+		return @unlink($this->lockPath);
 	}
 
-	public function filterActions() {
+	/**
+	 * @return array<FreshRSS_FilterAction>
+	 */
+	public function filterActions(): array {
 		if ($this->filterActions == null) {
 			$this->filterActions = array();
 			$filters = $this->attributes('filters');
@@ -579,7 +593,7 @@ class FreshRSS_Feed extends Minz_Model {
 		}
 	}
 
-	public function filtersAction($action) {
+	public function filtersAction(string $action) {
 		$action = trim($action);
 		if ($action == '') {
 			return array();
@@ -596,7 +610,7 @@ class FreshRSS_Feed extends Minz_Model {
 		return $filters;
 	}
 
-	public function _filtersAction($action, $filters) {
+	public function _filtersAction(string $action, $filters) {
 		$action = trim($action);
 		if ($action == '' || !is_array($filters)) {
 			return false;
@@ -609,7 +623,7 @@ class FreshRSS_Feed extends Minz_Model {
 			$filterAction = $filterActions[$i];
 			if ($filterAction == null || !is_array($filterAction->actions()) ||
 				$filterAction->booleanSearch() == null || trim($filterAction->booleanSearch()->getRawInput()) == '') {
-				array_splice($filterAction, $i, 1);
+				array_splice($filterActions, $i, 1);
 				continue;
 			}
 			$actions = $filterAction->actions();
@@ -657,7 +671,7 @@ class FreshRSS_Feed extends Minz_Model {
 
 	//<WebSub>
 
-	public function pubSubHubbubEnabled() {
+	public function pubSubHubbubEnabled(): bool {
 		$url = $this->selfUrl ? $this->selfUrl : $this->url;
 		$hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($url) . '/!hub.json';
 		if ($hubFile = @file_get_contents($hubFilename)) {
@@ -670,7 +684,7 @@ class FreshRSS_Feed extends Minz_Model {
 		return false;
 	}
 
-	public function pubSubHubbubError($error = true) {
+	public function pubSubHubbubError(bool $error = true): bool {
 		$url = $this->selfUrl ? $this->selfUrl : $this->url;
 		$hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($url) . '/!hub.json';
 		$hubFile = @file_get_contents($hubFilename);
@@ -683,6 +697,9 @@ class FreshRSS_Feed extends Minz_Model {
 		return false;
 	}
 
+	/**
+	 * @return string|false
+	 */
 	public function pubSubHubbubPrepare() {
 		$key = '';
 		if (Minz_Request::serverIsPublic(FreshRSS_Context::$system_conf->base_url) &&
@@ -731,7 +748,7 @@ class FreshRSS_Feed extends Minz_Model {
 	}
 
 	//Parameter true to subscribe, false to unsubscribe.
-	public function pubSubHubbubSubscribe($state) {
+	public function pubSubHubbubSubscribe(bool $state): bool {
 		if ($state) {
 			$url = $this->selfUrl ? $this->selfUrl : $this->url;
 		} else {

+ 58 - 46
app/Models/FeedDAO.php

@@ -2,7 +2,7 @@
 
 class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 
-	protected function addColumn($name) {
+	protected function addColumn(string $name) {
 		Minz_Log::warning(__method__ . ': ' . $name);
 		try {
 			if ($name === 'attributes') {	//v1.11.0
@@ -14,7 +14,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		return false;
 	}
 
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if (isset($errorInfo[0])) {
 			if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_COLUMN) {
 				foreach (['attributes'] as $column) {
@@ -27,7 +27,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		return false;
 	}
 
-	public function addFeed($valuesTmp) {
+	public function addFeed(array $valuesTmp) {
 		$sql = '
 			INSERT INTO `_feed`
 				(
@@ -84,7 +84,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		}
 	}
 
-	public function addFeedObject($feed) {
+	public function addFeedObject($feed): int {
 		// TODO: not sure if we should write this method in DAO since DAO
 		// should not be aware about feed class
 
@@ -120,7 +120,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		return $feed_search->id();
 	}
 
-	public function updateFeed($id, $valuesTmp) {
+	public function updateFeed(int $id, array $valuesTmp) {
 		if (isset($valuesTmp['name'])) {
 			$valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8');
 		}
@@ -163,21 +163,18 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		}
 	}
 
-	public function updateFeedAttribute($feed, $key, $value) {
-		if ($feed instanceof FreshRSS_Feed) {
-			$feed->_attributes($key, $value);
-			return $this->updateFeed(
-					$feed->id(),
-					array('attributes' => $feed->attributes())
-				);
-		}
-		return false;
+	public function updateFeedAttribute(FreshRSS_Feed $feed, string $key, $value) {
+		$feed->_attributes($key, $value);
+		return $this->updateFeed(
+				$feed->id(),
+				array('attributes' => $feed->attributes())
+			);
 	}
 
 	/**
 	 * @see updateCachedValue()
 	 */
-	public function updateLastUpdate($id, $inError = false, $mtime = 0) {
+	public function updateLastUpdate(int $id, bool $inError = false, int $mtime = 0) {
 		$sql = 'UPDATE `_feed` SET `lastUpdate`=?, error=? WHERE id=?';
 		$values = array(
 			$mtime <= 0 ? time() : $mtime,
@@ -195,12 +192,12 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		}
 	}
 
-	public function mute($id, $value = true) {
+	public function mute(int $id, bool $value = true) {
 		$sql = 'UPDATE `_feed` SET ttl=' . ($value ? '-' : '') . 'ABS(ttl) WHERE id=' . intval($id);
 		return $this->pdo->exec($sql);
 	}
 
-	public function changeCategory($idOldCat, $idNewCat) {
+	public function changeCategory(int $idOldCat, int $idNewCat) {
 		$catDAO = FreshRSS_Factory::createCategoryDao();
 		$newCat = $catDAO->searchById($idNewCat);
 		if (!$newCat) {
@@ -224,7 +221,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		}
 	}
 
-	public function deleteFeed($id) {
+	public function deleteFeed(int $id) {
 		$sql = 'DELETE FROM `_feed` WHERE id=?';
 		$stm = $this->pdo->prepare($sql);
 
@@ -238,7 +235,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			return false;
 		}
 	}
-	public function deleteFeedByCategory($id) {
+	public function deleteFeedByCategory(int $id) {
 		$sql = 'DELETE FROM `_feed` WHERE category=?';
 		$stm = $this->pdo->prepare($sql);
 
@@ -265,6 +262,9 @@ SQL;
 		}
 	}
 
+	/**
+	 * @return FreshRSS_Feed|null
+	 */
 	public function searchById($id) {
 		$sql = 'SELECT * FROM `_feed` WHERE id=:id';
 		$stm = $this->pdo->prepare($sql);
@@ -272,14 +272,13 @@ SQL;
 		$stm->execute();
 		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
 		$feed = self::daoToFeed($res);
-
-		if (isset($feed[$id])) {
-			return $feed[$id];
-		} else {
-			return null;
-		}
+		return $feed[$id] ?? null;
 	}
-	public function searchByUrl($url) {
+
+	/**
+	 * @return FreshRSS_Feed|null
+	 */
+	public function searchByUrl(string $url) {
 		$sql = 'SELECT * FROM `_feed` WHERE url=?';
 		$stm = $this->pdo->prepare($sql);
 
@@ -288,21 +287,19 @@ SQL;
 		$stm->execute($values);
 		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
 		$feed = current(self::daoToFeed($res));
-
-		if (isset($feed) && $feed !== false) {
-			return $feed;
-		} else {
-			return null;
-		}
+		return $feed == false ? null : $feed;
 	}
 
-	public function listFeedsIds() {
+	public function listFeedsIds(): array {
 		$sql = 'SELECT id FROM `_feed`';
 		$stm = $this->pdo->query($sql);
 		return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
 	}
 
-	public function listFeeds() {
+	/**
+	 * @return array<FreshRSS_Feed>
+	 */
+	public function listFeeds(): array {
 		$sql = 'SELECT * FROM `_feed` ORDER BY name';
 		$stm = $this->pdo->query($sql);
 		return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
@@ -327,7 +324,7 @@ SQL;
 	/**
 	 * For API
 	 */
-	public function arrayFeedCategoryNames() {
+	public function arrayFeedCategoryNames(): array {
 		$sql = <<<'SQL'
 SELECT f.id, f.name, c.name as c_name FROM `_feed` f
 INNER JOIN `_category` c ON c.id = f.category
@@ -347,7 +344,7 @@ SQL;
 	/**
 	 * Use $defaultCacheDuration == -1 to return all feeds, without filtering them by TTL.
 	 */
-	public function listFeedsOrderUpdate($defaultCacheDuration = 3600, $limit = 0) {
+	public function listFeedsOrderUpdate(int $defaultCacheDuration = 3600, int $limit = 0) {
 		$this->updateTTL();
 		$sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, ttl, attributes '
 			. 'FROM `_feed` '
@@ -369,7 +366,7 @@ SQL;
 		}
 	}
 
-	public function listTitles($id, $limit = null) {
+	public function listTitles(int $id, int $limit = 0) {
 		$sql = 'SELECT title FROM `_entry` WHERE id_feed=:id_feed ORDER BY id DESC'
 			. ($limit < 1 ? '' : ' LIMIT ' . intval($limit));
 
@@ -382,7 +379,7 @@ SQL;
 		return false;
 	}
 
-	public function listByCategory($cat) {
+	public function listByCategory(int $cat): array {
 		$sql = 'SELECT * FROM `_feed` WHERE category=?';
 		$stm = $this->pdo->prepare($sql);
 
@@ -397,7 +394,7 @@ SQL;
 		return $feeds;
 	}
 
-	public function countEntries($id) {
+	public function countEntries(int $id) {
 		$sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=?';
 		$stm = $this->pdo->prepare($sql);
 		$values = array($id);
@@ -407,7 +404,7 @@ SQL;
 		return $res[0]['count'];
 	}
 
-	public function countNotRead($id) {
+	public function countNotRead(int $id) {
 		$sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=? AND is_read=0';
 		$stm = $this->pdo->prepare($sql);
 		$values = array($id);
@@ -417,14 +414,17 @@ SQL;
 		return $res[0]['count'];
 	}
 
-	public function updateCachedValues($id = null) {
+	/**
+	 * @return int|false
+	 */
+	public function updateCachedValues(int $id = 0) {
 		//2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE
 		$sql = 'UPDATE `_feed` '
 			. 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `_entry` e1 WHERE e1.id_feed=`_feed`.id),'
 			. '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `_entry` e2 WHERE e2.id_feed=`_feed`.id AND e2.is_read=0)'
-			. ($id != null ? ' WHERE id=:id' : '');
+			. ($id != 0 ? ' WHERE id=:id' : '');
 		$stm = $this->pdo->prepare($sql);
-		if ($id != null) {
+		if ($id != 0) {
 			$stm->bindParam(':id', $id, PDO::PARAM_INT);
 		}
 
@@ -437,7 +437,10 @@ SQL;
 		}
 	}
 
-	public function keepMaxUnread($id, $n) {
+	/**
+	 * @return int|false
+	 */
+	public function keepMaxUnread(int $id, int $n) {
 		//Double SELECT for MySQL workaround ERROR 1093 (HY000)
 		$sql = <<<'SQL'
 UPDATE `_entry` SET is_read=1
@@ -477,7 +480,10 @@ SQL;
 		return $affected;
 	}
 
-	public function truncate($id) {
+	/**
+	 * @return int|false
+	 */
+	public function truncate(int $id) {
 		$sql = 'DELETE FROM `_entry` WHERE id_feed=:id';
 		$stm = $this->pdo->prepare($sql);
 		$stm->bindParam(':id', $id, PDO::PARAM_INT);
@@ -528,7 +534,10 @@ SQL;
 		$this->pdo->commit();
 	}
 
-	public static function daoToFeed($listDAO, $catID = null) {
+	/**
+	 * @return array<FreshRSS_Feed>
+	 */
+	public static function daoToFeed($listDAO, $catID = null): array {
 		$list = array();
 
 		if (!is_array($listDAO)) {
@@ -589,6 +598,9 @@ SQL;
 		}
 	}
 
+	/**
+	 * @return int|false
+	 */
 	public function count() {
 		$sql = 'SELECT COUNT(e.id) AS count FROM `_feed` e';
 		$stm = $this->pdo->query($sql);

+ 1 - 1
app/Models/FeedDAOSQLite.php

@@ -2,7 +2,7 @@
 
 class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO {
 
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if ($tableInfo = $this->pdo->query("PRAGMA table_info('feed')")) {
 			$columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1);
 			foreach (['attributes'] as $column) {

+ 3 - 0
app/Models/Tag.php

@@ -1,6 +1,9 @@
 <?php
 
 class FreshRSS_Tag extends Minz_Model {
+	/**
+	 * @var int
+	 */
 	private $id = 0;
 	private $name;
 	private $attributes = [];

+ 2 - 2
app/Models/TagDAO.php

@@ -2,7 +2,7 @@
 
 class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 
-	public function sqlIgnore() {
+	public function sqlIgnore(): string {
 		return 'IGNORE';
 	}
 
@@ -30,7 +30,7 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		return $ok;
 	}
 
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if (isset($errorInfo[0])) {
 			if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_TABLE_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_TABLE) {
 				if (stripos($errorInfo[2], 'tag') !== false) {

+ 1 - 1
app/Models/TagDAOPGSQL.php

@@ -2,7 +2,7 @@
 
 class FreshRSS_TagDAOPGSQL extends FreshRSS_TagDAO {
 
-	public function sqlIgnore() {
+	public function sqlIgnore(): string {
 		return '';	//TODO
 	}
 

+ 2 - 2
app/Models/TagDAOSQLite.php

@@ -2,11 +2,11 @@
 
 class FreshRSS_TagDAOSQLite extends FreshRSS_TagDAO {
 
-	public function sqlIgnore() {
+	public function sqlIgnore(): string {
 		return 'OR IGNORE';
 	}
 
-	protected function autoUpdateDb($errorInfo) {
+	protected function autoUpdateDb(array $errorInfo) {
 		if ($tableInfo = $this->pdo->query("SELECT sql FROM sqlite_master where name='tag'")) {
 			$showCreate = $tableInfo->fetchColumn();
 			if (stripos($showCreate, 'tag') === false) {

+ 1 - 1
app/Services/ImportService.php

@@ -154,7 +154,7 @@ class FreshRSS_Import_Service {
 				// addFeedObject checks if feed is already in DB so nothing else to
 				// check here
 				$id = $this->feedDAO->addFeedObject($feed);
-				$error = ($id === false);
+				$error = ($id == false);
 			} else {
 				$error = true;
 			}

+ 2 - 7
lib/lib_rss.php

@@ -193,11 +193,7 @@ function timestamptodate ($t, $hour = true) {
 	return @date ($date, $t);
 }
 
-/**
- * @param string $text
- * @return string
- */
-function html_only_entity_decode($text) {
+function html_only_entity_decode(string $text): string {
 	static $htmlEntitiesOnly = null;
 	if ($htmlEntitiesOnly === null) {
 		$htmlEntitiesOnly = array_flip(array_diff(
@@ -210,9 +206,8 @@ function html_only_entity_decode($text) {
 
 /**
  * @param array<string,mixed> $attributes
- * @return SimplePie
  */
-function customSimplePie($attributes = array()) {
+function customSimplePie($attributes = array()): SimplePie {
 	$limits = FreshRSS_Context::$system_conf->limits;
 	$simplePie = new SimplePie();
 	$simplePie->set_useragent(FRESHRSS_USERAGENT);

+ 53 - 70
p/api/fever.php

@@ -63,13 +63,7 @@ function debugInfo() {
 
 class FeverDAO extends Minz_ModelPdo
 {
-	/**
-	 * @param string $prefix
-	 * @param array $values
-	 * @param array $bindArray
-	 * @return string
-	 */
-	protected function bindParamArray($prefix, $values, &$bindArray) {
+	protected function bindParamArray(string $prefix, array $values, array &$bindArray): string {
 		$str = '';
 		for ($i = 0; $i < count($values); $i++) {
 			$str .= ':' . $prefix . $i . ',';
@@ -79,13 +73,9 @@ class FeverDAO extends Minz_ModelPdo
 	}
 
 	/**
-	 * @param array $feed_ids
-	 * @param array $entry_ids
-	 * @param int|null $max_id
-	 * @param int|null $since_id
 	 * @return FreshRSS_Entry[]
 	 */
-	public function findEntries(array $feed_ids, array $entry_ids, $max_id, $since_id) {
+	public function findEntries(array $feed_ids, array $entry_ids, string $max_id, string $since_id) {
 		$values = array();
 		$order = '';
 		$entryDAO = FreshRSS_Factory::createEntryDao();
@@ -98,11 +88,11 @@ class FeverDAO extends Minz_ModelPdo
 		if (!empty($entry_ids)) {
 			$bindEntryIds = $this->bindParamArray('id', $entry_ids, $values);
 			$sql .= " id IN($bindEntryIds)";
-		} elseif ($max_id != null) {
+		} elseif ($max_id != '') {
 			$sql .= ' id < :id';
 			$values[':id'] = $max_id;
 			$order = ' ORDER BY id DESC';
-		} elseif ($since_id != null) {
+		} elseif ($since_id != '') {
 			$sql .= ' id > :id';
 			$values[':id'] = $since_id;
 			$order = ' ORDER BY id ASC';
@@ -149,7 +139,7 @@ class FeverAPI
 	 * API Password sent from client is the result of the md5 sum of
 	 * your FreshRSS "username:your-api-password" combination
 	 */
-	private function authenticate() {
+	private function authenticate(): bool {
 		FreshRSS_Context::$user_conf = null;
 		Minz_Session::_param('currentUser');
 		$feverKey = empty($_POST['api_key']) ? '' : substr(trim($_POST['api_key']), 0, 128);
@@ -176,10 +166,7 @@ class FeverAPI
 		return false;
 	}
 
-	/**
-	 * @return bool
-	 */
-	public function isAuthenticatedApiUser() {
+	public function isAuthenticatedApiUser(): bool {
 		$this->authenticate();
 
 		if (FreshRSS_Context::$user_conf !== null) {
@@ -191,11 +178,9 @@ class FeverAPI
 
 	/**
 	 * This does all the processing, since the fever api does not have a specific variable that specifies the operation
-	 *
-	 * @return array
 	 * @throws Exception
 	 */
-	public function process() {
+	public function process(): array {
 		$response_arr = array();
 
 		if (!$this->isAuthenticatedApiUser()) {
@@ -247,7 +232,7 @@ class FeverAPI
 						break;
 					case 'feed':
 					case 'group':
-						$before = isset($_REQUEST['before']) ? $_REQUEST['before'] : null;
+						$before = $_REQUEST['before'] ?? '';
 						$this->{$method_name}($id, $before);
 						break;
 				}
@@ -271,12 +256,8 @@ class FeverAPI
 
 	/**
 	 * Returns the complete JSON, with 'api_version' and status as 'auth'.
-	 *
-	 * @param int $status
-	 * @param array $reply
-	 * @return string
 	 */
-	public function wrap($status, array $reply = array()) {
+	public function wrap(int $status, array $reply = array()): string {
 		$arr = array('api_version' => self::API_LEVEL, 'auth' => $status);
 
 		if ($status === self::STATUS_OK) {
@@ -289,10 +270,8 @@ class FeverAPI
 
 	/**
 	 * every authenticated method includes last_refreshed_on_time
-	 *
-	 * @return int
 	 */
-	protected function lastRefreshedOnTime() {
+	protected function lastRefreshedOnTime(): int {
 		$lastUpdate = 0;
 
 		$entries = $this->feedDAO->listFeedsOrderUpdate(-1, 1);
@@ -305,10 +284,7 @@ class FeverAPI
 		return $lastUpdate;
 	}
 
-	/**
-	 * @return array
-	 */
-	protected function getFeeds() {
+	protected function getFeeds(): array {
 		$feeds = array();
 		$myFeeds = $this->feedDAO->listFeeds();
 
@@ -328,10 +304,7 @@ class FeverAPI
 		return $feeds;
 	}
 
-	/**
-	 * @return array
-	 */
-	protected function getGroups() {
+	protected function getGroups(): array {
 		$groups = array();
 
 		$categoryDAO = FreshRSS_Factory::createCategoryDao();
@@ -348,10 +321,7 @@ class FeverAPI
 		return $groups;
 	}
 
-	/**
-	 * @return array
-	 */
-	protected function getFavicons() {
+	protected function getFavicons(): array {
 		$favicons = array();
 		$salt = FreshRSS_Context::$system_conf->salt;
 		$myFeeds = $this->feedDAO->listFeeds();
@@ -381,10 +351,7 @@ class FeverAPI
 		return $this->entryDAO->count();
 	}
 
-	/**
-	 * @return array
-	 */
-	protected function getFeedsGroup() {
+	protected function getFeedsGroup(): array {
 		$groups = array();
 		$ids = array();
 		$myFeeds = $this->feedDAO->listFeeds();
@@ -406,24 +373,19 @@ class FeverAPI
 
 	/**
 	 * AFAIK there is no 'hot links' alternative in FreshRSS
-	 * @return array
 	 */
-	protected function getLinks() {
+	protected function getLinks(): array {
 		return array();
 	}
 
 	/**
 	 * @param array $ids
-	 * @return string
 	 */
-	protected function entriesToIdList($ids = array()) {
+	protected function entriesToIdList(array $ids = array()): string {
 		return implode(',', array_values($ids));
 	}
 
-	/**
-	 * @return string
-	 */
-	protected function getUnreadItemIds() {
+	protected function getUnreadItemIds(): string {
 		$entries = $this->entryDAO->listIdsWhere('a', '', FreshRSS_Entry::STATE_NOT_READ, 'ASC', 0);
 		return $this->entriesToIdList($entries);
 	}
@@ -436,30 +398,39 @@ class FeverAPI
 		return $this->entriesToIdList($entries);
 	}
 
+	/**
+	 * @return integer|false
+	 */
 	protected function setItemAsRead($id) {
 		return $this->entryDAO->markRead($id, true);
 	}
 
+	/**
+	 * @return integer|false
+	 */
 	protected function setItemAsUnread($id) {
 		return $this->entryDAO->markRead($id, false);
 	}
 
+	/**
+	 * @return integer|false
+	 */
 	protected function setItemAsSaved($id) {
 		return $this->entryDAO->markFavorite($id, true);
 	}
 
+	/**
+	 * @return integer|false
+	 */
 	protected function setItemAsUnsaved($id) {
 		return $this->entryDAO->markFavorite($id, false);
 	}
 
-	/**
-	 * @return array
-	 */
-	protected function getItems() {
+	protected function getItems(): array {
 		$feed_ids = array();
 		$entry_ids = array();
-		$max_id = null;
-		$since_id = null;
+		$max_id = '';
+		$since_id = '';
 
 		if (isset($_REQUEST['feed_ids']) || isset($_REQUEST['group_ids'])) {
 			if (isset($_REQUEST['feed_ids'])) {
@@ -485,12 +456,18 @@ class FeverAPI
 
 		if (isset($_REQUEST['max_id'])) {
 			// use the max_id argument to request the previous $item_limit items
-			$max_id = ctype_digit('' . $_REQUEST['max_id']) ? intval($_REQUEST['max_id']) : null;
+			$max_id = '' . $_REQUEST['max_id'];
+			if (!ctype_digit($max_id)) {
+				$max_id = '';
+			}
 		} elseif (isset($_REQUEST['with_ids'])) {
 			$entry_ids = explode(',', $_REQUEST['with_ids']);
 		} elseif (isset($_REQUEST['since_id'])) {
 			// use the since_id argument to request the next $item_limit items
-			$since_id = ctype_digit('' . $_REQUEST['since_id']) ? intval($_REQUEST['since_id']) : null;
+			$since_id = '' . $_REQUEST['since_id'];
+			if (!ctype_digit($since_id)) {
+				$since_id = '';
+			}
 		}
 
 		$items = array();
@@ -526,23 +503,29 @@ class FeverAPI
 	/**
 	 * TODO replace by a dynamic fetch for id <= $before timestamp
 	 *
-	 * @param int $beforeTimestamp
-	 * @return int
+	 * @param int|string $beforeTimestamp
+	 * @return string
 	 */
-	protected function convertBeforeToId($beforeTimestamp) {
-		return $beforeTimestamp == 0 ? 0 : $beforeTimestamp . '000000';
+	protected function convertBeforeToId($beforeTimestamp): string {
+		return $beforeTimestamp == '0' ? '0' : $beforeTimestamp . '000000';
 	}
 
-	protected function setFeedAsRead($id, $before) {
+	/**
+	 * @return integer|false
+	 */
+	protected function setFeedAsRead(string $id, string $before) {
 		$before = $this->convertBeforeToId($before);
 		return $this->entryDAO->markReadFeed($id, $before);
 	}
 
-	protected function setGroupAsRead($id, $before) {
+	/**
+	 * @return integer|false
+	 */
+	protected function setGroupAsRead(string $id, string $before) {
 		$before = $this->convertBeforeToId($before);
 
 		// special case to mark all items as read
-		if ($id == 0) {
+		if ($id == '0') {
 			return $this->entryDAO->markReadEntries($before);
 		}