Skip to content

Rename ParameterResolverResolver and ResolverContainerResolver #31

Description

@henriquemoody

Summary

The current naming of the resolver interface and its sole implementation is semantically inverted: the interface name describes more than the implementation does, while the implementation name describes less. I would like to propose swapping them so the contract keeps the simple name and the implementation carries the qualifier that reflects what it actually does.

Current state

  • src/ParameterResolver.phpinterface ParameterResolver defines the contract: a single resolve(ReflectionFunctionAbstract $reflection, array $arguments): array method that turns a reflection plus arguments into an ordered, ready-to-splat list.
  • src/Resolver.phpfinal readonly class Resolver implements ParameterResolver. Its constructor is public function __construct(private ContainerInterface $container), and its docblock states: "Resolves the arguments to call a function or constructor with, autowiring any parameter that is not supplied from a PSR-11 container by type."

So the implementation is fundamentally about resolving from a PSR-11 container — yet it is named with the bare, generic word Resolver, while the generic interface is named with the more specific qualifier Parameter.

Problem

The implementation tells us more about itself than the type does:

  • The interface (ParameterResolver) only promises "resolve parameters into an argument list". It carries no commitment to a container, autowiring, or any particular strategy. The Parameter qualifier is reasonable, but the name still leans on a noun that the namespace (Respect\Parameter) already implies.
  • The implementation (Resolver) is the one that is concretely about something: it resolves parameters using a container. Calling it the bare Resolver hides that — its name describes less than the interface's name does, even though it is the more specific artifact.

In short: the qualifier that should sit on the implementation currently sits on the interface, and vice versa.

Proposal

Swap the roles of the two names:

Before After
Interface ParameterResolver Resolver
Implementation Resolver ContainerResolver
namespace Respect\Parameter;

interface Resolver
{
    /** @param array<int|string, mixed> $arguments */
    public function resolve(ReflectionFunctionAbstract $reflection, array $arguments): array;
}
namespace Respect\Parameter;

final readonly class ContainerResolver implements Resolver
{
    public function __construct(private ContainerInterface $container) {}
    // ...
}

Why this is better semantically

  • Resolver as the interface name is the natural, minimal name for the contract the package exposes; consumers depending on the abstraction import Resolver, which reads cleanly (Resolver $resolver).
  • ContainerResolver as the implementation name states the strategy directly: it resolves parameters from a container. Future alternative strategies (e.g. NullResolver, ArrayResolver, MemoizingResolver) get parallel, self-describing names instead of competing for the overloaded Resolver identifier.
  • The implementation's name now carries strictly more information than the interface's name, which is the usual convention for contract-vs-implementation pairs in this ecosystem.

The trade-off is that ContainerResolver is longer and slightly less "simple" than Resolver. I think that cost is worth it: the shorter name now belongs to the abstraction consumers actually depend on, and the longer name accurately points at what the concrete class does.

Backward compatibility

This is a BC break in the public namespace:

  • Respect\Parameter\ParameterResolver (interface) is removed/renamed.
  • Respect\Parameter\Resolver changes from a final readonly class to an interface.
  • References to update:
    • src/Resolver.php (the class declaration and implements clause).
    • tests/unit/ResolverTest.php: use Respect\Parameter\ParameterResolver;use Respect\Parameter\Resolver; (interface), use Respect\Parameter\Resolver;use Respect\Parameter\ContainerResolver; (impl), the #[CoversClass(Resolver::class)] attribute, all new Resolver(...) instantiations, and the test class name itself (ResolverTest → arguably ContainerResolverTest).

Since this is a rename touching public symbols, it would ideally ship as a major version bump with the old names possibly preserved as deprecated aliases for one cycle (e.g. interface ParameterResolver extends Resolver {} and class_alias(Resolver::class, …) — noting that a class and interface cannot share a name, so the old Resolver class name cannot coexist with the new Resolver interface without a different migration shape).

Notes

Happy to open a PR implementing the rename (interface + class + tests) if this is accepted. Flagging in advance that the previous Resolver class name cannot be kept as a transparent alias once Resolver becomes an interface, so the deprecation path needs a decision alongside the rename itself.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions