From b51328150259e7997e2aef44ee2f42e6ef7b013c Mon Sep 17 00:00:00 2001 From: Divarion-D Date: Fri, 3 Jul 2026 20:34:44 +0300 Subject: [PATCH] fix(redis): handle Redis connection errors and validate connection on creation --- phpstan-baseline.neon | 76 ++++++++--------------- phpstan.dist.neon | 7 ++- src/Cli/DaemonTrait.php | 9 ++- src/Infrastructure/Redis/RedisManager.php | 3 + 4 files changed, 42 insertions(+), 53 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 63456d5c..41de6541 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -492,24 +492,12 @@ parameters: count: 1 path: src/Core/Util/SystemInfo.php - - - message: '#^Offset ''fb_bouquets'' does not exist on array\{bouquets\: mixed\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Domain/Bouquet/BouquetService.php - - message: '#^Offset ''id'' does not exist on array\{bouquet\: mixed\}\.$#' identifier: offsetAccess.notFound count: 2 path: src/Domain/Bouquet/BouquetService.php - - - message: '#^Offset ''id'' does not exist on array\{bouquets\: mixed, fb_bouquets\: mixed\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Domain/Bouquet/BouquetService.php - - message: '#^Offset ''id'' does not exist on array\{bouquets\: mixed\}\.$#' identifier: offsetAccess.notFound @@ -1176,18 +1164,6 @@ parameters: count: 2 path: src/Infrastructure/ResellerApiDispatcher.php - - - message: '#^Offset ''isp'' does not exist on array\{type\: null\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Infrastructure/ResellerApiDispatcher.php - - - - message: '#^Offset ''isp'' on array\{type\: null\} in empty\(\) does not exist\.$#' - identifier: empty.offset - count: 1 - path: src/Infrastructure/ResellerApiDispatcher.php - - message: '#^Ternary operator condition is always true\.$#' identifier: ternary.alwaysTrue @@ -1222,154 +1198,154 @@ parameters: message: '#^Parameter \#1 \$prefix of function uniqid expects string, int\<0, max\> given\.$#' identifier: argument.type count: 1 - path: src/Modules/ministra/PortalHandler.php + path: src/Modules/ministra_85a7d/PortalHandler.php - message: '#^Variable \$rEPG might not be defined\.$#' identifier: variable.undefined count: 1 - path: src/Modules/ministra/PortalHandler.php + path: src/Modules/ministra_85a7d/PortalHandler.php - message: '#^Parameter \#4 \$month of function mktime expects int\|null, string given\.$#' identifier: argument.type count: 5 - path: src/Modules/ministra/PortalHelpers.php + path: src/Modules/ministra_85a7d/PortalHelpers.php - message: '#^Parameter \#5 \$day of function mktime expects int\|null, string given\.$#' identifier: argument.type count: 2 - path: src/Modules/ministra/PortalHelpers.php + path: src/Modules/ministra_85a7d/PortalHelpers.php - message: '#^Parameter \#6 \$year of function mktime expects int\|null, string given\.$#' identifier: argument.type count: 5 - path: src/Modules/ministra/PortalHelpers.php + path: src/Modules/ministra_85a7d/PortalHelpers.php - message: '#^Comparison operation "\>" between int\<1, max\> and 0 is always true\.$#' identifier: greater.alwaysTrue count: 1 - path: src/Modules/plex/PlexCron.php + path: src/Modules/plex_20cd9/PlexCron.php - message: '#^Method XcVm\\Module\\Plex\\PlexCron\:\:getBouquet\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 1 - path: src/Modules/plex/PlexCron.php + path: src/Modules/plex_20cd9/PlexCron.php - message: '#^Negated boolean expression is always false\.$#' identifier: booleanNot.alwaysFalse count: 1 - path: src/Modules/plex/PlexCron.php + path: src/Modules/plex_20cd9/PlexCron.php - message: '#^If condition is always true\.$#' identifier: if.alwaysTrue count: 1 - path: src/Modules/plex/PlexItem.php + path: src/Modules/plex_20cd9/PlexItem.php - message: '#^Method XcVm\\Module\\Plex\\PlexItem\:\:getEpisode\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 2 - path: src/Modules/plex/PlexItem.php + path: src/Modules/plex_20cd9/PlexItem.php - message: '#^Method XcVm\\Module\\Plex\\PlexItem\:\:getMovie\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 1 - path: src/Modules/plex/PlexItem.php + path: src/Modules/plex_20cd9/PlexItem.php - message: '#^Method XcVm\\Module\\Plex\\PlexItem\:\:getSerie\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 1 - path: src/Modules/plex/PlexItem.php + path: src/Modules/plex_20cd9/PlexItem.php - message: '#^Method XcVm\\Module\\Plex\\PlexItem\:\:getSeriesByID\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 1 - path: src/Modules/plex/PlexItem.php + path: src/Modules/plex_20cd9/PlexItem.php - message: '#^Variable \$rBG might not be defined\.$#' identifier: variable.undefined count: 2 - path: src/Modules/plex/PlexItem.php + path: src/Modules/plex_20cd9/PlexItem.php - message: '#^Variable \$rThumb might not be defined\.$#' identifier: variable.undefined count: 3 - path: src/Modules/plex/PlexItem.php + path: src/Modules/plex_20cd9/PlexItem.php - message: '#^Method XcVm\\Module\\Watch\\WatchCron\:\:getBouquet\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 1 - path: src/Modules/watch/WatchCron.php + path: src/Modules/watch_2541a/WatchCron.php - message: '#^Variable \$rStreamDatabase might not be defined\.$#' identifier: variable.undefined count: 1 - path: src/Modules/watch/WatchCron.php + path: src/Modules/watch_2541a/WatchCron.php - message: '#^If condition is always true\.$#' identifier: if.alwaysTrue count: 1 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php - message: '#^Method XcVm\\Module\\Watch\\WatchItem\:\:getBouquet\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 1 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php - message: '#^Method XcVm\\Module\\Watch\\WatchItem\:\:getEpisode\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 2 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php - message: '#^Method XcVm\\Module\\Watch\\WatchItem\:\:getMovie\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 1 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php - message: '#^Method XcVm\\Module\\Watch\\WatchItem\:\:getSerie\(\) should return array\|null but return statement is missing\.$#' identifier: return.missing count: 1 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php - message: '#^Parameter \#2 \$data of function fwrite expects string, int\<1, max\> given\.$#' identifier: argument.type count: 1 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php - message: '#^Variable \$rImage might not be defined\.$#' identifier: variable.undefined count: 1 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php - message: '#^Variable \$rRelease might not be defined\.$#' identifier: variable.undefined count: 1 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php - message: '#^Variable \$rSeries might not be defined\.$#' identifier: variable.undefined count: 2 - path: src/Modules/watch/WatchItem.php + path: src/Modules/watch_2541a/WatchItem.php diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 1a1b71ad..fff78f1f 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -58,7 +58,8 @@ parameters: # Bundled third-party libraries (not our code): the php-tmdb client # and the Mobile-Detect library. Like the vendored packages above, # they are indexed for symbol resolution but not analysed. - - src/Modules/tmdb/lib/* + # (wildcard: module dirs carry install-hash suffixes, e.g. tmdb_f4e6e) + - src/Modules/tmdb*/lib/* treatPhpDocTypesAsCertain: false reportUnmatchedIgnoredErrors: false @@ -209,6 +210,10 @@ parameters: # Proprietary ioncube-encoded class, shipped only in licensed builds. # Every call site is guarded with class_exists('XC_VM'). - '#unknown class XC_VM\.#' + # Same class: method_exists('XC_VM', ...) probes for APIs that exist + # only in licensed builds; PHPStan treats the unknown class as absent + # and calls the guard impossible. + - '#method_exists\(\) with .XC_VM.#' # External vendor library (maxmind-db/reader), installed at runtime and # not part of the source tree. - '#MaxMind\\Db\\Reader#' diff --git a/src/Cli/DaemonTrait.php b/src/Cli/DaemonTrait.php index 570b3327..d905acce 100644 --- a/src/Cli/DaemonTrait.php +++ b/src/Cli/DaemonTrait.php @@ -180,8 +180,13 @@ trait DaemonTrait { if (!(SettingsManager::getAll()['redis_handler'] ?? false)) { return true; } - if (!RedisManager::instance() || !RedisManager::instance()->ping()) { - echo "Redis connection lost\n"; + try { + if (!RedisManager::instance() || !RedisManager::instance()->ping()) { + echo "Redis connection lost\n"; + return false; + } + } catch (\RedisException $e) { + echo 'Redis connection lost: ' . $e->getMessage() . "\n"; return false; } return true; diff --git a/src/Infrastructure/Redis/RedisManager.php b/src/Infrastructure/Redis/RedisManager.php index dc4c4e49..3be29514 100644 --- a/src/Infrastructure/Redis/RedisManager.php +++ b/src/Infrastructure/Redis/RedisManager.php @@ -121,6 +121,9 @@ class RedisManager { } $rRedis->setOption(\Redis::OPT_READ_TIMEOUT, 2.0); $rRedis->setOption(\Redis::OPT_TCP_KEEPALIVE, 60); + // Validate the fresh connection: surfaces NOAUTH/WRONGPASS here + // instead of on the first real command at a call site. + $rRedis->ping(); return $rRedis; } catch (\Exception $e) { return null;