diff --git a/classes/API.php b/classes/API.php index 31a3bec97..241663b3d 100644 --- a/classes/API.php +++ b/classes/API.php @@ -14,8 +14,7 @@ class API extends Handler { const E_OPERATION_FAILED = "E_OPERATION_FAILED"; const E_NOT_FOUND = "E_NOT_FOUND"; - /** @var int|null */ - private $seq; + private ?int $seq = null; /** * @param array $reply diff --git a/classes/Config.php b/classes/Config.php index 9d671215a..356f81391 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -252,17 +252,15 @@ class Config { Config::HTTP_429_THROTTLE_INTERVAL => [ 3600, Config::T_INT ] ]; - /** @var Config|null */ - private static $instance; + private static ?Config $instance = null; /** @var array> */ - private $params = []; + private array $params = []; /** @var array */ - private $version = []; + private array $version = []; - /** @var Db_Migrations|null $migrations */ - private $migrations; + private Db_Migrations $migrations; public static function get_instance() : Config { if (self::$instance == null) diff --git a/classes/Db.php b/classes/Db.php index 4331b662e..2d8511258 100644 --- a/classes/Db.php +++ b/classes/Db.php @@ -1,11 +1,8 @@ $instances */ - private static $instances = []; + private static array $instances = []; /** * https://stackoverflow.com/a/53662733 @@ -301,8 +300,11 @@ class DiskCache implements Cache_Adapter { if ($this->exists($local_filename) && !$force) return true; - $data = UrlHelper::fetch(array_merge(["url" => $url, - "max_size" => Config::get(Config::MAX_CACHE_FILE_SIZE)], $options)); + $data = UrlHelper::fetch([ + 'url' => $url, + 'max_size' => Config::get(Config::MAX_CACHE_FILE_SIZE), + ...$options, + ]); if ($data) return $this->put($local_filename, $data) > 0; diff --git a/classes/FeedEnclosure.php b/classes/FeedEnclosure.php index b5f5cc411..10dd2ae33 100644 --- a/classes/FeedEnclosure.php +++ b/classes/FeedEnclosure.php @@ -1,21 +1,9 @@ getAttribute("rel") == "enclosure") { $enc = new FeedEnclosure(); - - $enc->type = clean($link->getAttribute("type")); - $enc->length = clean($link->getAttribute("length")); - $enc->link = clean($link->getAttribute("href")); + $enc->type = clean($link->getAttribute('type')); + $enc->length = clean($link->getAttribute('length')); + $enc->link = clean($link->getAttribute('href')); if (!empty($base)) { $enc->link = UrlHelper::rewrite_relative($base, $enc->link); diff --git a/classes/FeedItem_Common.php b/classes/FeedItem_Common.php index 1471dc3e3..5ec958699 100644 --- a/classes/FeedItem_Common.php +++ b/classes/FeedItem_Common.php @@ -1,18 +1,13 @@ elem = $elem; - $this->xpath = $xpath; $this->doc = $doc; + $this->xpath = $xpath; try { $source = $elem->getElementsByTagName("source")->item(0); @@ -98,12 +93,11 @@ abstract class FeedItem_Common extends FeedItem { foreach ($enclosures as $enclosure) { $enc = new FeedEnclosure(); - - $enc->type = clean($enclosure->getAttribute("type")); - $enc->link = clean($enclosure->getAttribute("url")); - $enc->length = clean($enclosure->getAttribute("length")); - $enc->height = clean($enclosure->getAttribute("height")); - $enc->width = clean($enclosure->getAttribute("width")); + $enc->type = clean($enclosure->getAttribute('type')); + $enc->link = clean($enclosure->getAttribute('url')); + $enc->length = clean($enclosure->getAttribute('length')); + $enc->height = clean($enclosure->getAttribute('height')); + $enc->width = clean($enclosure->getAttribute('width')); $medium = clean($enclosure->getAttribute("medium")); if (!$enc->type && $medium) { @@ -119,17 +113,16 @@ abstract class FeedItem_Common extends FeedItem { $enclosures = $this->xpath->query("media:group", $this->elem); foreach ($enclosures as $enclosure) { - $enc = new FeedEnclosure(); - /** @var DOMElement|null */ $content = $this->xpath->query("media:content", $enclosure)->item(0); if ($content) { - $enc->type = clean($content->getAttribute("type")); - $enc->link = clean($content->getAttribute("url")); - $enc->length = clean($content->getAttribute("length")); - $enc->height = clean($content->getAttribute("height")); - $enc->width = clean($content->getAttribute("width")); + $enc = new FeedEnclosure(); + $enc->type = clean($content->getAttribute('type')); + $enc->link = clean($content->getAttribute('url')); + $enc->length = clean($content->getAttribute('length')); + $enc->height = clean($content->getAttribute('height')); + $enc->width = clean($content->getAttribute('width')); $medium = clean($content->getAttribute("medium")); if (!$enc->type && $medium) { @@ -152,11 +145,10 @@ abstract class FeedItem_Common extends FeedItem { foreach ($enclosures as $enclosure) { $enc = new FeedEnclosure(); - - $enc->type = "image/generic"; - $enc->link = clean($enclosure->getAttribute("url")); - $enc->height = clean($enclosure->getAttribute("height")); - $enc->width = clean($enclosure->getAttribute("width")); + $enc->type = 'image/generic'; + $enc->link = clean($enclosure->getAttribute('url')); + $enc->height = clean($enclosure->getAttribute('height')); + $enc->width = clean($enclosure->getAttribute('width')); array_push($encs, $enc); } diff --git a/classes/FeedItem_RSS.php b/classes/FeedItem_RSS.php index ab1764577..6e1435310 100644 --- a/classes/FeedItem_RSS.php +++ b/classes/FeedItem_RSS.php @@ -142,12 +142,11 @@ class FeedItem_RSS extends FeedItem_Common { foreach ($enclosures as $enclosure) { $enc = new FeedEnclosure(); - - $enc->type = clean($enclosure->getAttribute("type")); - $enc->link = clean($enclosure->getAttribute("url")); - $enc->length = clean($enclosure->getAttribute("length")); - $enc->height = clean($enclosure->getAttribute("height")); - $enc->width = clean($enclosure->getAttribute("width")); + $enc->type = clean($enclosure->getAttribute('type')); + $enc->link = clean($enclosure->getAttribute('url')); + $enc->length = clean($enclosure->getAttribute('length')); + $enc->height = clean($enclosure->getAttribute('height')); + $enc->width = clean($enclosure->getAttribute('width')); array_push($encs, $enc); } diff --git a/classes/FeedParser.php b/classes/FeedParser.php index c288ffc91..fd26226ae 100644 --- a/classes/FeedParser.php +++ b/classes/FeedParser.php @@ -1,29 +1,23 @@ */ - private $libxml_errors = []; + private array $libxml_errors = []; /** @var array */ - private $items = []; + private array $items = []; - /** @var string|null */ - private $link; + private ?string $link = null; - /** @var string|null */ - private $title; + private ?string $title = null; /** @var FeedParser::FEED_*|null */ - private $type; + private ?int $type = null; - /** @var DOMXPath|null */ - private $xpath; + private ?DOMXPath $xpath = null; const FEED_UNKNOWN = -1; const FEED_RDF = 0; diff --git a/classes/Logger.php b/classes/Logger.php index 049b63d5e..05a117a0b 100644 --- a/classes/Logger.php +++ b/classes/Logger.php @@ -1,10 +1,8 @@ > plugin name -> (potential profile array) -> key -> value */ private array $storage = []; - /** @var array> */ - private array $feeds = []; + /** @var array */ + private array $special_feeds = []; /** @var array API method name, Plugin sender */ private array $api_methods = []; @@ -759,45 +759,51 @@ class PluginHost { } } - // Plugin feed functions are *EXPERIMENTAL*! + /** + * Add a special (plugin-provided) feed + * + * @param int $cat_id only -1 (Feeds::CATEGORY_SPECIAL) is supported + * @return false|int false if the feed wasn't added (e.g. $cat_id wasn't Feeds::CATEGORY_SPECIAL), + * otherwise an integer "feed ID" that might change between executions + */ + function add_feed(int $cat_id, string $title, string $icon, Plugin $sender): false|int { + if ($cat_id !== Feeds::CATEGORY_SPECIAL) + return false; - // cat_id: only -1 (Feeds::CATEGORY_SPECIAL) is supported (Special) - function add_feed(int $cat_id, string $title, string $icon, Plugin $sender): int { + $id = count($this->special_feeds); - if (empty($this->feeds[$cat_id])) - $this->feeds[$cat_id] = []; - - $id = count($this->feeds[$cat_id]); - - array_push($this->feeds[$cat_id], - ['id' => $id, 'title' => $title, 'sender' => $sender, 'icon' => $icon]); + $this->special_feeds[] = [ + 'id' => $id, + 'title' => $title, + 'sender' => $sender, + 'icon' => $icon, + ]; return $id; } /** + * Get special (plugin-provided) feeds + * + * @param int $cat_id only -1 (Feeds::CATEGORY_SPECIAL) is supported * @return array */ function get_feeds(int $cat_id) { - return $this->feeds[$cat_id] ?? []; + return $cat_id === Feeds::CATEGORY_SPECIAL ? $this->special_feeds : []; } /** - * convert feed_id (e.g. -129) to pfeed_id first - * - * @return (Plugin&IVirtualFeed)|null + * Get the Plugin handling a specific virtual feed. + * + * Convert feed_id (e.g. -129) to pfeed_id first. + * + * @return (Plugin&IVirtualFeed)|null a Plugin that implements IVirtualFeed, otherwise null */ function get_feed_handler(int $pfeed_id): ?Plugin { - foreach ($this->feeds as $cat) { - foreach ($cat as $feed) { - if ($feed['id'] == $pfeed_id) { - if (implements_interface($feed['sender'], 'IVirtualFeed')) { - /** @var Plugin&IVirtualFeed $feed['sender'] */ - return $feed['sender']; - } else { - return null; - } - } + foreach ($this->special_feeds as $feed) { + if ($feed['id'] == $pfeed_id) { + /** @var Plugin&IVirtualFeed $feed['sender'] */ + return implements_interface($feed['sender'], 'IVirtualFeed') ? $feed['sender'] : null; } } return null; @@ -851,14 +857,12 @@ class PluginHost { */ function get_method_url(Plugin $sender, string $method, array $params = []): string { return Config::get_self_url() . "/backend.php?" . - http_build_query( - array_merge( - [ - "op" => "pluginhandler", - "plugin" => strtolower(get_class($sender)), - "method" => $method - ], - $params)); + http_build_query([ + 'op' => 'pluginhandler', + 'plugin' => strtolower(get_class($sender)), + 'method' => $method, + ...$params, + ]); } // shortcut syntax (disabled for now) @@ -880,12 +884,10 @@ class PluginHost { 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( - array_merge( - [ - "op" => strtolower(get_class($sender) . self::PUBLIC_METHOD_DELIMITER . $method), - ], - $params)); + http_build_query([ + 'op' => strtolower(get_class($sender) . self::PUBLIC_METHOD_DELIMITER . $method), + ...$params, + ]); } user_error("get_public_method_url: requested method '$method' of '" . get_class($sender) . "' is private."); return null; diff --git a/classes/Pref_Filters.php b/classes/Pref_Filters.php index 811644758..f3a39f0e5 100644 --- a/classes/Pref_Filters.php +++ b/classes/Pref_Filters.php @@ -13,7 +13,7 @@ class Pref_Filters extends Handler_Protected { const MAX_ACTIONS_TO_DISPLAY = 3; /** @var array> $action_descriptions */ - private $action_descriptions = []; + private array $action_descriptions = []; function before(string $method) : bool { diff --git a/classes/Prefs.php b/classes/Prefs.php index f7e6e39c0..08f402d40 100644 --- a/classes/Prefs.php +++ b/classes/Prefs.php @@ -144,14 +144,12 @@ class Prefs { Prefs::_PREFS_MIGRATED ]; - /** @var Prefs|null */ - private static $instance; + private static ?Prefs $instance = null; /** @var array */ - private $cache = []; + private array $cache = []; - /** @var PDO */ - private $pdo; + private ?PDO $pdo = null; public static function get_instance() : Prefs { if (self::$instance == null) diff --git a/classes/Sessions.php b/classes/Sessions.php index c54815b0f..330790eeb 100644 --- a/classes/Sessions.php +++ b/classes/Sessions.php @@ -53,7 +53,6 @@ class Sessions implements \SessionHandlerInterface { return true; } - #[\ReturnTypeWillChange] public function read(string $id): false|string { $sth = Db::pdo()->prepare('SELECT data FROM ttrss_sessions WHERE id=?'); $sth->execute([$id]); @@ -93,7 +92,6 @@ class Sessions implements \SessionHandlerInterface { /** * @return int|false the number of deleted sessions on success, or false on failure */ - #[\ReturnTypeWillChange] public function gc(int $max_lifetime): false|int { $result = Db::pdo()->query('DELETE FROM ttrss_sessions WHERE expire < ' . time()); return $result === false ? false : $result->rowCount(); diff --git a/include/controls.php b/include/controls.php index 5c6174e3d..8932dd048 100755 --- a/include/controls.php +++ b/include/controls.php @@ -56,21 +56,21 @@ * @param array $attributes */ function number_spinner_tag(string $name, string $value, array $attributes = [], string $id = ""): string { - return input_tag($name, $value, "text", array_merge(["dojoType" => "dijit.form.NumberSpinner"], $attributes), $id); + return input_tag($name, $value, 'text', ['dojoType' => 'dijit.form.NumberSpinner', ...$attributes], $id); } /** * @param array $attributes */ function submit_tag(string $value, array $attributes = []): string { - return button_tag($value, "submit", array_merge(["class" => "alt-primary"], $attributes)); + return button_tag($value, 'submit', ['class' => 'alt-primary', ...$attributes]); } /** * @param array $attributes */ function cancel_dialog_tag(string $value, array $attributes = []): string { - return button_tag($value, "", array_merge(["onclick" => "App.dialogOf(this).hide()"], $attributes)); + return button_tag($value, '', ['onclick' => 'App.dialogOf(this).hide()', ...$attributes]); } /** diff --git a/include/controls_compat.php b/include/controls_compat.php index 5a2a9f2ba..d22f7fc3c 100644 --- a/include/controls_compat.php +++ b/include/controls_compat.php @@ -5,15 +5,13 @@ */ function stylesheet_tag(string $filename, array $attributes = []): string { - $attributes_str = \Controls\attributes_to_string( - array_merge( - [ - "href" => "$filename?" . filemtime($filename), - "rel" => "stylesheet", - "type" => "text/css", - "data-orig-href" => $filename - ], - $attributes)); + $attributes_str = \Controls\attributes_to_string([ + 'href' => "$filename?" . filemtime($filename), + 'rel' => 'stylesheet', + 'type' => 'text/css', + 'data-orig-href' => $filename, + ...$attributes, + ]); return "\n"; } @@ -22,14 +20,12 @@ function stylesheet_tag(string $filename, array $attributes = []): string { * @param array $attributes */ function javascript_tag(string $filename, array $attributes = []): string { - $attributes_str = \Controls\attributes_to_string( - array_merge( - [ - "src" => "$filename?" . filemtime($filename), - "type" => "text/javascript", - "charset" => "utf-8" - ], - $attributes)); + $attributes_str = \Controls\attributes_to_string([ + 'src' => "$filename?" . filemtime($filename), + 'type' => 'text/javascript', + 'charset' => 'utf-8', + ...$attributes, + ]); return "\n"; } diff --git a/tests/integration/ApiTest.php b/tests/integration/ApiTest.php index 4b47ec4b0..65c3828f6 100644 --- a/tests/integration/ApiTest.php +++ b/tests/integration/ApiTest.php @@ -3,12 +3,8 @@ use PHPUnit\Framework\TestCase; /** @group integration */ final class ApiTest extends TestCase { - - /** @var string */ - private $api_url; - - /** @var string */ - private $sid; + private string $api_url; + private string $sid; function __construct() { $this->api_url = getenv('API_URL');