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.php — interface 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.php — final 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.
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.php—interface ParameterResolverdefines the contract: a singleresolve(ReflectionFunctionAbstract $reflection, array $arguments): arraymethod that turns a reflection plus arguments into an ordered, ready-to-splat list.src/Resolver.php—final readonly class Resolver implements ParameterResolver. Its constructor ispublic 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 qualifierParameter.Problem
The implementation tells us more about itself than the type does:
ParameterResolver) only promises "resolve parameters into an argument list". It carries no commitment to a container, autowiring, or any particular strategy. TheParameterqualifier is reasonable, but the name still leans on a noun that the namespace (Respect\Parameter) already implies.Resolver) is the one that is concretely about something: it resolves parameters using a container. Calling it the bareResolverhides 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:
ParameterResolverResolverResolverContainerResolverWhy this is better semantically
Resolveras the interface name is the natural, minimal name for the contract the package exposes; consumers depending on the abstraction importResolver, which reads cleanly (Resolver $resolver).ContainerResolveras 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 overloadedResolveridentifier.The trade-off is that
ContainerResolveris longer and slightly less "simple" thanResolver. 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\Resolverchanges from afinal readonly classto aninterface.src/Resolver.php(the class declaration andimplementsclause).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, allnew Resolver(...)instantiations, and the test class name itself (ResolverTest→ arguablyContainerResolverTest).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 {}andclass_alias(Resolver::class, …)— noting that a class and interface cannot share a name, so the oldResolverclass name cannot coexist with the newResolverinterface 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
Resolverclass name cannot be kept as a transparent alias onceResolverbecomes an interface, so the deprecation path needs a decision alongside the rename itself.