Bugfix Issue 1433 PlanImport Legacy Wrong Root Count Null - sebiboga/testlink-upgraded GitHub Wiki
Issue 1433 — planImport (legacy): well-formed XML with wrong root element ⇒ HTTP 500 count(null) at lib/plan/planImport.php:286
Issue: #1433
Branch: fix/issue-1433
Status: VERIFIED-FIXED (2026-09-11)
| Screen |
lib/plan/planImport.php (legacy POST, Smarty page) — importTestPlanLinksFromXML()
|
| Symptom | Uploading a WELL-FORMED XML file whose root is not <testplan> (e.g. the old planner export root <xml> wrapping <testplan><executables>...) returns an empty HTTP 500 body. |
| Root cause |
xpath('//executables') (line 279) matches at ANY depth, but $xml->executables (line 285) only reaches a DIRECT child of the root; on non-<testplan> roots it resolves to null, and count(null) (line 286) is a PHP 8 TypeError → uncaught fatal → empty 500. |
| Fix |
$xmlLinks = $xml->executables instanceof SimpleXMLElement ? $xml->executables->children() : null; $loops2do = is_null($xmlLinks) ? 0 : count($xmlLinks); — non-<testplan> roots now yield $loops2do = 0 (empty import report), restoring the legacy PHP 5/7 behavior instead of fataling. Supported <testplan>-root imports unchanged. No i18n changes (no user-facing string). |

-
Log in as admin (
POST /login.php,tl_login=admin&tl_password=admin, keep the session cookie). -
Fixture
tmp/fixtures_pimp.php: tproject 1 PIMP, plan 12 PIMP-Plan, platform 1 PIMP-Android. -
curl -b cookies.txt -F "importType=XML" -F "uploadFile=1" -F "[email protected]" "http://localhost:8082/lib/plan/planImport.php?tplan_id=12"withwrongroot.xml:<?xml version="1.0"?> <xml><testplan><executables><link> <platform><name>PIMP-Android</name></platform> <testcase><externalid>1</externalid><name>Login</name><version>1</version><execution_order>1</execution_order></testcase> </link></executables></testplan></xml>
-
Before fix: HTTP 500, empty body; fatal at
lib/plan/planImport.php:286. After fix: HTTP 200 (import screen renders, empty import report).
Expected: graceful non-500 page, mirroring the modern BFF fix #1407.
lib/plan/planImport.php:279-286:
if( $status_ok && $xml->xpath('//executables') )
{
...
$xmlLinks = $xml->executables->children();
$loops2do = count($xmlLinks); // line 286 → count(null) on PHP 8Chain:
-
:279—$xml->xpath('//executables')matches<executables>at any depth, so the block is entered for the<xml>-wrapped file. -
:285—$xml->executablesis a direct-child property access on the document root; root is<xml>whose direct child is<testplan>, so->executablesyields null →->children()null. -
:286—count(null)is a TypeError on PHP 8.x (PHP < 8 coerced null→0). Uncaught ⇒ request dies before the Smarty page renders.
Blast radius:
- Only
lib/plan/planImport.php:285-286crashes. Wrong-root files with no<executables>at any depth were already safe (block not entered). Crash was specific to files where<executables>exists at depth (the<xml>wrapper / old planner exports). - Modern BFF twin
api/planimport/index.php:145-151had the identical pattern, already fixed by #1407 (commit6b3aca759). This legacy copy was the remaining unguarded one; legacy screen is slated for removal via task #1409 (minimal patch only).
lib/plan/planImport.php:285-289 — mirror the verified #1407 guard:
$xmlLinks = $xml->executables instanceof SimpleXMLElement
? $xml->executables->children() : null;
$loops2do = is_null($xmlLinks) ? 0 : count($xmlLinks);-
$loops2do = 0⇒ loop skipped ⇒ function returns$msgnormally ⇒ page renders with an empty import report — byte-equivalent to the legacy PHP 5/7 (count(null)= 0) behavior. - Supported
<testplan>-root import path unchanged. - No i18n bundle changes (no user-facing string).
Rejected alternative: actually importing <xml>-wrapped planner exports (walking into
$xml->testplan) — a new feature the legacy import never supported, out of scope for a
minimal bug fix.
-
primary repro (
<xml>wrapper): pre-fix HTTP 500 + fatal at:286; post-fix HTTP 200, no fatal intmp/php_server.log([200]: POST /lib/plan/planImport.php). -
valid
<testplan>-root import: HTTP 200, reportTest case link #1 has platform but Test Plan has no linked platforms: Not imported— legacy processing intact (fixture plan has zero platforms). Proves the supported path is untouched. -
hygiene:
php -l lib/plan/planImport.phpclean;eventstable — no new Error/Warning (ERROR=1, WARNING=2 log_level), only pre-existing AUDIT(16) rows. -
browser (chrome-devtools): login admin/admin →
planImport.php?tplan_id=12→ uploadwrongroot.xml→ POST returns 200, empty import report. Screenshot:planImport-graceful-issue1433.png. -
diff hygiene: only
lib/plan/planImport.phptouched (+8/-2), zero i18n bundles.
- The #1407 fix left the legacy twin unguarded — the same crash, one codebase later. Confirms the value of fixing the pattern once and carrying it to the twin.
- 2.0.1 log levels:
ERROR=1, WARNING=2, INFO=4, DEBUG=8, AUDIT=16(lib/functions/logger.class.php:50-55) — used for the Event Viewer check. - Code-review subagent (AGENTS.md rule 16) reviewed the full diff before push.
Commit range (fix), (regression suite), (docs + wiki) on branch fix/issue-1433.
Docs mirror: docs/Bugfix-Issue-1433-PlanImport-Legacy-Wrong-Root-Count-Null.md,
screenshot docs/screenshots/issue-1433-planimport-graceful.png.
Regression suite: tmp/TLU_Test_Cases.md → "Regression — Issue #1433".