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 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 90 insertions(+), 32 deletions(-) (limited to 'classes/rssutils.php') 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')))); } -- cgit v1.2.3-54-g00ecf From 78acf18b70e3d6ba22e2c2db950e132cfb5d35be Mon Sep 17 00:00:00 2001 From: wn_ Date: Mon, 15 Nov 2021 02:40:45 +0000 Subject: Address PHPStan warnings in FeedItem classes. --- classes/article.php | 2 +- classes/feeditem.php | 30 +++++++++++++++--------- classes/feeditem/atom.php | 57 +++++++++++++++++++++++++++++++-------------- classes/feeditem/common.php | 50 ++++++++++++++++++++++++++++----------- classes/feeditem/rss.php | 41 +++++++++++++++++++++++++------- classes/rssutils.php | 2 +- 6 files changed, 129 insertions(+), 53 deletions(-) (limited to 'classes/rssutils.php') diff --git a/classes/article.php b/classes/article.php index b720971b9..4e25498bc 100755 --- a/classes/article.php +++ b/classes/article.php @@ -189,7 +189,7 @@ class Article extends Handler_Protected { //$tags_str = clean($_REQUEST["tags_str"]); //$tags = array_unique(array_map('trim', explode(",", $tags_str))); - $tags = FeedItem_Common::normalize_categories(explode(",", clean($_REQUEST["tags_str"]))); + $tags = FeedItem_Common::normalize_categories(explode(",", clean($_REQUEST["tags_str"] ?? ""))); $this->pdo->beginTransaction(); diff --git a/classes/feeditem.php b/classes/feeditem.php index 3a5e5dc09..fd7c54883 100644 --- a/classes/feeditem.php +++ b/classes/feeditem.php @@ -1,16 +1,24 @@ */ + abstract function get_categories(): array; + + /** @return array */ + abstract function get_enclosures(): array; + + abstract function get_author(): string; + abstract function get_language(): string; } diff --git a/classes/feeditem/atom.php b/classes/feeditem/atom.php index 51358f36c..36a2e91f5 100755 --- a/classes/feeditem/atom.php +++ b/classes/feeditem/atom.php @@ -2,7 +2,7 @@ class FeedItem_Atom extends FeedItem_Common { const NS_XML = "http://www.w3.org/XML/1998/namespace"; - function get_id() { + function get_id(): string { $id = $this->elem->getElementsByTagName("id")->item(0); if ($id) { @@ -12,6 +12,9 @@ class FeedItem_Atom extends FeedItem_Common { } } + /** + * @return int|false a timestamp on success, false otherwise + */ function get_date() { $updated = $this->elem->getElementsByTagName("updated")->item(0); @@ -30,10 +33,13 @@ class FeedItem_Atom extends FeedItem_Common { if ($date) { return strtotime($date->nodeValue); } + + // consistent with strtotime failing to parse + return false; } - function get_link() { + function get_link(): string { $links = $this->elem->getElementsByTagName("link"); foreach ($links as $link) { @@ -44,24 +50,27 @@ class FeedItem_Atom extends FeedItem_Common { $base = $this->xpath->evaluate("string(ancestor-or-self::*[@xml:base][1]/@xml:base)", $link); if ($base) - return rewrite_relative_url($base, clean(trim($link->getAttribute("href")))); + return UrlHelper::rewrite_relative($base, clean(trim($link->getAttribute("href")))); else return clean(trim($link->getAttribute("href"))); - } } + + return ''; } - function get_title() { + function get_title(): string { $title = $this->elem->getElementsByTagName("title")->item(0); - - if ($title) { - return clean(trim($title->nodeValue)); - } + return $title ? clean(trim($title->nodeValue)) : ''; } - /** $base is optional (returns $content if $base is null), $content is an HTML string */ - private function rewrite_content_to_base($base, $content) { + /** + * @param string|null $base optional (returns $content if $base is null) + * @param string $content an HTML string + * + * @return string the rewritten XML or original $content + */ + private function rewrite_content_to_base(?string $base = null, string $content) { if (!empty($base) && !empty($content)) { @@ -81,14 +90,17 @@ class FeedItem_Atom extends FeedItem_Common { } } - return $tmpdoc->saveXML(); + // Fall back to $content if saveXML somehow fails (i.e. returns false) + $modified_content = $tmpdoc->saveXML(); + return $modified_content !== false ? $modified_content : $content; } } return $content; } - function get_content() { + function get_content(): string { + /** @var DOMElement|null */ $content = $this->elem->getElementsByTagName("content")->item(0); if ($content) { @@ -108,10 +120,13 @@ class FeedItem_Atom extends FeedItem_Common { return $this->rewrite_content_to_base($base, $this->subtree_or_text($content)); } + + return ''; } // TODO: duplicate code should be merged with get_content() - function get_description() { + function get_description(): string { + /** @var DOMElement|null */ $content = $this->elem->getElementsByTagName("summary")->item(0); if ($content) { @@ -132,9 +147,13 @@ class FeedItem_Atom extends FeedItem_Common { return $this->rewrite_content_to_base($base, $this->subtree_or_text($content)); } + return ''; } - function get_categories() { + /** + * @return array + */ + function get_categories(): array { $categories = $this->elem->getElementsByTagName("category"); $cats = []; @@ -152,7 +171,10 @@ class FeedItem_Atom extends FeedItem_Common { return $this->normalize_categories($cats); } - function get_enclosures() { + /** + * @return array + */ + function get_enclosures(): array { $links = $this->elem->getElementsByTagName("link"); $encs = []; @@ -182,7 +204,7 @@ class FeedItem_Atom extends FeedItem_Common { return $encs; } - function get_language() { + function get_language(): string { $lang = $this->elem->getAttributeNS(self::NS_XML, "lang"); if (!empty($lang)) { @@ -195,5 +217,6 @@ class FeedItem_Atom extends FeedItem_Common { } } } + return ''; } } diff --git a/classes/feeditem/common.php b/classes/feeditem/common.php index 18afeaa94..6a9be8aca 100755 --- a/classes/feeditem/common.php +++ b/classes/feeditem/common.php @@ -1,16 +1,20 @@ elem = $elem; $this->xpath = $xpath; $this->doc = $doc; try { - $source = $elem->getElementsByTagName("source")->item(0); // we don't need element @@ -21,11 +25,12 @@ abstract class FeedItem_Common extends FeedItem { } } - function get_element() { + function get_element(): DOMElement { return $this->elem; } - function get_author() { + function get_author(): string { + /** @var DOMElement|null */ $author = $this->elem->getElementsByTagName("author")->item(0); if ($author) { @@ -51,7 +56,7 @@ abstract class FeedItem_Common extends FeedItem { return implode(", ", $authors); } - function get_comments_url() { + function get_comments_url(): string { //RSS only. Use a query here to avoid namespace clashes (e.g. with slash). //might give a wrong result if a default namespace was declared (possible with XPath 2.0) $com_url = $this->xpath->query("comments", $this->elem)->item(0); @@ -65,20 +70,28 @@ abstract class FeedItem_Common extends FeedItem { if ($com_url) return clean($com_url->nodeValue); + + return ''; } - function get_comments_count() { + function get_comments_count(): int { //also query for ATE stuff here $query = "slash:comments|thread:total|atom:link[@rel='replies']/@thread:count"; $comments = $this->xpath->query($query, $this->elem)->item(0); - if ($comments) { - return clean($comments->nodeValue); + if ($comments && is_numeric($comments->nodeValue)) { + return (int) clean($comments->nodeValue); } + + return 0; } - // this is common for both Atom and RSS types and deals with various media: elements - function get_enclosures() { + /** + * this is common for both Atom and RSS types and deals with various 'media:' elements + * + * @return array + */ + function get_enclosures(): array { $encs = []; $enclosures = $this->xpath->query("media:content", $this->elem); @@ -108,6 +121,7 @@ abstract class FeedItem_Common extends FeedItem { foreach ($enclosures as $enclosure) { $enc = new FeedEnclosure(); + /** @var DOMElement|null */ $content = $this->xpath->query("media:content", $enclosure)->item(0); if ($content) { @@ -150,11 +164,14 @@ abstract class FeedItem_Common extends FeedItem { return $encs; } - function count_children($node) { + function count_children(DOMElement $node): int { return $node->getElementsByTagName("*")->length; } - function subtree_or_text($node) { + /** + * @return false|string false on failure, otherwise string contents + */ + function subtree_or_text(DOMElement $node) { if ($this->count_children($node) == 0) { return $node->nodeValue; } else { @@ -162,7 +179,12 @@ abstract class FeedItem_Common extends FeedItem { } } - static function normalize_categories($cats) { + /** + * @param array $cats + * + * @return array + */ + static function normalize_categories(array $cats): array { $tmp = []; diff --git a/classes/feeditem/rss.php b/classes/feeditem/rss.php index 1f7953c51..7017d04e9 100755 --- a/classes/feeditem/rss.php +++ b/classes/feeditem/rss.php @@ -1,6 +1,6 @@ elem->getElementsByTagName("guid")->item(0); if ($id) { @@ -10,6 +10,9 @@ class FeedItem_RSS extends FeedItem_Common { } } + /** + * @return int|false a timestamp on success, false otherwise + */ function get_date() { $pubDate = $this->elem->getElementsByTagName("pubDate")->item(0); @@ -22,9 +25,12 @@ class FeedItem_RSS extends FeedItem_Common { if ($date) { return strtotime($date->nodeValue); } + + // consistent with strtotime failing to parse + return false; } - function get_link() { + function get_link(): string { $links = $this->xpath->query("atom:link", $this->elem); foreach ($links as $link) { @@ -37,6 +43,7 @@ class FeedItem_RSS extends FeedItem_Common { } } + /** @var DOMElement|null */ $link = $this->elem->getElementsByTagName("guid")->item(0); if ($link && $link->hasAttributes() && $link->getAttribute("isPermaLink") == "true") { @@ -48,9 +55,11 @@ class FeedItem_RSS extends FeedItem_Common { if ($link) { return clean(trim($link->nodeValue)); } + + return ''; } - function get_title() { + function get_title(): string { $title = $this->xpath->query("title", $this->elem)->item(0); if ($title) { @@ -64,10 +73,15 @@ class FeedItem_RSS extends FeedItem_Common { if ($title) { return clean(trim($title->nodeValue)); } + + return ''; } - function get_content() { + function get_content(): string { + /** @var DOMElement|null */ $contentA = $this->xpath->query("content:encoded", $this->elem)->item(0); + + /** @var DOMElement|null */ $contentB = $this->elem->getElementsByTagName("description")->item(0); if ($contentA && !$contentB) { @@ -85,17 +99,24 @@ class FeedItem_RSS extends FeedItem_Common { return mb_strlen($resultA) > mb_strlen($resultB) ? $resultA : $resultB; } + + return ''; } - function get_description() { + function get_description(): string { $summary = $this->elem->getElementsByTagName("description")->item(0); if ($summary) { return $summary->nodeValue; } + + return ''; } - function get_categories() { + /** + * @return array + */ + function get_categories(): array { $categories = $this->elem->getElementsByTagName("category"); $cats = []; @@ -112,7 +133,10 @@ class FeedItem_RSS extends FeedItem_Common { return $this->normalize_categories($cats); } - function get_enclosures() { + /** + * @return array + */ + function get_enclosures(): array { $enclosures = $this->elem->getElementsByTagName("enclosure"); $encs = array(); @@ -134,7 +158,7 @@ class FeedItem_RSS extends FeedItem_Common { return $encs; } - function get_language() { + function get_language(): string { $languages = $this->doc->getElementsByTagName('language'); if (count($languages) == 0) { @@ -143,5 +167,4 @@ class FeedItem_RSS extends FeedItem_Common { return clean($languages[0]->textContent); } - } diff --git a/classes/rssutils.php b/classes/rssutils.php index 3815b3ca1..baace6b20 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -687,7 +687,7 @@ class RSSUtils { } $entry_comments = mb_substr(strip_tags($item->get_comments_url()), 0, 245); - $num_comments = (int) $item->get_comments_count(); + $num_comments = $item->get_comments_count(); $entry_author = strip_tags($item->get_author()); $entry_guid = mb_substr($entry_guid, 0, 245); -- cgit v1.2.3-54-g00ecf From 2d830c6281c19a7ee29cd379f5aedc82deef3775 Mon Sep 17 00:00:00 2001 From: wn_ Date: Wed, 17 Nov 2021 20:45:26 +0000 Subject: Minor correction to RSSUtils::cache_enclosures() $enclosures param type. All FeedEnclosure values are currently strings, even though the numeric things get converted to int before getting inserted in 'ttrss_enclosures'. --- classes/rssutils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'classes/rssutils.php') diff --git a/classes/rssutils.php b/classes/rssutils.php index baace6b20..b886a060c 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -1275,7 +1275,7 @@ class RSSUtils { /** * TODO: move to DiskCache? * - * @param array> $enclosures An array of "enclosure arrays" [string $link, string $type, int $length, string, $title, int $width, int $height] + * @param array> $enclosures An array of "enclosure arrays" [string $link, string $type, string $length, string, $title, string $width, string $height] * @see RSSUtils::update_rss_feed() * @see FeedEnclosure */ -- cgit v1.2.3-54-g00ecf