Reviewed Rector batch (3 files) + a manual decomposition of massEdit on top.
Rector batch (reviewed against suite + PHPStan):
- FIX (recurring Rector bug): the boolean inversion again dropped the parens on
an assignment-in-condition in massEdit (array_search category DEL) — restored.
- massEdit: many correct empty-if/else -> guard inversions (verified).
- strlen(x) > 0 -> x !== '' in saveStreamOptions, TMDbService::buildUrl and
ResellerTableRenderer (behaviour-preserving).
massEdit decomposition (166 -> 96 lines, nesting ~10 -> 7), test-first:
- computeCategoryChange() — ADD union / DEL remove / SET replace (pure)
- planBouquetChanges() — SET/ADD/DEL bouquet attach/detach plan (pure)
- planServerTreeForStream()— per-stream streams_servers reconcile (update in
place / batch-insert buffer / delete marking)
- raiseTimeLimits() — the set_time_limit + ini_set block, shared with
massDelete (de-duplicated)
Tests: RadioServiceTest gains computeCategoryChange (5) + planBouquetChanges (3);
new RadioServiceMassEditTest characterizes massEdit end to end (invalid input,
and the server-tree ADD path) — it has no verifyPostTable/exit and
StreamProcess::updateStreams is a no-op with caching off, so the whole method
runs against the SQLite harness. That test guarded the server_tree extraction.
PHPStan level 5 clean; suite 721 tests / 0 errors.
process() was a 250-line, ~10-deep method riddled with the empty-if/else
anti-pattern — the exact shape Rector mis-transformed (which is why RadioService
was reverted from the Rector pass). Rebuild it safely, test-first:
Extracted 7 helpers, each covered by tests (RadioServiceTest, 18 tests):
- buildAutoRestart() — auto_restart schedule from days/time
- resolveSelectedIds() — created-name or numeric id (merged the duplicated
bouquet/category resolution)
- createMissingBouquets() — insert bouquets from bouquet_create_list
- createMissingCategories() — insert radio categories from category_create_list
- saveStreamOptions() — clear + re-insert streams_options (6 option types)
- syncServerTree() — reconcile streams_servers against the server tree
- syncBouquets() — attach to selected bouquets, detach on edit
Flattened the orchestrator: 250 -> 87 lines, nesting ~10 -> 4. Guard clauses for
validate / auth / no-source, ternaries for the flag fields, and the vestigial
single-element $rImportStreams loop removed (mutating $rArray directly is
equivalent to its per-iteration merge). exit() on auth failure is unchanged
(returning instead would be a semantic change). Behaviour preserved throughout.
RadioServiceProcessTest characterizes the outer guard flow (INVALID_INPUT, and
NO_SOURCES via the edit path) — the branches the flattening restructures. The
success path can't run under the SQLite harness (verifyPostTable hits MySQL
information_schema) so its internals are covered by the extracted-helper tests.
PHPStan level 5 clean; suite 711 tests / 0 errors (+20).