Bug Issue 1564 Locale Stray PhpTag JSON Corruption - sebiboga/testlink-upgraded GitHub Wiki
Bugfix β Issue 1564 β locale/{es_AR,fi_FI,id_ID,ko_KR,pl_PL}: dead $TLS_* lines after a stray ?> corrupt JSON in every API/BFF response for those locales
Issue: #1564
Branch: fix/issue-1564-locale-stray-php-tag
Status: FIXED & VERIFIED (2026-09-22)
Symptom
For the locales es_AR, fi_FI, id_ID, ko_KR, pl_PL, every API/BFF JSON
response was corrupted: the bundles carry dead PHP assignments AFTER a single
mid-file closing ?> tag, so lang_load() β require strings.txt echoes them
as literal text into the response body. Client JSON.parse() throws at position 0.
Measured pre-fix (fresh DB, tproject id 1 recreated, admin/admin):
GET /api/cfields/index.php/assignment?tproject_id=1&locale=pl
β body starts with: $TLS_href_nfr_performance = "NFR: Performance"; // ...
β json.load β JSONDecodeError: Expecting value: line 1 column 1 (char 0)
php -r 'require "locale/pl_PL/strings.txt"; var_export(isset($TLS_href_nfr_performance))'
β $TLS_href_nfr_performance = "NFR: Performance"; ... (dead lines echoed)
β false (dead key)
Affected locales matrix (pre-fix, locale= on the assignment endpoint):
| locale | body starts with | JSON parse |
|---|---|---|
| pl | $TLS_href_nfr_performance = ... |
FAIL (char 0) |
| es | $TLS_req_title_length_exceeded = ... |
FAIL (char 0) |
| fi | $TLS_href_nfr_performance = ... |
FAIL (char 0) |
| id | $TLS_href_nfr_performance = ... |
FAIL (char 0) |
| ko | $TLS_href_nfr_performance = ... |
FAIL (char 0) |
| en (control) | {"status":"ok",... |
OK |
Root cause chain
locale/es_AR/strings.txt:3222,locale/fi_FI/strings.txt:2148,locale/id_ID/strings.txt:2886,locale/ko_KR/strings.txt:2442,locale/pl_PL/strings.txt:3842β each file opens with<?php(line 1) and contains exactly ONE stray?>mid-file; the interpreter leaves PHP mode there and never re-enters it.- One or more
$TLS_*assignments follow that?>in each file (es_AR: 2 keys; the other four: the 8$TLS_href_nfr_*keys from #1470/#1462 + the 2$TLS_req_*keys). They are NOT parsed β they are literal text. lib/functions/lang_api.php:240(lang_load())requires the bundle; everything after?>is copied verbatim into the output buffer.- The BFF endpoints (e.g.
api/cfields/index.php/assignment,api/aside, etc.) calllang_get()with the client-selected locale (api/cfields/index.php:305-313assignLocale()), which triggerslang_load()β the literal$TLS_*text lands beforejson_encode(), so the response body stops being JSON. - The keys themselves are never defined at runtime β
lang_get()cannot resolve them (localization of those keys also broken).
Note: the $TLS_btn_report_test_automation / $TLS_href_tc_create_from_issues
lines mentioned during triage are at pl_PL:3827-3828, i.e. BEFORE the stray
?> (3842) β they are live and needed no change.
Approach β remove the freezing ?>, keep the existing translations
Chosen fix: delete the single stray ?> line from each of the 5 bundles. All
content after it rejoins PHP mode β every existing $TLS_* assignment becomes a
live definition at runtime and no literal text is emitted. The translations were
already present β they were just dead code.
- Files keep
<?phpon line 1 and no closing?>at EOF (idiomatic forrequired scripts; matches the other bundles).php -lstays clean. - Rejected alternatives:
- Moving the lines above the
?>β more churn, leaves a useless frozen tag. - Editing
lang_load()to strip output β wrong layer, larger blast radius, masks a frozen bundle instead of fixing it. - Deleting the dead lines β would drop translations that must be live.
- Moving the lines above the
Files changed
| File | Change |
|---|---|
locale/es_AR/strings.txt |
remove stray ?> (line 3222) β 1 line |
locale/fi_FI/strings.txt |
remove stray ?> (line 2148) β 1 line |
locale/id_ID/strings.txt |
remove stray ?> (line 2886) β 1 line |
locale/ko_KR/strings.txt |
remove stray ?> (line 2442) β 1 line |
locale/pl_PL/strings.txt |
remove stray ?> (line 3842) β 1 line |
CHANGELOG |
one KEY BUGFIX line |
tmp/TLU_Test_Cases.md |
Suite 1564 (6/6 PASS) |
Causeβfix commits: 97943a65e (fix), 432a0b503 (regression suite),
docs commit (this file).
Verification
- Bundle gate:
grep -c '?>'= 0 in all 5 files;php -l"No syntax errors detected" Γ5; line 1 still<?php. - Define gate:
php -r 'require ...; var_export(isset($TLS_href_nfr_performance)); var_export(isset($TLS_href_nfr_requirements)); var_export(isset($TLS_req_title_length_exceeded)); var_export(isset($TLS_req_docid_length_exceeded));'βtruetruetruetruefor all 5 bundles, zero stray stdout (pre-fix: keysfalse, dead lines echoed). - Live API (primary symptom gone):
GET /api/cfields/index.php/assignment?tproject_id=1&locale={pl,es,fi,id,ko}β valid JSON,status=okfor every locale;locale=enunchanged. - Localization really works now:
locale=pllocations codes 5/7 βPo tytule/Po warunkach wstΔpnychβ the #1563 keys render as real pl translations in a previously-corrupt locale (unblocks suite 1563's BLOCKED-BY-#1564 live checks). - Second endpoint sanity:
GET /api/aside/index.php?locale=pl&format=jsonβ valid JSON. - Event Viewer hygiene: no Error rows introduced by this fix. (Pre-existing
out-of-scope: level-32 LOCALIZATION fallback warnings for keys genuinely absent
from the pl_PL/fi_FI bundles β
before_summary,standard_location, etc.) - Code-review sweep: the only remaining
?>tags across ALL locale bundles sit on the final non-empty line ofde_DE,es_ES,it_IT,ru_RUβ legitimate end-of-file terminators (followed by nothing), correctly left untouched.
Regression suite: Suite 1564 in tmp/TLU_Test_Cases.md β 6/6 PASS.