diff options
35 files changed, 597 insertions, 216 deletions
diff --git a/classes/API.php b/classes/API.php index 6dbb1f246..826d1f333 100644 --- a/classes/API.php +++ b/classes/API.php @@ -235,7 +235,7 @@ class API extends Handler { } function updateArticle(): bool { - $article_ids = explode(",", clean($_REQUEST["article_ids"])); + $article_ids = array_filter(explode(",", clean($_REQUEST["article_ids"] ?? ""))); $mode = (int) clean($_REQUEST["mode"]); $data = clean($_REQUEST["data"] ?? ""); $field_raw = (int)clean($_REQUEST["field"]); @@ -300,10 +300,9 @@ class API extends Handler { } function getArticle(): bool { - $article_ids = explode(',', clean($_REQUEST['article_id'] ?? '')); + $article_ids = array_filter(explode(',', clean($_REQUEST['article_id'] ?? ''))); $sanitize_content = self::_param_to_bool($_REQUEST['sanitize'] ?? true); - // @phpstan-ignore-next-line if (count($article_ids)) { $entries = ORM::for_table('ttrss_entries') ->table_alias('e') @@ -550,26 +549,22 @@ class API extends Handler { /* Virtual feeds */ - $vfeeds = PluginHost::getInstance()->get_feeds(Feeds::CATEGORY_SPECIAL); - - if (is_array($vfeeds)) { - foreach ($vfeeds as $feed) { - if (!implements_interface($feed['sender'], 'IVirtualFeed')) - continue; + foreach (PluginHost::getInstance()->get_feeds(Feeds::CATEGORY_SPECIAL) as $feed) { + if (!implements_interface($feed['sender'], 'IVirtualFeed')) + continue; - /** @var IVirtualFeed $feed['sender'] */ - $unread = $feed['sender']->get_unread($feed['id']); + /** @var IVirtualFeed $feed['sender'] */ + $unread = $feed['sender']->get_unread($feed['id']); - if ($unread || !$unread_only) { - $row = [ - 'id' => PluginHost::pfeed_to_feed_id($feed['id']), - 'title' => $feed['title'], - 'unread' => $unread, - 'cat_id' => Feeds::CATEGORY_SPECIAL, - ]; + if ($unread || !$unread_only) { + $row = [ + 'id' => PluginHost::pfeed_to_feed_id($feed['id']), + 'title' => $feed['title'], + 'unread' => $unread, + 'cat_id' => Feeds::CATEGORY_SPECIAL, + ]; - array_push($feeds, $row); - } + array_push($feeds, $row); } } @@ -696,7 +691,6 @@ class API extends Handler { if (!$is_cat && is_numeric($feed_id) && $feed_id < PLUGIN_FEED_BASE_INDEX && $feed_id > LABEL_BASE_INDEX) { $pfeed_id = PluginHost::feed_to_pfeed_id($feed_id); - /** @var IVirtualFeed|false $handler */ $handler = PluginHost::getInstance()->get_feed_handler($pfeed_id); if ($handler) { @@ -858,7 +852,7 @@ class API extends Handler { array_push($headlines, $headline_row); } - } else if (is_numeric($result) && $result == -1) { + } else if ($result == -1) { $headlines_header['first_id_changed'] = true; } diff --git a/classes/Counters.php b/classes/Counters.php index 0f6b419ba..99d4fdab1 100644 --- a/classes/Counters.php +++ b/classes/Counters.php @@ -264,25 +264,21 @@ class Counters { array_push($ret, $cv); } - $feeds = PluginHost::getInstance()->get_feeds(Feeds::CATEGORY_SPECIAL); + foreach (PluginHost::getInstance()->get_feeds(Feeds::CATEGORY_SPECIAL) as $feed) { + if (!implements_interface($feed['sender'], 'IVirtualFeed')) + continue; - if (is_array($feeds)) { - foreach ($feeds as $feed) { - /** @var IVirtualFeed $feed['sender'] */ + /** @var Plugin&IVirtualFeed $feed['sender'] */ - if (!implements_interface($feed['sender'], 'IVirtualFeed')) - continue; - - $cv = [ - "id" => PluginHost::pfeed_to_feed_id($feed['id']), - "counter" => $feed['sender']->get_unread($feed['id']) - ]; + $cv = [ + "id" => PluginHost::pfeed_to_feed_id($feed['id']), + "counter" => $feed['sender']->get_unread($feed['id']) + ]; - if (method_exists($feed['sender'], 'get_total')) - $cv["auxcounter"] = $feed['sender']->get_total($feed['id']); + if (method_exists($feed['sender'], 'get_total')) + $cv["auxcounter"] = $feed['sender']->get_total($feed['id']); - array_push($ret, $cv); - } + array_push($ret, $cv); } return $ret; diff --git a/classes/Debug.php b/classes/Debug.php index 1cda12539..ba39b4d44 100644 --- a/classes/Debug.php +++ b/classes/Debug.php @@ -77,7 +77,7 @@ class Debug { */ public static function map_loglevel(int $level) : int { if (in_array($level, self::ALL_LOG_LEVELS)) { - /** @phpstan-ignore-next-line */ + /** @phpstan-ignore return.type (yes it is a Debug::LOG_* value) */ return $level; } else { user_error("Passed invalid debug log level: $level", E_USER_WARNING); diff --git a/classes/Digest.php b/classes/Digest.php index cad808711..287f5f8fc 100644 --- a/classes/Digest.php +++ b/classes/Digest.php @@ -163,7 +163,7 @@ class Digest $article_labels = Article::_get_labels($line["ref_id"], $user_id); $article_labels_formatted = ""; - if (is_array($article_labels) && count($article_labels) > 0) { + if (count($article_labels) > 0) { $article_labels_formatted = implode(", ", array_map(fn($a) => $a[1], $article_labels)); } diff --git a/classes/FeedItem_Atom.php b/classes/FeedItem_Atom.php index f6c96f959..b5eaca181 100644 --- a/classes/FeedItem_Atom.php +++ b/classes/FeedItem_Atom.php @@ -43,7 +43,6 @@ class FeedItem_Atom extends FeedItem_Common { $links = $this->elem->getElementsByTagName("link"); foreach ($links as $link) { - /** @phpstan-ignore-next-line */ if ($link->hasAttribute("href") && (!$link->hasAttribute("rel") || $link->getAttribute("rel") == "alternate" @@ -181,7 +180,6 @@ class FeedItem_Atom extends FeedItem_Common { $encs = []; foreach ($links as $link) { - /** @phpstan-ignore-next-line */ if ($link->hasAttribute("href") && $link->hasAttribute("rel")) { $base = $this->xpath->evaluate("string(ancestor-or-self::*[@xml:base][1]/@xml:base)", $link); diff --git a/classes/FeedItem_RSS.php b/classes/FeedItem_RSS.php index b5710ef4f..207d54dfb 100644 --- a/classes/FeedItem_RSS.php +++ b/classes/FeedItem_RSS.php @@ -34,7 +34,7 @@ class FeedItem_RSS extends FeedItem_Common { $links = $this->xpath->query("atom:link", $this->elem); foreach ($links as $link) { - if ($link && $link->hasAttribute("href") && + if ($link->hasAttribute("href") && (!$link->hasAttribute("rel") || $link->getAttribute("rel") == "alternate" || $link->getAttribute("rel") == "standout")) { diff --git a/classes/Feeds.php b/classes/Feeds.php index 34540ff11..b4df690a7 100644 --- a/classes/Feeds.php +++ b/classes/Feeds.php @@ -103,8 +103,6 @@ class Feeds extends Handler_Protected { $qfh_ret = []; if (!$cat_view && is_numeric($feed) && $feed < PLUGIN_FEED_BASE_INDEX && $feed > LABEL_BASE_INDEX) { - - /** @var IVirtualFeed|false $handler */ $handler = PluginHost::getInstance()->get_feed_handler( PluginHost::feed_to_pfeed_id($feed)); @@ -246,7 +244,7 @@ class Feeds extends Handler_Protected { $label_cache = json_decode($label_cache, true); if ($label_cache) { - if ($label_cache["no-labels"] ?? 0 == 1) + if (($label_cache["no-labels"] ?? 0) == 1) $labels = []; else $labels = $label_cache; @@ -936,15 +934,10 @@ class Feeds extends Handler_Protected { if ($is_cat) { return self::_get_cat_unread($n_feed, $owner_uid); - } else if(is_numeric($feed) && $feed < PLUGIN_FEED_BASE_INDEX && $feed > LABEL_BASE_INDEX) { // virtual Feed + } else if (is_numeric($feed) && $feed < PLUGIN_FEED_BASE_INDEX && $feed > LABEL_BASE_INDEX) { // virtual Feed $feed_id = PluginHost::feed_to_pfeed_id($feed); $handler = PluginHost::getInstance()->get_feed_handler($feed_id); - if (implements_interface($handler, 'IVirtualFeed')) { - /** @var IVirtualFeed $handler */ - return $handler->get_unread($feed_id); - } else { - return 0; - } + return $handler ? $handler->get_unread($feed_id) : 0; } else if ($n_feed == Feeds::FEED_RECENTLY_READ) { return 0; // tags @@ -1067,7 +1060,7 @@ class Feeds extends Handler_Protected { if (!$url) return ["code" => 2]; PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_PRE_SUBSCRIBE, - /** @phpstan-ignore-next-line */ + /** @phpstan-ignore closure.unusedUse, closure.unusedUse, closure.unusedUse */ function ($result) use (&$url, &$auth_login, &$auth_pass) { // arguments are updated inside the hook (if needed) }, diff --git a/classes/Handler_Public.php b/classes/Handler_Public.php index 208c8e40a..518dcad05 100644 --- a/classes/Handler_Public.php +++ b/classes/Handler_Public.php @@ -54,8 +54,6 @@ class Handler_Public extends Handler { PluginHost::feed_to_pfeed_id((int)$feed)); if ($handler) { - // 'get_headlines' is implemented by the plugin. - // @phpstan-ignore-next-line $qfh_ret = $handler->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); diff --git a/classes/Plugin.php b/classes/Plugin.php index cc50ae75e..62211f338 100644 --- a/classes/Plugin.php +++ b/classes/Plugin.php @@ -76,7 +76,7 @@ abstract class Plugin { * * @return string */ function __($msgid) { - /** @var Plugin $this -- this is a strictly template-related hack */ + // this is a strictly template-related hack return _dgettext(PluginHost::object_to_domain($this), $msgid); } @@ -87,7 +87,7 @@ abstract class Plugin { * * @return string */ function _ngettext($singular, $plural, $number) { - /** @var Plugin $this -- this is a strictly template-related hack */ + // this is a strictly template-related hack return _dngettext(PluginHost::object_to_domain($this), $singular, $plural, $number); } diff --git a/classes/PluginHost.php b/classes/PluginHost.php index 23ff03661..608355158 100644 --- a/classes/PluginHost.php +++ b/classes/PluginHost.php @@ -386,7 +386,7 @@ class PluginHost { * @param PluginHost::HOOK_* $type */ function del_hook(string $type, Plugin $sender): void { - if (is_array($this->hooks[$type])) { + if (array_key_exists($type, $this->hooks)) { foreach (array_keys($this->hooks[$type]) as $prio) { $key = array_search($sender, $this->hooks[$type][$prio]); @@ -589,7 +589,7 @@ class PluginHost { } } - /** @return array<string, array{'description': string, 'suffix': string, 'arghelp': string, 'class': Plugin}>> command type -> details array */ + /** @return array<string, array{'description': string, 'suffix': string, 'arghelp': string, 'class': Plugin}> command type -> details array */ function get_commands() { return $this->commands; } @@ -782,12 +782,21 @@ class PluginHost { return $this->feeds[$cat_id] ?? []; } - // convert feed_id (e.g. -129) to pfeed_id first + /** + * convert feed_id (e.g. -129) to pfeed_id first + * + * @return (Plugin&IVirtualFeed)|null + */ function get_feed_handler(int $pfeed_id): ?Plugin { foreach ($this->feeds as $cat) { foreach ($cat as $feed) { if ($feed['id'] == $pfeed_id) { - return $feed['sender']; + if (implements_interface($feed['sender'], 'IVirtualFeed')) { + /** @var Plugin&IVirtualFeed $feed['sender'] */ + return $feed['sender']; + } else { + return null; + } } } } diff --git a/classes/Pref_Feeds.php b/classes/Pref_Feeds.php index 5ca557c84..8101b9531 100644 --- a/classes/Pref_Feeds.php +++ b/classes/Pref_Feeds.php @@ -240,8 +240,7 @@ class Pref_Feeds extends Handler_Protected { /** * Uncategorized is a special case. * - * Define a minimal array shape to help PHPStan with the type of $cat['items'] - * @var array{items: array<int, array<string, mixed>>} $cat + * @var array{id: string, bare_id: int, auxcounter: int, name: string, items: array<int, array<string, mixed>>, type: 'category', checkbox: bool, unread: int, child_unread: int} */ $cat = [ 'id' => 'CAT:0', diff --git a/classes/Pref_Filters.php b/classes/Pref_Filters.php index 7a477d7db..87237342a 100644 --- a/classes/Pref_Filters.php +++ b/classes/Pref_Filters.php @@ -115,9 +115,6 @@ class Pref_Filters extends Handler_Protected { $glue = $filter['match_any_rule'] ? " OR " : " AND "; $scope_qpart = join($glue, $scope_qparts); - /** @phpstan-ignore-next-line */ - if (!$scope_qpart) $scope_qpart = "true"; - $rv = array(); //while ($found < $limit && $offset < $limit * 1000 && time() - $started < ini_get("max_execution_time") * 0.7) { @@ -714,7 +711,6 @@ class Pref_Filters extends Handler_Protected { } function editrule(): void { - /** @var array<int, int|string> */ $feed_ids = explode(",", clean($_REQUEST["ids"])); print json_encode([ diff --git a/classes/Pref_Prefs.php b/classes/Pref_Prefs.php index dd28bd3f2..47d6886f9 100644 --- a/classes/Pref_Prefs.php +++ b/classes/Pref_Prefs.php @@ -177,7 +177,7 @@ class Pref_Prefs extends Handler_Protected { $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if (implements_interface($authenticator, "IAuthModule2")) { - /** @var IAuthModule2 $authenticator */ + /** @var Plugin&IAuthModule2 $authenticator */ print format_notice($authenticator->change_password($_SESSION["uid"], $old_pw, $new_pw)); } else { print "ERROR: ".format_error("Function not supported by authentication module."); @@ -976,7 +976,7 @@ class Pref_Prefs extends Handler_Protected { $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); - /** @var Auth_Internal|false $authenticator -- this is only here to make check_password() visible to static analyzer */ + /** @var Auth_Internal|null $authenticator -- this is only here to make check_password() visible to static analyzer */ if ($authenticator->check_password($_SESSION["uid"], $password)) { if (UserHelper::enable_otp($_SESSION["uid"], $otp_check)) { print "OK"; @@ -991,7 +991,7 @@ class Pref_Prefs extends Handler_Protected { function otpdisable(): void { $password = clean($_REQUEST["password"]); - /** @var Auth_Internal|false $authenticator -- this is only here to make check_password() visible to static analyzer */ + /** @var Auth_Internal|null $authenticator -- this is only here to make check_password() visible to static analyzer */ $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator->check_password($_SESSION["uid"], $password)) { @@ -1316,14 +1316,8 @@ class Pref_Prefs extends Handler_Protected { function updateLocalPlugins(): void { if ($_SESSION["access_level"] >= UserHelper::ACCESS_LEVEL_ADMIN) { - $plugins = explode(",", $_REQUEST["plugins"] ?? ""); - - if ($plugins !== false) { - $plugins = array_filter($plugins, 'strlen'); - } - + $plugins = array_filter(explode(",", $_REQUEST["plugins"] ?? ""), "strlen"); $root_dir = Config::get_self_dir(); - $rv = []; if ($plugins) { diff --git a/classes/UserHelper.php b/classes/UserHelper.php index 0c2ed349b..27e022624 100644 --- a/classes/UserHelper.php +++ b/classes/UserHelper.php @@ -50,7 +50,7 @@ class UserHelper { */ public static function map_access_level(int $level) : int { if (in_array($level, self::ACCESS_LEVELS)) { - /** @phpstan-ignore-next-line */ + /** @phpstan-ignore return.type (yes it is a UserHelper::ACCESS_LEVEL_* value) */ return $level; } else { user_error("Passed invalid user access level: $level", E_USER_WARNING); @@ -503,7 +503,6 @@ class UserHelper { return $authenticator->check_password($owner_uid, $password); } else { - /** @var Auth_Internal|false $authenticator -- this is only here to make check_password() visible to static analyzer */ $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator && diff --git a/composer.json b/composer.json index 25e22ad4c..e91c8d2e7 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "guzzlehttp/guzzle": "^7.0" }, "require-dev": { - "phpstan/phpstan": "1.10.3", + "phpstan/phpstan": "2.0.1", "phpunit/phpunit": "9.5.16", "phpunit/php-code-coverage": "^9.2" } diff --git a/composer.lock b/composer.lock index e23b042a1..96bd47dc0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "478a27f2e837c92757db206b443c67a4", + "content-hash": "f071cb2cab2cf7d4ef32c4eb7655e350", "packages": [ { "name": "beberlei/assert", @@ -1781,20 +1781,20 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "5419375b5891add97dc74be71e6c1c34baaddf64" + "reference": "ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/5419375b5891add97dc74be71e6c1c34baaddf64", - "reference": "5419375b5891add97dc74be71e6c1c34baaddf64", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d", + "reference": "ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -1819,8 +1819,11 @@ "static analysis" ], "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.10.3" + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { @@ -1830,13 +1833,9 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2023-02-25T14:47:13+00:00" + "time": "2024-11-11T15:43:04+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/include/errorhandler.php b/include/errorhandler.php index ea464f77a..4f773bc19 100644 --- a/include/errorhandler.php +++ b/include/errorhandler.php @@ -2,40 +2,38 @@ /** * @param array<int, array<string, mixed>> $trace */ -function format_backtrace($trace): string { +function format_backtrace(array $trace): string { $rv = ""; $idx = 1; - if (is_array($trace)) { - foreach ($trace as $e) { - if (isset($e["file"]) && isset($e["line"])) { - $fmt_args = []; - - if (is_array($e["args"] ?? false)) { - foreach ($e["args"] as $a) { - if (is_object($a)) { - array_push($fmt_args, "{" . get_class($a) . "}"); - } else if (is_array($a)) { - array_push($fmt_args, "[" . truncate_string(json_encode($a), 256, "...")) . "]"; - } else if (is_resource($a)) { - array_push($fmt_args, truncate_string(get_resource_type($a), 256, "...")); - } else if (is_string($a)) { - array_push($fmt_args, truncate_string($a, 256, "...")); - } + foreach ($trace as $e) { + if (isset($e["file"]) && isset($e["line"])) { + $fmt_args = []; + + if (is_array($e["args"] ?? false)) { + foreach ($e["args"] as $a) { + if (is_object($a)) { + array_push($fmt_args, "{" . get_class($a) . "}"); + } else if (is_array($a)) { + array_push($fmt_args, "[" . truncate_string(json_encode($a), 256, "...")) . "]"; + } else if (is_resource($a)) { + array_push($fmt_args, truncate_string(get_resource_type($a), 256, "...")); + } else if (is_string($a)) { + array_push($fmt_args, truncate_string($a, 256, "...")); } } + } - $filename = str_replace(dirname(__DIR__) . "/", "", $e["file"]); + $filename = str_replace(dirname(__DIR__) . "/", "", $e["file"]); - $rv .= sprintf("%d. %s(%s): %s(%s)\n", - $idx, - $filename, - $e["line"], - $e["function"], - implode(", ", $fmt_args)); + $rv .= sprintf("%d. %s(%s): %s(%s)\n", + $idx, + $filename, + $e["line"], + $e["function"], + implode(", ", $fmt_args)); - $idx++; - } + $idx++; } } diff --git a/include/functions.php b/include/functions.php index c32924743..a79a19711 100644 --- a/include/functions.php +++ b/include/functions.php @@ -123,6 +123,7 @@ // create a list like "en" => 0.8 $langs = array_combine($lang_parse[1], $lang_parse[4]); + /** @phpstan-ignore function.alreadyNarrowedType (PHP 7.4 will return false if array_value has an issue) */ if (is_array($langs)) { // set default to 1 for any without q factor foreach ($langs as $lang => $val) { diff --git a/include/login_form.php b/include/login_form.php index 0545a51be..5ff2b378e 100755 --- a/include/login_form.php +++ b/include/login_form.php @@ -39,16 +39,14 @@ <?php foreach (PluginHost::getInstance()->get_plugins() as $n => $p) { - if (method_exists($p, "get_login_js")) { - $script = $p->get_login_js(); - - if ($script) { - echo "try { - $script - } catch (e) { - console.warn('failed to initialize plugin JS: $n', e); - }"; - } + $script = $p->get_login_js(); + + if ($script) { + echo "try { + $script + } catch (e) { + console.warn('failed to initialize plugin JS: $n', e); + }"; } } ?> @@ -36,8 +36,9 @@ <style type="text/css"> <?php foreach (PluginHost::getInstance()->get_plugins() as $p) { - if (method_exists($p, "get_css")) { - echo $p->get_css(); + $css = $p->get_css(); + if ($css) { + echo $css; } } ?> @@ -73,16 +74,14 @@ <script type="text/javascript"> <?php foreach (PluginHost::getInstance()->get_plugins() as $n => $p) { - if (method_exists($p, "get_js")) { - $script = $p->get_js(); - - if ($script) { - echo "try { - $script - } catch (e) { - console.warn('failed to initialize plugin JS: $n', e); - }"; - } + $script = $p->get_js(); + + if ($script) { + echo "try { + $script + } catch (e) { + console.warn('failed to initialize plugin JS: $n', e); + }"; } } ?> diff --git a/phpstan.neon b/phpstan.neon index 8b0c352a2..74daee0ce 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,8 +5,18 @@ parameters: maximumNumberOfProcesses: 4 reportUnmatchedIgnoredErrors: false ignoreErrors: - - '#Constant.*\b(SUBSTRING_FOR_DATE|SCHEMA_VERSION|SELF_USER_AGENT|LABEL_BASE_INDEX|PLUGIN_FEED_BASE_INDEX)\b.*not found#' - - '#Comparison operation ">" between int<1, max> and 0 is always true.#' + # set_include_path detection issue + - message: '#"(autoload|colors|config|errorhandler|login_form|sessions)\.php"#' + identifier: requireOnce.fileNotFound + + # undetected as DOMElement + - '#Call to an undefined method DOMNode::#' + - '#Access to an undefined property DOMNode::#' + - '#expects DOMElement, DOMNode given#' + + # allow passing a 'strlen' string, etc. as the array_filter callback + - "/Parameter #2 \\$callback of function array_filter expects .+ '.+' given/" + - message: '##' paths: - lib/* @@ -64,16 +64,14 @@ <script type="text/javascript"> <?php foreach (PluginHost::getInstance()->get_plugins() as $n => $p) { - if (method_exists($p, "get_prefs_js")) { - $script = $p->get_prefs_js(); - - if ($script) { - echo "try { - $script - } catch (e) { - console.warn('failed to initialize plugin JS: $n', e); - }"; - } + $script = $p->get_prefs_js(); + + if ($script) { + echo "try { + $script + } catch (e) { + console.warn('failed to initialize plugin JS: $n', e); + }"; } } ?> diff --git a/update.php b/update.php index c8fe69fdb..c7f274419 100755 --- a/update.php +++ b/update.php @@ -215,7 +215,7 @@ } if (isset($options["daemon"])) { - // @phpstan-ignore-next-line + // @phpstan-ignore while.alwaysTrue (single process daemon will always run) while (true) { $quiet = (isset($options["quiet"])) ? "--quiet" : ""; $log = isset($options['log']) ? '--log '.$options['log'] : ''; diff --git a/vendor/bin/phpstan b/vendor/bin/phpstan index 20451337e..d76c0be76 100644..100755 --- a/vendor/bin/phpstan +++ b/vendor/bin/phpstan @@ -112,9 +112,8 @@ if (PHP_VERSION_ID < 80000) { (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) ) { - include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan'); - exit(0); + return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan'); } } -include __DIR__ . '/..'.'/phpstan/phpstan/phpstan'; +return include __DIR__ . '/..'.'/phpstan/phpstan/phpstan'; diff --git a/vendor/bin/phpstan.phar b/vendor/bin/phpstan.phar index caa3e24f8..fecf96f69 100644..100755 --- a/vendor/bin/phpstan.phar +++ b/vendor/bin/phpstan.phar @@ -112,9 +112,8 @@ if (PHP_VERSION_ID < 80000) { (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) ) { - include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar'); - exit(0); + return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar'); } } -include __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar'; +return include __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar'; diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 469e1edec..e7db092fe 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -11,7 +11,7 @@ return array( 'chillerlan\\QRCode\\' => array($vendorDir . '/chillerlan/php-qrcode/src'), 'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'), 'Soundasleep\\' => array($vendorDir . '/soundasleep/html2text/src'), - 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), 'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 420e21019..4dc835e99 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -176,8 +176,8 @@ class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056 ), 'Psr\\Http\\Message\\' => array ( - 0 => __DIR__ . '/..' . '/psr/http-message/src', - 1 => __DIR__ . '/..' . '/psr/http-factory/src', + 0 => __DIR__ . '/..' . '/psr/http-factory/src', + 1 => __DIR__ . '/..' . '/psr/http-message/src', ), 'Psr\\Http\\Client\\' => array ( diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index da72ace1a..91fa11459 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1288,26 +1288,26 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.3", - "version_normalized": "1.10.3.0", + "version": "2.0.1", + "version_normalized": "2.0.1.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "5419375b5891add97dc74be71e6c1c34baaddf64" + "reference": "ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/5419375b5891add97dc74be71e6c1c34baaddf64", - "reference": "5419375b5891add97dc74be71e6c1c34baaddf64", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d", + "reference": "ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" }, - "time": "2023-02-25T14:47:13+00:00", + "time": "2024-11-11T15:43:04+00:00", "bin": [ "phpstan", "phpstan.phar" @@ -1329,8 +1329,11 @@ "static analysis" ], "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.10.3" + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { @@ -1340,10 +1343,6 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], "install-path": "../phpstan/phpstan" diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 0fc897074..799734701 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => '__root__', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'df489df309dc831b357a9cc36fe72ad5a99d22e0', + 'reference' => '6273e26ea463e2762f2d736455f4912de7171cfa', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'df489df309dc831b357a9cc36fe72ad5a99d22e0', + 'reference' => '6273e26ea463e2762f2d736455f4912de7171cfa', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -184,9 +184,9 @@ 'dev_requirement' => true, ), 'phpstan/phpstan' => array( - 'pretty_version' => '1.10.3', - 'version' => '1.10.3.0', - 'reference' => '5419375b5891add97dc74be71e6c1c34baaddf64', + 'pretty_version' => '2.0.1', + 'version' => '2.0.1.0', + 'reference' => 'ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d', 'type' => 'library', 'install_path' => __DIR__ . '/../phpstan/phpstan', 'aliases' => array(), diff --git a/vendor/phpstan/phpstan/README.md b/vendor/phpstan/phpstan/README.md index 790f234c6..e3bb94063 100644 --- a/vendor/phpstan/phpstan/README.md +++ b/vendor/phpstan/phpstan/README.md @@ -24,50 +24,56 @@ can be checked before you run the actual line. ## Sponsors -<a href="https://coders.thecodingmachine.com/phpstan"><img src="https://i.imgur.com/kQhNOTP.png" alt="TheCodingMachine" width="247" height="64"></a> +<a href="https://coders.thecodingmachine.com/phpstan"><img src="website/src/images/sponsor/tcm.png" alt="TheCodingMachine" width="247" height="64"></a> -<a href="https://packagist.com/?utm_source=phpstan&utm_medium=readme&utm_campaign=sponsorlogo"><img src="https://i.imgur.com/B2T63Do.png" alt="Private Packagist" width="283" height="64"></a> +<a href="https://packagist.com/?utm_source=phpstan&utm_medium=readme&utm_campaign=sponsorlogo"><img src="website/src/images/sponsor/packagist.png" alt="Private Packagist" width="283" height="64"></a> <br> -<a href="https://careers.tuigroup.com/jobs/"><img src="https://i.imgur.com/uw5rAlR.png" alt="Musement" width="247" height="49"></a> +<a href="https://www.cdn77.com/"><img src="website/src/images/sponsor/cdn77.png" alt="CDN77" width="283" height="64"></a> -<a href="https://blackfire.io/docs/introduction?utm_source=phpstan&utm_medium=github_readme&utm_campaign=logo"><img src="https://i.imgur.com/zR8rsqk.png" alt="Blackfire.io" width="254" height="64"></a> +<a href="https://blackfire.io/docs/introduction?utm_source=phpstan&utm_medium=github_readme&utm_campaign=logo"><img src="website/src/images/sponsor/blackfire.png" alt="Blackfire.io" width="254" height="64"></a> <br> -<a href="https://www.iodigital.com/"><img src="https://i.imgur.com/fJlw1n9.png" alt="iO" width="254" height="65"></a> +<a href="https://www.iodigital.com/"><img src="website/src/images/sponsor/io.png" alt="iO" width="254" height="65"></a> -<a href="https://jobs.ticketswap.com/"><img src="https://i.imgur.com/lhzcutK.png" alt="TicketSwap" width="269" height="64"></a> +<a href="https://www.fame.fi/"><img src="website/src/images/sponsor/fame.png" alt="Fame Helsinki" width="283" height="64"></a> <br> -<a href="https://www.startupjobs.cz/startup/shipmonk"><img src="https://i.imgur.com/bAC47za.jpg" alt="ShipMonk" width="290" height="64"></a> +<a href="https://www.startupjobs.cz/startup/shipmonk"><img src="website/src/images/sponsor/shipmonk.jpg" alt="ShipMonk" width="290" height="64"></a> -<a href="https://togetter.com/"><img src="https://i.imgur.com/x9n5cj3.png" alt="Togetter" width="283" height="64"></a> +<a href="https://togetter.com/"><img src="website/src/images/sponsor/togetter.png" alt="Togetter" width="283" height="64"></a> <br> -<a href="https://join.rightcapital.com/?utm_source=phpstan&utm_medium=github&utm_campaign=sponsorship"><img src="https://i.imgur.com/1AhB5tW.png" alt="RightCapital" width="283" height="64"></a> +<a href="https://join.rightcapital.com/?utm_source=phpstan&utm_medium=github&utm_campaign=sponsorship"><img src="website/src/images/sponsor/rightcapital.png" alt="RightCapital" width="283" height="64"></a> -<a href="https://www.contentkingapp.com/?ref=php-developer&utm_source=phpstan&utm_medium=referral&utm_campaign=sponsorship"><img src="https://i.imgur.com/HHhbPGN.png" alt="ContentKing" width="283" height="64"></a> +<a href="https://www.contentkingapp.com/?ref=php-developer&utm_source=phpstan&utm_medium=referral&utm_campaign=sponsorship"><img src="website/src/images/sponsor/contentking.png" alt="ContentKing" width="283" height="64"></a> <br> -<a href="https://zol.fr?utm_source=phpstan"><img src="https://i.imgur.com/dzDgd4s.png" alt="ZOL" width="283" height="64"></a> +<a href="https://zol.fr?utm_source=phpstan"><img src="website/src/images/sponsor/zol.png" alt="ZOL" width="283" height="64"></a> -<a href="https://www.psyonix.com/"><img src="https://i.imgur.com/p8svxQZ.png" alt="Psyonix" width="254" height="65"></a> +<a href="https://www.edgenext.com/"><img src="website/src/images/sponsor/edgenext.png" alt="EdgeNext" width="283" height="64"></a> <br> -<a href="https://www.shopware.com/en/"><img src="https://i.imgur.com/L4X5w9s.png" alt="Shopware" width="284" height="64"></a> +<a href="https://www.shopware.com/en/"><img src="website/src/images/sponsor/shopware.png" alt="Shopware" width="284" height="64"></a> -<a href="https://craftcms.com/"><img src="https://i.imgur.com/xJWThke.png" alt="Craft CMS" width="283" height="64"></a> +<a href="https://craftcms.com/"><img src="website/src/images/sponsor/craftcms.png" alt="Craft CMS" width="283" height="64"></a> <br> -<a href="https://www.worksome.com/"><img src="https://i.imgur.com/TQKSwOl.png" alt="Worksome" width="283" height="64"></a> +<a href="https://www.worksome.com/"><img src="website/src/images/sponsor/worksome.png" alt="Worksome" width="283" height="64"></a> -<a href="https://www.campoint.net/"><img src="https://i.imgur.com/fR6eMUm.png" alt="campoint AG" width="283" height="64"></a> +<a href="https://www.campoint.net/"><img src="website/src/images/sponsor/campoint.png" alt="campoint AG" width="283" height="64"></a> <br> -<a href="https://www.crisp.nl/"><img src="https://i.imgur.com/jRJyPve.png" alt="Crisp.nl" width="283" height="64"></a> +<a href="https://www.crisp.nl/"><img src="website/src/images/sponsor/crisp.png" alt="Crisp.nl" width="283" height="64"></a> -<a href="https://inviqa.com/"><img src="https://i.imgur.com/G99rj45.png" alt="Inviqa" width="254" height="65"></a> +<a href="https://inviqa.com/"><img src="website/src/images/sponsor/inviqa.png" alt="Inviqa" width="254" height="65"></a> <br> -<a href="https://www.cdn77.com/"><img src="https://i.imgur.com/Oo3wA3m.png" alt="CDN77" width="283" height="64"></a> +<a href="https://www.getresponse.com/"><img src="website/src/images/sponsor/getresponse.png" alt="GetResponse" width="283" height="64"></a> + +<a href="https://www.shoptet.cz/"><img src="website/src/images/sponsor/shoptet.png" alt="Shoptet" width="283" height="64"></a> +<br> +<a href="https://route4me.com/"><img src="website/src/images/sponsor/route4me.png" alt="Route4Me: Route Optimizer and Route Planner Software" width="283" height="64"></a> + +<a href="https://jobs.ticketswap.com/"><img src="website/src/images/sponsor/ticketswap.png" alt="TicketSwap" width="269" height="64"></a> [**You can now sponsor my open-source work on PHPStan through GitHub Sponsors.**](https://github.com/sponsors/ondrejmirtes) Does GitHub already have your 💳? Do you use PHPStan to find 🐛 before they reach production? [Send a couple of 💸 a month my way too.](https://github.com/sponsors/ondrejmirtes) Thank you! -One-time donations [through PayPal](https://paypal.me/phpstan) are also accepted. To request an invoice, [contact me](mailto:ondrej@mirtes.cz) through e-mail. +One-time donations [through Revolut.me](https://revolut.me/ondrejmirtes) are also accepted. To request an invoice, [contact me](mailto:ondrej@mirtes.cz) through e-mail. ## Documentation diff --git a/vendor/phpstan/phpstan/UPGRADING.md b/vendor/phpstan/phpstan/UPGRADING.md new file mode 100644 index 000000000..522949096 --- /dev/null +++ b/vendor/phpstan/phpstan/UPGRADING.md @@ -0,0 +1,338 @@ +Upgrading from PHPStan 1.x to 2.0 +================================= + +## PHP version requirements + +PHPStan now requires PHP 7.4 or newer to run. + +## Upgrading guide for end users + +The best way to get ready for upgrade to PHPStan 2.0 is to update to the **latest PHPStan 1.12 release** +and enable [**Bleeding Edge**](https://phpstan.org/blog/what-is-bleeding-edge). This will enable the new rules and behaviours that 2.0 turns on for all users. + +Also make sure to install and enable [`phpstan/phpstan-deprecation-rules`](https://github.com/phpstan/phpstan-deprecation-rules). + +Once you get to a green build with no deprecations showed on latest PHPStan 1.12.x with Bleeding Edge enabled, you can update all your related PHPStan dependencies to 2.0 in `composer.json`: + +```json +"require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-doctrine": "^2.0", + "phpstan/phpstan-nette": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "phpstan/phpstan-webmozart-assert": "^2.0", + ... +} +``` + +Don't forget to update [3rd party PHPStan extensions](https://phpstan.org/user-guide/extension-library) as well. + +After changing your `composer.json`, run `composer update 'phpstan/*' -W`. + +It's up to you whether you go through the new reported errors or if you just put them all to the [baseline](https://phpstan.org/user-guide/baseline) ;) Everyone who's on PHPStan 1.12 should be able to upgrade to PHPStan 2.0. + +### Noteworthy changes to code analysis + +* [**Enhancements in handling parameters passed by reference**](https://phpstan.org/blog/enhancements-in-handling-parameters-passed-by-reference) +* [**Validate inline PHPDoc `@var` tag type**](https://phpstan.org/blog/phpstan-1-10-comes-with-lie-detector#validate-inline-phpdoc-%40var-tag-type) +* [**List type enforced**](https://phpstan.org/blog/phpstan-1-9-0-with-phpdoc-asserts-list-type#list-type) +* **Always `true` conditions always reported**: previously reported only with phpstan-strict-rules, this is now always reported. + +### Removed option `checkMissingIterableValueType` + +It's strongly recommended to add the missing array typehints. + +If you want to continue ignoring missing typehints from arrays, add `missingType.iterableValue` error identifier to your `ignoreErrors`: + +```neon +parameters: + ignoreErrors: + - + identifier: missingType.iterableValue +``` + +### Removed option `checkGenericClassInNonGenericObjectType` + +It's strongly recommended to add the missing generic typehints. + +If you want to continue ignoring missing typehints from generics, add `missingType.generics` error identifier to your `ignoreErrors`: + +```neon +parameters: + ignoreErrors: + - + identifier: missingType.generics +``` + +### Removed `checkAlwaysTrue*` options + +These options have been removed because PHPStan now always behaves as if these were set to `true`: + +* `checkAlwaysTrueCheckTypeFunctionCall` +* `checkAlwaysTrueInstanceof` +* `checkAlwaysTrueStrictComparison` +* `checkAlwaysTrueLooseComparison` + +### Removed option `excludes_analyse` + +It has been replaced with [`excludePaths`](https://phpstan.org/user-guide/ignoring-errors#excluding-whole-files). + +### Paths in `excludePaths` and `ignoreErrors` have to be a valid file path or a fnmatch pattern + +If you are excluding a file path that might not exist but you still want to have it in `excludePaths`, append `(?)`: + +```neon +parameters: + excludePaths: + - tests/*/data/* + - src/broken + - node_modules (?) # optional path, might not exist +``` + +If you have the same situation in `ignoreErrors` (ignoring an error in a path that might not exist), use `reportUnmatchedIgnoredErrors: false`. + +```neon +parameters: + reportUnmatchedIgnoredErrors: false +``` + +Appending `(?)` in `ignoreErrors` is not supported. + +### Changes in 1st party PHPStan extensions + +* [phpstan-doctrine](https://github.com/phpstan/phpstan-doctrine) + * Removed config parameter `searchOtherMethodsForQueryBuilderBeginning` (extension now behaves as when this was set to `true`) + * Removed config parameter `queryBuilderFastAlgorithm` (extension now behaves as when this was set to `false`) +* [phpstan-symfony](https://github.com/phpstan/phpstan-symfony) + * Removed legacy options with `_` in the name + * `container_xml_path` -> use `containerXmlPath` + * `constant_hassers` -> use `constantHassers` + * `console_application_loader` -> use `consoleApplicationLoader` + +### Minor backward compatibility breaks + +* Removed unused config parameter `cache.nodesByFileCountMax` +* Removed unused config parameter `memoryLimitFile` +* Removed unused feature toggle `disableRuntimeReflectionProvider` +* Removed unused config parameter `staticReflectionClassNamePatterns` +* Remove `fixerTmpDir` config parameter, use `pro.tmpDir` instead +* Remove `tempResultCachePath` config parameter, use `resultCachePath` instead +* `additionalConfigFiles` config parameter must be a list + +## Upgrading guide for extension developers + +> [!NOTE] +> Please switch to PHPStan 2.0 in a new major version of your extension. It's not feasible to try to support both PHPStan 1.x and PHPStan 2.x with the same extension code. +> +> You can definitely get closer to supporting PHPStan 2.0 without increasing major version by solving reported deprecations and other issues by analysing your extension code with PHPStan & phpstan-deprecation-rules & Bleeding Edge, but the final leap and solving backward incompatibilities should be done by requiring `"phpstan/phpstan": "^2.0"` in your `composer.json`, and releasing a new major version. + +### PHPStan now uses nikic/php-parser v5 + +See [UPGRADING](https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md) guide for PHP-Parser. + +The most notable change is how `throw` statement is represented. Previously, `throw` statements like `throw $e;` were represented using the `Stmt\Throw_` class, while uses inside other expressions (such as `$x ?? throw $e`) used the `Expr\Throw_` class. + +Now, `throw $e;` is represented as a `Stmt\Expression` that contains an `Expr\Throw_`. The +`Stmt\Throw_` class has been removed. + +### PHPStan now uses phpstan/phpdoc-parser v2 + +See [UPGRADING](https://github.com/phpstan/phpdoc-parser/blob/2.0.x/UPGRADING.md) guide for phpstan/phpdoc-parser. + +### Returning plain strings as errors no longer supported, use RuleErrorBuilder + +Identifiers are also required in custom rules. + +Learn more: [Using RuleErrorBuilder to enrich reported errors in custom rules](https://phpstan.org/blog/using-rule-error-builder) + +**Before**: + +```php +return ['My error']; +``` + +**After**: + +```php +return [ + RuleErrorBuilder::message('My error') + ->identifier('my.error') + ->build(), +]; +``` + +### Deprecate various `instanceof *Type` in favour of new methods on `Type` interface + +Learn more: [Why Is instanceof *Type Wrong and Getting Deprecated?](https://phpstan.org/blog/why-is-instanceof-type-wrong-and-getting-deprecated) + +### Removed deprecated `ParametersAcceptorSelector::selectSingle()` + +Use [`ParametersAcceptorSelector::selectFromArgs()`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ParametersAcceptorSelector.html#_selectFromArgs) instead. It should be used in most places where `selectSingle()` was previously used, like dynamic return type extensions. + +**Before**: + +```php +$defaultReturnType = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(); +``` + +**After**: + +```php +$defaultReturnType = ParametersAcceptorSelector::selectFromArgs( + $scope, + $functionCall->getArgs(), + $functionReflection->getVariants() +)->getReturnType(); +``` + +If you're analysing function or method body itself and you're using one of the following methods, ask for `getParameters()` and `getReturnType()` directly on the reflection object: + +* [InClassMethodNode::getMethodReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.InClassMethodNode.html) +* [InFunctionNode::getFunctionReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.InFunctionNode.html) +* [FunctionReturnStatementsNode::getFunctionReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.FunctionReturnStatementsNode.html) +* [MethodReturnStatementsNode::getMethodReflection()](https://apiref.phpstan.org/2.0.x/PHPStan.Node.MethodReturnStatementsNode.html) +* [Scope::getFunction()](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.Scope.html#_getFunction) + +**Before**: + +```php +$function = $node->getFunctionReflection(); +$returnType = ParametersAcceptorSelector::selectSingle($function->getVariants())->getReturnType(); +``` + +**After**: + +```php +$returnType = $node->getFunctionReflection()->getReturnType(); +``` + +### Changed `TypeSpecifier::create()` and `SpecifiedTypes` constructor parameters + +[`PHPStan\Analyser\TypeSpecifier::create()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.TypeSpecifier.html#_create) now accepts (all parameters are required): + +* `Expr $expr` +* `Type $type` +* `TypeSpecifierContext $context` +* `Scope $scope` + +If you want to change `$overwrite` or `$rootExpr` (previous parameters also used to be accepted by this method), call `setAlwaysOverwriteTypes()` and `setRootExpr()` on [`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) (object returned by `TypeSpecifier::create()`). These methods return a new object (SpecifiedTypes is immutable). + +[`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) constructor now accepts: + +* `array $sureTypes` +* `array $sureNotTypes` + +If you want to change `$overwrite` or `$rootExpr` (previous parameters also used to be accepted by the constructor), call `setAlwaysOverwriteTypes()` and `setRootExpr()`. These methods return a new object (SpecifiedTypes is immutable). + +### `ConstantArrayType` no longer extends `ArrayType` + +`Type::getArrays()` now returns `list<ArrayType|ConstantArrayType>`. + +Using `$type instanceof ArrayType` is [being deprecated anyway](https://phpstan.org/blog/why-is-instanceof-type-wrong-and-getting-deprecated) so the impact of this change should be minimal. + +### Changed `TypeSpecifier::specifyTypesInCondition()` + +This method no longer accepts `Expr $rootExpr`. If you want to change it, call `setRootExpr()` on [`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) (object returned by `TypeSpecifier::specifyTypesInCondition()`). `setRootExpr()` method returns a new object (SpecifiedTypes is immutable). + +### Node attributes `parent`, `previous`, `next` are no longer available + +Learn more: https://phpstan.org/blog/preprocessing-ast-for-custom-rules + +### Removed config parameter `scopeClass` + +As a replacement you can implement [`PHPStan\Type\ExpressionTypeResolverExtension`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.ExpressionTypeResolverExtension.html) interface instead and register it as a service. + +### Removed `PHPStan\Broker\Broker` + +Use [`PHPStan\Reflection\ReflectionProvider`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ReflectionProvider.html) instead. + +`BrokerAwareExtension` was also removed. Ask for `ReflectionProvider` in the extension constructor instead. + +Instead of `PHPStanTestCase::createBroker()`, call `PHPStanTestCase::createReflectionProvider()`. + +### List type is enabled for everyone + +Removed static methods from `AccessoryArrayListType` class: + +* `isListTypeEnabled()` +* `setListTypeEnabled()` +* `intersectWith()` + +Instead of `AccessoryArrayListType::intersectWith($type)`, do `TypeCombinator::intersect($type, new AccessoryArrayListType())`. + +### Minor backward compatibility breaks + +* Classes that were previously `@final` were made `final` +* Parameter `$callableParameters` of [`MutatingScope::enterAnonymousFunction()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.MutatingScope.html#_enterAnonymousFunction) and [`enterArrowFunction()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.MutatingScope.html#_enterArrowFunction) made required +* Parameter `StatementContext $context` of [`NodeScopeResolver::processStmtNodes()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.NodeScopeResolver.html#_processStmtNodes) made required +* ClassPropertiesNode - remove `$extensions` parameter from [`getUninitializedProperties()`](https://apiref.phpstan.org/2.0.x/PHPStan.Node.ClassPropertiesNode.html#_getUninitializedProperties) +* `Type::getSmallerType()`, `Type::getSmallerOrEqualType()`, `Type::getGreaterType()`, `Type::getGreaterOrEqualType()`, `Type::isSmallerThan()`, `Type::isSmallerThanOrEqual()` now require [`PhpVersion`](https://apiref.phpstan.org/2.0.x/PHPStan.Php.PhpVersion.html) as argument. +* `CompoundType::isGreaterThan()`, `CompoundType::isGreaterThanOrEqual()` now require [`PhpVersion`](https://apiref.phpstan.org/2.0.x/PHPStan.Php.PhpVersion.html) as argument. +* Removed `ReflectionProvider::supportsAnonymousClasses()` (all reflection providers support anonymous classes) +* Remove `ArrayType::generalizeKeys()` +* Remove `ArrayType::count()`, use `Type::getArraySize()` instead +* Remove `ArrayType::castToArrayKeyType()`, `Type::toArrayKey()` instead +* Remove `UnionType::pickTypes()`, use `pickFromTypes()` instead +* Remove `RegexArrayShapeMatcher::matchType()`, use `matchExpr()` instead +* Remove unused `PHPStanTestCase::$useStaticReflectionProvider` +* Remove `PHPStanTestCase::getReflectors()`, use `getReflector()` instead +* Remove `ClassReflection::getFileNameWithPhpDocs()`, use `getFileName()` instead +* Remove `AnalysisResult::getInternalErrors()`, use `getInternalErrorObjects()` instead +* Remove `ConstantReflection::getValue()`, use `getValueExpr()` instead. To get `Type` from `Expr`, use `Scope::getType()` or `InitializerExprTypeResolver::getType()` +* Remove `PropertyTag::getType()`, use `getReadableType()` / `getWritableType()` instead +* Remove `GenericTypeVariableResolver`, use [`Type::getTemplateType()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getTemplateType) instead +* Rename `Type::isClassStringType()` to `Type::isClassString()` +* Remove `Scope::isSpecified()`, use `hasExpressionType()` instead +* Remove `ConstantArrayType::isEmpty()`, use `isIterableAtLeastOnce()->no()` instead +* Remove `ConstantArrayType::getNextAutoIndex()` +* Removed methods from `ConstantArrayType` - `getFirst*Type` and `getLast*Type` + * Use `getFirstIterable*Type` and `getLastIterable*Type` instead +* Remove `ConstantArrayType::generalizeToArray()` +* Remove `ConstantArrayType::findTypeAndMethodName()`, use `findTypeAndMethodNames()` instead +* Remove `ConstantArrayType::removeLast()`, use [`Type::popArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_popArray) instead +* Remove `ConstantArrayType::removeFirst()`, use [`Type::shiftArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_shiftArray) instead +* Remove `ConstantArrayType::reverse()`, use [`Type::reverseArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_reverseArray) instead +* Remove `ConstantArrayType::chunk()`, use [`Type::chunkArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_chunkArray) instead +* Remove `ConstantArrayType::slice()`, use [`Type::sliceArray()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_sliceArray) instead +* Made `TypeUtils` thinner by removing methods: + * Remove `TypeUtils::getArrays()` and `getAnyArrays()`, use [`Type::getArrays()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getArrays) instead + * Remove `TypeUtils::getConstantArrays()` and `getOldConstantArrays()`, use [`Type::getConstantArrays()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantArrays) instead + * Remove `TypeUtils::getConstantStrings()`, use [`Type::getConstantStrings()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantStrings) instead + * Remove `TypeUtils::getConstantTypes()` and `getAnyConstantTypes()`, use [`Type::isConstantValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantValue) or [`Type::generalize()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_generalize) + * Remove `TypeUtils::generalizeType()`, use [`Type::generalize()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_generalize) instead + * Remove `TypeUtils::getDirectClassNames()`, use [`Type::getObjectClassNames()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getObjectClassNames) instead + * Remove `TypeUtils::getConstantScalars()`, use [`Type::isConstantScalarValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantScalarValue) or [`Type::getConstantScalarTypes()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantScalarTypes) instead + * Remove `TypeUtils::getEnumCaseObjects()`, use [`Type::getEnumCases()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getEnumCases) instead + * Remove `TypeUtils::containsCallable()`, use [`Type::isCallable()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isCallable) instead +* Removed `Scope::doNotTreatPhpDocTypesAsCertain()`, use `getNativeType()` instead +* Parameter `$isList` in `ConstantArrayType` constructor can only be `TrinaryLogic`, no longer `bool` +* Parameter `$nextAutoIndexes` in `ConstantArrayType` constructor can only be `non-empty-list<int>`, no longer `int` +* Remove `ConstantType` interface, use [`Type::isConstantValue()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantValue) instead +* `acceptsNamedArguments()` in `FunctionReflection`, `ExtendedMethodReflection` and `CallableParametersAcceptor` interfaces returns `TrinaryLogic` instead of `bool` +* Remove `FunctionReflection::isFinal()` +* [`Type::getProperty()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getProperty) now returns [`ExtendedPropertyReflection`](https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ExtendedPropertyReflection.html) +* Remove `__set_state()` on objects that should not be serialized in cache +* Parameter `$selfClass` of [`TypehintHelper::decideTypeFromReflection()`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.TypehintHelper.html#_decideTypeFromReflection) no longer accepts `string` +* `LevelsTestCase::dataTopics()` data provider made static +* `PHPStan\Node\Printer\Printer` no longer autowired as `PhpParser\PrettyPrinter\Standard`, use `PHPStan\Node\Printer\Printer` in the typehint +* Remove `Type::acceptsWithReason()`, `Type:accepts()` return type changed from `TrinaryLogic` to [`AcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +* Remove `CompoundType::isAcceptedWithReasonBy()`, `CompoundType::isAcceptedBy()` return type changed from `TrinaryLogic` to [`AcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +Remove `Type::isSuperTypeOfWithReason()`, `Type:isSuperTypeOf()` return type changed from `TrinaryLogic` to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* Remove `CompoundType::isSubTypeOfWithReasonBy()`, `CompoundType::isSubTypeOf()` return type changed from `TrinaryLogic` to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* Remove `TemplateType::isValidVarianceWithReason()`, changed `TemplateType::isValidVariance()` return type to [`IsSuperTypeOfResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html) +* `RuleLevelHelper::accepts()` return type changed from `bool` to [`RuleLevelHelperAcceptsResult`](https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html) +* Changes around `ClassConstantReflection` + * Class `ClassConstantReflection` removed from BC promise, renamed to `RealClassConstantReflection` + * Interface `ConstantReflection` renamed to `ClassConstantReflection` + * Added more methods around PHPDoc types and native types to the (new) `ClassConstantReflection` + * Interface `GlobalConstantReflection` renamed to `ConstantReflection` +* Renamed interfaces and classes from `*WithPhpDocs` to `Extended*` + * `ParametersAcceptorWithPhpDocs` -> `ExtendedParametersAcceptor` + * `ParameterReflectionWithPhpDocs` -> `ExtendedParameterReflection` + * `FunctionVariantWithPhpDocs` -> `ExtendedFunctionVariant` +* `ClassPropertyNode::getNativeType()` return type changed from AST node to `Type|null` +* Class `PHPStan\Node\ClassMethod` (accessible from `ClassMethodsNode`) is no longer an AST node + * Call `PHPStan\Node\ClassMethod::getNode()` to access the original AST node diff --git a/vendor/phpstan/phpstan/bootstrap.php b/vendor/phpstan/phpstan/bootstrap.php index 204221a3a..a5d341bfd 100644 --- a/vendor/phpstan/phpstan/bootstrap.php +++ b/vendor/phpstan/phpstan/bootstrap.php @@ -3,12 +3,17 @@ namespace PHPStan; use Composer\Autoload\ClassLoader; +use function class_exists; +use const PHP_VERSION_ID; final class PharAutoloader { /** @var ClassLoader */ private static $composerAutoloader; + /** @var bool */ + private static $polyfillsLoaded = false; + final public static function loadClass(string $class): void { if (!extension_loaded('phar') || defined('__PHPSTAN_RUNNING__')) { return; @@ -23,10 +28,7 @@ final class PharAutoloader self::$composerAutoloader = require 'phar://' . __DIR__ . '/phpstan.phar/vendor/autoload.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/jetbrains/phpstorm-stubs/PhpStormStubsMap.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/async/src/functions_include.php'; - require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-stream/src/functions_include.php'; - require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-timer/src/functions_include.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise/src/functions_include.php'; - require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/ringcentral/psr7/src/functions_include.php'; } self::$composerAutoloader->loadClass($class); @@ -40,6 +42,58 @@ final class PharAutoloader throw new \Exception('Phar wrapper is not registered. Please review your php.ini settings.'); } + if (!self::$polyfillsLoaded) { + self::$polyfillsLoaded = true; + + if ( + PHP_VERSION_ID < 80000 + && empty($GLOBALS['__composer_autoload_files']['a4a119a56e50fbb293281d9a48007e0e']) + && !class_exists(\Symfony\Polyfill\Php80\Php80::class, false) + ) { + $GLOBALS['__composer_autoload_files']['a4a119a56e50fbb293281d9a48007e0e'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php80/Php80.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php80/bootstrap.php'; + } + + if ( + empty($GLOBALS['__composer_autoload_files']['0e6d7bf4a5811bfa5cf40c5ccd6fae6a']) + && !class_exists(\Symfony\Polyfill\Mbstring\Mbstring::class, false) + ) { + $GLOBALS['__composer_autoload_files']['0e6d7bf4a5811bfa5cf40c5ccd6fae6a'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-mbstring/Mbstring.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-mbstring/bootstrap.php'; + } + + if ( + empty($GLOBALS['__composer_autoload_files']['e69f7f6ee287b969198c3c9d6777bd38']) + && !class_exists(\Symfony\Polyfill\Intl\Normalizer\Normalizer::class, false) + ) { + $GLOBALS['__composer_autoload_files']['e69f7f6ee287b969198c3c9d6777bd38'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-normalizer/Normalizer.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-normalizer/bootstrap.php'; + } + + if ( + !extension_loaded('intl') + && empty($GLOBALS['__composer_autoload_files']['8825ede83f2f289127722d4e842cf7e8']) + && !class_exists(\Symfony\Polyfill\Intl\Grapheme\Grapheme::class, false) + ) { + $GLOBALS['__composer_autoload_files']['8825ede83f2f289127722d4e842cf7e8'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-grapheme/Grapheme.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-intl-grapheme/bootstrap.php'; + } + + if ( + PHP_VERSION_ID < 80100 + && empty ($GLOBALS['__composer_autoload_files']['23c18046f52bef3eea034657bafda50f']) + && !class_exists(\Symfony\Polyfill\Php81\Php81::class, false) + ) { + $GLOBALS['__composer_autoload_files']['23c18046f52bef3eea034657bafda50f'] = true; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/Php81.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/bootstrap.php'; + } + } + $filename = str_replace('\\', DIRECTORY_SEPARATOR, $class); if (strpos($class, 'PHPStan\\BetterReflection\\') === 0) { $filename = substr($filename, strlen('PHPStan\\BetterReflection\\')); diff --git a/vendor/phpstan/phpstan/composer.json b/vendor/phpstan/phpstan/composer.json index 55182f6cf..dc62c19ce 100644 --- a/vendor/phpstan/phpstan/composer.json +++ b/vendor/phpstan/phpstan/composer.json @@ -4,7 +4,7 @@ "license": ["MIT"], "keywords": ["dev", "static analysis"], "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -15,5 +15,12 @@ ], "autoload": { "files": ["bootstrap.php"] + }, + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "forum": "https://github.com/phpstan/phpstan/discussions", + "source": "https://github.com/phpstan/phpstan-src", + "docs": "https://phpstan.org/user-guide/getting-started", + "security": "https://github.com/phpstan/phpstan/security/policy" } } diff --git a/vendor/phpstan/phpstan/phpstan.phar b/vendor/phpstan/phpstan/phpstan.phar Binary files differindex 210214f8b..1bfd1e5cb 100755 --- a/vendor/phpstan/phpstan/phpstan.phar +++ b/vendor/phpstan/phpstan/phpstan.phar diff --git a/vendor/phpstan/phpstan/phpstan.phar.asc b/vendor/phpstan/phpstan/phpstan.phar.asc index 7c45832eb..1f97d1db1 100644 --- a/vendor/phpstan/phpstan/phpstan.phar.asc +++ b/vendor/phpstan/phpstan/phpstan.phar.asc @@ -1,16 +1,16 @@ -----BEGIN PGP SIGNATURE----- -iQIzBAABCgAdFiEE0yaA1ZV9xxFr4pwUzxoQjQ565yAFAmP6H1oACgkQzxoQjQ56 -5yCVhxAAlXqhfvKCITSAJjkaIhncEBHpT5Ogby65BX1O5+SkKyc/OO7JnjlN8cLB -DKbJpdc8P2Dbfo/uBHVR5YiNX5CWYzgdMzCpLQI20Ffa7qkeRbA5ZS2AfdIfLqhD -PswlQIYxg0F3lj0L+joTxfBiHgmR5wnOUx1sLXnEbqGtFzzGstPGDg9gjKKh5EnO -g207jrm1we05+k3kKzr233/ENvQD6bBWLr2yngyMoirOuDpurAuPjkBL7vN6PDbx -DlyGDazsgU/R7aE2FtqmFC8KIU/BgnpbCSWTHBEJAqtncHbwTgneyEm+88lT6cUt -YM72Rh1+QTvS3WgJ/q7AQRik2aE83Kz2rc3XOl1ADd45J3Oan1rqQXdnKtH6qZeT -wMk6PE8SQPJ8NMHy3LT048PfMOakAtgBL5xonbVqX0IFAvKAMRijwBCNKat2yRsB -0VGnoeH7klf1fSSo0xOD20Pd0uIILAWZlBjb2e/1NRytFQkokAr1bsLD/VJHiNYh -lPQ4OztCT/uumJOAgTKm10T56CK1u0VxhIVtQ+/NkTvAwrmpX0KKqbYPP/1hZqVO -vFaWJR4iLFNt+il7dlv56jmTAneUrGrWkQdVFbz5FVpqz7abIo+/cevbSSndFZCI -K1mn42aFdYXSqVTFpkzeOQeJPPysEYDysFk7ClhE+F48HN7WAtw= -=A7BG +iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmcyJeMACgkQUcZzBf/C +5cCV0A//Sg3/cyjMFpusDfq8uk57ADL2jerPD4oPRG9/smKWf14pAsWwk7PQOcQe +zb2f+YW1tEouiyc/+RQoc+Xkv2om2N7RwaH4C7/dNnV6HYEsQS3U5cP4le/d32U6 +0wtu0Se6FieFEc5Z/wEZzbGZCa3xToIFXIC8awNvZO6p5JGB29dWeIZgbmpHHN/6 +yIxBGom0PYIf5My+J9nTlM0hYICKKGzU4NdCQzFVdenA/YCdXwr8lvec1ZwKcb2z +Y6sJWaBvYIGPg4ej5oVuEm/vUx4NXnKpmn5Up+wOXRjzoS6ZcduO9gxCwgJ8Uh1r +MzdTbeopFh/ZIw7HgoG32BS1yZWjBFzW5MUwCzugil9Q6wfkc5hCWFG0DvKbhZK3 +6rCPqfJQ//8C+870Ej+rX6nndn93KHp696dRjiSDFVnB3kFOUcHhFYdaSzdaWZ2x +E26PwyI4NBIynS153EIo2S1poIAWsAXQTWaFyRqrUq+QkeioxdKJr7UVtmZJcqH3 +v5lytzJwYvmYk57CjCoJDWwY6y1b7YN1zkjzlNozlcvbF4ftzIIXZW5CcOeg1Dij +jZEOhVnEygbCvYcXCn3RSC5OZe0wz/B5EuT4HnLpeyyE+tLg2evqfJXqwagwRZoL +EiMpKZlYY2kEdgezzefYH1ElCvmjaa5BRTlXYswVW/G7KKJ6fq8= +=lVJA -----END PGP SIGNATURE----- |