Root cause of every failed whole-repository PHP scan, bisected from 637 files
to one file to one function to one construct:
<?php
function f($n) {
h("p", function($p) { global $g; return $p . $g; });
}
A `global` declaration inside a CLOSURE makes php2cpg 4.0.623 emit a node with
two AST parents, and Joern then refuses to apply its own dataflow overlay:
java.lang.AssertionError: Iterable was expected to have exactly one
element, but it has 2. Hint: trying to resolve astParent ... (which is
probably a malformed cpg)
Controlled, because four earlier hypotheses were refuted by measurement and I
was not going to add a fifth:
global inside a closure WITH use -> ASSERTS
global inside a closure, NO use -> ASSERTS (so `use` is irrelevant)
use, global OUTSIDE the closure -> ok (so scope is the trigger)
The property that matters: one such file costs the WHOLE GRAPH, not itself.
That is why a 2,500-request taint batch and a 13-request dominance batch failed
identically in ~25s, and why I twice called this a per-request cost problem. It
never was. Nothing was ever served; the graph is rejected at load.
Rare enough to exclude rather than surrender to -- 1 of 637 files in Paid
Memberships Pro, 1 of 357 in WP Statistics, 0 of 169 in MW WP Form, about 0.2%
-- so the construct is detected by reading the source, those files are excluded
from the build, and they are reported as `unparsed` so the coverage section
names them instead of implying they were clean.
Repository-scale PHP goes from blocked to 99.8% scanned with a stated gap.
854 tests pass; gates byte-identical.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG