From 4cc3374f9f6cdd6ea05d4d16942d0165d8b2ed90 Mon Sep 17 00:00:00 2001 From: wn_ Date: Wed, 10 Nov 2021 21:38:25 +0000 Subject: Initial go at PHPStan rule level 6. --- classes/config.php | 41 +++++++++++++++++++++++++++++++++-------- classes/db.php | 9 ++++----- classes/errors.php | 6 +++++- classes/logger.php | 13 ++++++++----- classes/logger/adapter.php | 4 ++-- classes/logger/sql.php | 2 +- classes/logger/stdout.php | 3 ++- classes/logger/syslog.php | 3 ++- classes/urlhelper.php | 42 +++++++++++++++++++++++++++--------------- 9 files changed, 84 insertions(+), 39 deletions(-) (limited to 'classes') diff --git a/classes/config.php b/classes/config.php index 53a31b61c..9096a4953 100644 --- a/classes/config.php +++ b/classes/config.php @@ -231,9 +231,13 @@ class Config { Config::T_STRING ], ]; + /** @var Config|null */ private static $instance; + /** @var array> */ private $params = []; + + /** @var array */ private $version = []; /** @var Db_Migrations|null $migrations */ @@ -268,10 +272,16 @@ class Config { directory, its contents are displayed instead of git commit-based version, this could be generated based on source git tree commit used when creating the package */ + /** + * @return array|string + */ static function get_version(bool $as_string = true) { return self::get_instance()->_get_version($as_string); } + /** + * @return array|string + */ private function _get_version(bool $as_string = true) { $root_dir = dirname(__DIR__); @@ -298,7 +308,10 @@ class Config { return $as_string ? $this->version["version"] : $this->version; } - static function get_version_from_git(string $dir) { + /** + * @return array + */ + static function get_version_from_git(string $dir): array { $descriptorspec = [ 1 => ["pipe", "w"], // STDOUT 2 => ["pipe", "w"], // STDERR @@ -364,6 +377,9 @@ class Config { return self::get_migrations()->get_version(); } + /** + * @return bool|int|string + */ static function cast_to(string $value, int $type_hint) { switch ($type_hint) { case self::T_BOOL: @@ -375,24 +391,30 @@ class Config { } } + /** + * @return bool|int|string + */ private function _get(string $param) { list ($value, $type_hint) = $this->params[$param]; return $this->cast_to($value, $type_hint); } - private function _add(string $param, string $default, int $type_hint) { + private function _add(string $param, string $default, int $type_hint): void { $override = getenv(self::_ENVVAR_PREFIX . $param); $this->params[$param] = [ self::cast_to($override !== false ? $override : $default, $type_hint), $type_hint ]; } - static function add(string $param, string $default, int $type_hint = Config::T_STRING) { + static function add(string $param, string $default, int $type_hint = Config::T_STRING): void { $instance = self::get_instance(); - return $instance->_add($param, $default, $type_hint); + $instance->_add($param, $default, $type_hint); } + /** + * @return bool|int|string + */ static function get(string $param) { $instance = self::get_instance(); @@ -431,6 +453,9 @@ class Config { /* sanity check stuff */ + /** + * @return array> A list of entries identifying tt-rss tables with bad config + */ private static function check_mysql_tables() { $pdo = Db::pdo(); @@ -447,7 +472,7 @@ class Config { return $bad_tables; } - static function sanity_check() { + static function sanity_check(): void { /* we don't actually need the DB object right now but some checks below might use ORM which won't be initialized @@ -621,11 +646,11 @@ class Config { } } - private static function format_error($msg) { + private static function format_error(string $msg): string { return "
$msg
"; } - static function get_override_links() { + static function get_override_links(): string { $rv = ""; $local_css = get_theme_path(self::get(self::LOCAL_OVERRIDE_STYLESHEET)); @@ -637,7 +662,7 @@ class Config { return $rv; } - static function get_user_agent() { + static function get_user_agent(): string { return sprintf(self::get(self::HTTP_USER_AGENT), self::get_version()); } } diff --git a/classes/db.php b/classes/db.php index 7b669cf32..2cc89f5ba 100755 --- a/classes/db.php +++ b/classes/db.php @@ -17,7 +17,7 @@ class Db } } - static function NOW() { + static function NOW(): string { return date("Y-m-d H:i:s", time()); } @@ -25,7 +25,7 @@ class Db // } - public static function get_dsn() { + public static function get_dsn(): string { $db_port = Config::get(Config::DB_PORT) ? ';port=' . Config::get(Config::DB_PORT) : ''; $db_host = Config::get(Config::DB_HOST) ? ';host=' . Config::get(Config::DB_HOST) : ''; if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET)) { @@ -88,12 +88,11 @@ class Db return self::$instance->pdo; } - public static function sql_random_function() { + public static function sql_random_function(): string { if (Config::get(Config::DB_TYPE) == "mysql") { return "RAND()"; - } else { - return "RANDOM()"; } + return "RANDOM()"; } } diff --git a/classes/errors.php b/classes/errors.php index 3599c2639..31be558cf 100644 --- a/classes/errors.php +++ b/classes/errors.php @@ -7,7 +7,11 @@ class Errors { const E_SCHEMA_MISMATCH = "E_SCHEMA_MISMATCH"; const E_URL_SCHEME_MISMATCH = "E_URL_SCHEME_MISMATCH"; - static function to_json(string $code, array $params = []) { + /** + * @param Errors::E_* $code + * @param array $params + */ + static function to_json(string $code, array $params = []): string { return json_encode(["error" => ["code" => $code, "params" => $params]]); } } diff --git a/classes/logger.php b/classes/logger.php index 42ab4452c..ef6173a42 100755 --- a/classes/logger.php +++ b/classes/logger.php @@ -1,6 +1,9 @@ 'E_USER_DEPRECATED', 32767 => 'E_ALL']; - static function log_error(int $errno, string $errstr, string $file, int $line, $context) { + static function log_error(int $errno, string $errstr, string $file, int $line, string $context): bool { return self::get_instance()->_log_error($errno, $errstr, $file, $line, $context); } - private function _log_error($errno, $errstr, $file, $line, $context) { + private function _log_error(int $errno, string $errstr, string $file, int $line, string $context): bool { //if ($errno == E_NOTICE) return false; if ($this->adapter) @@ -38,11 +41,11 @@ class Logger { return false; } - static function log(int $errno, string $errstr, $context = "") { + static function log(int $errno, string $errstr, string $context = ""): bool { return self::get_instance()->_log($errno, $errstr, $context); } - private function _log(int $errno, string $errstr, $context = "") { + private function _log(int $errno, string $errstr, string $context = ""): bool { if ($this->adapter) return $this->adapter->log_error($errno, $errstr, '', 0, $context); else @@ -65,7 +68,7 @@ class Logger { $this->adapter = new Logger_Stdout(); break; default: - $this->adapter = false; + $this->adapter = null; } if ($this->adapter && !implements_interface($this->adapter, "Logger_Adapter")) diff --git a/classes/logger/adapter.php b/classes/logger/adapter.php index 79f641441..b0287b5fa 100644 --- a/classes/logger/adapter.php +++ b/classes/logger/adapter.php @@ -1,4 +1,4 @@ 117) { diff --git a/classes/logger/stdout.php b/classes/logger/stdout.php index e906853ce..b15649028 100644 --- a/classes/logger/stdout.php +++ b/classes/logger/stdout.php @@ -1,7 +1,7 @@ $parts + */ + static function build_url(array $parts): string { $tmp = $parts['scheme'] . "://" . $parts['host']; if (isset($parts['path'])) $tmp .= $parts['path']; @@ -81,7 +84,10 @@ class UrlHelper { } // extended filtering involves validation for safe ports and loopback - static function validate($url, $extended_filtering = false) { + /** + * @return bool|string false if something went wrong, otherwise the URL string + */ + static function validate(string $url, bool $extended_filtering = false) { $url = clean($url); @@ -138,7 +144,10 @@ class UrlHelper { return $url; } - static function resolve_redirects($url, $timeout, $nest = 0) { + /** + * @return bool|string + */ + static function resolve_redirects(string $url, int $timeout, int $nest = 0) { // too many redirects if ($nest > 10) @@ -189,12 +198,15 @@ class UrlHelper { return false; } - // TODO: max_size currently only works for CURL transfers + /** + * @return bool|string false if something went wrong, otherwise string contents + */ + // TODO: max_size currently only works for CURL transfers // TODO: multiple-argument way is deprecated, first parameter is a hash now public static function fetch($options /* previously: 0: $url , 1: $type = false, 2: $login = false, 3: $pass = false, 4: $post_query = false, 5: $timeout = false, 6: $timestamp = 0, 7: $useragent = false*/) { - self::$fetch_last_error = false; + self::$fetch_last_error = ""; self::$fetch_last_error_code = -1; self::$fetch_last_error_content = ""; self::$fetch_last_content_type = ""; @@ -510,7 +522,7 @@ class UrlHelper { } } - public static function url_to_youtube_vid($url) { + public static function url_to_youtube_vid(string $url) { # : bool|string $url = str_replace("youtube.com", "youtube-nocookie.com", $url); $regexps = [ -- cgit v1.2.3-54-g00ecf From 7a919a79d7b504c591ba2360b68f2e401675fdae Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 11:08:04 +0000 Subject: Fix some additional PHPStan warnings in UrlHelper. --- classes/urlhelper.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'classes') diff --git a/classes/urlhelper.php b/classes/urlhelper.php index 5f175af3c..a660af170 100644 --- a/classes/urlhelper.php +++ b/classes/urlhelper.php @@ -16,7 +16,7 @@ class UrlHelper { static bool $fetch_curl_used; /** - * @param array $parts + * @param array $parts */ static function build_url(array $parts): string { $tmp = $parts['scheme'] . "://" . $parts['host']; @@ -113,6 +113,11 @@ class UrlHelper { } else { $tokens['host'] = idn_to_ascii($tokens['host']); } + + // if `idn_to_ascii` failed + if ($tokens['host'] === false) { + return false; + } } } @@ -199,6 +204,7 @@ class UrlHelper { } /** + * @param array|string $options * @return bool|string false if something went wrong, otherwise string contents */ // TODO: max_size currently only works for CURL transfers @@ -522,7 +528,10 @@ class UrlHelper { } } - public static function url_to_youtube_vid(string $url) { # : bool|string + /** + * @return bool|string false if the provided URL didn't match expected patterns, otherwise the video ID string + */ + public static function url_to_youtube_vid(string $url) { $url = str_replace("youtube.com", "youtube-nocookie.com", $url); $regexps = [ -- cgit v1.2.3-54-g00ecf From 14ca0f2ac01c4ff9ffa27387326c6ebe5f002005 Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 12:26:30 +0000 Subject: Address PHPStan warnings in 'classes/counters.php'. --- classes/counters.php | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'classes') diff --git a/classes/counters.php b/classes/counters.php index 8a8b8bc71..1375a6694 100644 --- a/classes/counters.php +++ b/classes/counters.php @@ -1,7 +1,10 @@ > + */ + static function get_all(): array { return array_merge( self::get_global(), self::get_virt(), @@ -11,7 +14,12 @@ class Counters { ); } - static function get_conditional(array $feed_ids = null, array $label_ids = null) { + /** + * @param array $feed_ids + * @param array $label_ids + * @return array> + */ + static function get_conditional(array $feed_ids = null, array $label_ids = null): array { return array_merge( self::get_global(), self::get_virt(), @@ -21,7 +29,10 @@ class Counters { ); } - static private function get_cat_children(int $cat_id, int $owner_uid) { + /** + * @return array + */ + static private function get_cat_children(int $cat_id, int $owner_uid): array { $unread = 0; $marked = 0; @@ -40,7 +51,11 @@ class Counters { return [$unread, $marked]; } - private static function get_cats(array $cat_ids = null) { + /** + * @param array $cat_ids + * @return array> + */ + private static function get_cats(array $cat_ids = null): array { $ret = []; /* Labels category */ @@ -129,7 +144,11 @@ class Counters { return $ret; } - private static function get_feeds(array $feed_ids = null) { + /** + * @param array $feed_ids + * @return array> + */ + private static function get_feeds(array $feed_ids = null): array { $ret = []; @@ -199,7 +218,10 @@ class Counters { return $ret; } - private static function get_global() { + /** + * @return array> + */ + private static function get_global(): array { $ret = [ [ "id" => "global-unread", @@ -219,7 +241,10 @@ class Counters { return $ret; } - private static function get_virt() { + /** + * @return array> + */ + private static function get_virt(): array { $ret = []; @@ -263,7 +288,11 @@ class Counters { return $ret; } - static function get_labels(array $label_ids = null) { + /** + * @param array $label_ids + * @return array> + */ + static function get_labels(array $label_ids = null): array { $ret = []; -- cgit v1.2.3-54-g00ecf From eb068fbc47de8ce6bb7d1a368cfa1a20e9a2dc90 Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 16:47:51 +0000 Subject: Address PHPStan warnings in 'classes/prefs.php'. --- classes/prefs.php | 54 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 12 deletions(-) (limited to 'classes') diff --git a/classes/prefs.php b/classes/prefs.php index 85e7c34db..7e6033f4d 100644 --- a/classes/prefs.php +++ b/classes/prefs.php @@ -141,7 +141,10 @@ class Prefs { Prefs::_PREFS_MIGRATED ]; + /** @var Prefs|null */ private static $instance; + + /** @var array */ private $cache = []; /** @var PDO */ @@ -154,10 +157,13 @@ class Prefs { return self::$instance; } - static function is_valid(string $pref_name) { + static function is_valid(string $pref_name): bool { return isset(self::_DEFAULTS[$pref_name]); } + /** + * @return bool|int|null|string + */ static function get_default(string $pref_name) { if (self::is_valid($pref_name)) return self::_DEFAULTS[$pref_name][0]; @@ -181,10 +187,16 @@ class Prefs { // } + /** + * @return array> + */ static function get_all(int $owner_uid, int $profile_id = null) { return self::get_instance()->_get_all($owner_uid, $profile_id); } + /** + * @return array> + */ private function _get_all(int $owner_uid, int $profile_id = null) { $rv = []; @@ -205,7 +217,7 @@ class Prefs { return $rv; } - private function cache_all(int $owner_uid, $profile_id = null) { + private function cache_all(int $owner_uid, ?int $profile_id): void { if (!$profile_id) $profile_id = null; // fill cache with defaults @@ -232,11 +244,17 @@ class Prefs { } } - static function get(string $pref_name, int $owner_uid, int $profile_id = null) { + /** + * @return bool|int|null|string + */ + static function get(string $pref_name, int $owner_uid, ?int $profile_id) { return self::get_instance()->_get($pref_name, $owner_uid, $profile_id); } - private function _get(string $pref_name, int $owner_uid, int $profile_id = null) { + /** + * @return bool|int|null|string + */ + private function _get(string $pref_name, int $owner_uid, ?int $profile_id) { if (isset(self::_DEFAULTS[$pref_name])) { if (!$profile_id || in_array($pref_name, self::_PROFILE_BLACKLIST)) $profile_id = null; @@ -274,12 +292,15 @@ class Prefs { return null; } - private function _is_cached(string $pref_name, int $owner_uid, int $profile_id = null) { + private function _is_cached(string $pref_name, int $owner_uid, ?int $profile_id): bool { $cache_key = sprintf("%d/%d/%s", $owner_uid, $profile_id, $pref_name); return isset($this->cache[$cache_key]); } - private function _get_cache(string $pref_name, int $owner_uid, int $profile_id = null) { + /** + * @return bool|int|null|string + */ + private function _get_cache(string $pref_name, int $owner_uid, ?int $profile_id) { $cache_key = sprintf("%d/%d/%s", $owner_uid, $profile_id, $pref_name); if (isset($this->cache[$cache_key])) @@ -288,17 +309,23 @@ class Prefs { return null; } - private function _set_cache(string $pref_name, $value, int $owner_uid, int $profile_id = null) { + /** + * @param bool|int|string $value + */ + private function _set_cache(string $pref_name, $value, int $owner_uid, ?int $profile_id): void { $cache_key = sprintf("%d/%d/%s", $owner_uid, $profile_id, $pref_name); $this->cache[$cache_key] = $value; } - static function set(string $pref_name, $value, int $owner_uid, int $profile_id = null, bool $strip_tags = true) { + /** + * @param bool|int|string $value + */ + static function set(string $pref_name, $value, int $owner_uid, ?int $profile_id, bool $strip_tags = true): bool { return self::get_instance()->_set($pref_name, $value, $owner_uid, $profile_id); } - private function _delete(string $pref_name, int $owner_uid, int $profile_id = null) { + private function _delete(string $pref_name, int $owner_uid, ?int $profile_id): bool { $sth = $this->pdo->prepare("DELETE FROM ttrss_user_prefs2 WHERE pref_name = :name AND owner_uid = :uid AND (profile = :profile OR (:profile IS NULL AND profile IS NULL))"); @@ -306,7 +333,10 @@ class Prefs { return $sth->execute(["uid" => $owner_uid, "profile" => $profile_id, "name" => $pref_name ]); } - private function _set(string $pref_name, $value, int $owner_uid, int $profile_id = null, bool $strip_tags = true) { + /** + * @param bool|int|string $value + */ + private function _set(string $pref_name, $value, int $owner_uid, ?int $profile_id, bool $strip_tags = true): bool { if (!$profile_id) $profile_id = null; if ($profile_id && in_array($pref_name, self::_PROFILE_BLACKLIST)) @@ -359,7 +389,7 @@ class Prefs { return false; } - function migrate(int $owner_uid, int $profile_id = null) { + function migrate(int $owner_uid, ?int $profile_id): void { if (get_schema_version() < 141) return; @@ -401,7 +431,7 @@ class Prefs { } } - static function reset(int $owner_uid, int $profile_id = null) { + static function reset(int $owner_uid, ?int $profile_id): void { if (!$profile_id) $profile_id = null; $sth = Db::pdo()->prepare("DELETE FROM ttrss_user_prefs2 -- cgit v1.2.3-54-g00ecf From 3f8aaffd3499cd49912c3e2cb663d8572a96851e Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 18:53:52 +0000 Subject: Address PHPStan warnings in 'classes/rssutils.php'. This also includes a minor tweak in 'update.php' to account for 'getopt()' potentially returning false (indicating failure). --- classes/rssutils.php | 122 +++++++++++++++++++++++++++++++++++++-------------- update.php | 2 +- 2 files changed, 91 insertions(+), 33 deletions(-) (limited to 'classes') diff --git a/classes/rssutils.php b/classes/rssutils.php index 927a6c251..3815b3ca1 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -1,6 +1,9 @@ $article + */ + static function calculate_article_hash(array $article, PluginHost $pluginhost): string { $tmp = ""; $ignored_fields = [ "feed", "guid", "guid_hashed", "owner_uid", "force_catchup" ]; @@ -21,16 +24,16 @@ class RSSUtils { } // Strips utf8mb4 characters (i.e. emoji) for mysql - static function strip_utf8mb4(string $str) { + static function strip_utf8mb4(string $str): string { return preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $str); } - static function cleanup_feed_browser() { + static function cleanup_feed_browser(): void { $pdo = Db::pdo(); $pdo->query("DELETE FROM ttrss_feedbrowser_cache"); } - static function cleanup_feed_icons() { + static function cleanup_feed_icons(): void { $pdo = Db::pdo(); $sth = $pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ?"); @@ -52,7 +55,10 @@ class RSSUtils { } } - static function update_daemon_common(int $limit = 0, array $options = []) { + /** + * @param array $options + */ + static function update_daemon_common(int $limit = 0, array $options = []): int { if (!$limit) $limit = Config::get(Config::DAEMON_FEED_LIMIT); if (Config::get_schema_version() != Config::SCHEMA_VERSION) { @@ -272,7 +278,7 @@ class RSSUtils { } /** this is used when subscribing */ - static function update_basic_info(int $feed_id) { + static function update_basic_info(int $feed_id): void { $feed = ORM::for_table('ttrss_feeds') ->select_many('id', 'owner_uid', 'feed_url', 'auth_pass', 'auth_login', 'title', 'site_url') ->find_one($feed_id); @@ -728,6 +734,7 @@ class RSSUtils { }, $e, $feed); + // TODO: Just use FeedEnclosure (and modify it to cover whatever justified this)? $e_item = array( rewrite_relative_url($site_url, $e->link), $e->type, $e->length, $e->title, $e->width, $e->height); @@ -1265,8 +1272,14 @@ class RSSUtils { return true; } - /* TODO: move to DiskCache? */ - static function cache_enclosures($enclosures, $site_url) { + /** + * TODO: move to DiskCache? + * + * @param array> $enclosures An array of "enclosure arrays" [string $link, string $type, int $length, string, $title, int $width, int $height] + * @see RSSUtils::update_rss_feed() + * @see FeedEnclosure + */ + static function cache_enclosures(array $enclosures, string $site_url): void { $cache = new DiskCache("images"); if ($cache->is_writable()) { @@ -1298,7 +1311,7 @@ class RSSUtils { } /* TODO: move to DiskCache? */ - static function cache_media_url($cache, $url, $site_url) { + static function cache_media_url(DiskCache $cache, string $url, string $site_url): void { $url = rewrite_relative_url($site_url, $url); $local_filename = sha1($url); @@ -1322,7 +1335,7 @@ class RSSUtils { } /* TODO: move to DiskCache? */ - static function cache_media($html, $site_url) { + static function cache_media(string $html, string $site_url): void { $cache = new DiskCache("images"); if ($html && $cache->is_writable()) { @@ -1351,7 +1364,7 @@ class RSSUtils { } } - static function expire_error_log() { + static function expire_error_log(): void { Debug::log("Removing old error log entries..."); $pdo = Db::pdo(); @@ -1365,14 +1378,16 @@ class RSSUtils { } } - // deprecated; table not used - static function expire_feed_archive() { + /** + * @deprecated table not used + */ + static function expire_feed_archive(): void { $pdo = Db::pdo(); $pdo->query("DELETE FROM ttrss_archived_feeds"); } - static function expire_lock_files() { + static function expire_lock_files(): void { Debug::log("Removing old lock files...", Debug::LOG_VERBOSE); $num_deleted = 0; @@ -1413,7 +1428,15 @@ class RSSUtils { return $params; } */ - static function get_article_filters($filters, $title, $content, $link, $author, $tags, &$matched_rules = false, &$matched_filters = false) { + /** + * @param array> $filters + * @param array $tags + * @param array>|null $matched_rules + * @param array>|null $matched_filters + * + * @return array> An array of filter action arrays with keys "type" and "param" + */ + static function get_article_filters(array $filters, string $title, string $content, string $link, string $author, array $tags, array &$matched_rules = null, array &$matched_filters = null): array { $matches = array(); foreach ($filters as $filter) { @@ -1497,16 +1520,26 @@ class RSSUtils { return $matches; } - static function find_article_filter($filters, $filter_name) { + /** + * @param array> $filters An array of filter action arrays with keys "type" and "param" + * + * @return array|null A filter action array with keys "type" and "param" + */ + static function find_article_filter(array $filters, string $filter_name): ?array { foreach ($filters as $f) { if ($f["type"] == $filter_name) { return $f; }; } - return false; + return null; } - static function find_article_filters($filters, $filter_name) { + /** + * @param array> $filters An array of filter action arrays with keys "type" and "param" + * + * @return array> An array of filter action arrays with keys "type" and "param" + */ + static function find_article_filters(array $filters, string $filter_name): array { $results = array(); foreach ($filters as $f) { @@ -1517,7 +1550,10 @@ class RSSUtils { return $results; } - static function calculate_article_score($filters) { + /** + * @param array> $filters An array of filter action arrays with keys "type" and "param" + */ + static function calculate_article_score(array $filters): int { $score = 0; foreach ($filters as $f) { @@ -1528,7 +1564,12 @@ class RSSUtils { return $score; } - static function labels_contains_caption($labels, $caption) { + /** + * @param array> $labels An array of label arrays like [int $feed_id, string $caption, string $fg_color, string $bg_color] + * + * @see Article::_get_labels() + */ + static function labels_contains_caption(array $labels, string $caption): bool { foreach ($labels as $label) { if ($label[1] == $caption) { return true; @@ -1538,7 +1579,11 @@ class RSSUtils { return false; } - static function assign_article_to_label_filters($id, $filters, $owner_uid, $article_labels) { + /** + * @param array> $filters An array of filter action arrays with keys "type" and "param" + * @param array> $article_labels An array of label arrays like [int $feed_id, string $caption, string $fg_color, string $bg_color] + */ + static function assign_article_to_label_filters(int $id, array $filters, int $owner_uid, $article_labels): void { foreach ($filters as $f) { if ($f["type"] == "label") { if (!self::labels_contains_caption($article_labels, $f["param"])) { @@ -1548,20 +1593,20 @@ class RSSUtils { } } - static function make_guid_from_title($title) { + static function make_guid_from_title(string $title): ?string { return preg_replace("/[ \"\',.:;]/", "-", mb_strtolower(strip_tags($title), 'utf-8')); } /* counter cache is no longer used, if called truncate leftover data */ - static function cleanup_counters_cache() { + static function cleanup_counters_cache(): void { $pdo = Db::pdo(); $pdo->query("DELETE FROM ttrss_counters_cache"); $pdo->query("DELETE FROM ttrss_cat_counters_cache"); } - static function disable_failed_feeds() { + static function disable_failed_feeds(): void { if (Config::get(Config::DAEMON_UNSUCCESSFUL_DAYS_LIMIT) > 0) { $pdo = Db::pdo(); @@ -1599,7 +1644,7 @@ class RSSUtils { } } - static function housekeeping_user($owner_uid) { + static function housekeeping_user(int $owner_uid): void { $tmph = new PluginHost(); UserHelper::load_user_plugins($owner_uid, $tmph); @@ -1607,7 +1652,7 @@ class RSSUtils { $tmph->run_hooks(PluginHost::HOOK_HOUSE_KEEPING); } - static function housekeeping_common() { + static function housekeeping_common(): void { DiskCache::expire(); self::expire_lock_files(); @@ -1623,6 +1668,9 @@ class RSSUtils { PluginHost::getInstance()->run_hooks(PluginHost::HOOK_HOUSE_KEEPING); } + /** + * @return false|string + */ static function update_favicon(string $site_url, int $feed) { $icon_file = Config::get(Config::ICONS_DIR) . "/$feed.ico"; @@ -1687,11 +1735,14 @@ class RSSUtils { return $icon_file; } - static function is_gzipped($feed_data) { + static function is_gzipped(string $feed_data): bool { return strpos(substr($feed_data, 0, 3), "\x1f" . "\x8b" . "\x08", 0) === 0; } + /** + * @return array> An array of filter arrays with keys "id", "match_any_rule", "inverse", "rules", and "actions" + */ static function load_filters(int $feed_id, int $owner_uid) { $filters = array(); @@ -1809,7 +1860,7 @@ class RSSUtils { * * @param string $url A feed or page URL * @access public - * @return mixed The favicon URL, or false if none was found. + * @return false|string The favicon URL string, or false if none was found. */ static function get_favicon_url(string $url) { @@ -1843,8 +1894,12 @@ class RSSUtils { return $favicon_url; } - // https://community.tt-rss.org/t/problem-with-img-srcset/3519 - static function decode_srcset($srcset) { + /** + * @see https://community.tt-rss.org/t/problem-with-img-srcset/3519 + * + * @return array> An array of srcset subitem arrays with keys "url" and "size" + */ + static function decode_srcset(string $srcset): array { $matches = []; preg_match_all( @@ -1862,7 +1917,10 @@ class RSSUtils { return $matches; } - static function encode_srcset($matches) { + /** + * @param array> $matches An array of srcset subitem arrays with keys "url" and "size" + */ + static function encode_srcset(array $matches): string { $tokens = []; foreach ($matches as $m) { @@ -1872,7 +1930,7 @@ class RSSUtils { return implode(",", $tokens); } - static function function_enabled($func) { + static function function_enabled(string $func): bool { return !in_array($func, explode(',', str_replace(" ", "", ini_get('disable_functions')))); } diff --git a/update.php b/update.php index 36c66b06c..6cd61a451 100755 --- a/update.php +++ b/update.php @@ -108,7 +108,7 @@ $options = getopt("", array_keys($options_map)); - if (count($options) == 0 || isset($options["help"]) ) { + if ($options === false || count($options) == 0 || isset($options["help"]) ) { print "Tiny Tiny RSS CLI management tool\n"; print "=================================\n"; print "Options:\n\n"; -- cgit v1.2.3-54-g00ecf From 03495c11ed69f6311e9c7596cc53c5b15ce82bf6 Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 19:59:25 +0000 Subject: Address PHPStan warnings in 'classes/sanitizer.php'. This also includes some minor tweaks to things that call 'Sanitizer::sanitize()'. --- classes/api.php | 4 ++-- classes/feeds.php | 2 +- classes/handler/public.php | 4 ++-- classes/sanitizer.php | 19 ++++++++++++++----- include/functions.php | 10 ++++++++-- plugins/share/init.php | 2 +- 6 files changed, 28 insertions(+), 13 deletions(-) (limited to 'classes') diff --git a/classes/api.php b/classes/api.php index 033aa8654..7d6ac174c 100755 --- a/classes/api.php +++ b/classes/api.php @@ -351,7 +351,7 @@ class API extends Handler { $article['content'] = Sanitizer::sanitize( $entry->content, self::_param_to_bool($entry->hide_images), - false, $entry->site_url, false, $entry->id); + null, $entry->site_url, null, $entry->id); } else { $article['content'] = $entry->content; } @@ -746,7 +746,7 @@ class API extends Handler { $headline_row["content"] = Sanitizer::sanitize( $line["content"], self::_param_to_bool($line['hide_images']), - false, $line["site_url"], false, $line["id"]); + null, $line["site_url"], null, $line["id"]); } else { $headline_row["content"] = $line["content"]; } diff --git a/classes/feeds.php b/classes/feeds.php index cd2633ffb..20aa9c05d 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -271,7 +271,7 @@ class Feeds extends Handler_Protected { $this->_mark_timestamp(" pre-sanitize"); $line["content"] = Sanitizer::sanitize($line["content"], - $line['hide_images'], false, $line["site_url"], $highlight_words, $line["id"]); + $line['hide_images'], null, $line["site_url"], $highlight_words, $line["id"]); $this->_mark_timestamp(" sanitize"); diff --git a/classes/handler/public.php b/classes/handler/public.php index 14474d0bb..9a9f7b892 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -109,7 +109,7 @@ class Handler_Public extends Handler { $tpl->setVariable('ARTICLE_EXCERPT', $line["content_preview"], true); $content = Sanitizer::sanitize($line["content"], false, $owner_uid, - $feed_site_url, false, $line["id"]); + $feed_site_url, null, $line["id"]); $content = DiskCache::rewrite_urls($content); @@ -207,7 +207,7 @@ class Handler_Public extends Handler { $article['link'] = $line['link']; $article['title'] = $line['title']; $article['excerpt'] = $line["content_preview"]; - $article['content'] = Sanitizer::sanitize($line["content"], false, $owner_uid, $feed_site_url, false, $line["id"]); + $article['content'] = Sanitizer::sanitize($line["content"], false, $owner_uid, $feed_site_url, null, $line["id"]); $article['updated'] = date('c', strtotime($line["updated"])); if (!empty($line['note'])) $article['note'] = $line['note']; diff --git a/classes/sanitizer.php b/classes/sanitizer.php index 3f6e9504e..2770aece2 100644 --- a/classes/sanitizer.php +++ b/classes/sanitizer.php @@ -1,6 +1,10 @@ $allowed_elements + * @param array $disallowed_attributes + */ + private static function strip_harmful_tags(DOMDocument $doc, array $allowed_elements, $disallowed_attributes): DOMDocument { $xpath = new DOMXPath($doc); $entries = $xpath->query('//*'); @@ -40,7 +44,7 @@ class Sanitizer { return $doc; } - public static function iframe_whitelisted($entry) { + public static function iframe_whitelisted(DOMNode $entry): bool { $src = parse_url($entry->getAttribute("src"), PHP_URL_HOST); if (!empty($src)) @@ -49,11 +53,16 @@ class Sanitizer { return false; } - private static function is_prefix_https() { + private static function is_prefix_https(): bool { return parse_url(Config::get(Config::SELF_URL_PATH), PHP_URL_SCHEME) == 'https'; } - public static function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) { + /** + * @param array|null $highlight_words Words to highlight in the HTML output. + * + * @return false|string The HTML, or false if an error occurred. + */ + public static function sanitize(string $str, bool $force_remove_images = false, int $owner = null, string $site_url = null, array $highlight_words = null, int $article_id = null) { if (!$owner && isset($_SESSION["uid"])) $owner = $_SESSION["uid"]; @@ -183,7 +192,7 @@ class Sanitizer { $div->appendChild($entry); } - if ($highlight_words && is_array($highlight_words)) { + if (is_array($highlight_words)) { foreach ($highlight_words as $word) { // http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph diff --git a/include/functions.php b/include/functions.php index 36519fd44..238cbe7f5 100644 --- a/include/functions.php +++ b/include/functions.php @@ -181,8 +181,14 @@ return Feeds::_get_counters($feed, $is_cat, true, $_SESSION["uid"]); } - /** function is @deprecated by Sanitizer::sanitize() */ - function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) { + /** + * @deprecated by Sanitizer::sanitize() + * + * @param array|null $highlight_words Words to highlight in the HTML output. + * + * @return false|string The HTML, or false if an error occurred. + */ + function sanitize(string $str, bool $force_remove_images = false, int $owner = null, string $site_url = null, array $highlight_words = null, int $article_id = null) { return Sanitizer::sanitize($str, $force_remove_images, $owner, $site_url, $highlight_words, $article_id); } diff --git a/plugins/share/init.php b/plugins/share/init.php index 359d86802..8da417e52 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -133,7 +133,7 @@ class Share extends Plugin { $line["content"] = Sanitizer::sanitize($line["content"], $line['hide_images'], - $owner_uid, $line["site_url"], false, $line["id"]); + $owner_uid, $line["site_url"], null, $line["id"]); PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ARTICLE, function ($result) use (&$line) { -- cgit v1.2.3-54-g00ecf From f704d25ab15c5ffd988403d36b90fe76fb72916e Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 20:12:47 +0000 Subject: Address PHPStan warnings in 'classes/timehelper.php'. --- classes/feeds.php | 2 +- classes/timehelper.php | 8 ++++---- include/functions.php | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'classes') diff --git a/classes/feeds.php b/classes/feeds.php index 20aa9c05d..529a8e403 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -299,7 +299,7 @@ class Feeds extends Handler_Protected { $this->_mark_timestamp(" enclosures"); $line["updated_long"] = TimeHelper::make_local_datetime($line["updated"],true); - $line["updated"] = TimeHelper::make_local_datetime($line["updated"], false, false, false, true); + $line["updated"] = TimeHelper::make_local_datetime($line["updated"], false, null, false, true); $line['imported'] = T_sprintf("Imported at %s", TimeHelper::make_local_datetime($line["date_entered"], false)); diff --git a/classes/timehelper.php b/classes/timehelper.php index 4317f343f..e66f82f90 100644 --- a/classes/timehelper.php +++ b/classes/timehelper.php @@ -1,7 +1,7 @@ Date: Thu, 11 Nov 2021 20:25:13 +0000 Subject: Address PHPStan warnings in 'classes/userhelper.php'. --- classes/userhelper.php | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'classes') diff --git a/classes/userhelper.php b/classes/userhelper.php index ea714b76b..c09cabb12 100644 --- a/classes/userhelper.php +++ b/classes/userhelper.php @@ -32,7 +32,7 @@ class UserHelper { /** has administrator permissions */ const ACCESS_LEVEL_ADMIN = 10; - static function authenticate(string $login = null, string $password = null, bool $check_only = false, string $service = null) { + static function authenticate(string $login = null, string $password = null, bool $check_only = false, string $service = null): bool { if (!Config::get(Config::SINGLE_USER_MODE)) { $user_id = false; $auth_module = false; @@ -99,7 +99,7 @@ class UserHelper { } } - static function load_user_plugins(int $owner_uid, PluginHost $pluginhost = null) { + static function load_user_plugins(int $owner_uid, PluginHost $pluginhost = null): void { if (!$pluginhost) $pluginhost = PluginHost::getInstance(); @@ -114,7 +114,7 @@ class UserHelper { } } - static function login_sequence() { + static function login_sequence(): void { $pdo = Db::pdo(); if (Config::get(Config::SINGLE_USER_MODE)) { @@ -159,7 +159,7 @@ class UserHelper { } } - static function print_user_stylesheet() { + static function print_user_stylesheet(): void { $value = get_pref(Prefs::USER_STYLESHEET); if ($value) { @@ -170,7 +170,7 @@ class UserHelper { } - static function get_user_ip() { + static function get_user_ip(): ?string { foreach (["HTTP_X_REAL_IP", "REMOTE_ADDR"] as $hdr) { if (isset($_SERVER[$hdr])) return $_SERVER[$hdr]; @@ -179,7 +179,7 @@ class UserHelper { return null; } - static function get_login_by_id(int $id) { + static function get_login_by_id(int $id): ?string { $user = ORM::for_table('ttrss_users') ->find_one($id); @@ -189,7 +189,7 @@ class UserHelper { return null; } - static function find_user_by_login(string $login) { + static function find_user_by_login(string $login): ?int { $user = ORM::for_table('ttrss_users') ->where('login', $login) ->find_one(); @@ -200,7 +200,7 @@ class UserHelper { return null; } - static function logout() { + static function logout(): void { if (session_status() === PHP_SESSION_ACTIVE) session_destroy(); @@ -211,11 +211,11 @@ class UserHelper { session_commit(); } - static function get_salt() { + static function get_salt(): string { return substr(bin2hex(get_random_bytes(125)), 0, 250); } - static function reset_password($uid, $format_output = false, $new_password = "") { + static function reset_password(int $uid, bool $format_output = false, string $new_password = ""): void { $user = ORM::for_table('ttrss_users')->find_one($uid); $message = ""; @@ -298,7 +298,7 @@ class UserHelper { } } - static function get_otp_secret(int $owner_uid, bool $show_if_enabled = false) { + static function get_otp_secret(int $owner_uid, bool $show_if_enabled = false): ?string { $user = ORM::for_table('ttrss_users')->find_one($owner_uid); if ($user) { @@ -333,7 +333,7 @@ class UserHelper { return null; } - static function is_default_password() { + static function is_default_password(): bool { $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator && @@ -345,10 +345,12 @@ class UserHelper { return false; } - static function hash_password(string $pass, string $salt, string $algo = "") { - - if (!$algo) $algo = self::HASH_ALGOS[0]; - + /** + * @param UserHelper::HASH_ALGO_* $algo + * + * @return false|string False if the password couldn't be hashed, otherwise the hash string. + */ + static function hash_password(string $pass, string $salt, string $algo = self::HASH_ALGOS[0]) { $pass_hash = ""; switch ($algo) { -- cgit v1.2.3-54-g00ecf From 58ea0d43390ce85db10f6b616ff8775b06815dd4 Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 21:02:06 +0000 Subject: Address PHPStan warnings in 'classes/debug.php'. --- classes/debug.php | 49 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) (limited to 'classes') diff --git a/classes/debug.php b/classes/debug.php index 2ae81e41a..f7c23cf1c 100644 --- a/classes/debug.php +++ b/classes/debug.php @@ -6,47 +6,60 @@ class Debug { const LOG_EXTENDED = 2; /** @deprecated */ - public static $LOG_DISABLED = self::LOG_DISABLED; + public static int $LOG_DISABLED = self::LOG_DISABLED; /** @deprecated */ - public static $LOG_NORMAL = self::LOG_NORMAL; + public static int $LOG_NORMAL = self::LOG_NORMAL; /** @deprecated */ - public static $LOG_VERBOSE = self::LOG_VERBOSE; + public static int $LOG_VERBOSE = self::LOG_VERBOSE; /** @deprecated */ - public static $LOG_EXTENDED = self::LOG_EXTENDED; + public static int $LOG_EXTENDED = self::LOG_EXTENDED; - private static $enabled = false; - private static $quiet = false; - private static $logfile = false; - private static $loglevel = self::LOG_NORMAL; + private static bool $enabled = false; + private static bool $quiet = false; + private static ?string $logfile = null; - public static function set_logfile($logfile) { + /** + * @var Debug::LOG_* + */ + private static int $loglevel = self::LOG_NORMAL; + + public static function set_logfile(string $logfile): void { self::$logfile = $logfile; } - public static function enabled() { + public static function enabled(): bool { return self::$enabled; } - public static function set_enabled($enable) { + public static function set_enabled(bool $enable): void { self::$enabled = $enable; } - public static function set_quiet($quiet) { + public static function set_quiet(bool $quiet): void { self::$quiet = $quiet; } - public static function set_loglevel($level) { + /** + * @param Debug::LOG_* $level + */ + public static function set_loglevel($level): void { self::$loglevel = $level; } - public static function get_loglevel() { + /** + * @return Debug::LOG_* + */ + public static function get_loglevel(): int { return self::$loglevel; } - public static function log($message, int $level = 0) { + /** + * @param Debug::LOG_* $level + */ + public static function log(string $message, int $level = Debug::LOG_NORMAL): bool { if (!self::$enabled || self::$loglevel < $level) return false; @@ -73,7 +86,7 @@ class Debug { if (!$locked) { fclose($fp); user_error("Unable to lock debugging log file: " . self::$logfile, E_USER_WARNING); - return; + return false; } } @@ -86,7 +99,7 @@ class Debug { fclose($fp); if (self::$quiet) - return; + return false; } else { user_error("Unable to open debugging log file: " . self::$logfile, E_USER_WARNING); @@ -94,5 +107,7 @@ class Debug { } print "[$ts] $message\n"; + + return true; } } -- cgit v1.2.3-54-g00ecf From 728a71150a6e63dafeae311e591c5b43ce4e317e Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 21:33:12 +0000 Subject: Fix 'TimeHelper::make_local_datetime()' (null is allowed). --- classes/timehelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'classes') diff --git a/classes/timehelper.php b/classes/timehelper.php index e66f82f90..453ee0cee 100644 --- a/classes/timehelper.php +++ b/classes/timehelper.php @@ -21,7 +21,7 @@ class TimeHelper { } } - static function make_local_datetime(string $timestamp, bool $long, int $owner_uid = null, + static function make_local_datetime(?string $timestamp, bool $long, int $owner_uid = null, bool $no_smart_dt = false, bool $eta_min = false): string { if (!$owner_uid) $owner_uid = $_SESSION['uid']; -- cgit v1.2.3-54-g00ecf From 50997df57a8128fb72e15e0a6ca50401928d3900 Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 21:46:44 +0000 Subject: Address PHPStan warnings in 'inclasses/digest.php'. --- classes/digest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'classes') diff --git a/classes/digest.php b/classes/digest.php index 94e5cd1fc..7adf9b449 100644 --- a/classes/digest.php +++ b/classes/digest.php @@ -1,7 +1,7 @@ , 3: string} + */ static function prepare_headlines_digest(int $user_id, int $days = 1, int $limit = 1000) { $tpl = new Templator(); -- cgit v1.2.3-54-g00ecf From 2d5603b196047188bb543573cdf725fb10ec0401 Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 22:07:32 +0000 Subject: Address PHPStan warnings in 'classes/diskcache.php'. --- classes/diskcache.php | 81 +++++++++++++++++++++++++++++++++------------------ classes/urlhelper.php | 2 +- 2 files changed, 53 insertions(+), 30 deletions(-) (limited to 'classes') diff --git a/classes/diskcache.php b/classes/diskcache.php index d7ea26d3b..9fa043aee 100644 --- a/classes/diskcache.php +++ b/classes/diskcache.php @@ -1,9 +1,13 @@ + */ + private array $mimeMap = [ 'video/3gpp2' => '3g2', 'video/3gp' => '3gp', 'video/3gpp' => '3gp', @@ -190,21 +194,22 @@ class DiskCache { 'text/x-scriptzsh' => 'zsh' ]; - public function __construct($dir) { + public function __construct(string $dir) { $this->dir = Config::get(Config::CACHE_DIR) . "/" . basename(clean($dir)); } - public function get_dir() { + public function get_dir(): string { return $this->dir; } - public function make_dir() { + public function make_dir(): bool { if (!is_dir($this->dir)) { return mkdir($this->dir); } + return false; } - public function is_writable($filename = "") { + public function is_writable(?string $filename = null): bool { if ($filename) { if (file_exists($this->get_full_path($filename))) return is_writable($this->get_full_path($filename)); @@ -215,44 +220,55 @@ class DiskCache { } } - public function exists($filename) { + public function exists(string $filename): bool { return file_exists($this->get_full_path($filename)); } - public function get_size($filename) { + /** + * @return int|false -1 if the file doesn't exist, false if an error occurred, size in bytes otherwise + */ + public function get_size(string $filename) { if ($this->exists($filename)) return filesize($this->get_full_path($filename)); else return -1; } - public function get_full_path($filename) { + public function get_full_path(string $filename): string { return $this->dir . "/" . basename(clean($filename)); } - public function put($filename, $data) { + /** + * @param mixed $data + * + * @return int|false Bytes written or false if an error occurred. + */ + public function put(string $filename, $data) { return file_put_contents($this->get_full_path($filename), $data); } - public function touch($filename) { + public function touch(string $filename): bool { return touch($this->get_full_path($filename)); } - public function get($filename) { + public function get(string $filename): ?string { if ($this->exists($filename)) return file_get_contents($this->get_full_path($filename)); else return null; } - public function get_mime_type($filename) { + /** + * @return false|null|string false if detection failed, null if the file doesn't exist, string mime content type otherwise + */ + public function get_mime_type(string $filename) { if ($this->exists($filename)) return mime_content_type($this->get_full_path($filename)); else return null; } - public function get_fake_extension($filename) { + public function get_fake_extension(string $filename): string { $mimetype = $this->get_mime_type($filename); if ($mimetype) @@ -261,7 +277,10 @@ class DiskCache { return ""; } - public function send($filename) { + /** + * @return bool|int false if the file doesn't exist (or unreadable) or isn't audio/video, true if a plugin handled, otherwise int of bytes sent + */ + public function send(string $filename) { $fake_extension = $this->get_fake_extension($filename); if ($fake_extension) @@ -272,7 +291,7 @@ class DiskCache { return $this->send_local_file($this->get_full_path($filename)); } - public function get_url($filename) { + public function get_url(string $filename): string { return Config::get_self_url() . "/public.php?op=cached&file=" . basename($this->dir) . "/" . basename($filename); } @@ -280,8 +299,7 @@ class DiskCache { // this is called separately after sanitize() and plugin render article hooks to allow // plugins work on original source URLs used before caching // NOTE: URLs should be already absolutized because this is called after sanitize() - static public function rewrite_urls($str) - { + static public function rewrite_urls(string $str): string { $res = trim($str); if (!$res) return ''; @@ -338,7 +356,7 @@ class DiskCache { return $res; } - static function expire() { + static function expire(): void { $dirs = array_filter(glob(Config::get(Config::CACHE_DIR) . "/*"), "is_dir"); foreach ($dirs as $cache_dir) { @@ -362,14 +380,19 @@ class DiskCache { } } - /* this is essentially a wrapper for readfile() which allows plugins to hook - output with httpd-specific "fast" implementation i.e. X-Sendfile or whatever else - - hook function should return true if request was handled (or at least attempted to) - - note that this can be called without user context so the plugin to handle this - should be loaded systemwide in config.php */ - function send_local_file($filename) { + /* */ + /** + * this is essentially a wrapper for readfile() which allows plugins to hook + * output with httpd-specific "fast" implementation i.e. X-Sendfile or whatever else + * + * hook function should return true if request was handled (or at least attempted to) + * + * note that this can be called without user context so the plugin to handle this + * should be loaded systemwide in config.php + * + * @return bool|int false if the file doesn't exist (or unreadable) or isn't audio/video, true if a plugin handled, otherwise int of bytes sent + */ + function send_local_file(string $filename) { if (file_exists($filename)) { if (is_writable($filename)) touch($filename); diff --git a/classes/urlhelper.php b/classes/urlhelper.php index a660af170..0592bf28c 100644 --- a/classes/urlhelper.php +++ b/classes/urlhelper.php @@ -205,7 +205,7 @@ class UrlHelper { /** * @param array|string $options - * @return bool|string false if something went wrong, otherwise string contents + * @return false|string false if something went wrong, otherwise string contents */ // TODO: max_size currently only works for CURL transfers // TODO: multiple-argument way is deprecated, first parameter is a hash now -- cgit v1.2.3-54-g00ecf From 95277fd099987f8173287c9565494633157a0ac8 Mon Sep 17 00:00:00 2001 From: wn_ Date: Thu, 11 Nov 2021 22:28:13 +0000 Subject: Address PHPStan warnings in 'classes/labels.php'. --- classes/labels.php | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'classes') diff --git a/classes/labels.php b/classes/labels.php index 570f24f4f..b9c480f82 100644 --- a/classes/labels.php +++ b/classes/labels.php @@ -1,15 +1,15 @@ prepare("SELECT id FROM ttrss_labels2 WHERE LOWER(caption) = LOWER(?) @@ -23,7 +23,7 @@ class Labels } } - static function find_caption($label, $owner_uid) { + static function find_caption(int $label, int $owner_uid): string { $pdo = Db::pdo(); $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 WHERE id = ? @@ -37,18 +37,24 @@ class Labels } } - static function get_as_hash($owner_uid) { + /** + * @return array> + */ + static function get_as_hash(int $owner_uid): array { $rv = []; $labels = Labels::get_all($owner_uid); foreach ($labels as $i => $label) { - $rv[$label["id"]] = $labels[$i]; + $rv[(int)$label["id"]] = $labels[$i]; } return $rv; } - static function get_all($owner_uid) { + /** + * @return array> An array of label detail arrays + */ + static function get_all(int $owner_uid) { $rv = array(); $pdo = Db::pdo(); @@ -64,7 +70,12 @@ class Labels return $rv; } - static function update_cache($owner_uid, $id, $labels = false, $force = false) { + /** + * @param array>|null $labels + * + * @see Article::_get_labels() + */ + static function update_cache(int $owner_uid, int $id, ?array $labels = null, bool $force = false): void { $pdo = Db::pdo(); if ($force) @@ -81,7 +92,7 @@ class Labels } - static function clear_cache($id) { + static function clear_cache(int $id): void { $pdo = Db::pdo(); @@ -91,7 +102,7 @@ class Labels } - static function remove_article($id, $label, $owner_uid) { + static function remove_article(int $id, string $label, int $owner_uid): void { $label_id = self::find_id($label, $owner_uid); @@ -109,7 +120,7 @@ class Labels self::clear_cache($id); } - static function add_article($id, $label, $owner_uid) { + static function add_article(int $id, string $label, int $owner_uid): void { $label_id = self::find_id($label, $owner_uid); @@ -138,7 +149,7 @@ class Labels } - static function remove($id, $owner_uid) { + static function remove(int $id, int $owner_uid): void { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; $pdo = Db::pdo(); @@ -182,7 +193,10 @@ class Labels if (!$tr_in_progress) $pdo->commit(); } - static function create($caption, $fg_color = '', $bg_color = '', $owner_uid = false) { + /** + * @return false|int false if the check for an existing label failed, otherwise the number of rows inserted (1 on success) + */ + static function create(string $caption, ?string $fg_color = '', ?string $bg_color = '', ?int $owner_uid = null) { if (!$owner_uid) $owner_uid = $_SESSION['uid']; -- cgit v1.2.3-54-g00ecf From a0f37c3206fce8a1fb5a2d82d3d3206990ca1e9c Mon Sep 17 00:00:00 2001 From: wn_ Date: Fri, 12 Nov 2021 00:06:00 +0000 Subject: Address PHPStan warnings in 'classes/pluginhost.php'. --- classes/pluginhost.php | 238 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 164 insertions(+), 74 deletions(-) (limited to 'classes') diff --git a/classes/pluginhost.php b/classes/pluginhost.php index b506a957a..36e050377 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -1,20 +1,38 @@ >> hook types -> priority levels -> Plugins */ + private array $hooks = []; + + /** @var array */ + private array $plugins = []; + + /** @var array> handler type -> method type -> Plugin */ + private array $handlers = []; + + /** @var array command type -> details array */ + private array $commands = []; + + /** @var array> plugin name -> (potential profile array) -> key -> value */ + private array $storage = []; + + /** @var array> */ + private array $feeds = []; + + /** @var array API method name, Plugin sender */ + private array $api_methods = []; + + /** @var array> */ + private array $plugin_actions = []; + + private ?int $owner_uid = null; + private bool $data_loaded = false; + private static ?PluginHost $instance = null; const API_VERSION = 2; const PUBLIC_METHOD_DELIMITER = "--"; @@ -174,13 +192,13 @@ class PluginHost { const KIND_SYSTEM = 2; const KIND_USER = 3; - static function object_to_domain(Plugin $plugin) { + static function object_to_domain(Plugin $plugin): string { return strtolower(get_class($plugin)); } function __construct() { $this->pdo = Db::pdo(); - $this->storage = array(); + $this->storage = []; } private function __clone() { @@ -194,18 +212,18 @@ class PluginHost { return self::$instance; } - private function register_plugin(string $name, Plugin $plugin) { + private function register_plugin(string $name, Plugin $plugin): void { //array_push($this->plugins, $plugin); $this->plugins[$name] = $plugin; } /** needed for compatibility with API 1 */ - function get_link() { + function get_link(): bool { return false; } /** needed for compatibility with API 2 (?) */ - function get_dbh() { + function get_dbh(): bool { return false; } @@ -213,8 +231,11 @@ class PluginHost { return $this->pdo; } - function get_plugin_names() { - $names = array(); + /** + * @return array + */ + function get_plugin_names(): array { + $names = []; foreach ($this->plugins as $p) { array_push($names, get_class($p)); @@ -223,15 +244,21 @@ class PluginHost { return $names; } - function get_plugins() { + /** + * @return array + */ + function get_plugins(): array { return $this->plugins; } - function get_plugin(string $name) { + function get_plugin(string $name): ?Plugin { return $this->plugins[strtolower($name)] ?? null; } - function run_hooks(string $hook, ...$args) { + /** + * @param mixed $args + */ + function run_hooks(string $hook, ...$args): void { $method = strtolower($hook); foreach ($this->get_hooks($hook) as $plugin) { @@ -247,7 +274,11 @@ class PluginHost { } } - function run_hooks_until(string $hook, $check, ...$args) { + /** + * @param mixed $args + * @param mixed $check + */ + function run_hooks_until(string $hook, $check, ...$args): bool { $method = strtolower($hook); foreach ($this->get_hooks($hook) as $plugin) { @@ -267,7 +298,10 @@ class PluginHost { return false; } - function run_hooks_callback(string $hook, Closure $callback, ...$args) { + /** + * @param mixed $args + */ + function run_hooks_callback(string $hook, Closure $callback, ...$args): void { $method = strtolower($hook); foreach ($this->get_hooks($hook) as $plugin) { @@ -284,7 +318,10 @@ class PluginHost { } } - function chain_hooks_callback(string $hook, Closure $callback, &...$args) { + /** + * @param mixed $args + */ + function chain_hooks_callback(string $hook, Closure $callback, &...$args): void { $method = strtolower($hook); foreach ($this->get_hooks($hook) as $plugin) { @@ -301,7 +338,7 @@ class PluginHost { } } - function add_hook(string $type, Plugin $sender, int $priority = 50) { + function add_hook(string $type, Plugin $sender, int $priority = 50): void { $priority = (int) $priority; if (!method_exists($sender, strtolower($type))) { @@ -325,7 +362,7 @@ class PluginHost { ksort($this->hooks[$type]); } - function del_hook(string $type, Plugin $sender) { + function del_hook(string $type, Plugin $sender): void { if (is_array($this->hooks[$type])) { foreach (array_keys($this->hooks[$type]) as $prio) { $key = array_search($sender, $this->hooks[$type][$prio]); @@ -337,6 +374,9 @@ class PluginHost { } } + /** + * @return array + */ function get_hooks(string $type) { if (isset($this->hooks[$type])) { $tmp = []; @@ -346,11 +386,10 @@ class PluginHost { } return $tmp; - } else { - return []; } + return []; } - function load_all(int $kind, int $owner_uid = null, bool $skip_init = false) { + function load_all(int $kind, int $owner_uid = null, bool $skip_init = false): void { $plugins = array_merge(glob("plugins/*"), glob("plugins.local/*")); $plugins = array_filter($plugins, "is_dir"); @@ -361,7 +400,7 @@ class PluginHost { $this->load(join(",", $plugins), $kind, $owner_uid, $skip_init); } - function load(string $classlist, int $kind, int $owner_uid = null, bool $skip_init = false) { + function load(string $classlist, int $kind, int $owner_uid = null, bool $skip_init = false): void { $plugins = explode(",", $classlist); $this->owner_uid = (int) $owner_uid; @@ -434,27 +473,27 @@ class PluginHost { $this->load_data(); } - function is_system(Plugin $plugin) { + function is_system(Plugin $plugin): bool { $about = $plugin->about(); - return $about[3] ?? false; + return ($about[3] ?? false) === true; } // only system plugins are allowed to modify routing - function add_handler(string $handler, $method, Plugin $sender) { + function add_handler(string $handler, string $method, Plugin $sender): void { $handler = str_replace("-", "_", strtolower($handler)); $method = strtolower($method); if ($this->is_system($sender)) { if (!isset($this->handlers[$handler])) { - $this->handlers[$handler] = array(); + $this->handlers[$handler] = []; } $this->handlers[$handler][$method] = $sender; } } - function del_handler(string $handler, $method, Plugin $sender) { + function del_handler(string $handler, string $method, Plugin $sender): void { $handler = str_replace("-", "_", strtolower($handler)); $method = strtolower($method); @@ -463,7 +502,10 @@ class PluginHost { } } - function lookup_handler($handler, $method) { + /** + * @return false|Plugin false if the handler couldn't be found, otherwise the Plugin/handler + */ + function lookup_handler(string $handler, string $method) { $handler = str_replace("-", "_", strtolower($handler)); $method = strtolower($method); @@ -478,7 +520,7 @@ class PluginHost { return false; } - function add_command(string $command, string $description, Plugin $sender, string $suffix = "", string $arghelp = "") { + function add_command(string $command, string $description, Plugin $sender, string $suffix = "", string $arghelp = ""): void { $command = str_replace("-", "_", strtolower($command)); $this->commands[$command] = array("description" => $description, @@ -487,27 +529,34 @@ class PluginHost { "class" => $sender); } - function del_command(string $command) { + function del_command(string $command): void { $command = "-" . strtolower($command); unset($this->commands[$command]); } - function lookup_command($command) { + /** + * @return false|Plugin false if the command couldn't be found, otherwise the registered Plugin + */ + function lookup_command(string $command) { $command = "-" . strtolower($command); - if (is_array($this->commands[$command])) { + if (array_key_exists($command, $this->commands) && is_array($this->commands[$command])) { return $this->commands[$command]["class"]; } else { return false; } } + /** @return array> command type -> details array */ function get_commands() { return $this->commands; } - function run_commands(array $args) { + /** + * @param array $args + */ + function run_commands(array $args): void { foreach ($this->get_commands() as $command => $data) { if (isset($args[$command])) { $command = str_replace("-", "", $command); @@ -516,7 +565,7 @@ class PluginHost { } } - private function load_data() { + private function load_data(): void { if ($this->owner_uid && !$this->data_loaded && get_schema_version() > 100) { $sth = $this->pdo->prepare("SELECT name, content FROM ttrss_plugin_storage WHERE owner_uid = ?"); @@ -530,7 +579,7 @@ class PluginHost { } } - private function save_data(string $plugin) { + private function save_data(string $plugin): void { if ($this->owner_uid) { if (!$this->pdo_data) @@ -543,7 +592,7 @@ class PluginHost { $sth->execute([$this->owner_uid, $plugin]); if (!isset($this->storage[$plugin])) - $this->storage[$plugin] = array(); + $this->storage[$plugin] = []; $content = serialize($this->storage[$plugin]); @@ -563,8 +612,12 @@ class PluginHost { } } - // same as set(), but sets data to current preference profile - function profile_set(Plugin $sender, string $name, $value) { + /** + * same as set(), but sets data to current preference profile + * + * @param mixed $value + */ + function profile_set(Plugin $sender, string $name, $value): void { $profile_id = $_SESSION["profile"] ?? null; if ($profile_id) { @@ -582,26 +635,32 @@ class PluginHost { $this->save_data(get_class($sender)); } else { - return $this->set($sender, $name, $value); + $this->set($sender, $name, $value); } } - function set(Plugin $sender, string $name, $value) { + /** + * @param mixed $value + */ + function set(Plugin $sender, string $name, $value): void { $idx = get_class($sender); if (!isset($this->storage[$idx])) - $this->storage[$idx] = array(); + $this->storage[$idx] = []; $this->storage[$idx][$name] = $value; $this->save_data(get_class($sender)); } - function set_array(Plugin $sender, array $params) { + /** + * @param array $params + */ + function set_array(Plugin $sender, array $params): void { $idx = get_class($sender); if (!isset($this->storage[$idx])) - $this->storage[$idx] = array(); + $this->storage[$idx] = []; foreach ($params as $name => $value) $this->storage[$idx][$name] = $value; @@ -609,7 +668,12 @@ class PluginHost { $this->save_data(get_class($sender)); } - // same as get(), but sets data to current preference profile + /** + * same as get(), but sets data to current preference profile + * + * @param mixed $default_value + * @return mixed + */ function profile_get(Plugin $sender, string $name, $default_value = false) { $profile_id = $_SESSION["profile"] ?? null; @@ -629,6 +693,10 @@ class PluginHost { } } + /** + * @param mixed $default_value + * @return mixed + */ function get(Plugin $sender, string $name, $default_value = false) { $idx = get_class($sender); @@ -641,6 +709,10 @@ class PluginHost { } } + /** + * @param array $default_value + * @return array + */ function get_array(Plugin $sender, string $name, array $default_value = []) { $tmp = $this->get($sender, $name); @@ -649,13 +721,16 @@ class PluginHost { return $tmp; } - function get_all($sender) { + /** + * @return array + */ + function get_all(Plugin $sender) { $idx = get_class($sender); return $this->storage[$idx] ?? []; } - function clear_data(Plugin $sender) { + function clear_data(Plugin $sender): void { if ($this->owner_uid) { $idx = get_class($sender); @@ -670,7 +745,7 @@ class PluginHost { // Plugin feed functions are *EXPERIMENTAL*! // cat_id: only -1 is supported (Special) - function add_feed(int $cat_id, string $title, string $icon, Plugin $sender) { + function add_feed(int $cat_id, string $title, string $icon, Plugin $sender): int { if (empty($this->feeds[$cat_id])) $this->feeds[$cat_id] = []; @@ -683,12 +758,15 @@ class PluginHost { return $id; } + /** + * @return array + */ function get_feeds(int $cat_id) { return $this->feeds[$cat_id] ?? []; } // convert feed_id (e.g. -129) to pfeed_id first - function get_feed_handler(int $pfeed_id) { + function get_feed_handler(int $pfeed_id): ?Plugin { foreach ($this->feeds as $cat) { foreach ($cat as $feed) { if ($feed['id'] == $pfeed_id) { @@ -696,46 +774,54 @@ class PluginHost { } } } + return null; } - static function pfeed_to_feed_id(int $pfeed) { + static function pfeed_to_feed_id(int $pfeed): int { return PLUGIN_FEED_BASE_INDEX - 1 - abs($pfeed); } - static function feed_to_pfeed_id(int $feed) { + static function feed_to_pfeed_id(int $feed): int { return PLUGIN_FEED_BASE_INDEX - 1 + abs($feed); } - function add_api_method(string $name, Plugin $sender) { + function add_api_method(string $name, Plugin $sender): void { if ($this->is_system($sender)) { $this->api_methods[strtolower($name)] = $sender; } } - function get_api_method(string $name) { - return $this->api_methods[$name]; + function get_api_method(string $name): ?Plugin { + return $this->api_methods[$name] ?? null; } - function add_filter_action(Plugin $sender, string $action_name, string $action_desc) { + function add_filter_action(Plugin $sender, string $action_name, string $action_desc): void { $sender_class = get_class($sender); if (!isset($this->plugin_actions[$sender_class])) - $this->plugin_actions[$sender_class] = array(); + $this->plugin_actions[$sender_class] = []; array_push($this->plugin_actions[$sender_class], array("action" => $action_name, "description" => $action_desc, "sender" => $sender)); } + /** + * @return array> + */ function get_filter_actions() { return $this->plugin_actions; } - function get_owner_uid() { + function get_owner_uid(): ?int { return $this->owner_uid; } - // handled by classes/pluginhandler.php, requires valid session - function get_method_url(Plugin $sender, string $method, $params = []) { + /** + * handled by classes/pluginhandler.php, requires valid session + * + * @param array $params + */ + function get_method_url(Plugin $sender, string $method, array $params = []): string { return Config::get_self_url() . "/backend.php?" . http_build_query( array_merge( @@ -758,8 +844,12 @@ class PluginHost { $params)); } */ - // WARNING: endpoint in public.php, exposed to unauthenticated users - function get_public_method_url(Plugin $sender, string $method, $params = []) { + /** + * WARNING: endpoint in public.php, exposed to unauthenticated users + * + * @param array $params + */ + function get_public_method_url(Plugin $sender, string $method, array $params = []): ?string { if ($sender->is_public_method($method)) { return Config::get_self_url() . "/public.php?" . http_build_query( @@ -768,18 +858,18 @@ class PluginHost { "op" => strtolower(get_class($sender) . self::PUBLIC_METHOD_DELIMITER . $method), ], $params)); - } else { - user_error("get_public_method_url: requested method '$method' of '" . get_class($sender) . "' is private."); } + user_error("get_public_method_url: requested method '$method' of '" . get_class($sender) . "' is private."); + return null; } - function get_plugin_dir(Plugin $plugin) { + function get_plugin_dir(Plugin $plugin): string { $ref = new ReflectionClass(get_class($plugin)); return dirname($ref->getFileName()); } // TODO: use get_plugin_dir() - function is_local(Plugin $plugin) { + function is_local(Plugin $plugin): bool { $ref = new ReflectionClass(get_class($plugin)); return basename(dirname(dirname($ref->getFileName()))) == "plugins.local"; } -- cgit v1.2.3-54-g00ecf From 57bf56f7945b639d0e39f4c7ad9da161a4a18888 Mon Sep 17 00:00:00 2001 From: wn_ Date: Fri, 12 Nov 2021 01:50:40 +0000 Subject: Address PHPStan warnings in 'classes/article.php'. Also related changes in some other classes. --- classes/article.php | 92 ++++++++++++++++++++++++++++++++++++----------------- classes/digest.php | 2 +- classes/feeds.php | 4 +-- classes/labels.php | 4 +-- classes/rpc.php | 8 ++--- 5 files changed, 71 insertions(+), 39 deletions(-) (limited to 'classes') diff --git a/classes/article.php b/classes/article.php index 04855ac9d..4af36d1c0 100755 --- a/classes/article.php +++ b/classes/article.php @@ -4,7 +4,11 @@ class Article extends Handler_Protected { const ARTICLE_KIND_VIDEO = 2; const ARTICLE_KIND_YOUTUBE = 3; - function redirect() { + const CATCHUP_MODE_MARK_AS_READ = 0; + const CATCHUP_MODE_MARK_AS_UNREAD = 1; + const CATCHUP_MODE_TOGGLE = 2; + + function redirect(): void { $article = ORM::for_table('ttrss_entries') ->table_alias('e') ->join('ttrss_user_entries', [ 'ref_id', '=', 'e.id'], 'ue') @@ -24,8 +28,7 @@ class Article extends Handler_Protected { print "Article not found or has an empty URL."; } - static function _create_published_article($title, $url, $content, $labels_str, - $owner_uid) { + static function _create_published_article(string $title, string $url, string $content, string $labels_str, int $owner_uid): bool { $guid = 'SHA1:' . sha1("ttshared:" . $url . $owner_uid); // include owner_uid to prevent global GUID clash @@ -158,14 +161,14 @@ class Article extends Handler_Protected { return $rc; } - function printArticleTags() { + function printArticleTags(): void { $id = (int) clean($_REQUEST['id'] ?? 0); print json_encode(["id" => $id, "tags" => self::_get_tags($id)]); } - function setScore() { + function setScore(): void { $ids = array_map("intval", clean($_REQUEST['ids'] ?? [])); $score = (int)clean($_REQUEST['score']); @@ -179,7 +182,7 @@ class Article extends Handler_Protected { print json_encode(["id" => $ids, "score" => $score]); } - function setArticleTags() { + function setArticleTags(): void { $id = clean($_REQUEST["id"]); @@ -254,18 +257,18 @@ class Article extends Handler_Protected { print ""; }*/ - function assigntolabel() { - return $this->_label_ops(true); + function assigntolabel(): void { + $this->_label_ops(true); } - function removefromlabel() { - return $this->_label_ops(false); + function removefromlabel(): void { + $this->_label_ops(false); } - private function _label_ops($assign) { + private function _label_ops(bool $assign): void { $reply = array(); - $ids = explode(",", clean($_REQUEST["ids"])); + $ids = array_map("intval", explode(",", clean($_REQUEST["ids"] ?? []))); $label_id = clean($_REQUEST["lid"]); $label = Labels::find_caption($label_id, $_SESSION["uid"]); @@ -289,11 +292,10 @@ class Article extends Handler_Protected { print json_encode($reply); } - static function _format_enclosures($id, - $always_display_enclosures, - $article_content, - $hide_images = false) { - + /** + * @return array{'formatted': string, 'entries': array>} + */ + static function _format_enclosures(int $id, bool $always_display_enclosures, string $article_content, bool $hide_images = false): array { $enclosures = self::_get_enclosures($id); $enclosures_formatted = ""; @@ -366,7 +368,10 @@ class Article extends Handler_Protected { return $rv; } - static function _get_tags($id, $owner_uid = 0, $tag_cache = false) { + /** + * @return array + */ + static function _get_tags(int $id, int $owner_uid = 0, ?string $tag_cache = null): array { $a_id = $id; @@ -383,12 +388,14 @@ class Article extends Handler_Protected { /* check cache first */ - if ($tag_cache === false) { + if (!$tag_cache) { $csth = $pdo->prepare("SELECT tag_cache FROM ttrss_user_entries WHERE ref_id = ? AND owner_uid = ?"); $csth->execute([$id, $owner_uid]); - if ($row = $csth->fetch()) $tag_cache = $row["tag_cache"]; + if ($row = $csth->fetch()) { + $tag_cache = $row["tag_cache"]; + } } if ($tag_cache) { @@ -416,7 +423,7 @@ class Article extends Handler_Protected { return $tags; } - function getmetadatabyid() { + function getmetadatabyid(): void { $article = ORM::for_table('ttrss_entries') ->join('ttrss_user_entries', ['ref_id', '=', 'id'], 'ue') ->where('ue.owner_uid', $_SESSION['uid']) @@ -429,7 +436,10 @@ class Article extends Handler_Protected { } } - static function _get_enclosures($id) { + /** + * @return array> + */ + static function _get_enclosures(int $id): array { $encs = ORM::for_table('ttrss_enclosures') ->where('post_id', $id) ->find_many(); @@ -452,7 +462,7 @@ class Article extends Handler_Protected { } - static function _purge_orphans() { + static function _purge_orphans(): void { // purge orphaned posts in main content table @@ -471,7 +481,11 @@ class Article extends Handler_Protected { } } - static function _catchup_by_id($ids, $cmode, $owner_uid = false) { + /** + * @param array $ids + * @param Article::CATCHUP_MODE_* $cmode + */ + static function _catchup_by_id($ids, int $cmode, ?int $owner_uid = null): void { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; @@ -479,11 +493,11 @@ class Article extends Handler_Protected { $ids_qmarks = arr_qmarks($ids); - if ($cmode == 1) { + if ($cmode == Article::CATCHUP_MODE_MARK_AS_UNREAD) { $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = true WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); - } else if ($cmode == 2) { + } else if ($cmode == Article::CATCHUP_MODE_TOGGLE) { $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = NOT unread,last_read = NOW() WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); @@ -496,7 +510,10 @@ class Article extends Handler_Protected { $sth->execute(array_merge($ids, [$owner_uid])); } - static function _get_labels($id, $owner_uid = false) { + /** + * @return array> + */ + static function _get_labels(int $id, ?int $owner_uid = null): array { $rv = array(); if (!$owner_uid) $owner_uid = $_SESSION["uid"]; @@ -543,6 +560,12 @@ class Article extends Handler_Protected { return $rv; } + /** + * @param array> $enclosures + * @param array $headline + * + * @return array + */ static function _get_image(array $enclosures, string $content, string $site_url, array $headline) { $article_image = ""; @@ -603,14 +626,14 @@ class Article extends Handler_Protected { } if ($article_image) { - $article_image = rewrite_relative_url($site_url, $article_image); + $article_image = UrlHelper::rewrite_relative($site_url, $article_image); if (!$article_kind && (count($enclosures) > 1 || (isset($elems) && $elems->length > 1))) $article_kind = Article::ARTICLE_KIND_ALBUM; } if ($article_stream) - $article_stream = rewrite_relative_url($site_url, $article_stream); + $article_stream = UrlHelper::rewrite_relative($site_url, $article_stream); } $cache = new DiskCache("images"); @@ -624,7 +647,12 @@ class Article extends Handler_Protected { return [$article_image, $article_stream, $article_kind]; } - // only cached, returns label ids (not label feed ids) + /** + * only cached, returns label ids (not label feed ids) + * + * @param array $article_ids + * @return array + */ static function _labels_of(array $article_ids) { if (count($article_ids) == 0) return []; @@ -651,6 +679,10 @@ class Article extends Handler_Protected { return array_unique($rv); } + /** + * @param array $article_ids + * @return array + */ static function _feeds_of(array $article_ids) { if (count($article_ids) == 0) return []; diff --git a/classes/digest.php b/classes/digest.php index 7adf9b449..15203166b 100644 --- a/classes/digest.php +++ b/classes/digest.php @@ -62,7 +62,7 @@ class Digest if ($rc && $do_catchup) { Debug::log("Marking affected articles as read..."); - Article::_catchup_by_id($affected_ids, 0, $line["id"]); + Article::_catchup_by_id($affected_ids, Article::CATCHUP_MODE_MARK_AS_READ, $line["id"]); } } else { Debug::log("No headlines"); diff --git a/classes/feeds.php b/classes/feeds.php index 529a8e403..c9f47463f 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -289,9 +289,9 @@ class Feeds extends Handler_Protected { if ($line["num_enclosures"] > 0) { $line["enclosures"] = Article::_format_enclosures($id, - $line["always_display_enclosures"], + sql_bool_to_bool($line["always_display_enclosures"]), $line["content"], - $line["hide_images"]); + sql_bool_to_bool($line["hide_images"])); } else { $line["enclosures"] = [ 'formatted' => '', 'entries' => [] ]; } diff --git a/classes/labels.php b/classes/labels.php index b9c480f82..d78f92139 100644 --- a/classes/labels.php +++ b/classes/labels.php @@ -71,11 +71,11 @@ class Labels } /** - * @param array>|null $labels + * @param array> $labels * * @see Article::_get_labels() */ - static function update_cache(int $owner_uid, int $id, ?array $labels = null, bool $force = false): void { + static function update_cache(int $owner_uid, int $id, array $labels, bool $force = false): void { $pdo = Db::pdo(); if ($force) diff --git a/classes/rpc.php b/classes/rpc.php index 0432ed2d3..6d56f039e 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -344,11 +344,11 @@ class RPC extends Handler_Protected { $ids_qmarks = arr_qmarks($ids); - if ($cmode == 0) { + if ($cmode == Article::CATCHUP_MODE_MARK_AS_READ) { $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET marked = false, last_marked = NOW() WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); - } else if ($cmode == 1) { + } else if ($cmode == Article::CATCHUP_MODE_MARK_AS_UNREAD) { $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET marked = true, last_marked = NOW() WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); @@ -365,11 +365,11 @@ class RPC extends Handler_Protected { $ids_qmarks = arr_qmarks($ids); - if ($cmode == 0) { + if ($cmode == Article::CATCHUP_MODE_MARK_AS_READ) { $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET published = false, last_published = NOW() WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); - } else if ($cmode == 1) { + } else if ($cmode == Article::CATCHUP_MODE_MARK_AS_UNREAD) { $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET published = true, last_published = NOW() WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); -- cgit v1.2.3-54-g00ecf From 5606e38bff619c388c9621dde30f0d54127a21f4 Mon Sep 17 00:00:00 2001 From: wn_ Date: Fri, 12 Nov 2021 02:01:31 +0000 Subject: Update signature of handler 'csrf_ignore' to include types. --- classes/feeds.php | 2 +- classes/handler.php | 2 +- classes/ihandler.php | 2 +- classes/opml.php | 2 +- classes/pluginhandler.php | 2 +- classes/pref/feeds.php | 2 +- classes/pref/filters.php | 2 +- classes/pref/labels.php | 2 +- classes/pref/prefs.php | 2 +- classes/pref/system.php | 2 +- classes/pref/users.php | 2 +- classes/rpc.php | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) (limited to 'classes') diff --git a/classes/feeds.php b/classes/feeds.php index c9f47463f..951675adb 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -8,7 +8,7 @@ class Feeds extends Handler_Protected { private $viewfeed_timestamp; private $viewfeed_timestamp_last; - function csrf_ignore($method) { + function csrf_ignore(string $method): bool { $csrf_ignored = array("index"); return array_search($method, $csrf_ignored) !== false; diff --git a/classes/handler.php b/classes/handler.php index 09557c284..4c79628db 100644 --- a/classes/handler.php +++ b/classes/handler.php @@ -8,7 +8,7 @@ class Handler implements IHandler { $this->args = $args; } - function csrf_ignore($method) { + function csrf_ignore(string $method): bool { return false; } diff --git a/classes/ihandler.php b/classes/ihandler.php index 01c9e3109..8345839c0 100644 --- a/classes/ihandler.php +++ b/classes/ihandler.php @@ -1,6 +1,6 @@ Date: Fri, 12 Nov 2021 04:48:06 +0000 Subject: Address PHPStan warnings in 'classes/feeds.php'. Also some minor related tweaks in other classes. --- classes/api.php | 4 +- classes/debug.php | 15 ++- classes/feeds.php | 286 +++++++++++++++++++++++++++++++++--------------------- classes/rpc.php | 2 +- 4 files changed, 187 insertions(+), 120 deletions(-) (limited to 'classes') diff --git a/classes/api.php b/classes/api.php index 7d6ac174c..1835d487c 100755 --- a/classes/api.php +++ b/classes/api.php @@ -409,8 +409,8 @@ class API extends Handler { function catchupFeed() { $feed_id = clean($_REQUEST["feed_id"]); - $is_cat = clean($_REQUEST["is_cat"]); - @$mode = clean($_REQUEST["mode"]); + $is_cat = clean($_REQUEST["is_cat"]) == "true"; + $mode = clean($_REQUEST['mode'] ?? ""); if (!in_array($mode, ["all", "1day", "1week", "2week"])) $mode = "all"; diff --git a/classes/debug.php b/classes/debug.php index f7c23cf1c..eca7b31db 100644 --- a/classes/debug.php +++ b/classes/debug.php @@ -5,6 +5,13 @@ class Debug { const LOG_VERBOSE = 1; const LOG_EXTENDED = 2; + const ALL_LOG_LEVELS = [ + Debug::LOG_DISABLED, + Debug::LOG_NORMAL, + Debug::LOG_VERBOSE, + Debug::LOG_EXTENDED, + ]; + /** @deprecated */ public static int $LOG_DISABLED = self::LOG_DISABLED; @@ -43,21 +50,21 @@ class Debug { } /** - * @param Debug::LOG_* $level + * @param int $level Debug::LOG_* */ - public static function set_loglevel($level): void { + public static function set_loglevel(int $level): void { self::$loglevel = $level; } /** - * @return Debug::LOG_* + * @return int Debug::LOG_* */ public static function get_loglevel(): int { return self::$loglevel; } /** - * @param Debug::LOG_* $level + * @param int $level Debug::LOG_* */ public static function log(string $message, int $level = Debug::LOG_NORMAL): bool { diff --git a/classes/feeds.php b/classes/feeds.php index 951675adb..24511c396 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -5,8 +5,11 @@ class Feeds extends Handler_Protected { const NEVER_GROUP_FEEDS = [ -6, 0 ]; const NEVER_GROUP_BY_DATE = [ -2, -1, -3 ]; - private $viewfeed_timestamp; - private $viewfeed_timestamp_last; + /** @var int|float int on 64-bit, float on 32-bit */ + private $viewfeed_timestamp; + + /** @var int|float int on 64-bit, float on 32-bit */ + private $viewfeed_timestamp_last; function csrf_ignore(string $method): bool { $csrf_ignored = array("index"); @@ -14,9 +17,12 @@ class Feeds extends Handler_Protected { return array_search($method, $csrf_ignored) !== false; } - private function _format_headlines_list($feed, $method, $view_mode, $limit, $cat_view, - $offset, $override_order = false, $include_children = false, $check_first_id = false, - $skip_first_id_check = false, $order_by = false) { + /** + * @return array{0: array, 1: int, 2: int, 3: bool, 4: array} $topmost_article_ids, $headlines_count, $feed, $disable_cache, $reply + */ + private function _format_headlines_list(int $feed, string $method, string $view_mode, int $limit, bool $cat_view, + int $offset, string $override_order, bool $include_children, bool $check_first_id, + bool $skip_first_id_check, string $order_by): array { $disable_cache = false; @@ -80,6 +86,8 @@ class Feeds extends Handler_Protected { "include_children" => $include_children, "order_by" => $order_by); + // Implemented by a plugin, so ignore the undefined 'get_headlines' method. + // @phpstan-ignore-next-line $qfh_ret = $handler->get_headlines(PluginHost::feed_to_pfeed_id($feed), $options); } @@ -433,7 +441,7 @@ class Feeds extends Handler_Protected { return array($topmost_article_ids, $headlines_count, $feed, $disable_cache, $reply); } - function catchupAll() { + function catchupAll(): void { $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET last_read = NOW(), unread = false WHERE unread = true AND owner_uid = ?"); $sth->execute([$_SESSION['uid']]); @@ -441,7 +449,7 @@ class Feeds extends Handler_Protected { print json_encode(array("message" => "UPDATE_COUNTERS")); } - function view() { + function view(): void { $reply = array(); $feed = $_REQUEST["feed"]; @@ -450,7 +458,7 @@ class Feeds extends Handler_Protected { $limit = 30; $cat_view = $_REQUEST["cat"] == "true"; $next_unread_feed = $_REQUEST["nuf"] ?? 0; - $offset = $_REQUEST["skip"] ?? 0; + $offset = (int) ($_REQUEST["skip"] ?? 0); $order_by = $_REQUEST["order_by"] ?? ""; $check_first_id = $_REQUEST["fid"] ?? 0; @@ -538,7 +546,10 @@ class Feeds extends Handler_Protected { print json_encode($reply); } - private function _generate_dashboard_feed() { + /** + * @return array> + */ + private function _generate_dashboard_feed(): array { $reply = array(); $reply['headlines']['id'] = -5; @@ -580,7 +591,10 @@ class Feeds extends Handler_Protected { return $reply; } - private function _generate_error_feed($error) { + /** + * @return array + */ + private function _generate_error_feed(string $error): array { $reply = array(); $reply['headlines']['id'] = -7; @@ -596,13 +610,13 @@ class Feeds extends Handler_Protected { return $reply; } - function subscribeToFeed() { + function subscribeToFeed(): void { print json_encode([ "cat_select" => \Controls\select_feeds_cats("cat") ]); } - function search() { + function search(): void { print json_encode([ "show_language" => Config::get(Config::DB_TYPE) == "pgsql", "show_syntax_help" => count(PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH)) == 0, @@ -611,7 +625,7 @@ class Feeds extends Handler_Protected { ]); } - function opensite() { + function opensite(): void { $feed = ORM::for_table('ttrss_feeds') ->find_one((int)$_REQUEST['feed_id']); @@ -628,10 +642,14 @@ class Feeds extends Handler_Protected { print "Feed not found or has an empty site URL."; } - function updatedebugger() { + function updatedebugger(): void { header("Content-type: text/html"); - $xdebug = isset($_REQUEST["xdebug"]) ? (int)$_REQUEST["xdebug"] : 1; + $xdebug = isset($_REQUEST["xdebug"]) ? (int)$_REQUEST["xdebug"] : Debug::LOG_VERBOSE; + + if (!in_array($xdebug, Debug::ALL_LOG_LEVELS)) { + $xdebug = Debug::LOG_VERBOSE; + } Debug::set_enabled(true); Debug::set_loglevel($xdebug); @@ -644,9 +662,9 @@ class Feeds extends Handler_Protected { $sth->execute([$feed_id, $_SESSION['uid']]); if (!$sth->fetch()) { - print "Access denied."; - return; - } + print "Access denied."; + return; + } ?> @@ -731,7 +749,10 @@ class Feeds extends Handler_Protected { } - static function _catchup($feed, $cat_view, $owner_uid = false, $mode = 'all', $search = false) { + /** + * @param array $search + */ + static function _catchup(string $feed_id_or_tag_name, bool $cat_view, ?int $owner_uid = null, string $mode = 'all', ?array $search = null): void { if (!$owner_uid) $owner_uid = $_SESSION['uid']; @@ -785,14 +806,16 @@ class Feeds extends Handler_Protected { $date_qpart = "true"; } - if (is_numeric($feed)) { + if (is_numeric($feed_id_or_tag_name)) { + $feed_id = (int) $feed_id_or_tag_name; + if ($cat_view) { - if ($feed >= 0) { + if ($feed_id >= 0) { - if ($feed > 0) { - $children = self::_get_child_cats($feed, $owner_uid); - array_push($children, $feed); + if ($feed_id > 0) { + $children = self::_get_child_cats($feed_id, $owner_uid); + array_push($children, $feed_id); $children = array_map("intval", $children); $children = join(",", $children); @@ -810,7 +833,7 @@ class Feeds extends Handler_Protected { (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart AND $search_qpart) as tmp)"); $sth->execute([$owner_uid]); - } else if ($feed == -2) { + } else if ($feed_id == -2) { $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*) @@ -819,18 +842,18 @@ class Feeds extends Handler_Protected { $sth->execute([$owner_uid]); } - } else if ($feed > 0) { + } else if ($feed_id > 0) { $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id AND owner_uid = ? AND unread = true AND feed_id = ? AND $date_qpart AND $search_qpart) as tmp)"); - $sth->execute([$owner_uid, $feed]); + $sth->execute([$owner_uid, $feed_id]); - } else if ($feed < 0 && $feed > LABEL_BASE_INDEX) { // special, like starred + } else if ($feed_id < 0 && $feed_id > LABEL_BASE_INDEX) { // special, like starred - if ($feed == -1) { + if ($feed_id == -1) { $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM @@ -839,7 +862,7 @@ class Feeds extends Handler_Protected { $sth->execute([$owner_uid]); } - if ($feed == -2) { + if ($feed_id == -2) { $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM @@ -848,7 +871,7 @@ class Feeds extends Handler_Protected { $sth->execute([$owner_uid]); } - if ($feed == -3) { + if ($feed_id == -3) { $intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE); @@ -867,7 +890,7 @@ class Feeds extends Handler_Protected { $sth->execute([$owner_uid]); } - if ($feed == -4) { + if ($feed_id == -4) { $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM @@ -875,10 +898,9 @@ class Feeds extends Handler_Protected { AND owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); $sth->execute([$owner_uid]); } + } else if ($feed_id < LABEL_BASE_INDEX) { // label - } else if ($feed < LABEL_BASE_INDEX) { // label - - $label_id = Labels::feed_to_label_id($feed); + $label_id = Labels::feed_to_label_id($feed_id); $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN @@ -887,23 +909,21 @@ class Feeds extends Handler_Protected { AND label_id = ? AND ref_id = article_id AND owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); $sth->execute([$label_id, $owner_uid]); - } - } else { // tag + $tag_name = $feed_id_or_tag_name; + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id AND post_int_id = int_id AND tag_name = ? AND ttrss_user_entries.owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); - $sth->execute([$feed, $owner_uid]); - + $sth->execute([$tag_name, $owner_uid]); } } - static function _get_counters($feed, $is_cat = false, $unread_only = false, - $owner_uid = false) { + static function _get_counters(int $feed, bool $is_cat = false, bool $unread_only = false, ?int $owner_uid = null): int { $n_feed = (int) $feed; $need_entries = false; @@ -1002,7 +1022,7 @@ class Feeds extends Handler_Protected { } } - function add() { + function add(): void { $feed = clean($_REQUEST['feed']); $cat = clean($_REQUEST['cat'] ?? ''); $need_auth = isset($_REQUEST['need_auth']); @@ -1015,7 +1035,7 @@ class Feeds extends Handler_Protected { } /** - * @return array (code => Status code, message => error message if available) + * @return array (code => Status code, message => error message if available) * * 0 - OK, Feed already exists * 1 - OK, Feed added @@ -1029,8 +1049,7 @@ class Feeds extends Handler_Protected { * 7 - Error while creating feed database entry. * 8 - Permission denied (ACCESS_LEVEL_READONLY). */ - static function _subscribe($url, $cat_id = 0, - $auth_login = '', $auth_pass = '') : array { + static function _subscribe(string $url, int $cat_id = 0, string $auth_login = '', string $auth_pass = ''): array { $user = ORM::for_table("ttrss_users")->find_one($_SESSION['uid']); @@ -1109,15 +1128,18 @@ class Feeds extends Handler_Protected { } } - static function _get_icon_file($feed_id) { + static function _get_icon_file(int $feed_id): string { return Config::get(Config::ICONS_DIR) . "/$feed_id.ico"; } - static function _has_icon($id) { + static function _has_icon(int $id): bool { return is_file(Config::get(Config::ICONS_DIR) . "/$id.ico") && filesize(Config::get(Config::ICONS_DIR) . "/$id.ico") > 0; } - static function _get_icon($id) { + /** + * @return false|string false if the icon ID was unrecognized, otherwise, the icon identifier string + */ + static function _get_icon(int $id) { switch ($id) { case 0: return "archive"; @@ -1137,7 +1159,7 @@ class Feeds extends Handler_Protected { } else { $icon = self::_get_icon_file($id); - if ($icon && file_exists($icon)) { + if ($icon && file_exists($icon)) { return Config::get(Config::ICONS_URL) . "/" . basename($icon) . "?" . filemtime($icon); } } @@ -1147,6 +1169,9 @@ class Feeds extends Handler_Protected { return false; } + /** + * @return false|int false if the feed couldn't be found by URL+owner, otherwise the feed ID + */ static function _find_by_url(string $feed_url, int $owner_uid) { $feed = ORM::for_table('ttrss_feeds') ->where('owner_uid', $owner_uid) @@ -1160,7 +1185,11 @@ class Feeds extends Handler_Protected { } } - /** $owner_uid defaults to $_SESSION['uid] */ + /** + * $owner_uid defaults to $_SESSION['uid'] + * + * @return false|int false if the category/feed couldn't be found by title, otherwise its ID + */ static function _find_by_title(string $title, bool $cat = false, int $owner_uid = 0) { $res = false; @@ -1184,8 +1213,8 @@ class Feeds extends Handler_Protected { } } - static function _get_title($id, bool $cat = false) { - $pdo = Db::pdo(); + static function _get_title(int $id, bool $cat = false): string { + $pdo = Db::pdo(); if ($cat) { return self::_get_cat_title($id); @@ -1197,7 +1226,7 @@ class Feeds extends Handler_Protected { return __("Fresh articles"); } else if ($id == -4) { return __("All articles"); - } else if ($id === 0 || $id === "0") { + } else if ($id === 0) { return __("Archived articles"); } else if ($id == -6) { return __("Recently read"); @@ -1226,12 +1255,12 @@ class Feeds extends Handler_Protected { } } else { - return $id; + return "$id"; } } // only real cats - static function _get_cat_marked(int $cat, int $owner_uid = 0) { + static function _get_cat_marked(int $cat, int $owner_uid = 0): int { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; @@ -1245,16 +1274,17 @@ class Feeds extends Handler_Protected { WHERE (cat_id = :cat OR (:cat IS NULL AND cat_id IS NULL)) AND owner_uid = :uid) AND owner_uid = :uid"); + $sth->execute(["cat" => $cat ? $cat : null, "uid" => $owner_uid]); - $row = $sth->fetch(); - return $row["marked"]; - } else { - return 0; + if ($row = $sth->fetch()) { + return (int) $row["marked"]; + } } + return 0; } - static function _get_cat_unread(int $cat, int $owner_uid = 0) { + static function _get_cat_unread(int $cat, int $owner_uid = 0): int { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; @@ -1265,14 +1295,15 @@ class Feeds extends Handler_Protected { $sth = $pdo->prepare("SELECT SUM(CASE WHEN unread THEN 1 ELSE 0 END) AS unread FROM ttrss_user_entries WHERE feed_id IN (SELECT id FROM ttrss_feeds - WHERE (cat_id = :cat OR (:cat IS NULL AND cat_id IS NULL)) + WHERE (cat_id = :cat OR (:cat IS NULL AND cat_id IS NULL)) AND owner_uid = :uid) AND owner_uid = :uid"); - $sth->execute(["cat" => $cat ? $cat : null, "uid" => $owner_uid]); - $row = $sth->fetch(); - return $row["unread"]; + $sth->execute(["cat" => $cat ? $cat : null, "uid" => $owner_uid]); + if ($row = $sth->fetch()) { + return (int) $row["unread"]; + } } else if ($cat == -1) { return 0; } else if ($cat == -2) { @@ -1280,15 +1311,19 @@ class Feeds extends Handler_Protected { $sth = $pdo->prepare("SELECT COUNT(DISTINCT article_id) AS unread FROM ttrss_user_entries ue, ttrss_user_labels2 l WHERE article_id = ref_id AND unread IS true AND ue.owner_uid = :uid"); + $sth->execute(["uid" => $owner_uid]); - $row = $sth->fetch(); - return $row["unread"]; + if ($row = $sth->fetch()) { + return (int) $row["unread"]; + } } + + return 0; } // only accepts real cats (>= 0) - static function _get_cat_children_unread(int $cat, int $owner_uid = 0) { + static function _get_cat_children_unread(int $cat, int $owner_uid = 0): int { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; $pdo = Db::pdo(); @@ -1307,7 +1342,7 @@ class Feeds extends Handler_Protected { return $unread; } - static function _get_global_unread(int $user_id = 0) { + static function _get_global_unread(int $user_id = 0): int { if (!$user_id) $user_id = $_SESSION["uid"]; @@ -1323,7 +1358,7 @@ class Feeds extends Handler_Protected { return $row["count"]; } - static function _get_cat_title(int $cat_id) { + static function _get_cat_title(int $cat_id): string { switch ($cat_id) { case 0: return __("Uncategorized"); @@ -1343,7 +1378,7 @@ class Feeds extends Handler_Protected { } } - private static function _get_label_unread($label_id, int $owner_uid = 0) { + private static function _get_label_unread(int $label_id, ?int $owner_uid = null): int { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; $pdo = Db::pdo(); @@ -1360,7 +1395,11 @@ class Feeds extends Handler_Protected { } } - static function _get_headlines($params) { + /** + * @param array $params + * @return array $result, $feed_title, $feed_site_url, $last_error, $last_updated, $highlight_words, $first_id, $is_vfeed, $query_error_override + */ + static function _get_headlines($params): array { $pdo = Db::pdo(); @@ -1577,7 +1616,7 @@ class Feeds extends Handler_Protected { } else if ($feed <= LABEL_BASE_INDEX) { // labels $label_id = Labels::feed_to_label_id($feed); - $query_strategy_part = "label_id = ".$pdo->quote($label_id)." AND + $query_strategy_part = "label_id = $label_id AND ttrss_labels2.id = ttrss_user_labels2.label_id AND ttrss_user_labels2.article_id = ref_id"; @@ -1857,7 +1896,10 @@ class Feeds extends Handler_Protected { } - static function _get_parent_cats(int $cat, int $owner_uid) { + /** + * @return array + */ + static function _get_parent_cats(int $cat, int $owner_uid): array { $rv = array(); $pdo = Db::pdo(); @@ -1874,7 +1916,10 @@ class Feeds extends Handler_Protected { return $rv; } - static function _get_child_cats(int $cat, int $owner_uid) { + /** + * @return array + */ + static function _get_child_cats(int $cat, int $owner_uid): array { $rv = array(); $pdo = Db::pdo(); @@ -1891,7 +1936,11 @@ class Feeds extends Handler_Protected { return $rv; } - static function _cats_of(array $feeds, int $owner_uid, bool $with_parents = false) { + /** + * @param array $feeds + * @return array + */ + static function _cats_of(array $feeds, int $owner_uid, bool $with_parents = false): array { if (count($feeds) == 0) return []; @@ -1930,24 +1979,27 @@ class Feeds extends Handler_Protected { } } - private function _color_of($name) { - $colormap = [ "#1cd7d7","#d91111","#1212d7","#8e16e5","#7b7b7b", - "#39f110","#0bbea6","#ec0e0e","#1534f2","#b9e416", - "#479af2","#f36b14","#10c7e9","#1e8fe7","#e22727" ]; + private function _color_of(string $name): string { + $colormap = [ "#1cd7d7","#d91111","#1212d7","#8e16e5","#7b7b7b", + "#39f110","#0bbea6","#ec0e0e","#1534f2","#b9e416", + "#479af2","#f36b14","#10c7e9","#1e8fe7","#e22727" ]; - $sum = 0; + $sum = 0; - for ($i = 0; $i < strlen($name); $i++) { - $sum += ord($name[$i]); - } + for ($i = 0; $i < strlen($name); $i++) { + $sum += ord($name[$i]); + } - $sum %= count($colormap); + $sum %= count($colormap); - return $colormap[$sum]; + return $colormap[$sum]; } - private static function _get_feeds_from_html($url, $content) { - $url = UrlHelper::validate($url); + /** + * @return array array of feed URL -> feed title + */ + private static function _get_feeds_from_html(string $url, string $content): array { + $url = UrlHelper::validate($url); $baseUrl = substr($url, 0, strrpos($url, '/') + 1); $feedUrls = []; @@ -1964,9 +2016,7 @@ class Feeds extends Handler_Protected { if ($title == '') { $title = $entry->getAttribute('type'); } - $feedUrl = rewrite_relative_url( - $baseUrl, $entry->getAttribute('href') - ); + $feedUrl = UrlHelper::rewrite_relative($baseUrl, $entry->getAttribute('href')); $feedUrls[$feedUrl] = $title; } } @@ -1974,11 +2024,11 @@ class Feeds extends Handler_Protected { return $feedUrls; } - static function _is_html($content) { + static function _is_html(string $content): bool { return preg_match("/where('owner_uid', $owner_uid) ->find_one($id); @@ -1987,7 +2037,7 @@ class Feeds extends Handler_Protected { $cat->delete(); } - static function _add_cat(string $title, int $owner_uid, int $parent_cat = null, int $order_id = 0) { + static function _add_cat(string $title, int $owner_uid, int $parent_cat = null, int $order_id = 0): bool { $cat = ORM::for_table('ttrss_feed_categories') ->where('owner_uid', $owner_uid) @@ -2011,13 +2061,13 @@ class Feeds extends Handler_Protected { return false; } - static function _clear_access_keys(int $owner_uid) { + static function _clear_access_keys(int $owner_uid): void { $key = ORM::for_table('ttrss_access_keys') ->where('owner_uid', $owner_uid) ->delete_many(); } - static function _update_access_key(string $feed_id, bool $is_cat, int $owner_uid) { + static function _update_access_key(int $feed_id, bool $is_cat, int $owner_uid): ?string { $key = ORM::for_table('ttrss_access_keys') ->where('owner_uid', $owner_uid) ->where('feed_id', $feed_id) @@ -2027,7 +2077,7 @@ class Feeds extends Handler_Protected { return self::_get_access_key($feed_id, $is_cat, $owner_uid); } - static function _get_access_key(string $feed_id, bool $is_cat, int $owner_uid) { + static function _get_access_key(int $feed_id, bool $is_cat, int $owner_uid): ?string { $key = ORM::for_table('ttrss_access_keys') ->where('owner_uid', $owner_uid) ->where('feed_id', $feed_id) @@ -2036,21 +2086,23 @@ class Feeds extends Handler_Protected { if ($key) { return $key->access_key; - } else { - $key = ORM::for_table('ttrss_access_keys')->create(); + } - $key->owner_uid = $owner_uid; - $key->feed_id = $feed_id; - $key->is_cat = $is_cat; - $key->access_key = uniqid_short(); + $key = ORM::for_table('ttrss_access_keys')->create(); - if ($key->save()) { - return $key->access_key; - } + $key->owner_uid = $owner_uid; + $key->feed_id = $feed_id; + $key->is_cat = $is_cat; + $key->access_key = uniqid_short(); + + if ($key->save()) { + return $key->access_key; } + + return null; } - static function _purge(int $feed_id, int $purge_interval) { + static function _purge(int $feed_id, int $purge_interval): ?int { if (!$purge_interval) $purge_interval = self::_get_purge_interval($feed_id); @@ -2079,7 +2131,7 @@ class Feeds extends Handler_Protected { if ($purge_interval <= 0) { Debug::log("purge_feed: purging disabled for this feed, nothing to do.", Debug::$LOG_VERBOSE); - return; + return null; } if (!$purge_unread) @@ -2120,7 +2172,7 @@ class Feeds extends Handler_Protected { return $rows_deleted; } - private static function _get_purge_interval(int $feed_id) { + private static function _get_purge_interval(int $feed_id): int { $feed = ORM::for_table('ttrss_feeds')->find_one($feed_id); if ($feed) { @@ -2133,7 +2185,10 @@ class Feeds extends Handler_Protected { } } - private static function _search_to_sql($search, $search_language, $owner_uid) { + /** + * @return array{0: string, 1: array} [$search_query_part, $search_words] + */ + private static function _search_to_sql(string $search, string $search_language, int $owner_uid): array { $keywords = str_getcsv(preg_replace('/(-?\w+)\:"(\w+)/', '"${1}:${2}', trim($search)), ' '); $query_keywords = array(); $search_words = array(); @@ -2226,7 +2281,7 @@ class Feeds extends Handler_Protected { array_push($query_keywords, "($not (ttrss_entries.id IN ( SELECT article_id FROM ttrss_user_labels2 WHERE - label_id = ".$pdo->quote($label_id).")))"); + label_id = $label_id)))"); } else { array_push($query_keywords, "(false)"); } @@ -2300,7 +2355,10 @@ class Feeds extends Handler_Protected { return array($search_query_part, $search_words); } - static function _order_to_override_query($order) { + /** + * @return array{0: string, 1: bool} + */ + static function _order_to_override_query(string $order): array { $query = ""; $skip_first_id = false; @@ -2310,7 +2368,9 @@ class Feeds extends Handler_Protected { }, $order); - if ($query) return [$query, $skip_first_id]; + if (is_string($query) && $query !== "") { + return [$query, $skip_first_id]; + } switch ($order) { case "title": @@ -2328,7 +2388,7 @@ class Feeds extends Handler_Protected { return [$query, $skip_first_id]; } - private function _mark_timestamp($label) { + private function _mark_timestamp(string $label): void { if (empty($_REQUEST['timestamps'])) return; diff --git a/classes/rpc.php b/classes/rpc.php index 4024aae2e..60119a605 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -227,7 +227,7 @@ class RPC extends Handler_Protected { $search_query = clean($_REQUEST['search_query']); $search_lang = clean($_REQUEST['search_lang']); - Feeds::_catchup($feed_id, $is_cat, false, $mode, [$search_query, $search_lang]); + Feeds::_catchup($feed_id, $is_cat, null, $mode, [$search_query, $search_lang]); // return counters here synchronously so that frontend can figure out next unread feed properly print json_encode(['counters' => Counters::get_all()]); -- cgit v1.2.3-54-g00ecf From b0eb347839d3350b57bd614eee67e32a574661d5 Mon Sep 17 00:00:00 2001 From: wn_ Date: Fri, 12 Nov 2021 05:04:55 +0000 Subject: Fix a warning in 'classes/counters.php'. --- classes/counters.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'classes') diff --git a/classes/counters.php b/classes/counters.php index 1375a6694..50d103a5c 100644 --- a/classes/counters.php +++ b/classes/counters.php @@ -273,6 +273,10 @@ class Counters { if (is_array($feeds)) { foreach ($feeds as $feed) { + if (!method_exists($feed['sender'], 'get_unread')) { + continue; + } + $cv = [ "id" => PluginHost::pfeed_to_feed_id($feed['id']), "counter" => $feed['sender']->get_unread($feed['id']) -- cgit v1.2.3-54-g00ecf From 011c941e7cdfce21d415eb6fa479c411776c79ce Mon Sep 17 00:00:00 2001 From: wn_ Date: Fri, 12 Nov 2021 05:24:02 +0000 Subject: Fix some PHPStan warnings in 'classes/db/migrations.php', 'classes/db/prefs.php', and 'classes/debug.php'. --- classes/db/migrations.php | 37 +++++++++++++++++++++++-------------- classes/db/prefs.php | 10 ++++++++-- classes/debug.php | 2 +- 3 files changed, 32 insertions(+), 17 deletions(-) (limited to 'classes') diff --git a/classes/db/migrations.php b/classes/db/migrations.php index 3008af535..cb74c247a 100644 --- a/classes/db/migrations.php +++ b/classes/db/migrations.php @@ -1,29 +1,29 @@ pdo = Db::pdo(); } - function initialize_for_plugin(Plugin $plugin, bool $base_is_latest = true, string $schema_suffix = "sql") { + function initialize_for_plugin(Plugin $plugin, bool $base_is_latest = true, string $schema_suffix = "sql"): void { $plugin_dir = PluginHost::getInstance()->get_plugin_dir($plugin); $this->initialize($plugin_dir . "/${schema_suffix}", strtolower("ttrss_migrations_plugin_" . get_class($plugin)), $base_is_latest); } - function initialize(string $root_path, string $migrations_table, bool $base_is_latest = true, int $max_version_override = 0) { + function initialize(string $root_path, string $migrations_table, bool $base_is_latest = true, int $max_version_override = 0): void { $this->base_path = "$root_path/" . Config::get(Config::DB_TYPE); $this->migrations_path = $this->base_path . "/migrations"; $this->migrations_table = $migrations_table; @@ -31,7 +31,7 @@ class Db_Migrations { $this->max_version_override = $max_version_override; } - private function set_version(int $version) { + private function set_version(int $version): void { Debug::log("Updating table {$this->migrations_table} with version ${version}...", Debug::LOG_EXTENDED); $sth = $this->pdo->query("SELECT * FROM {$this->migrations_table}"); @@ -66,11 +66,15 @@ class Db_Migrations { } } - private function create_migrations_table() { + private function create_migrations_table(): void { $this->pdo->query("CREATE TABLE IF NOT EXISTS {$this->migrations_table} (schema_version integer not null)"); } - private function migrate_to(int $version) { + /** + * @throws PDOException + * @return bool false if the migration failed, otherwise true (or an exception) + */ + private function migrate_to(int $version): bool { try { if ($version <= $this->get_version()) { Debug::log("Refusing to apply version $version: current version is higher", Debug::LOG_VERBOSE); @@ -110,8 +114,10 @@ class Db_Migrations { Debug::log("Migration finished, current version: " . $this->get_version(), Debug::LOG_VERBOSE); Logger::log(E_USER_NOTICE, "Applied migration to version $version for {$this->migrations_table}"); + return true; } else { Debug::log("Migration failed: schema file is empty or missing.", Debug::LOG_VERBOSE); + return false; } } catch (PDOException $e) { @@ -174,6 +180,9 @@ class Db_Migrations { return !$this->is_migration_needed(); } + /** + * @return array + */ private function get_lines(int $version) : array { if ($version > 0) $filename = "{$this->migrations_path}/${version}.sql"; diff --git a/classes/db/prefs.php b/classes/db/prefs.php index 821216622..209ef58c1 100644 --- a/classes/db/prefs.php +++ b/classes/db/prefs.php @@ -2,11 +2,17 @@ class Db_Prefs { // this class is a stub for the time being (to be removed) - function read($pref_name, $user_id = false, $die_on_error = false) { + /** + * @return bool|int|null|string + */ + function read(string $pref_name, ?int $user_id = null, bool $die_on_error = false) { return get_pref($pref_name, $user_id); } - function write($pref_name, $value, $user_id = false, $strip_tags = true) { + /** + * @param mixed $value + */ + function write(string $pref_name, $value, ?int $user_id = null, bool $strip_tags = true): bool { return set_pref($pref_name, $value, $user_id, $strip_tags); } } diff --git a/classes/debug.php b/classes/debug.php index eca7b31db..6e8c46ed2 100644 --- a/classes/debug.php +++ b/classes/debug.php @@ -29,7 +29,7 @@ class Debug { private static ?string $logfile = null; /** - * @var Debug::LOG_* + * @var int Debug::LOG_* */ private static int $loglevel = self::LOG_NORMAL; -- cgit v1.2.3-54-g00ecf From 9db5e402a0283deaae7d06496f410e9ab8deb1b4 Mon Sep 17 00:00:00 2001 From: wn_ Date: Fri, 12 Nov 2021 05:42:55 +0000 Subject: Address PHPStan warnings in 'classes/rpc.php'. Also a couple minor fixes in 'classes/article.php' and 'classes/labels.php'. --- classes/article.php | 2 +- classes/labels.php | 2 +- classes/rpc.php | 76 ++++++++++++++++++++++++++++++++++------------------- 3 files changed, 51 insertions(+), 29 deletions(-) (limited to 'classes') diff --git a/classes/article.php b/classes/article.php index 4af36d1c0..b4f28dc28 100755 --- a/classes/article.php +++ b/classes/article.php @@ -483,7 +483,7 @@ class Article extends Handler_Protected { /** * @param array $ids - * @param Article::CATCHUP_MODE_* $cmode + * @param int $cmode Article::CATCHUP_MODE_* */ static function _catchup_by_id($ids, int $cmode, ?int $owner_uid = null): void { diff --git a/classes/labels.php b/classes/labels.php index d78f92139..5a17d665e 100644 --- a/classes/labels.php +++ b/classes/labels.php @@ -71,7 +71,7 @@ class Labels } /** - * @param array> $labels + * @param array>> $labels * * @see Article::_get_labels() */ diff --git a/classes/rpc.php b/classes/rpc.php index 60119a605..75d008b8b 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -7,7 +7,10 @@ class RPC extends Handler_Protected { return array_search($method, $csrf_ignored) !== false; }*/ - private function _translations_as_array() { + /** + * @return array + */ + private function _translations_as_array(): array { global $text_domains; @@ -37,7 +40,7 @@ class RPC extends Handler_Protected { } - function togglepref() { + function togglepref(): void { $key = clean($_REQUEST["key"]); set_pref($key, !get_pref($key)); $value = get_pref($key); @@ -45,7 +48,7 @@ class RPC extends Handler_Protected { print json_encode(array("param" =>$key, "value" => $value)); } - function setpref() { + function setpref(): void { // set_pref escapes input, so no need to double escape it here $key = clean($_REQUEST['key']); $value = $_REQUEST['value']; @@ -55,7 +58,7 @@ class RPC extends Handler_Protected { print json_encode(array("param" =>$key, "value" => $value)); } - function mark() { + function mark(): void { $mark = clean($_REQUEST["mark"]); $id = clean($_REQUEST["id"]); @@ -68,7 +71,7 @@ class RPC extends Handler_Protected { print json_encode(array("message" => "UPDATE_COUNTERS")); } - function delete() { + function delete(): void { $ids = explode(",", clean($_REQUEST["ids"])); $ids_qmarks = arr_qmarks($ids); @@ -81,7 +84,7 @@ class RPC extends Handler_Protected { print json_encode(array("message" => "UPDATE_COUNTERS")); } - function publ() { + function publ(): void { $pub = clean($_REQUEST["pub"]); $id = clean($_REQUEST["id"]); @@ -94,7 +97,7 @@ class RPC extends Handler_Protected { print json_encode(array("message" => "UPDATE_COUNTERS")); } - function getRuntimeInfo() { + function getRuntimeInfo(): void { $reply = [ 'runtime-info' => $this->_make_runtime_info() ]; @@ -102,7 +105,7 @@ class RPC extends Handler_Protected { print json_encode($reply); } - function getAllCounters() { + function getAllCounters(): void { @$seq = (int) $_REQUEST['seq']; $feed_id_count = (int)$_REQUEST["feed_id_count"]; @@ -133,7 +136,7 @@ class RPC extends Handler_Protected { } /* GET["cmode"] = 0 - mark as read, 1 - as unread, 2 - toggle */ - function catchupSelected() { + function catchupSelected(): void { $ids = array_map("intval", clean($_REQUEST["ids"] ?? [])); $cmode = (int)clean($_REQUEST["cmode"]); @@ -145,7 +148,7 @@ class RPC extends Handler_Protected { "feeds" => Article::_feeds_of($ids)]); } - function markSelected() { + function markSelected(): void { $ids = array_map("intval", clean($_REQUEST["ids"] ?? [])); $cmode = (int)clean($_REQUEST["cmode"]); @@ -157,7 +160,7 @@ class RPC extends Handler_Protected { "feeds" => Article::_feeds_of($ids)]); } - function publishSelected() { + function publishSelected(): void { $ids = array_map("intval", clean($_REQUEST["ids"] ?? [])); $cmode = (int)clean($_REQUEST["cmode"]); @@ -169,7 +172,7 @@ class RPC extends Handler_Protected { "feeds" => Article::_feeds_of($ids)]); } - function sanityCheck() { + function sanityCheck(): void { $_SESSION["hasSandbox"] = clean($_REQUEST["hasSandbox"]) === "true"; $_SESSION["clientTzOffset"] = clean($_REQUEST["clientTzOffset"]); @@ -220,7 +223,7 @@ class RPC extends Handler_Protected { print ""; }*/ - function catchupFeed() { + function catchupFeed(): void { $feed_id = clean($_REQUEST['feed_id']); $is_cat = clean($_REQUEST['is_cat']) == "true"; $mode = clean($_REQUEST['mode'] ?? ''); @@ -235,7 +238,7 @@ class RPC extends Handler_Protected { //print json_encode(array("message" => "UPDATE_COUNTERS")); } - function setWidescreen() { + function setWidescreen(): void { $wide = (int) clean($_REQUEST["wide"]); set_pref(Prefs::WIDESCREEN_MODE, $wide); @@ -243,7 +246,7 @@ class RPC extends Handler_Protected { print json_encode(["wide" => $wide]); } - static function updaterandomfeed_real() { + static function updaterandomfeed_real(): void { $default_interval = (int) Prefs::get_default(Prefs::DEFAULT_UPDATE_INTERVAL); @@ -336,11 +339,14 @@ class RPC extends Handler_Protected { } - function updaterandomfeed() { + function updaterandomfeed(): void { self::updaterandomfeed_real(); } - private function markArticlesById($ids, $cmode) { + /** + * @param array $ids + */ + private function markArticlesById(array $ids, int $cmode): void { $ids_qmarks = arr_qmarks($ids); @@ -361,7 +367,10 @@ class RPC extends Handler_Protected { $sth->execute(array_merge($ids, [$_SESSION['uid']])); } - private function publishArticlesById($ids, $cmode) { + /** + * @param array $ids + */ + private function publishArticlesById(array $ids, int $cmode): void { $ids_qmarks = arr_qmarks($ids); @@ -382,7 +391,7 @@ class RPC extends Handler_Protected { $sth->execute(array_merge($ids, [$_SESSION['uid']])); } - function log() { + function log(): void { $msg = clean($_REQUEST['msg'] ?? ""); $file = basename(clean($_REQUEST['file'] ?? "")); $line = (int) clean($_REQUEST['line'] ?? 0); @@ -396,7 +405,7 @@ class RPC extends Handler_Protected { } } - function checkforupdates() { + function checkforupdates(): void { $rv = ["changeset" => [], "plugins" => []]; $version = Config::get_version(false); @@ -425,7 +434,10 @@ class RPC extends Handler_Protected { print json_encode($rv); } - private function _make_init_params() { + /** + * @return array + */ + private function _make_init_params(): array { $params = array(); foreach ([Prefs::ON_CATCHUP_SHOW_NEXT_FEED, Prefs::HIDE_READ_FEEDS, @@ -481,7 +493,7 @@ class RPC extends Handler_Protected { return $params; } - private function image_to_base64($filename) { + private function image_to_base64(string $filename): string { if (file_exists($filename)) { $ext = pathinfo($filename, PATHINFO_EXTENSION); @@ -493,7 +505,10 @@ class RPC extends Handler_Protected { } } - static function _make_runtime_info() { + /** + * @return array + */ + static function _make_runtime_info(): array { $data = array(); $pdo = Db::pdo(); @@ -562,7 +577,10 @@ class RPC extends Handler_Protected { return $data; } - static function get_hotkeys_info() { + /** + * @return array> + */ + static function get_hotkeys_info(): array { $hotkeys = array( __("Navigation") => array( "next_feed" => __("Open next feed"), @@ -642,8 +660,12 @@ class RPC extends Handler_Protected { return $hotkeys; } - // {3} - 3 panel mode only - // {C} - combined mode only + /** + * {3} - 3 panel mode only + * {C} - combined mode only + * + * @return array{0: array, 1: array} $prefixes, $hotkeys + */ static function get_hotkeys_map() { $hotkeys = array( "k" => "next_feed", @@ -728,7 +750,7 @@ class RPC extends Handler_Protected { return array($prefixes, $hotkeys); } - function hotkeyHelp() { + function hotkeyHelp(): void { $info = self::get_hotkeys_info(); $imap = self::get_hotkeys_map(); $omap = array(); -- cgit v1.2.3-54-g00ecf From 2c41bc7fbc9013e79e929a31e3824cf040afc54a Mon Sep 17 00:00:00 2001 From: wn_ Date: Fri, 12 Nov 2021 06:16:18 +0000 Subject: Address PHPStan warnings in 'classes/mailer.php', 'classes/opml.php', and 'classes/pluginhandler.php'. --- classes/mailer.php | 14 ++++++++++---- classes/opml.php | 32 ++++++++++++++++++++------------ classes/pluginhandler.php | 2 +- 3 files changed, 31 insertions(+), 17 deletions(-) (limited to 'classes') diff --git a/classes/mailer.php b/classes/mailer.php index 8238904ee..4eb13aec8 100644 --- a/classes/mailer.php +++ b/classes/mailer.php @@ -1,8 +1,12 @@ $params + * @return bool|int bool if the default mail function handled the request, otherwise an int as described in Mailer#mail() + */ + function mail(array $params) { $to_name = $params["to_name"] ?? ""; $to_address = $params["to_address"]; @@ -26,6 +30,8 @@ class Mailer { // 4. set error message if needed via passed Mailer instance function set_error() foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEND_MAIL) as $p) { + // Implemented via plugin, so ignore the undefined method 'hook_send_mail'. + // @phpstan-ignore-next-line $rc = $p->hook_send_mail($this, $params); if ($rc == 1) @@ -46,12 +52,12 @@ class Mailer { return $rc; } - function set_error($message) { + function set_error(string $message): void { $this->last_error = $message; user_error("Error sending mail: $message", E_USER_WARNING); } - function error() { + function error(): string { return $this->last_error; } } diff --git a/classes/opml.php b/classes/opml.php index f60918061..b9f5f2eab 100644 --- a/classes/opml.php +++ b/classes/opml.php @@ -7,6 +7,9 @@ class OPML extends Handler_Protected { return array_search($method, $csrf_ignored) !== false; } + /** + * @return bool|int|void false if writing the file failed, true if printing succeeded, int if bytes were written to a file, or void if $owner_uid is missing + */ function export() { $output_name = sprintf("tt-rss_%s_%s.opml", $_SESSION["name"], date("Y-m-d")); $include_settings = $_REQUEST["include_settings"] == "1"; @@ -17,7 +20,7 @@ class OPML extends Handler_Protected { return $rc; } - function import() { + function import(): void { $owner_uid = $_SESSION["uid"]; header('Content-Type: text/html; charset=utf-8'); @@ -42,13 +45,11 @@ class OPML extends Handler_Protected { "; print ""; - - } // Export - private function opml_export_category(int $owner_uid, int $cat_id, bool $hide_private_feeds = false, bool $include_settings = true) { + private function opml_export_category(int $owner_uid, int $cat_id, bool $hide_private_feeds = false, bool $include_settings = true): string { if ($hide_private_feeds) $hide_qpart = "(private IS false AND auth_login = '' AND auth_pass = '')"; @@ -124,6 +125,9 @@ class OPML extends Handler_Protected { return $out; } + /** + * @return bool|int|void false if writing the file failed, true if printing succeeded, int if bytes were written to a file, or void if $owner_uid is missing + */ function opml_export(string $filename, int $owner_uid, bool $hide_private_feeds = false, bool $include_settings = true, bool $file_output = false) { if (!$owner_uid) return; @@ -290,13 +294,14 @@ class OPML extends Handler_Protected { if ($file_output) return file_put_contents($filename, $res) > 0; - else - print $res; + + print $res; + return true; } // Import - private function opml_import_feed(DOMNode $node, int $cat_id, int $owner_uid, int $nest) { + private function opml_import_feed(DOMNode $node, int $cat_id, int $owner_uid, int $nest): void { $attrs = $node->attributes; $feed_title = mb_substr($attrs->getNamedItem('text')->nodeValue, 0, 250); @@ -341,7 +346,7 @@ class OPML extends Handler_Protected { } } - private function opml_import_label(DOMNode $node, int $owner_uid, int $nest) { + private function opml_import_label(DOMNode $node, int $owner_uid, int $nest): void { $attrs = $node->attributes; $label_name = $attrs->getNamedItem('label-name')->nodeValue; @@ -358,7 +363,7 @@ class OPML extends Handler_Protected { } } - private function opml_import_preference(DOMNode $node, int $owner_uid, int $nest) { + private function opml_import_preference(DOMNode $node, int $owner_uid, int $nest): void { $attrs = $node->attributes; $pref_name = $attrs->getNamedItem('pref-name')->nodeValue; @@ -372,7 +377,7 @@ class OPML extends Handler_Protected { } } - private function opml_import_filter(DOMNode $node, int $owner_uid, int $nest) { + private function opml_import_filter(DOMNode $node, int $owner_uid, int $nest): void { $attrs = $node->attributes; $filter_type = $attrs->getNamedItem('filter-type')->nodeValue; @@ -526,7 +531,7 @@ class OPML extends Handler_Protected { } } - private function opml_import_category(DOMDocument $doc, ?DOMNode $root_node, int $owner_uid, int $parent_id, int $nest) { + private function opml_import_category(DOMDocument $doc, ?DOMNode $root_node, int $owner_uid, int $parent_id, int $nest): void { $default_cat_id = (int) $this->get_feed_category('Imported feeds', $owner_uid, 0); if ($root_node) { @@ -601,6 +606,9 @@ class OPML extends Handler_Protected { } /** $filename is optional; assumes HTTP upload with $_FILES otherwise */ + /** + * @return bool|void false on failure, true if successful, void if $owner_uid is missing + */ function opml_import(int $owner_uid, string $filename = "") { if (!$owner_uid) return; @@ -667,7 +675,7 @@ class OPML extends Handler_Protected { return true; } - private function opml_notice(string $msg, int $prefix_length = 0) { + private function opml_notice(string $msg, int $prefix_length = 0): void { if (php_sapi_name() == "cli") { Debug::log(str_repeat(" ", $prefix_length) . $msg); } else { diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php index 9b3772ddc..5c73920e5 100644 --- a/classes/pluginhandler.php +++ b/classes/pluginhandler.php @@ -4,7 +4,7 @@ class PluginHandler extends Handler_Protected { return true; } - function catchall($method) { + function catchall(string $method): void { $plugin_name = clean($_REQUEST["plugin"]); $plugin = PluginHost::getInstance()->get_plugin($plugin_name); $csrf_token = ($_POST["csrf_token"] ?? ""); -- cgit v1.2.3-54-g00ecf From d3a81f598b24d6ae4f98415fac9509df6749eaf8 Mon Sep 17 00:00:00 2001 From: wn_ Date: Fri, 12 Nov 2021 21:17:31 +0000 Subject: Switch class properties from PHP typing to PHPDoc for compatibility with PHP < 7.4.0 --- classes/db/migrations.php | 37 ++++++++++++++++++++++++++---------- classes/debug.php | 48 ++++++++++++++++++++++++++++++++--------------- classes/diskcache.php | 4 +++- classes/mailer.php | 4 +++- classes/pluginhost.php | 41 +++++++++++++++++++++++++--------------- classes/urlhelper.php | 32 +++++++++++++++++++++++-------- 6 files changed, 116 insertions(+), 50 deletions(-) (limited to 'classes') diff --git a/classes/db/migrations.php b/classes/db/migrations.php index cb74c247a..6e20ddf7f 100644 --- a/classes/db/migrations.php +++ b/classes/db/migrations.php @@ -1,16 +1,33 @@ pdo = Db::pdo(); diff --git a/classes/debug.php b/classes/debug.php index 6e8c46ed2..e20126b86 100644 --- a/classes/debug.php +++ b/classes/debug.php @@ -1,9 +1,9 @@ $params diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 36e050377..173a75611 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -1,38 +1,49 @@ >> hook types -> priority levels -> Plugins */ - private array $hooks = []; + private $hooks = []; /** @var array */ - private array $plugins = []; + private $plugins = []; /** @var array> handler type -> method type -> Plugin */ - private array $handlers = []; + private $handlers = []; /** @var array command type -> details array */ - private array $commands = []; + private $commands = []; /** @var array> plugin name -> (potential profile array) -> key -> value */ - private array $storage = []; + private $storage = []; /** @var array> */ - private array $feeds = []; + private $feeds = []; /** @var array API method name, Plugin sender */ - private array $api_methods = []; + private $api_methods = []; /** @var array> */ - private array $plugin_actions = []; + private $plugin_actions = []; + + /** @var int|null */ + private $owner_uid = null; + + /** @var bool */ + private $data_loaded = false; - private ?int $owner_uid = null; - private bool $data_loaded = false; - private static ?PluginHost $instance = null; + /** @var PluginHost|null */ + private static $instance = null; const API_VERSION = 2; const PUBLIC_METHOD_DELIMITER = "--"; diff --git a/classes/urlhelper.php b/classes/urlhelper.php index 0592bf28c..351d66b8d 100644 --- a/classes/urlhelper.php +++ b/classes/urlhelper.php @@ -6,14 +6,30 @@ class UrlHelper { "tel" ]; - static string $fetch_last_error; - static int $fetch_last_error_code; - static string $fetch_last_error_content; - static string $fetch_last_content_type; - static string $fetch_last_modified; - static string $fetch_effective_url; - static string $fetch_effective_ip_addr; - static bool $fetch_curl_used; + // TODO: class properties can be switched to PHP typing if/when the minimum PHP_VERSION is raised to 7.4.0+ + /** @var string */ + static $fetch_last_error; + + /** @var int */ + static $fetch_last_error_code; + + /** @var string */ + static $fetch_last_error_content; + + /** @var string */ + static $fetch_last_content_type; + + /** @var string */ + static $fetch_last_modified; + + /** @var string */ + static $fetch_effective_url; + + /** @var string */ + static $fetch_effective_ip_addr; + + /** @var bool */ + static $fetch_curl_used; /** * @param array $parts -- cgit v1.2.3-54-g00ecf From 25775bb4075e70aa4fad4620d077d4a0e59bb139 Mon Sep 17 00:00:00 2001 From: wn_ Date: Sat, 13 Nov 2021 04:14:18 +0000 Subject: Fix type of 'check_first_id' in Feeds '_format_headlines_list'. --- classes/feeds.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'classes') diff --git a/classes/feeds.php b/classes/feeds.php index 24511c396..22906cb54 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -21,7 +21,7 @@ class Feeds extends Handler_Protected { * @return array{0: array, 1: int, 2: int, 3: bool, 4: array} $topmost_article_ids, $headlines_count, $feed, $disable_cache, $reply */ private function _format_headlines_list(int $feed, string $method, string $view_mode, int $limit, bool $cat_view, - int $offset, string $override_order, bool $include_children, bool $check_first_id, + int $offset, string $override_order, bool $include_children, ?int $check_first_id = null, bool $skip_first_id_check, string $order_by): array { $disable_cache = false; -- cgit v1.2.3-54-g00ecf From 1ec003ce352f5bf1418986d7b96c35e75231ffde Mon Sep 17 00:00:00 2001 From: wn_ Date: Sat, 13 Nov 2021 14:05:43 +0000 Subject: Typing IHandler methods, typing Handler_Public, fix type of $feed_id (might be tag). --- classes/api.php | 2 +- classes/feeds.php | 14 +++++++++++-- classes/handler.php | 13 ++++++++++--- classes/handler/administrative.php | 2 +- classes/handler/protected.php | 2 +- classes/handler/public.php | 40 +++++++++++++++++++++----------------- classes/ihandler.php | 4 ++-- 7 files changed, 49 insertions(+), 28 deletions(-) (limited to 'classes') diff --git a/classes/api.php b/classes/api.php index 1835d487c..125741c73 100755 --- a/classes/api.php +++ b/classes/api.php @@ -27,7 +27,7 @@ class API extends Handler { ]); } - function before($method) { + function before(string $method): bool { if (parent::before($method)) { header("Content-Type: text/json"); diff --git a/classes/feeds.php b/classes/feeds.php index 22906cb54..0c75215c8 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -2067,7 +2067,12 @@ class Feeds extends Handler_Protected { ->delete_many(); } - static function _update_access_key(int $feed_id, bool $is_cat, int $owner_uid): ?string { + /** + * @param string $feed_id may be a feed ID or tag + * + * @see Handler_Public#generate_syndicated_feed() + */ + static function _update_access_key(string $feed_id, bool $is_cat, int $owner_uid): ?string { $key = ORM::for_table('ttrss_access_keys') ->where('owner_uid', $owner_uid) ->where('feed_id', $feed_id) @@ -2077,7 +2082,12 @@ class Feeds extends Handler_Protected { return self::_get_access_key($feed_id, $is_cat, $owner_uid); } - static function _get_access_key(int $feed_id, bool $is_cat, int $owner_uid): ?string { + /** + * @param string $feed_id may be a feed ID or tag + * + * @see Handler_Public#generate_syndicated_feed() + */ + static function _get_access_key(string $feed_id, bool $is_cat, int $owner_uid): ?string { $key = ORM::for_table('ttrss_access_keys') ->where('owner_uid', $owner_uid) ->where('feed_id', $feed_id) diff --git a/classes/handler.php b/classes/handler.php index 4c79628db..3ee42cedb 100644 --- a/classes/handler.php +++ b/classes/handler.php @@ -1,9 +1,16 @@ */ protected $args; - function __construct($args) { + /** + * @param array $args + */ + function __construct(array $args) { $this->pdo = Db::pdo(); $this->args = $args; } @@ -12,11 +19,11 @@ class Handler implements IHandler { return false; } - function before($method) { + function before(string $method): bool { return true; } - function after() { + function after(): bool { return true; } diff --git a/classes/handler/administrative.php b/classes/handler/administrative.php index f2f5b36ba..533cb3630 100644 --- a/classes/handler/administrative.php +++ b/classes/handler/administrative.php @@ -1,6 +1,6 @@ = UserHelper::ACCESS_LEVEL_ADMIN) { return true; diff --git a/classes/handler/protected.php b/classes/handler/protected.php index 8e9e5ca1d..a15fc0956 100644 --- a/classes/handler/protected.php +++ b/classes/handler/protected.php @@ -1,7 +1,7 @@ get_headlines(PluginHost::feed_to_pfeed_id((int)$feed), $params); } else { user_error("Failed to find handler for plugin feed ID: $feed", E_USER_ERROR); - return false; + return; } } else { @@ -247,7 +251,7 @@ class Handler_Public extends Handler { } } - function getUnread() { + function getUnread(): void { $login = clean($_REQUEST["login"]); $fresh = clean($_REQUEST["fresh"]) == "1"; @@ -265,7 +269,7 @@ class Handler_Public extends Handler { } } - function getProfiles() { + function getProfiles(): void { $login = clean($_REQUEST["login"]); $rv = []; @@ -288,7 +292,7 @@ class Handler_Public extends Handler { print json_encode($rv); } - function logout() { + function logout(): void { if (validate_csrf($_POST["csrf_token"])) { UserHelper::logout(); header("Location: index.php"); @@ -298,7 +302,7 @@ class Handler_Public extends Handler { } } - function rss() { + function rss(): void { $feed = clean($_REQUEST["id"]); $key = clean($_REQUEST["key"]); $is_cat = clean($_REQUEST["is_cat"] ?? false); @@ -333,21 +337,21 @@ class Handler_Public extends Handler { header('HTTP/1.1 403 Forbidden'); } - function updateTask() { + function updateTask(): void { PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK); } - function housekeepingTask() { + function housekeepingTask(): void { PluginHost::getInstance()->run_hooks(PluginHost::HOOK_HOUSE_KEEPING); } - function globalUpdateFeeds() { + function globalUpdateFeeds(): void { RPC::updaterandomfeed_real(); PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK); } - function login() { + function login(): void { if (!Config::get(Config::SINGLE_USER_MODE)) { $login = clean($_POST["login"]); @@ -403,12 +407,12 @@ class Handler_Public extends Handler { } } - function index() { + function index(): void { header("Content-Type: text/plain"); print Errors::to_json(Errors::E_UNKNOWN_METHOD); } - function forgotpass() { + function forgotpass(): void { startup_gettext(); session_start(); @@ -587,7 +591,7 @@ class Handler_Public extends Handler { print ""; } - function dbupdate() { + function dbupdate(): void { startup_gettext(); if (!Config::get(Config::SINGLE_USER_MODE) && ($_SESSION["access_level"] ?? 0) < 10) { @@ -730,7 +734,7 @@ class Handler_Public extends Handler { Date: Sat, 13 Nov 2021 14:15:20 +0000 Subject: Address PHPStan warnings in 'classes/api.php'. --- classes/api.php | 109 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 49 deletions(-) (limited to 'classes') diff --git a/classes/api.php b/classes/api.php index 125741c73..d2668beee 100755 --- a/classes/api.php +++ b/classes/api.php @@ -13,13 +13,20 @@ class API extends Handler { const E_UNKNOWN_METHOD = "UNKNOWN_METHOD"; const E_OPERATION_FAILED = "E_OPERATION_FAILED"; + /** @var int|null */ private $seq; - private static function _param_to_bool($p) { + /** + * @param mixed $p + */ + private static function _param_to_bool($p): bool { return $p && ($p !== "f" && $p !== "false"); } - private function _wrap($status, $reply) { + /** + * @param array $reply + */ + private function _wrap(int $status, array $reply): void { print json_encode([ "seq" => $this->seq, "status" => $status, @@ -48,17 +55,17 @@ class API extends Handler { return false; } - function getVersion() { + function getVersion(): void { $rv = array("version" => Config::get_version()); $this->_wrap(self::STATUS_OK, $rv); } - function getApiLevel() { + function getApiLevel(): void { $rv = array("level" => self::API_LEVEL); $this->_wrap(self::STATUS_OK, $rv); } - function login() { + function login(): void { if (session_status() == PHP_SESSION_ACTIVE) { session_destroy(); @@ -87,22 +94,20 @@ class API extends Handler { } else { $this->_wrap(self::STATUS_ERR, array("error" => self::E_API_DISABLED)); } - } else { - $this->_wrap(self::STATUS_ERR, array("error" => self::E_LOGIN_ERROR)); - return; } + $this->_wrap(self::STATUS_ERR, array("error" => self::E_LOGIN_ERROR)); } - function logout() { + function logout(): void { UserHelper::logout(); $this->_wrap(self::STATUS_OK, array("status" => "OK")); } - function isLoggedIn() { + function isLoggedIn(): void { $this->_wrap(self::STATUS_OK, array("status" => $_SESSION["uid"] != '')); } - function getUnread() { + function getUnread(): void { $feed_id = clean($_REQUEST["feed_id"] ?? ""); $is_cat = clean($_REQUEST["is_cat"] ?? ""); @@ -114,12 +119,12 @@ class API extends Handler { } /* Method added for ttrss-reader for Android */ - function getCounters() { + function getCounters(): void { $this->_wrap(self::STATUS_OK, Counters::get_all()); } - function getFeeds() { - $cat_id = clean($_REQUEST["cat_id"]); + function getFeeds(): void { + $cat_id = (int) clean($_REQUEST["cat_id"]); $unread_only = self::_param_to_bool(clean($_REQUEST["unread_only"] ?? 0)); $limit = (int) clean($_REQUEST["limit"] ?? 0); $offset = (int) clean($_REQUEST["offset"] ?? 0); @@ -130,7 +135,7 @@ class API extends Handler { $this->_wrap(self::STATUS_OK, $feeds); } - function getCategories() { + function getCategories(): void { $unread_only = self::_param_to_bool(clean($_REQUEST["unread_only"] ?? false)); $enable_nested = self::_param_to_bool(clean($_REQUEST["enable_nested"] ?? false)); $include_empty = self::_param_to_bool(clean($_REQUEST['include_empty'] ?? false)); @@ -186,11 +191,11 @@ class API extends Handler { $this->_wrap(self::STATUS_OK, $cats); } - function getHeadlines() { + function getHeadlines(): void { $feed_id = clean($_REQUEST["feed_id"]); - if ($feed_id !== "") { + if ($feed_id !== "" && is_numeric($feed_id)) { - if (is_numeric($feed_id)) $feed_id = (int) $feed_id; + $feed_id = (int) $feed_id; $limit = (int)clean($_REQUEST["limit"] ?? 0 ); @@ -237,7 +242,7 @@ class API extends Handler { } } - function updateArticle() { + function updateArticle(): void { $article_ids = explode(",", clean($_REQUEST["article_ids"])); $mode = (int) clean($_REQUEST["mode"]); $data = clean($_REQUEST["data"] ?? ""); @@ -303,7 +308,7 @@ class API extends Handler { } - function getArticle() { + function getArticle(): void { $article_ids = explode(',', clean($_REQUEST['article_id'] ?? '')); $sanitize_content = self::_param_to_bool($_REQUEST['sanitize'] ?? true); @@ -351,7 +356,7 @@ class API extends Handler { $article['content'] = Sanitizer::sanitize( $entry->content, self::_param_to_bool($entry->hide_images), - null, $entry->site_url, null, $entry->id); + false, $entry->site_url, null, $entry->id); } else { $article['content'] = $entry->content; } @@ -375,7 +380,10 @@ class API extends Handler { } } - private function _get_config() { + /** + * @return array|bool|int|string> + */ + private function _get_config(): array { $config = [ "icons_dir" => Config::get(Config::ICONS_DIR), "icons_url" => Config::get(Config::ICONS_URL) @@ -391,13 +399,13 @@ class API extends Handler { return $config; } - function getConfig() { + function getConfig(): void { $config = $this->_get_config(); $this->_wrap(self::STATUS_OK, $config); } - function updateFeed() { + function updateFeed(): void { $feed_id = (int) clean($_REQUEST["feed_id"]); if (!ini_get("open_basedir")) { @@ -407,10 +415,10 @@ class API extends Handler { $this->_wrap(self::STATUS_OK, array("status" => "OK")); } - function catchupFeed() { + function catchupFeed(): void { $feed_id = clean($_REQUEST["feed_id"]); - $is_cat = clean($_REQUEST["is_cat"]) == "true"; - $mode = clean($_REQUEST['mode'] ?? ""); + $is_cat = clean($_REQUEST["is_cat"]); + $mode = clean($_REQUEST["mode"] ?? ""); if (!in_array($mode, ["all", "1day", "1week", "2week"])) $mode = "all"; @@ -420,13 +428,13 @@ class API extends Handler { $this->_wrap(self::STATUS_OK, array("status" => "OK")); } - function getPref() { + function getPref(): void { $pref_name = clean($_REQUEST["pref_name"]); $this->_wrap(self::STATUS_OK, array("value" => get_pref($pref_name))); } - function getLabels() { + function getLabels(): void { $article_id = (int)clean($_REQUEST['article_id'] ?? -1); $rv = []; @@ -462,7 +470,7 @@ class API extends Handler { $this->_wrap(self::STATUS_OK, $rv); } - function setArticleLabel() { + function setArticleLabel(): void { $article_ids = explode(",", clean($_REQUEST["article_ids"])); $label_id = (int) clean($_REQUEST['label_id']); @@ -491,7 +499,7 @@ class API extends Handler { } - function index($method) { + function index(string $method): void { $plugin = PluginHost::getInstance()->get_api_method(strtolower($method)); if ($plugin && method_exists($plugin, $method)) { @@ -504,7 +512,7 @@ class API extends Handler { } } - function shareToPublished() { + function shareToPublished(): void { $title = strip_tags(clean($_REQUEST["title"])); $url = strip_tags(clean($_REQUEST["url"])); $content = strip_tags(clean($_REQUEST["content"])); @@ -516,13 +524,12 @@ class API extends Handler { } } - private static function _api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested = false) { + /** + * @return array + */ + private static function _api_get_feeds(int $cat_id, bool $unread_only, int $limit, int $offset, bool $include_nested = false): array { $feeds = []; - $limit = (int) $limit; - $offset = (int) $offset; - $cat_id = (int) $cat_id; - /* Labels */ /* API only: -4 All feeds, including virtual feeds */ @@ -632,13 +639,16 @@ class API extends Handler { return $feeds; } - private static function _api_get_headlines($feed_id, $limit, $offset, - $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order, - $include_attachments, $since_id, - $search = "", $include_nested = false, $sanitize_content = true, - $force_update = false, $excerpt_length = 100, $check_first_id = false, $skip_first_id_check = false) { + /** + * @return array{0: array>, 1: array} $headlines, $headlines_header + */ + private static function _api_get_headlines(int $feed_id, int $limit, int $offset, + string $filter, bool $is_cat, bool $show_excerpt, bool $show_content, ?string $view_mode, string $order, + bool $include_attachments, int $since_id, string $search = "", bool $include_nested = false, + bool $sanitize_content = true, bool $force_update = false, int $excerpt_length = 100, ?int $check_first_id = null, + bool $skip_first_id_check = false): array { - if ($force_update && $feed_id > 0 && is_numeric($feed_id)) { + if ($force_update && is_numeric($feed_id) && $feed_id > 0) { // Update the feed if required with some basic flood control $feed = ORM::for_table('ttrss_feeds') @@ -746,7 +756,7 @@ class API extends Handler { $headline_row["content"] = Sanitizer::sanitize( $line["content"], self::_param_to_bool($line['hide_images']), - null, $line["site_url"], null, $line["id"]); + false, $line["site_url"], null, $line["id"]); } else { $headline_row["content"] = $line["content"]; } @@ -803,7 +813,7 @@ class API extends Handler { return array($headlines, $headlines_header); } - function unsubscribeFeed() { + function unsubscribeFeed(): void { $feed_id = (int) clean($_REQUEST["feed_id"]); $feed_exists = ORM::for_table('ttrss_feeds') @@ -818,7 +828,7 @@ class API extends Handler { } } - function subscribeToFeed() { + function subscribeToFeed(): void { $feed_url = clean($_REQUEST["feed_url"]); $category_id = (int) clean($_REQUEST["category_id"]); $login = clean($_REQUEST["login"]); @@ -833,7 +843,7 @@ class API extends Handler { } } - function getFeedTree() { + function getFeedTree(): void { $include_empty = self::_param_to_bool(clean($_REQUEST['include_empty'])); $pf = new Pref_Feeds($_REQUEST); @@ -846,7 +856,7 @@ class API extends Handler { } // only works for labels or uncategorized for the time being - private function _is_cat_empty($id) { + private function _is_cat_empty(int $id): bool { if ($id == -2) { $label_count = ORM::for_table('ttrss_labels2') ->where('owner_uid', $_SESSION['uid']) @@ -865,7 +875,8 @@ class API extends Handler { return false; } - private function _get_custom_sort_types() { + /** @return array */ + private function _get_custom_sort_types(): array { $ret = []; PluginHost::getInstance()->run_hooks_callback(PluginHost::HOOK_HEADLINES_CUSTOM_SORT_MAP, function ($result) use (&$ret) { -- cgit v1.2.3-54-g00ecf From 45431170b629908b9fc39d19a87cf64d90bc9faf Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Nov 2021 17:31:13 +0300 Subject: fix phpstan warnings in classes/db/migrations.php --- classes/db/migrations.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'classes') diff --git a/classes/db/migrations.php b/classes/db/migrations.php index 6e20ddf7f..aecd9186c 100644 --- a/classes/db/migrations.php +++ b/classes/db/migrations.php @@ -21,10 +21,10 @@ class Db_Migrations { private $pdo; /** @var int */ - private $cached_version; + private $cached_version = 0; /** @var int */ - private $cached_max_version; + private $cached_max_version = 0; /** @var int */ private $max_version_override; @@ -65,7 +65,7 @@ class Db_Migrations { } function get_version() : int { - if (isset($this->cached_version)) + if ($this->cached_version) return $this->cached_version; try { @@ -152,7 +152,7 @@ class Db_Migrations { if ($this->max_version_override > 0) return $this->max_version_override; - if (isset($this->cached_max_version)) + if ($this->cached_max_version) return $this->cached_max_version; $migrations = glob("{$this->migrations_path}/*.sql"); -- cgit v1.2.3-54-g00ecf From 77b8dc738616723bd891b09731478f7a4a77672e Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Nov 2021 17:48:52 +0300 Subject: fix phpstan warnings in classes/feedparser.php --- classes/feedparser.php | 58 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 21 deletions(-) (limited to 'classes') diff --git a/classes/feedparser.php b/classes/feedparser.php index daba271fb..05412ef1e 100644 --- a/classes/feedparser.php +++ b/classes/feedparser.php @@ -1,19 +1,35 @@ */ + private $libxml_errors = []; + + /** @var array */ private $items; + + /** @var string */ private $link; + + /** @var string */ private $title; + + /** @var int */ private $type; + + /** @var DOMXPath */ private $xpath; const FEED_RDF = 0; const FEED_RSS = 1; const FEED_ATOM = 2; - function __construct($data) { + function __construct(string $data) { libxml_use_internal_errors(true); libxml_clear_errors(); $this->doc = new DOMDocument(); @@ -26,7 +42,7 @@ class FeedParser { if ($error) { foreach (libxml_get_errors() as $error) { if ($error->level == LIBXML_ERR_FATAL) { - if(!isset($this->error)) //currently only the first error is reported + if ($this->error) //currently only the first error is reported $this->error = $this->format_error($error); $this->libxml_errors [] = $this->format_error($error); } @@ -37,7 +53,7 @@ class FeedParser { $this->items = array(); } - function init() { + function init() : void { $root = $this->doc->firstChild; $xpath = new DOMXPath($this->doc); $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); @@ -69,7 +85,7 @@ class FeedParser { $this->type = $this::FEED_ATOM; break; default: - if( !isset($this->error) ){ + if (!isset($this->error) ){ $this->error = "Unknown/unsupported feed type"; } return; @@ -100,6 +116,7 @@ class FeedParser { if (!$link) $link = $xpath->query("//atom03:feed/atom03:link[@rel='alternate']")->item(0); + /** @var DOMElement|null $link */ if ($link && $link->hasAttributes()) { $this->link = $link->getAttribute("href"); } @@ -121,6 +138,7 @@ class FeedParser { $this->title = $title->nodeValue; } + /** @var DOMElement|null $link */ $link = $xpath->query("//channel/link")->item(0); if ($link) { @@ -173,39 +191,37 @@ class FeedParser { } } - function format_error($error) { - if ($error) { - return sprintf("LibXML error %s at line %d (column %d): %s", - $error->code, $error->line, $error->column, - $error->message); - } else { - return ""; - } + function format_error(LibXMLError $error) : string { + return sprintf("LibXML error %s at line %d (column %d): %s", + $error->code, $error->line, $error->column, + $error->message); } // libxml may have invalid unicode data in error messages - function error() { + function error() : string { return UConverter::transcode($this->error, 'UTF-8', 'UTF-8'); } - // WARNING: may return invalid unicode data - function errors() { + /** @return array - WARNING: may return invalid unicode data */ + function errors() : array { return $this->libxml_errors; } - function get_link() { + function get_link() : string { return clean($this->link); } - function get_title() { + function get_title() : string { return clean($this->title); } - function get_items() { + /** @return array */ + function get_items() : array { return $this->items; } - function get_links($rel) { + /** @return array */ + function get_links(string $rel) : array { $rv = array(); switch ($this->type) { -- cgit v1.2.3-54-g00ecf From a7983d475efb56711382bd320bb0be503dba0c93 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Nov 2021 17:51:26 +0300 Subject: fix phpstan warnings in classes/api.php --- classes/api.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'classes') diff --git a/classes/api.php b/classes/api.php index d2668beee..764cb04da 100755 --- a/classes/api.php +++ b/classes/api.php @@ -356,7 +356,7 @@ class API extends Handler { $article['content'] = Sanitizer::sanitize( $entry->content, self::_param_to_bool($entry->hide_images), - false, $entry->site_url, null, $entry->id); + null, $entry->site_url, null, $entry->id); } else { $article['content'] = $entry->content; } @@ -485,9 +485,9 @@ class API extends Handler { foreach ($article_ids as $id) { if ($assign) - Labels::add_article($id, $label, $_SESSION["uid"]); + Labels::add_article((int)$id, $label, $_SESSION["uid"]); else - Labels::remove_article($id, $label, $_SESSION["uid"]); + Labels::remove_article((int)$id, $label, $_SESSION["uid"]); ++$num_updated; @@ -756,7 +756,7 @@ class API extends Handler { $headline_row["content"] = Sanitizer::sanitize( $line["content"], self::_param_to_bool($line['hide_images']), - false, $line["site_url"], null, $line["id"]); + null, $line["site_url"], null, $line["id"]); } else { $headline_row["content"] = $line["content"]; } -- cgit v1.2.3-54-g00ecf From 8a83f061bfa907af167090133738c1dc065dc69d Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Nov 2021 17:52:03 +0300 Subject: fix phpstan warnings in classes/sanitizer.php --- classes/sanitizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'classes') diff --git a/classes/sanitizer.php b/classes/sanitizer.php index 2770aece2..e0db811c6 100644 --- a/classes/sanitizer.php +++ b/classes/sanitizer.php @@ -44,7 +44,7 @@ class Sanitizer { return $doc; } - public static function iframe_whitelisted(DOMNode $entry): bool { + public static function iframe_whitelisted(DOMElement $entry): bool { $src = parse_url($entry->getAttribute("src"), PHP_URL_HOST); if (!empty($src)) -- cgit v1.2.3-54-g00ecf From b381e9579295b238d44532a50edb6422b8c6b4ab Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Nov 2021 18:18:05 +0300 Subject: experimental: auto-generate and add all plugin hook methods to Plugin class --- classes/plugin.php | 194 +++++++++++++++++++++++++++++++ classes/plugin.tpl | 62 ++++++++++ utils/generate-plugin-hook-prototypes.sh | 30 +++++ 3 files changed, 286 insertions(+) create mode 100644 classes/plugin.tpl create mode 100644 utils/generate-plugin-hook-prototypes.sh (limited to 'classes') diff --git a/classes/plugin.php b/classes/plugin.php index ecafa7888..1b6702d72 100644 --- a/classes/plugin.php +++ b/classes/plugin.php @@ -58,4 +58,198 @@ abstract class Plugin { return vsprintf($this->__($msgid), $args); } + /* plugin hook methods (auto-generated) */ + + function hook_article_button($line) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_article_filter($article) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_prefs_tab($tab) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_prefs_tab_section($section) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_prefs_tabs() { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_feed_parsed($parser, $feed_id) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_update_task($cli_options) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_auth_user($login, $password, $service) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_hotkey_map($hotkeys) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_render_article($article) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_render_article_cdm($article) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_feed_fetched($feed_data, $fetch_url, $owner_uid, $feed) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_render_article_api($params) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_toolbar_button() { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_action_item() { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_headline_toolbar_button($feed_id, $is_cat) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_hotkey_info($hotkeys) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_article_left_button($row) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_prefs_edit_feed($feed_id) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_prefs_save_feed($feed_id) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_fetch_feed($feed_data, $fetch_url, $owner_uid, $feed, $last_article_timestamp, $auth_login, $auth_pass) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_query_headlines($row) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_house_keeping() { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_search($query) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_format_enclosures($rv, $result, $id, $always_display_enclosures, $article_content, $hide_images) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_subscribe_feed($contents, $url, $auth_login, $auth_pass) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_headlines_before($feed, $is_cat, $qfh_ret) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_render_enclosure($entry, $id, $rv) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_article_filter_action($article, $action) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_article_export_feed($line, $feed, $is_cat, $owner_uid) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_main_toolbar_button() { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_enclosure_entry($entry, $id, $rv) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_format_article($html, $row) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_feed_basic_info($basic_info, $fetch_url, $owner_uid, $feed_id, $auth_login, $auth_pass) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_send_local_file($filename) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_unsubscribe_feed($feed_id, $owner_uid) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_send_mail(Mailer $mailer, $params) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_filter_triggered($feed_id, $owner_uid, $article, $matched_filters, $matched_rules, $article_filters) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_get_full_text($url) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_article_image($enclosures, $content, $site_url) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_feed_tree() { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_iframe_whitelisted($url) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_enclosure_imported($enclosure, $feed) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_headlines_custom_sort_map() { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_headlines_custom_sort_override($order) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_headline_toolbar_select_menu_item($feed_id, $is_cat) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + + function hook_pre_subscribe($url, $auth_login, $auth_pass) { + user_error("Dummy method invoked.", E_USER_ERROR); + } + } diff --git a/classes/plugin.tpl b/classes/plugin.tpl new file mode 100644 index 000000000..10f5b8ce7 --- /dev/null +++ b/classes/plugin.tpl @@ -0,0 +1,62 @@ +pdo = Db::pdo(); + } + + function flags() { + /* associative array, possible keys: + needs_curl = boolean + */ + return array(); + } + + function is_public_method($method) { + return false; + } + + function csrf_ignore($method) { + return false; + } + + function get_js() { + return ""; + } + + function get_prefs_js() { + return ""; + } + + function api_version() { + return Plugin::API_VERSION_COMPAT; + } + + /* gettext-related helpers */ + + function __($msgid) { + return _dgettext(PluginHost::object_to_domain($this), $msgid); + } + + function _ngettext($singular, $plural, $number) { + return _dngettext(PluginHost::object_to_domain($this), $singular, $plural, $number); + } + + function T_sprintf() { + $args = func_get_args(); + $msgid = array_shift($args); + + return vsprintf($this->__($msgid), $args); + } + + /** AUTO_GENERATED_HOOKS_GO_HERE **/ +} diff --git a/utils/generate-plugin-hook-prototypes.sh b/utils/generate-plugin-hook-prototypes.sh new file mode 100644 index 000000000..586f3f2c6 --- /dev/null +++ b/utils/generate-plugin-hook-prototypes.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +TMPFILE=$(mktemp) + +grep 'hook_.*(' ../classes/pluginhost.php | sed -e 's#[\t ]*/[* ]*##' \ + -e 's# [*]/$##' \ + -e 's# *(byref) *##' \ + -e 's#GLOBAL: ##' | while read F; do + + cat << EOF >> $TMPFILE + function $F { + user_error("Dummy method invoked.", E_USER_ERROR); + } + +EOF +done + +cat ../classes/plugin.tpl | while IFS=\n read L; do + case $L in + *AUTO_GENERATED_HOOKS_GO_HERE* ) + echo "\t/* plugin hook methods (auto-generated) */\n" + cat $TMPFILE + ;; + * ) + echo "$L" + ;; + esac +done > ../classes/plugin.php + +rm -f -- $TMPFILE -- cgit v1.2.3-54-g00ecf From 70051742afdd05ab66d9265edb063eb5b6615765 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Nov 2021 18:21:04 +0300 Subject: experimental: also don't keep base plugin template as a non-analyzed file --- classes/plugin-template.php | 62 ++++++++++++++++++++++++++++++++ classes/plugin.tpl | 62 -------------------------------- utils/generate-plugin-hook-prototypes.sh | 5 ++- 3 files changed, 66 insertions(+), 63 deletions(-) create mode 100644 classes/plugin-template.php delete mode 100644 classes/plugin.tpl (limited to 'classes') diff --git a/classes/plugin-template.php b/classes/plugin-template.php new file mode 100644 index 000000000..ad6d07ee0 --- /dev/null +++ b/classes/plugin-template.php @@ -0,0 +1,62 @@ +pdo = Db::pdo(); + } + + function flags() { + /* associative array, possible keys: + needs_curl = boolean + */ + return array(); + } + + function is_public_method($method) { + return false; + } + + function csrf_ignore($method) { + return false; + } + + function get_js() { + return ""; + } + + function get_prefs_js() { + return ""; + } + + function api_version() { + return Plugin::API_VERSION_COMPAT; + } + + /* gettext-related helpers */ + + function __($msgid) { + return _dgettext(PluginHost::object_to_domain($this), $msgid); + } + + function _ngettext($singular, $plural, $number) { + return _dngettext(PluginHost::object_to_domain($this), $singular, $plural, $number); + } + + function T_sprintf() { + $args = func_get_args(); + $msgid = array_shift($args); + + return vsprintf($this->__($msgid), $args); + } + + /** AUTO_GENERATED_HOOKS_GO_HERE **/ +} diff --git a/classes/plugin.tpl b/classes/plugin.tpl deleted file mode 100644 index 10f5b8ce7..000000000 --- a/classes/plugin.tpl +++ /dev/null @@ -1,62 +0,0 @@ -pdo = Db::pdo(); - } - - function flags() { - /* associative array, possible keys: - needs_curl = boolean - */ - return array(); - } - - function is_public_method($method) { - return false; - } - - function csrf_ignore($method) { - return false; - } - - function get_js() { - return ""; - } - - function get_prefs_js() { - return ""; - } - - function api_version() { - return Plugin::API_VERSION_COMPAT; - } - - /* gettext-related helpers */ - - function __($msgid) { - return _dgettext(PluginHost::object_to_domain($this), $msgid); - } - - function _ngettext($singular, $plural, $number) { - return _dngettext(PluginHost::object_to_domain($this), $singular, $plural, $number); - } - - function T_sprintf() { - $args = func_get_args(); - $msgid = array_shift($args); - - return vsprintf($this->__($msgid), $args); - } - - /** AUTO_GENERATED_HOOKS_GO_HERE **/ -} diff --git a/utils/generate-plugin-hook-prototypes.sh b/utils/generate-plugin-hook-prototypes.sh index 586f3f2c6..edf1ed5fe 100644 --- a/utils/generate-plugin-hook-prototypes.sh +++ b/utils/generate-plugin-hook-prototypes.sh @@ -15,8 +15,11 @@ grep 'hook_.*(' ../classes/pluginhost.php | sed -e 's#[\t ]*/[* ]*##' \ EOF done -cat ../classes/plugin.tpl | while IFS=\n read L; do +cat ../classes/plugin-template.php | while IFS=\n read L; do case $L in + *PluginTemplate* ) + echo "$L" | sed 's/PluginTemplate/Plugin/' + ;; *AUTO_GENERATED_HOOKS_GO_HERE* ) echo "\t/* plugin hook methods (auto-generated) */\n" cat $TMPFILE -- cgit v1.2.3-54-g00ecf From f2323bda81a8fb4f80fff043b356449ef0233305 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Nov 2021 18:26:11 +0300 Subject: fix phpstan warnings in classes/plugin-template.php --- classes/plugin-template.php | 26 +++++++++++++++----------- classes/plugin.php | 26 +++++++++++++++----------- 2 files changed, 30 insertions(+), 22 deletions(-) (limited to 'classes') diff --git a/classes/plugin-template.php b/classes/plugin-template.php index ad6d07ee0..12437525b 100644 --- a/classes/plugin-template.php +++ b/classes/plugin-template.php @@ -5,53 +5,57 @@ abstract class PluginTemplate { /** @var PDO $pdo */ protected $pdo; - abstract function init(PluginHost $host); + abstract function init(PluginHost $host) : void; - abstract function about(); + /** @return array */ + abstract function about() : array; // return array(1.0, "plugin", "No description", "No author", false); function __construct() { $this->pdo = Db::pdo(); } - function flags() { + /** @return array */ + function flags() : array { /* associative array, possible keys: needs_curl = boolean */ return array(); } - function is_public_method($method) { + function is_public_method(string $method) : bool { return false; } - function csrf_ignore($method) { + function csrf_ignore(string $method) : bool { return false; } - function get_js() { + function get_js() : string { return ""; } - function get_prefs_js() { + function get_prefs_js() : string { return ""; } - function api_version() { + function api_version() : int { return Plugin::API_VERSION_COMPAT; } /* gettext-related helpers */ - function __($msgid) { + function __(string $msgid) : string { + /** @var Plugin $this -- this is a strictly template-related hack */ return _dgettext(PluginHost::object_to_domain($this), $msgid); } - function _ngettext($singular, $plural, $number) { + function _ngettext(string $singular, string $plural, int $number) : string { + /** @var Plugin $this -- this is a strictly template-related hack */ return _dngettext(PluginHost::object_to_domain($this), $singular, $plural, $number); } - function T_sprintf() { + function T_sprintf() : string { $args = func_get_args(); $msgid = array_shift($args); diff --git a/classes/plugin.php b/classes/plugin.php index 1b6702d72..08a122023 100644 --- a/classes/plugin.php +++ b/classes/plugin.php @@ -5,53 +5,57 @@ abstract class Plugin { /** @var PDO $pdo */ protected $pdo; - abstract function init(PluginHost $host); + abstract function init(PluginHost $host) : void; - abstract function about(); + /** @return array */ + abstract function about() : array; // return array(1.0, "plugin", "No description", "No author", false); function __construct() { $this->pdo = Db::pdo(); } - function flags() { + /** @return array */ + function flags() : array { /* associative array, possible keys: needs_curl = boolean */ return array(); } - function is_public_method($method) { + function is_public_method(string $method) : bool { return false; } - function csrf_ignore($method) { + function csrf_ignore(string $method) : bool { return false; } - function get_js() { + function get_js() : string { return ""; } - function get_prefs_js() { + function get_prefs_js() : string { return ""; } - function api_version() { + function api_version() : int { return Plugin::API_VERSION_COMPAT; } /* gettext-related helpers */ - function __($msgid) { + function __(string $msgid) : string { + /** @var Plugin $this -- this is a strictly template-related hack */ return _dgettext(PluginHost::object_to_domain($this), $msgid); } - function _ngettext($singular, $plural, $number) { + function _ngettext(string $singular, string $plural, int $number) : string { + /** @var Plugin $this -- this is a strictly template-related hack */ return _dngettext(PluginHost::object_to_domain($this), $singular, $plural, $number); } - function T_sprintf() { + function T_sprintf() : string { $args = func_get_args(); $msgid = array_shift($args); -- cgit v1.2.3-54-g00ecf From b37a03fb31cf1c394e36ccf082bb5d3359f3a1fb Mon Sep 17 00:00:00 2001 From: wn_ Date: Sat, 13 Nov 2021 14:41:22 +0000 Subject: Fix the type of Labels::update_cache() --- classes/article.php | 2 ++ classes/labels.php | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'classes') diff --git a/classes/article.php b/classes/article.php index b4f28dc28..b720971b9 100755 --- a/classes/article.php +++ b/classes/article.php @@ -553,6 +553,8 @@ class Article extends Handler_Protected { } if (count($rv) > 0) + // PHPStan has issues with the shape of $rv for some reason (array vs non-empty-array). + // @phpstan-ignore-next-line Labels::update_cache($owner_uid, $id, $rv); else Labels::update_cache($owner_uid, $id, array("no-labels" => 1)); diff --git a/classes/labels.php b/classes/labels.php index 5a17d665e..026e6621f 100644 --- a/classes/labels.php +++ b/classes/labels.php @@ -71,7 +71,8 @@ class Labels } /** - * @param array>> $labels + * @param array{'no-labels': 1}|array> $labels + * [label_id, caption, fg_color, bg_color] * * @see Article::_get_labels() */ -- cgit v1.2.3-54-g00ecf From a18473e4c0d2187f1308c7d77fb40d552bb194ed Mon Sep 17 00:00:00 2001 From: wn_ Date: Sat, 13 Nov 2021 15:50:37 +0000 Subject: Address PHPStan warnings in 'classes/pref/feeds.php'. --- classes/pref/feeds.php | 175 ++++++++++++++++++++++++++----------------------- 1 file changed, 94 insertions(+), 81 deletions(-) (limited to 'classes') diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index ce1a53bef..ec6abf9dd 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -11,7 +11,10 @@ class Pref_Feeds extends Handler_Protected { return array_search($method, $csrf_ignored) !== false; } - public static function get_ts_languages() { + /** + * @return array + */ + public static function get_ts_languages(): array { if (Config::get(Config::DB_TYPE) == 'pgsql') { return array_map('ucfirst', array_column(ORM::for_table('pg_ts_config')->select('cfgname')->find_array(), 'cfgname')); @@ -20,7 +23,7 @@ class Pref_Feeds extends Handler_Protected { return []; } - function renameCat() { + function renameCat(): void { $cat = ORM::for_table("ttrss_feed_categories") ->where("owner_uid", $_SESSION["uid"]) ->find_one($_REQUEST['id']); @@ -33,7 +36,10 @@ class Pref_Feeds extends Handler_Protected { } } - private function get_category_items($cat_id) { + /** + * @return array> + */ + private function get_category_items(int $cat_id): array { if (clean($_REQUEST['mode'] ?? 0) != 2) $search = $_SESSION["prefs_feed_search"] ?? ""; @@ -103,11 +109,14 @@ class Pref_Feeds extends Handler_Protected { return $items; } - function getfeedtree() { + function getfeedtree(): void { print json_encode($this->_makefeedtree()); } - function _makefeedtree() { + /** + * @return array|string> + */ + function _makefeedtree(): array { if (clean($_REQUEST['mode'] ?? 0) != 2) $search = $_SESSION["prefs_feed_search"] ?? ""; @@ -184,7 +193,7 @@ class Pref_Feeds extends Handler_Protected { if (count($labels)) { foreach ($labels as $label) { $label_id = Labels::label_to_feed_id($label->id); - $feed = $this->feedlist_init_feed($label_id, false, 0); + $feed = $this->feedlist_init_feed($label_id, null, false); $feed['fg_color'] = $label->fg_color; $feed['bg_color'] = $label->bg_color; array_push($cat['items'], $feed); @@ -319,19 +328,22 @@ class Pref_Feeds extends Handler_Protected { ]; } - function catsortreset() { + function catsortreset(): void { $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET order_id = 0 WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); } - function feedsortreset() { + function feedsortreset(): void { $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET order_id = 0 WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); } - private function process_category_order(&$data_map, $item_id, $parent_id = false, $nest_level = 0) { + /** + * @param array $data_map + */ + private function process_category_order(array &$data_map, string $item_id = '', string $parent_id = '', int $nest_level = 0): void { $prefix = ""; for ($i = 0; $i < $nest_level; $i++) @@ -403,7 +415,7 @@ class Pref_Feeds extends Handler_Protected { } } - function savefeedorder() { + function savefeedorder(): void { $data = json_decode($_POST['payload'], true); #file_put_contents("/tmp/saveorder.json", clean($_POST['payload'])); @@ -417,8 +429,9 @@ class Pref_Feeds extends Handler_Protected { if (is_array($data) && is_array($data['items'])) { # $cat_order_id = 0; + /** @var array */ $data_map = array(); - $root_item = false; + $root_item = ''; foreach ($data['items'] as $item) { @@ -439,7 +452,7 @@ class Pref_Feeds extends Handler_Protected { } } - function removeIcon() { + function removeIcon(): void { $feed_id = (int) $_REQUEST["feed_id"]; $icon_file = Config::get(Config::ICONS_DIR) . "/$feed_id.ico"; @@ -459,7 +472,7 @@ class Pref_Feeds extends Handler_Protected { } } - function uploadIcon() { + function uploadIcon(): void { $feed_id = (int) $_REQUEST['feed_id']; $tmp_file = tempnam(Config::get(Config::CACHE_DIR) . '/upload', 'icon'); @@ -502,7 +515,7 @@ class Pref_Feeds extends Handler_Protected { print json_encode(['rc' => $rc, 'icon_url' => Feeds::_get_icon($feed_id)]); } - function editfeed() { + function editfeed(): void { global $purge_intervals; global $update_intervals; @@ -564,12 +577,12 @@ class Pref_Feeds extends Handler_Protected { } } - private function _batch_toggle_checkbox($name) { + private function _batch_toggle_checkbox(string $name): string { return \Controls\checkbox_tag("", false, "", ["data-control-for" => $name, "title" => __("Check to enable field"), "onchange" => "App.dialogOf(this).toggleField(this)"]); } - function editfeeds() { + function editfeeds(): void { global $purge_intervals; global $update_intervals; @@ -677,15 +690,15 @@ class Pref_Feeds extends Handler_Protected { editsaveops(true); + function batchEditSave(): void { + $this->editsaveops(true); } - function editSave() { - return $this->editsaveops(false); + function editSave(): void { + $this->editsaveops(false); } - private function editsaveops($batch) { + private function editsaveops(bool $batch): void { $feed_title = clean($_POST["title"]); $feed_url = clean($_POST["feed_url"]); @@ -774,11 +787,11 @@ class Pref_Feeds extends Handler_Protected { break; case "update_interval": - $qpart = "update_interval = " . $this->pdo->quote($upd_intl); + $qpart = "update_interval = " . $upd_intl; // made int above break; case "purge_interval": - $qpart = "purge_interval =" . $this->pdo->quote($purge_intl); + $qpart = "purge_interval = " . $purge_intl; // made int above break; case "auth_login": @@ -790,33 +803,33 @@ class Pref_Feeds extends Handler_Protected { break; case "private": - $qpart = "private = " . $this->pdo->quote($private); + $qpart = "private = " . $private; // made int above break; case "include_in_digest": - $qpart = "include_in_digest = " . $this->pdo->quote($include_in_digest); + $qpart = "include_in_digest = " . $include_in_digest; // made int above break; case "always_display_enclosures": - $qpart = "always_display_enclosures = " . $this->pdo->quote($always_display_enclosures); + $qpart = "always_display_enclosures = " . $always_display_enclosures; // made int above break; case "mark_unread_on_update": - $qpart = "mark_unread_on_update = " . $this->pdo->quote($mark_unread_on_update); + $qpart = "mark_unread_on_update = " . $mark_unread_on_update; // made int above break; case "cache_images": - $qpart = "cache_images = " . $this->pdo->quote($cache_images); + $qpart = "cache_images = " . $cache_images; // made int above break; case "hide_images": - $qpart = "hide_images = " . $this->pdo->quote($hide_images); + $qpart = "hide_images = " . $hide_images; // made int above break; case "cat_id": if (get_pref(Prefs::ENABLE_FEED_CATS)) { if ($cat_id) { - $qpart = "cat_id = " . $this->pdo->quote($cat_id); + $qpart = "cat_id = " . $cat_id; // made int above } else { $qpart = 'cat_id = NULL'; } @@ -841,39 +854,36 @@ class Pref_Feeds extends Handler_Protected { $this->pdo->commit(); } - return; } - function remove() { - - $ids = explode(",", clean($_REQUEST["ids"])); + function remove(): void { + /** @var array */ + $ids = array_map('intval', explode(",", clean($_REQUEST["ids"]))); foreach ($ids as $id) { self::remove_feed($id, $_SESSION["uid"]); } - - return; } - function removeCat() { + function removeCat(): void { $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { Feeds::_remove_cat((int)$id, $_SESSION["uid"]); } } - function addCat() { + function addCat(): void { $feed_cat = clean($_REQUEST["cat"]); Feeds::_add_cat($feed_cat, $_SESSION['uid']); } - function importOpml() { + function importOpml(): void { $opml = new OPML($_REQUEST); $opml->opml_import($_SESSION["uid"]); } - private function index_feeds() { + private function index_feeds(): void { $error_button = "