diff --git a/src/Cli/Commands/StartupCommand.php b/src/Cli/Commands/StartupCommand.php index d62bddbf..3c832e3c 100644 --- a/src/Cli/Commands/StartupCommand.php +++ b/src/Cli/Commands/StartupCommand.php @@ -78,8 +78,8 @@ class StartupCommand implements CommandInterface { // Core scripts/binaries lose their executable bit on a mode-dropping // deploy, and a missing php-fpm pool config leaves that worker down. - self::ensureExecutableScripts(['service', 'update', 'bin/redis/redis-server', 'bin/daemons.sh'], MAIN_HOME); - self::ensurePhpFpmPoolConfigs(MAIN_HOME); + $this->ensureExecutableScripts(['service', 'update', 'bin/redis/redis-server', 'bin/daemons.sh'], MAIN_HOME); + $this->ensurePhpFpmPoolConfigs(MAIN_HOME); // ── Установка crontab и запуск кэша ────────────────── if (posix_getpwuid(posix_geteuid())['name'] == 'root') { @@ -105,7 +105,7 @@ class StartupCommand implements CommandInterface { * * @param list $rScripts Paths relative to $rBase. */ - private static function ensureExecutableScripts(array $rScripts, string $rBase): void { + private function ensureExecutableScripts(array $rScripts, string $rBase): void { foreach ($rScripts as $rScript) { $rPath = $rBase . $rScript; if (file_exists($rPath) && !is_executable($rPath)) { @@ -118,7 +118,7 @@ class StartupCommand implements CommandInterface { * Regenerate any missing php-fpm pool config (1..4.conf) from the template, * so a worker whose config was lost comes back on the next boot. */ - private static function ensurePhpFpmPoolConfigs(string $rBase): void { + private function ensurePhpFpmPoolConfigs(string $rBase): void { $rTemplatePath = $rBase . 'bin/php/etc/template'; if (!file_exists($rTemplatePath)) { return; diff --git a/src/Cli/CronJobs/StreamsCronJob.php b/src/Cli/CronJobs/StreamsCronJob.php index 82b32f2d..07ceb98a 100644 --- a/src/Cli/CronJobs/StreamsCronJob.php +++ b/src/Cli/CronJobs/StreamsCronJob.php @@ -392,10 +392,7 @@ class StreamsCronJob implements CommandInterface { $db->query('UPDATE `streams_servers` SET `bitrate` = ?, `stream_info` = ?, `audio_codec` = ?, `video_codec` = ?, `resolution` = ?, `compatible` = ? WHERE `stream_id` = ? AND `server_id` = ?', $rBitrate, json_encode($rFFProbeOutput), $rAudioCodec, $rVideoCodec, $rResolution, $rCompatible, $rStream['id'], SERVER_ID); } - $rUUIDs = self::connectionUuidsForStream( - ConnectionTracker::getConnections(SERVER_ID, null, $rStream['id']), - $rStream['id'] - ); + $rUUIDs = $this->connectionUuidsForStream(ConnectionTracker::getConnections(SERVER_ID, null, $rStream['id']), $rStream['id']); $rConDir = CONS_TMP_PATH . $rStream['id'] . '/'; // The per-stream connection dir only exists once a client connects, @@ -459,7 +456,7 @@ class StreamsCronJob implements CommandInterface { * @param mixed $rStreamID Stream id to keep (rows without one are kept). * @return list */ - private static function connectionUuidsForStream($rConnections, $rStreamID): array { + private function connectionUuidsForStream($rConnections, $rStreamID): array { $rUUIDs = []; foreach (self::collectUuidRows($rConnections) as $rRow) { if (empty($rRow['stream_id']) || $rRow['stream_id'] == $rStreamID) { diff --git a/src/Core/Localization/Translator.php b/src/Core/Localization/Translator.php index 9c12ee0d..3e40463b 100644 --- a/src/Core/Localization/Translator.php +++ b/src/Core/Localization/Translator.php @@ -101,7 +101,7 @@ class Translator { */ public static function isRtl(?string $lang = null): bool { $lang ??= self::$currentLang; - return in_array(strtolower((string) $lang), ['ar', 'fa', 'ur', 'he'], true); + return in_array(strtolower($lang), ['ar', 'fa', 'ur', 'he'], true); } /** diff --git a/src/Infrastructure/Bootstrap/ResellerScopeBootstrap.php b/src/Infrastructure/Bootstrap/ResellerScopeBootstrap.php index 05a44d85..eddda864 100644 --- a/src/Infrastructure/Bootstrap/ResellerScopeBootstrap.php +++ b/src/Infrastructure/Bootstrap/ResellerScopeBootstrap.php @@ -46,8 +46,7 @@ final class ResellerScopeBootstrap implements ScopeBootstrap { } // Expire session after timeout - if ( - isset($_SESSION['reseller'], $_SESSION['rlast_activity']) + if (isset($_SESSION['reseller'], $_SESSION['rlast_activity']) && ($rSessionTimeout * 60) < (time() - $_SESSION['rlast_activity']) ) { foreach (['reseller', 'rip', 'rcode', 'rverify', 'rlast_activity'] as $rKey) { diff --git a/src/Public/Controllers/Admin/ActiveCodeDetailsController.php b/src/Public/Controllers/Admin/ActiveCodeDetailsController.php index edbea478..bc409045 100644 --- a/src/Public/Controllers/Admin/ActiveCodeDetailsController.php +++ b/src/Public/Controllers/Admin/ActiveCodeDetailsController.php @@ -20,7 +20,6 @@ use XcVm\Domain\Line\PackageService; class ActiveCodeDetailsController { use \XcVm\Infrastructure\Database\DatabaseAware; - /** * action=active_code_details — full voucher & companion line details. */ diff --git a/src/Public/Controllers/Admin/TableController.php b/src/Public/Controllers/Admin/TableController.php index fc966e99..31b77b85 100644 --- a/src/Public/Controllers/Admin/TableController.php +++ b/src/Public/Controllers/Admin/TableController.php @@ -4834,20 +4834,6 @@ class TableController extends BaseAdminController { exit; } - /** - * A database value made safe for HTML text or a quoted attribute. Rows arrive - * with only < and > entity-encoded (Database::clean_row), so they are decoded - * first and encoded once, quotes included. - */ - private static function htmlValue(mixed $rValue): string { - return htmlspecialchars(html_entity_decode((string) $rValue, ENT_QUOTES), ENT_QUOTES); - } - - /** A database value as a JavaScript string literal, for an event-handler attribute. */ - private static function jsArgument(mixed $rValue): string { - return htmlspecialchars((string) json_encode(html_entity_decode((string) $rValue, ENT_QUOTES), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), ENT_QUOTES); - } - private function handleParentServers($rReturn, $rStart, $rLimit, $rIsAPI) { global $db, $rServers; if (!Authorization::check("adv", "servers")) { diff --git a/src/Public/Controllers/Api/PlayerApiController.php b/src/Public/Controllers/Api/PlayerApiController.php index 52932cbf..d7453eec 100644 --- a/src/Public/Controllers/Api/PlayerApiController.php +++ b/src/Public/Controllers/Api/PlayerApiController.php @@ -302,7 +302,7 @@ class PlayerApiController { [$rStreamIDs, $rMulti] = $this->requestedStreamIds(); - if (!empty($rStreamIDs) && (is_null($this->userInfo['exp_date']) || time() < $this->userInfo['exp_date'])) { + if ($rStreamIDs !== [] && (is_null($this->userInfo['exp_date']) || time() < $this->userInfo['exp_date'])) { $rFromNow = !empty($rRequest['from_now']) && 0 < $rRequest['from_now']; $rEPGs = []; @@ -1063,6 +1063,6 @@ class PlayerApiController { } } - return !empty($rReturn) ? $rReturn : ['m3u8', 'ts', 'rtmp']; + return $rReturn !== [] ? $rReturn : ['m3u8', 'ts', 'rtmp']; } } diff --git a/src/Public/Controllers/PlayerV2/HomeController.php b/src/Public/Controllers/PlayerV2/HomeController.php index 0729faa3..1339ec0f 100644 --- a/src/Public/Controllers/PlayerV2/HomeController.php +++ b/src/Public/Controllers/PlayerV2/HomeController.php @@ -185,7 +185,7 @@ class HomeController extends BasePlayerV2Controller { } // 3. Fallback to direct catalog items if popular file has no matches - if (empty($rPopularNow)) { + if ($rPopularNow === []) { // Pick up to 5 movies if ($vodIdsSafe !== []) { $db->query('SELECT `id`, `stream_display_name`, `year`, `rating`, `movie_properties` FROM `streams` WHERE `id` IN (' . implode(',', array_slice($vodIdsSafe, 0, 10)) . ') LIMIT 5;'); @@ -239,7 +239,7 @@ class HomeController extends BasePlayerV2Controller { $heroSlides[] = $item; } } - if (empty($heroSlides) && !empty($rPopularNow)) { + if ($heroSlides === [] && $rPopularNow !== []) { $heroSlides = $rPopularNow; } $heroSlides = array_slice($heroSlides, 0, 5); diff --git a/src/Public/Controllers/PlayerV2/SeriesController.php b/src/Public/Controllers/PlayerV2/SeriesController.php index 1b59a6bc..6a171f15 100644 --- a/src/Public/Controllers/PlayerV2/SeriesController.php +++ b/src/Public/Controllers/PlayerV2/SeriesController.php @@ -499,7 +499,7 @@ class SeriesController extends BasePlayerV2Controller { } // Fallback seasons from episodes map if seasons table was empty - if (empty($seasons) && !empty($episodesMap)) { + if ($seasons === [] && $episodesMap !== []) { $seasonKeys = array_keys($episodesMap); sort($seasonKeys, SORT_NUMERIC); foreach ($seasonKeys as $sNum) { diff --git a/src/Streaming/Fanout/FanoutConfig.php b/src/Streaming/Fanout/FanoutConfig.php index 3941d28f..2e43391b 100644 --- a/src/Streaming/Fanout/FanoutConfig.php +++ b/src/Streaming/Fanout/FanoutConfig.php @@ -170,7 +170,7 @@ class FanoutConfig { */ private static function debugCats(string $rValue): string { $rValue = strtolower(trim($rValue)); - if ($rValue === '' || $rValue === 'off' || $rValue === '0' || $rValue === 'false' || $rValue === 'no') { + if (in_array($rValue, ['', 'off', '0', 'false', 'no'], true)) { return ''; } if (in_array($rValue, ['all', '1', 'true', 'yes', 'on'], true)) { diff --git a/tests/Unit/StartupCommandRuntimeFilesTest.php b/tests/Unit/StartupCommandRuntimeFilesTest.php index 0ebb4518..5df20493 100644 --- a/tests/Unit/StartupCommandRuntimeFilesTest.php +++ b/tests/Unit/StartupCommandRuntimeFilesTest.php @@ -12,10 +12,15 @@ final class StartupCommandRuntimeFilesTest extends TestCase { private string $dir; + /** + * Call a private helper. Whether it is static is an implementation detail + * (Rector's LocallyCalledStaticMethodToNonStatic flips it), so bind an + * instance only when the method actually needs one. + */ private function call(string $rMethod, ...$rArgs) { $rM = new ReflectionMethod(StartupCommand::class, $rMethod); $rM->setAccessible(true); - return $rM->invoke(null, ...$rArgs); + return $rM->invoke($rM->isStatic() ? null : new StartupCommand(), ...$rArgs); } protected function setUp(): void { diff --git a/tests/Unit/StreamsCronConnectionsTest.php b/tests/Unit/StreamsCronConnectionsTest.php index b1cd6071..54723f13 100644 --- a/tests/Unit/StreamsCronConnectionsTest.php +++ b/tests/Unit/StreamsCronConnectionsTest.php @@ -11,16 +11,23 @@ use XcVm\Cli\CronJobs\StreamsCronJob; */ final class StreamsCronConnectionsTest extends TestCase { - private function uuids($rConnections, $rStreamID): array { - $rM = new ReflectionMethod(StreamsCronJob::class, 'connectionUuidsForStream'); + /** + * Call a private helper. Whether it is static is an implementation detail + * (Rector's LocallyCalledStaticMethodToNonStatic flips it), so bind an + * instance only when the method actually needs one. + */ + private function call(string $rMethod, ...$rArgs) { + $rM = new ReflectionMethod(StreamsCronJob::class, $rMethod); $rM->setAccessible(true); - return $rM->invoke(null, $rConnections, $rStreamID); + return $rM->invoke($rM->isStatic() ? null : new StreamsCronJob(), ...$rArgs); + } + + private function uuids($rConnections, $rStreamID): array { + return $this->call('connectionUuidsForStream', $rConnections, $rStreamID); } private function collect($rNode): array { - $rM = new ReflectionMethod(StreamsCronJob::class, 'collectUuidRows'); - $rM->setAccessible(true); - return $rM->invoke(null, $rNode); + return $this->call('collectUuidRows', $rNode); } public function testRedisPairShapeFiltersByStream(): void {