Skip to content

[Feature]: Report mode: caller frame, declaring docblock, merged reports #105

Closed

Description

@fdaviddpt

Context

We ran report mode (#94, v0.11.0 / v0.11.1) over about 20 modules of a large codebase, one PHPUnit pass per module, and triaged a few hundred violations by hand. Report mode is exactly what we needed: tests keep running and we get the whole picture in one JSON. Three small additions would cut most of the triage time.

Today a record looks like:

{
  "file": ".../SiVat/BusinessEntities/SiVatFieldTrait.class.php",
  "line": 32,
  "function": "Shared\\\\Components\\\\Fields\\\\DecimalInputField::$maxValue",
  "kind": "property",
  "target": "$maxValue",
  "expected": "int",
  "given": "float (99.999)",
  "message": "..."
}

1. Where the value came from (caller frame)

file/line is where the check fired. For parameters and returns that is often generic framework code, e.g. a list renderer calling $entity->url('edit') for every row: the record points at the renderer, not at which entity class or which test produced the call. The first question in every triage is "is the producer production code or a test fixture?", and answering it means grepping.

Suggestion: an optional caller (one frame above the checked call, file:line + function), or a short trace (2-3 frames) behind a config flag, since full traces would bloat the report.

2. Where the expected type is declared

For inherited members the docblock that has to change is often not in the reported class. Example above: DecimalInputField::$maxValue is checked against @var int, which is declared on its parent TextInputField. Same for traits (EntityMetadataJsonTrait::$metadataJsonOptions reported on Project, Location, Tag...).

Suggestion: declared_in (file:line of the @var / @param / @return that produced expected).

3. One report for a whole run

TYPEPHP_REPORT_FILE takes one path per process, so a run over N modules (or N ParaTest workers, see my comment on #94) leaves N files with overlapping records: a violation in shared code shows up once per module that hits it. We merge and deduplicate them ourselves.

Suggestion, either of:

  • append/merge mode: the report file is read back, records keyed by (file, line, target, expected, given) are merged with an occurrence count, or
  • a small CLI command, e.g. typephp report:merge a.json b.json > all.json, with the same key and count.

A count would also help prioritise: one record hit 400 times is a different fix from 400 distinct records.

Not a request

None of this blocks us. Thanks for the turnaround on the earlier reports (#93-#103 all released in 0.11.0).

Activity

  1. linked a pull request that will close this issueInternal improvements 37 #106on Oct 6, 2026
  2. rcalicdan commented on Oct 6, 2026

    @rcalicdan
    Member

    @fdaviddpt Hello, This is a great quality of life improvement.. I had added this feature in v0.11.2 release

    Also documented it's usage and behavior

    https://typephp-php.github.io/docs/core-concepts/violation-reporting#json-report-document-schema
    https://typephp-php.github.io/docs/core-concepts/violation-reporting#understanding-location-diagnostics-the-tri-part-model
    https://typephp-php.github.io/docs/getting-started/cli-commands#merging-audit-reports-report-merge

    and also fixed the bug in #104 ..

    Don't hesitate to submit more issues so we can help each other..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions