# Bug fix — Issue #1735: `reqSpecSearch.php:116` `count()` on an uninitialised `$itemSet` — HTTP 500 / 0 bytes The legacy **Search → Requirement Specification Results** screen (`lib/requirements/reqSpecSearch.php`) answered **HTTP 500 with a 0-byte body** because of ``` Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in …/lib/requirements/reqSpecSearch.php:116 ``` `$gui->row_qty=count($itemSet);` sat **outside** the only block that assigns `$itemSet`, so any state that skips that block makes `count(null)` a PHP-8 `TypeError`. On 1.9.20 (PHP 5.x) the same expression was only a warning and still rendered an empty table. ## Resolution — already retired by #1825 (no new code) The controller that contained the defect was **deleted** when this screen was modernized: * `d1b2a884c` — *feat(#1825): retire both legacy req-spec search controllers + switch the link* (2026-10-04) turned `lib/requirements/reqSpecSearch.php` into a session-guarded redirect **shim**; * `7e859c3b9` — follow-up hardening. The screen is now the Dashio page [`Search Requirement Specifications`](Search-Requirement-Specifications.md) backed by `api/requirements/index.php` (`GET /reqspec-search`), whose counter is **initialised** (`:2176` `$count = 0;`) — `count(null)` cannot occur — and whose empty state is the explicit `else { $warning = 'no_records_found'; }` (`:2212-2214`), exactly the behaviour the issue's *Expected* section asked for. ![Search Requirement Specifications — modern replacement (issue #1735)](issue-1735-search-reqspec-modern.png) ![Search results render, no HTTP 500 (issue #1735)](issue-1735-search-reqspec-results.png) ## Measured evidence Current default branch (`bce8fb392`): ``` $ curl ... "$BASE/lib/requirements/reqSpecSearch.php" HTTP 302 size=0 redirect=…/gui/templates/requirements/searchReqSpec.html?tproject_id=1 # XHR flavour HTTP 405 size=170 {"status":"error","code":"retired_endpoint","message":"This legacy endpoint no longer serves the search results. …"} $ curl ... "$BASE/api/requirements/index.php/reqspec-search?tproject_id=1" HTTP 200 {"status":"ok","count":2,"rows":[…],"warning":"" …} ``` Discrimination — restoring the pre-#1825 controller and probing as a user with **no** session project reproduces the 500 (`testproject::get_by_id(0)` in `reqSpecCommands::__construct`, reached from `reqSpecSearch.php:34`); with a project set the same pre-fix file reaches `count($itemSet)` at line 116. ## Root cause | hop | file:line (pre-#1825) | code | |---|---|---| | 1 | `reqSpecSearch.php:43` | `$itemSet = null;` | | 2 | `reqSpecSearch.php:104` | `if($args->tprojectID) { … }` — the only block assigning `$itemSet` | | 3 | `reqSpecSearch.php:113` | `$itemSet = $db->fetchRowsIntoMap(…)` | | 4 | **`reqSpecSearch.php:116`** | `$gui->row_qty=count($itemSet);` — unconditional → `TypeError` | ## Blast radius / why no re-patch | reference | status | |---|---| | `lib/requirements/reqSpecSearch.php` | redirect shim only | | `gui/templates/{dashio,tl-classic}/requirements/reqSpecSearchForm.tpl:25` | dead legacy `form action` (screen no longer rendered; modern action `…/reqSpecSearchForm.html`, `lib/general/frmWorkArea.php:34`) | | `lib/functions/tlReqMgrSystem.class.php:673`, `api/requirements/index.php:2095` | comments only | Re-patching `count($itemSet)` would keep a second redundant search implementation alive behind a page the ASIDE no longer links to; the retirement removes the whole class of PHP-8 defects from the path. ## Verification `bash tmp/verify_1735.sh` — self-cleaning, creates its own fixture, exits non-zero on any failure: **15 PASS / 0 FAIL** (exit 0) — legacy URL 302/405 (never 500), modern screen 200, BFF results / filtered count / `no_records_found`, zero Event-Viewer Error/Warning rows, and no `count($itemSet)` in either the shim or the BFF. ## Related * Issue **#1629** — the sibling 500 fix that let the request reach this screen's own defect. * Issue **#1825** — the modernization that retired this controller.