Skip to content

Add CompositeMapperCompilerProvider for typed provider graph traversal - #126

Draft
janedbal wants to merge 1 commit into
masterfrom
composite-mapper-compiler-provider
Draft

Add CompositeMapperCompilerProvider for typed provider graph traversal#126
janedbal wants to merge 1 commit into
masterfrom
composite-mapper-compiler-provider

Conversation

@janedbal

Copy link
Copy Markdown
Member

Motivation

Consumers that pre-generate mappers (e.g. a Symfony command compiling input + output mappers for every *Input class and everything reachable from it) need to walk a provider graph to discover all transitively referenced classes — every reachable MapDelegate. Until now that required reflecting over provider internals (get_mangled_object_vars() over a plain object), which static analysis cannot attribute to any class: shipmonk/dead-code-detector reports it as an unknown read over unknown type and it degrades dead-code analysis of the whole consumer project.

What this adds

  • CompositeMapperCompilerProvider — implemented by providers composed of other providers, exposing them via getInnerMapperCompilerProviders(): list<InputMapperCompilerProvider|OutputMapperCompilerProvider>. Implemented on all 12 composite attributes; notably MapArrayShape exposes the mappers wrapped in ArrayShapeItemMapping and MapObject exposes both constructor-arg and property providers.
  • MapperCompilerProviderUtils::iterate() — yields a provider and all transitively nested providers, using only the interface. Consumers can then e.g. filter for MapDelegate without any reflection.

Completeness is enforced mechanically

Two tests keep implementations honest:

  1. testTypedTraversalMatchesReflectiveTraversal compares the typed traversal against a reflective property walk over a graph exercising every attribute provider — forgetting a nested provider in any implementation fails the test naming the missed provider.
  2. testProviderGraphContainsAllAttributeProviders globs src/Compiler/Attribute and forces every current and future provider attribute into that test graph — a new composite attribute cannot ship without exposing its inner providers.

Direction / follow-up

This is intended as a building block: input-mapper should ideally support mapper pre-generation natively. A follow-up could add e.g. a MapperPregenerator that takes root classes, expands MapDelegate dependencies via this traversal, and compiles input + output mappers for all of them — so consumer applications just call it from their console command instead of owning the discovery logic.

Backward compatible: purely additive, custom providers opt in by implementing the interface.

Co-Authored-By: Claude Code

Drafted by Claude

Consumers sometimes need to walk a provider graph to discover all
transitively referenced classes, e.g. to pre-generate mappers for every
MapDelegate reachable from an Input class. Until now that required
reflecting over provider internals (get_mangled_object_vars over
plain `object`), which static analysis cannot attribute to any class —
shipmonk/dead-code-detector reports it as an unknown read over unknown
type and degrades dead code analysis of the consumer project.

Every provider composed of other providers now exposes them via
CompositeMapperCompilerProvider::getInnerMapperCompilerProviders(),
and MapperCompilerProviderUtils::iterate() walks the graph using only
that interface.

The completeness of the implementations is enforced mechanically:
a test compares the typed traversal against a reflective walk over a
graph exercising every attribute provider, and a companion test forces
every (current and future) attribute provider class into that graph.

Co-Authored-By: Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant