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).