phanalist

E0045: Input-Dependent Live-Heap Growth

E0045 reports loops that retain memory in proportion to an input-sized collection. It distinguishes cumulative allocation from live heap: allocating temporary values is not the same as keeping them reachable until the request or worker finishes.

What it checks

The finding reports the symbolic retained growth, cumulative allocation, and peak live heap. Retained size covers memory added by the loop; pre-existing input arrays are included in the peak estimate but not in the reported retained growth. For example, a retained array may be described as count($items) * bytes($items); this is a symbolic estimate, not a measurement of PHP’s internal zval or hashtable layout.

Model limits

E0045 does not run a garbage collector or know when PHP will free a value. It uses conservative reachability rules and the following simplifications:

The rule is deliberately conservative around unknown callees: an unknown call with a tracked argument may invalidate that value’s growth information instead of claiming that memory is released. A callee summary only contributes retained growth when the retained value is derived from one of the callee’s parameters. Helpers that throw fresh exceptions or return unrelated temporary objects do not make their callers retain input-sized data.

When retained growth is unknown, the rule requires direct local accumulation or cumulative allocation at or above heap_alloc_threshold. Validation and predicate loops that only call helpers and discard their results are therefore not reported as live-heap growth. A discarded helper return value does not contribute retained growth, but in-place mutators such as array_push() still count as local accumulation.

Configuration

rules:
  E0045:
    runtime_threshold: quadratic
    heap_alloc_threshold: quadratic
    retained_element_bytes: 32
    report_input_cardinality: true
    include_cli_input: false
    max_findings_per_function: 20

heap_alloc_threshold controls when cumulative allocation is reported. retained_element_bytes is the estimated per-element size used when the element type is unknown. Lowering it makes retention findings more sensitive.