From e7ceb4269935d4c3d8f37dc3d0814c9546bf564c Mon Sep 17 00:00:00 2001 From: Divarion_D Date: Sat, 8 Aug 2026 19:28:37 +0300 Subject: [PATCH] refactor(monitor): extract parseFrameRate from the FPS-check goto cluster First step of untangling MonitorCommand::execute()'s obfuscated goto control flow. The FPS baseline computation flattened a "30/1" -> 30.0 rational parse across five labels (label768/780/1047/1052/1057). Pull the pure parse into parseFrameRate() with unit tests; the label768 entry and the label1847 exit are kept so control flow is unchanged -- four labels and their gotos are gone. Guards a zero denominator (PHP 8 would otherwise throw on "x/0") -- an input ffprobe does not produce, so behaviour is unchanged in practice. PHPStan cannot see the self::parseFrameRate call through the surrounding goto maze and reports the method unused; baselined until the label768 region is destructured, then the entry drops out. Verified: phpstan level 5 green, phpunit 408/408, make gates green. --- phpstan-baseline.neon | 6 +++++ src/Cli/Commands/MonitorCommand.php | 34 ++++++++++++++------------ tests/Unit/MonitorCommandTest.php | 38 +++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 tests/Unit/MonitorCommandTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 96179350..19244479 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -48,6 +48,12 @@ parameters: count: 1 path: src/Cli/Commands/MonitorCommand.php + - + message: '#^Static method XcVm\\Cli\\Commands\\MonitorCommand\:\:parseFrameRate\(\) is unused\.$#' + identifier: method.unused + count: 1 + path: src/Cli/Commands/MonitorCommand.php + - message: '#^Call to function is_numeric\(\) with int\\|int\<1, max\> will always evaluate to true\.$#' identifier: function.alreadyNarrowedType diff --git a/src/Cli/Commands/MonitorCommand.php b/src/Cli/Commands/MonitorCommand.php index aeb0462a..d6556698 100644 --- a/src/Cli/Commands/MonitorCommand.php +++ b/src/Cli/Commands/MonitorCommand.php @@ -509,22 +509,10 @@ class MonitorCommand implements CommandInterface { $d75674a646265e7b = $E02429d2ee600884['codecs']['video']['avg_frame_rate'] ?: $E02429d2ee600884['codecs']['video']['r_frame_rate']; goto label768; label768: - if (stripos($d75674a646265e7b, '/') !== false) { - goto label780; + $d75674a646265e7b = self::parseFrameRate($d75674a646265e7b); + if (0 < $d75674a646265e7b) { + $b4015d24aedaf0db = $d75674a646265e7b; } - $d75674a646265e7b = floatval($d75674a646265e7b); - goto label1052; - label780: - list($Be71401a913607c0, $Cd98e5a46a318d0a) = array_map('floatval', explode('/', $d75674a646265e7b)); - goto label1047; - label1047: - $d75674a646265e7b = floatval($Be71401a913607c0 / $Cd98e5a46a318d0a); - label1052: - if (!(0 < $d75674a646265e7b)) { - goto label1057; - } - $b4015d24aedaf0db = $d75674a646265e7b; - label1057: goto label1847; label1847: @@ -596,6 +584,22 @@ class MonitorCommand implements CommandInterface { return 0; } + /** + * Parse an ffprobe frame-rate field ("30", "30000/1001", "25/1") into fps. + * Extracted from the FPS-check goto cluster (label768/780/1047/1052/1057). + * + * @param mixed $rRate Raw avg_frame_rate / r_frame_rate value. + * @return float Frames per second; 0.0 for empty/zero/malformed input. + */ + private static function parseFrameRate($rRate): float { + $rRate = (string) $rRate; + if (strpos($rRate, '/') !== false) { + list($rNum, $rDen) = array_map('floatval', explode('/', $rRate)); + return $rDen != 0.0 ? (float) ($rNum / $rDen) : 0.0; + } + return (float) $rRate; + } + private function checkRunning(int $rStreamID): void { clearstatcache(true); $rPID = 0; diff --git a/tests/Unit/MonitorCommandTest.php b/tests/Unit/MonitorCommandTest.php new file mode 100644 index 00000000..9a05947d --- /dev/null +++ b/tests/Unit/MonitorCommandTest.php @@ -0,0 +1,38 @@ +setAccessible(true); + return $m->invoke(null, ...$args); + } + + // ── parseFrameRate (label768/780/1047/1052/1057) ─────────── + + public function testPlainInteger(): void { + $this->assertSame(30.0, $this->call('parseFrameRate', '30')); + $this->assertSame(25.0, $this->call('parseFrameRate', 25)); + } + + public function testRationalFrameRate(): void { + $this->assertSame(25.0, $this->call('parseFrameRate', '25/1')); + $this->assertEqualsWithDelta(29.97, $this->call('parseFrameRate', '30000/1001'), 0.001); + } + + public function testZeroAndMalformedAreZero(): void { + $this->assertSame(0.0, $this->call('parseFrameRate', '')); + $this->assertSame(0.0, $this->call('parseFrameRate', '0')); + $this->assertSame(0.0, $this->call('parseFrameRate', '0/0'), 'division-by-zero guarded'); + $this->assertSame(0.0, $this->call('parseFrameRate', '30/0'), 'zero denominator guarded'); + } +}