Skip to content

Split Resolver into focused contracts (Resolver, Reflector, Augmenter)#27

Draft
henriquemoody wants to merge 3 commits into
Respect:mainfrom
henriquemoody:refactoring
Draft

Split Resolver into focused contracts (Resolver, Reflector, Augmenter)#27
henriquemoody wants to merge 3 commits into
Respect:mainfrom
henriquemoody:refactoring

Conversation

@henriquemoody

Copy link
Copy Markdown
Member

Summary

Reshapes the package around small, single-purpose contracts so consumers
depend on behaviour, not on the PSR-11 implementation. The Resolver becomes
an interface, the container-free reflection helpers move out into their own
class, and a new Augmenter contract covers the factory use case the resolver
was never meant to serve.

This is a breaking change; the branch aliases to 2.0.x-dev.

Changes

Introduce the Augmenter contract

  • New Augmenter interface and ContainerAugmenter implementation.
  • Unlike the resolver — which completes a call by padding gaps with defaults
    or null — the augmenter keeps the given arguments authoritative: never
    rebound, reordered, or padded. It only fills genuinely unfilled parameters
    with container services, added as named arguments.
  • Variadic, builtin-typed, and already-filled parameters are never augmented;
    extra arguments pass through unchanged.
  • Types listed as unresolvableTypes are never looked up in the container,
    which keeps value-like classes (clocks, dates) from being served as frozen
    services.
  • Augmentable parameters are reflected once and cached per callable.

Extract Reflector from Resolver

  • reflectCallable() and acceptsType() don't depend on the container and are
    useful on their own, so they move to a dedicated Reflector.

Turn Resolver into an interface

  • Resolver is now the resolution contract; the concrete PSR-11 class is
    ContainerResolver, mirroring the Augmenter/ContainerAugmenter split.
  • The static helpers already living in Reflector are dropped from the class.

Upgrading from 1.x

  • Resolver is now an interface; instantiate ContainerResolver instead.
  • Resolver::reflectCallable() and Resolver::acceptsType() moved to Reflector.
  • The new Augmenter/ContainerAugmenter fill unfilled parameters without
    touching the given arguments.

Tests

  • New ContainerAugmenterTest and ReflectorTest; ResolverTest renamed to
    ContainerResolverTest.
  • Added fixtures (OptionalServiceConsumer, tests/fixtures/functions.php).

🤖 Generated with Claude Code

henriquemoody and others added 3 commits June 12, 2026 23:38
Unlike the resolver, which completes a call by padding gaps with
defaults or null, the augmenter keeps the given arguments authoritative
and only adds container services for parameters left unfilled. This
suits factories that pass user input straight to a constructor.

Types listed as unresolvable are never looked up in the container,
which keeps value-like classes (clocks, dates) from being served as
frozen services.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The static helpers reflectCallable() and acceptsType() do not depend on
the container and are useful on their own. Moving them to a dedicated
Reflector lets the next step reduce the Resolver to its resolution
contract. The Resolver copies remain until that refactoring lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consumers should depend on the resolution contract, not on the PSR-11
implementation, mirroring the Augmenter/ContainerAugmenter split. The
concrete class becomes ContainerResolver, and the static helpers that
already moved to Reflector are dropped from it.

This is a breaking change, so the branch now aliases to 2.0.x-dev.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@henriquemoody henriquemoody marked this pull request as draft June 12, 2026 22:32
@alganet

alganet commented Jun 12, 2026

Copy link
Copy Markdown
Member

I'll wait for your response in #26. If we don't introduce another kind of resolving, I would prefer keeping it contained in a single class.

Even after merging augmenter+resolver as I proposed in the diff, Resolver.php stays very small at less than 300 lines. IMHO, introducing _other kinds of resolving* complicates things more than the benefit obtained by separating responsibilities.

I could be wrong though, and maybe you have a plan or a use case that my comment on #26 does not address, or perhaps a better reasoning for making the architecture larger. I'm all ears!

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.

2 participants