ioc-interop / interface
Interoperable IOC container interfaces for PHP.
Installs: 15
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 2
Open Issues: 0
pkg:composer/ioc-interop/interface
Requires (Dev)
- pds/composer-script-names: ^1.0
- pds/skeleton: ^1.0
- phpstan/phpstan: ^2.0
This package is auto-updated.
Last update: 2026-01-24 15:06:30 UTC
README
Ioc-Interop provides an interoperable package of standard interfaces for inversion-of-control (IOC) container functionality. It reflects, refines, and reconciles the common practices identified within several pre-existing projects.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174).
Interfaces
This package defines the following interfaces:
-
IocContainer affords obtaining services by name, whether as shared instances or new unshared instances.
-
IocInstanceFactory affords instantiating a class.
-
IocServices affords a registry of service instances, definitions, and aliases.
-
IocProvider affords provision of service instances, definitions, and aliases to an IocServices instance.
-
IocDefinition affords building a service, including both instantiation and extended post-instantiation logic.
-
IocResolver affords resolving a class name to a new instance of that class.
-
IocParametersResolver affords resolving an array of parameters to an array of named arguments.
-
IocParameterResolver affords resolving a parameter to an argument value.
-
IocContainerFactory affords obtaining a new instance of IocContainer.
-
IocThrowable extends Throwable to mark an Exception as IOC-related. It adds no class members.
-
IocTypeAliases defines PHPStan type aliases to aid static analysis.
IocContainer
IocContainer affords obtaining services by name, whether as shared instances or new unshared instances.
-
Directives:
- Implementations MUST retain an instance of the container itself under
a
$serviceNameofIocContainer::class.
- Implementations MUST retain an instance of the container itself under
a
-
Notes:
-
This interface does not afford service registration. The container will need to obtain services from IocServices somehow:
-
Some implementors will prefer an "open" approach, where the services are set and modified directly on the container itself, in which case implementing both IocContainer and IocServices, or a container implementation extending a services implementation, is reasonable.
-
Other implementors will prefer a "closed" approach, where an IocServices implementation is encapsulated but not exposed by an IocContainer implementation.
-
-
Keep the container itself as a service. This allows factory and builder services to depend on the container; it may be easiest to do so as part of
__construct().
-
IocContainer Methods
-
public function hasService(ioc_service_name_string $serviceName) : bool;
-
Is the container capable of returning a shared instance of the service?
-
Directives:
-
Implementations MUST convert the
$serviceNameargument to its alias, if an alias exists for that$serviceName. -
Implementations MUST return
trueif ...-
the container has access to a shared instance of
$serviceName; or, -
the container has access to a service builder for
$serviceNamethat has a service factory; or, -
the
$serviceNameexists as an instantiable class.
-
-
-
-
public function getService( ioc_service_name_string $serviceName, ) : ioc_service_object;
-
Returns a shared instance of a service, instantiating it if necessary.
-
Directives:
-
Implementations MUST convert the
$serviceNameargument to its alias, if an alias exists for that$serviceName. -
Implementations MUST throw IocThrowable if the container cannot return a shared instance of the service.
-
Implementations MUST return the same instance of the service service each time this method is called.
-
-
Notes:
- Create and retain a new instance if necessary. In practice,
this likely means calling
newService($serviceName)and holding onto the newly-created instance for later calls togetService($serviceName).
- Create and retain a new instance if necessary. In practice,
this likely means calling
-
IocInstanceFactory
IocInstanceFactory affords instantiating a class.
IocInstanceFactory Methods
-
public function newInstance( string $class, mixed[] $arguments = [], ) : ($class is class-string<T> ? T
-
Returns a new instance of the
$classwith$argumentsconstructor argument overrides. -
Notes:
- Use this for custom factory classes. The IocResolver needs an IocContainer as its first parameter, this method does not. In turn, that means this class probably ought to be constructed with both a container and a class resolver, so that this method can forward to the class resolver with the container.
-
IocServices
IocServices affords a registry of service instances, definitions, and aliases.
-
Notes:
- TBD Prime the implementation with an IocResolver instance.
IocServices Methods
-
public function hasInstance(ioc_service_name_string $serviceName) : bool;
- Has a shared instance of the
$serviceNamebeen set?
- Has a shared instance of the
-
public function getInstance( ioc_service_name_string $serviceName, ) : ioc_service_object;
-
Returns the shared instance of the
$serviceName. -
Directives:
- Implementations MUST throw IocThrowable if a shared instance
of the
$serviceNameis not available.
- Implementations MUST throw IocThrowable if a shared instance
of the
-
-
public function setInstance( ioc_service_name_string $serviceName, ioc_service_object $instance, ) : void;
- Sets the shared instance of the
$serviceName.
- Sets the shared instance of the
-
public function unsetInstance(ioc_service_name_string $serviceName) : void;
- Unsets the shared instance of the
$serviceName.
- Unsets the shared instance of the
-
public function hasDefinition(ioc_service_name_string $serviceName) : bool;
- Has an IocDefinition for the
$serviceNamebeen set?
- Has an IocDefinition for the
-
public function getDefinition( ioc_service_name_string $serviceName, ) : IocDefinition;
-
Returns the IocDefinition for the
$serviceName, instantiating it if needed. -
Notes:
- TBD Create using newDefinition() and retain for later return.
-
-
public function newDefinition( ioc_service_name_string $serviceName, ) : IocDefinition;
- Returns a new IocDefinition for the
$serviceName.
- Returns a new IocDefinition for the
-
public function setDefinition( ioc_service_name_string $serviceName, IocDefinition $definition, ) : void;
- Sets the IocDefinition for the
$serviceName.
- Sets the IocDefinition for the
-
public function unsetDefinition(ioc_service_name_string $serviceName) : void;
- Unsets the IocDefinition for the
$serviceName.
- Unsets the IocDefinition for the
-
public function hasAlias(ioc_service_name_string $serviceName) : bool;
- Has an alias for the
$serviceNamebeen set?
- Has an alias for the
-
public function getAlias( ioc_service_name_string $serviceName, ) : ioc_service_name_string;
-
Returns the alias for the
$serviceName. -
Directives:
-
Implementations MUST throw IocThrowable an alias for the
$serviceNameis not available. -
TBD Recursive resolution.
-
-
Notes:
- TBD Rescursive aliases are allowed.
-
-
public function setAlias( ioc_service_name_string $serviceName, ioc_service_name_string $alias, ) : void;
-
Sets the alias for one
$serviceNameto another service. -
Directives:
- TBD Circular tracking.
-
Notes:
- TBD Rescursive aliases are allowed.
-
-
public function unsetAlias(ioc_service_name_string $serviceName) : void;
- Unsets the alias for the
$serviceName.
- Unsets the alias for the
IocProvider
IocProvider affords provision of service instances, definitions, and aliases to an IocServices instance.
IocProvider Methods
-
public function provide(IocServices $services) : void;
-
Provides service instances, definitions, and aliases to the
$services. -
Notes:
- Provision includes a wide range of activity. The implementation
can set, unset, replace, modify, etc. the instances, definitions, and
aliases in the
$services.
- Provision includes a wide range of activity. The implementation
can set, unset, replace, modify, etc. the instances, definitions, and
aliases in the
-
IocDefinition
IocDefinition affords building a service, including both instantiation and extended post-instantiation logic.
IocDefinition Methods
-
public function hasFactory() : bool;
- Is there a factory that instantiates the service?
-
public function getFactory() : ioc_service_factory_callable;
-
Returns the factory that instantiates the service.
-
Directives:
- Implementations MUST throw IocThrowable if there is no factory for the service.
-
-
public function setFactory(callable $factory) : self;
-
Sets the factory that instantiates the service.
-
Notes:
- The
callabletype allows for a wide range of implementations. Cf. the https://php.net/callable documentation for more.
- The
-
-
public function unsetFactory() : $this;
- Unsets the factory that instantiates the service.
-
public function hasExtenders() : bool;
- Are there any post-instantiation extenders for the service?
-
public function getExtenders() : ioc_service_extender_callable[];
- Returns the post-instantiation extenders for the service.
-
public function setExtenders( ioc_service_extender_callable[] $extenders, ) : $this;
- Sets all post-instantiation extenders for the service.
-
public function unsetExtenders() : $this;
- Unsets all post-instantiation extenders for the service.
-
public function addExtender(ioc_service_extender_callable $extender) : $this;
-
Adds a single service extender to the builder.
-
Notes:
- The
callabletype allows for a wide range of implementations. Cf. the https://php.net/callable documentation for more.
- The
-
-
public function buildInstance(IocContainer $ioc) : object;
-
Creates and returns a new instance of the service.
-
Notes:
- TBD Instantiate (by factory or resolver) then apply extenders then return.
-
IocResolver
IocResolver affords resolving a class name to a new instance of that class.
IocResolver Methods
-
public function isResolvable(string $class) : bool;
- Does the
$classexist, and is it instantiable?
- Does the
-
public function resolve( IocContainer $ioc, string $class, mixed[] $arguments = [], ) : ($class is class-string<T> ? T
-
Given an IocContainer to locate constructor dependencies, returns a new instance of the
$classwith$argumentsconstructor argument overrides. -
Directives:
-
Implementations MUST support constructor injection using logic equivalent to that specified by IocParametersResolver.
-
Implementations MAY support other forms of injection, such as setter injection, property injection, and so on.
-
Implementations MUST throw IocThrowable if the
$classcannot be resolved.
-
-
IocParametersResolver
IocParametersResolver affords resolving an array of parameters to an array of named arguments.
IocParametersResolver Methods
-
public function resolveParameters( IocContainer $ioc, ReflectionParameter[] $parameters, mixed[] $arguments = [], ) : mixed[];
-
Resolves an array of parameters to an array of named parameter arguments, allowing for an array of override arguments.
-
Directives:
-
Implementations MUST NOT attempt to resolve parameters that already exist by name in the
$argumentsarray keys. -
When resolving a parameter, implementations MUST do so using logic equivalent to that specified by IocParameterResolver.
-
Implementations MUST return an array of arguments keyed by the parameter names.
-
-
Notes:
- Do not replace existing
$arguments. If an argument has already been given for a parameter name, there is no need to resolve the related parameter.
- Do not replace existing
-
IocParameterResolver
IocParameterResolver affords resolving a parameter to an argument value.
-
Directives:
-
Implementations MUST resolve parameters in this order:
-
If the parameter has an [Attribute][] that implements IocParameterResolver, implementations MUST resolve the parameter using that attribute.
-
Otherwise, if the parameter type is a [ReflectionNamedType][], and the container has a service for that type, implementations MUST resolve the parameter to that service.
-
Otherwise, implementations MAY attempt to resolve the parameter using implementation-specific logic; such logic is expressly not defined herein.
-
Otherwise, if the parameter has a default value, implementations MUST resolve the parameter to that value.
-
-
Implementations MUST throw IocThrowable if the parameter cannot be resolved.
-
-
Notes:
- This interface can be implemented as an attribute. Doing so allows
implementors to define custom resolution approaches for consumers to
apply to specific parameters. For example, implementors may declare a
#[GetEnv($name)]attribute to resolve the parameter to an environment value.
- This interface can be implemented as an attribute. Doing so allows
implementors to define custom resolution approaches for consumers to
apply to specific parameters. For example, implementors may declare a
IocParameterResolver Methods
-
public function resolveParameter( IocContainer $ioc, ReflectionParameter $parameter, ) : mixed;
-
Resolves the parameter to an argument value.
-
Notes:
- The return is
mixed. The resolved value might be anything at at all. This allows (e.g.) attribute implementations to obtain a service from the container, and then obtain a value from that service.
- The return is
-
IocContainerFactory
IocContainerFactory affords obtaining a new instance of IocContainer.
IocContainerFactory Methods
-
public function newContainer() : IocContainer;
-
Returns a new instance of IocContainer.
-
Notes:
- Container instantiation logic is not specified. Implementations might use providers, configuration files, attribute or annotation collection, or some other means to create and populate a container. Implementations might also choose to return a compiled or otherwise reconstituted container.
-
IocThrowable
IocThrowable extends Throwable to mark an Exception as IOC-related. It adds no class members.
IocTypeAliases
IocTypeAliases defines PHPStan type aliases to aid static analysis.
-
ioc_service_extender_callable callable(object,IocContainer):object- A
callablefor service post-instantiation logic; e.g. to set a property, call a setter or initializer method, decorate the service, etc.
- A
-
ioc_service_factory_callable callable(IocContainer):object- A
callablefor service instantiation logic.
- A
-
ioc_service_name_string class-string<T>|string- A
class-stringorstringname for a service.
- A
-
ioc_service_object ($serviceName is class-string<T> ? T : object)- The service
objectfor a given service name.
- The service
Implementations
-
Directives:
- Implementations MAY define additional class members not defined in these interfaces.
-
Notes:
- Reference implementations may be found at https://github.com/ioc-interop/impl.
Q & A
How is Ioc-Interop different from PSR-11?
PSR-11 is an earlier recommendation that offers an interface to get
items from a container, and to see if that container has a particular item.
The Ioc-Interop standard is more expansive.
-
Ioc-Interop is intended to contain only services (
object). PSR-11 is intended to contain anything (mixed). -
Ioc-Interop and PSR-11 each offer a method to "get" a service. Whereas PSR-11 does not specify the scope or lifetime of the service, Ioc-Interop specifies it as "shared" (aka "singleton" or "request-scoped").
-
Ioc-Interop and PSR-11 each offer a method to see if the container "has" a service. Whereas PSR-11 does not specify what "has" means, Ioc-Interop defines it to mean that the container has access to a shared instance of the service, or that it has access to the logic needed to build such an instance.
-
Ioc-Interop offers an IocInstanceFactory to explicitly create new instances. PSR-11 offers no similar interface.
-
Ioc-Interop offers an [IocServicesInterface][] to set/get/has/unset service instances, definitions, and aliases, separately from the container itself. PSR-11 offers no such interface.
-
Ioc-Interop offers IocResolver, IocParametersResolver, and IocParameterResolver interfaces. PSR-11 offers none.
-
Ioc-Interop offers a IocContainerFactory interface. PSR-11 offers none.
-
Ioc-Interop defines one IocThrowable interface. PSR-11 defines two exception marker iterfaces.
Is Ioc-Interop compatible with PSR-11?
No, in the sense that the method names, signatures, and intents are different.
Yes, in the sense that both may be implemented on the same class; the method names are different, and so are non-conflicting.
Is IocContainer a Dependency Injection system or a Service Locator?
IocContainer acts a Service Locator only when it is used as a dependency in order to retrieve other dependencies from it.
Why does IocContainer disallow non-object values?
TBD: To maintain conceptual integrity and consistent expectations. Given that
getService() returns a shared service, and newService() returns a new
instance, what does it mean to "get" a shared string value or a "new" string
value? How then to get non-object configuration values? Create config objects as
services. How to inject non-object values as constructor args? Consider
IocParameterResolver attributes.
Why does IocContainer define newService() instead of make(), create(), or build() ?
The researched projects use several different terms to indicate that a new
service will be returned: build (2 projects), create (3), get (6),
make (3), and new (2).
The terms get and make are ambiguous in the researched projects. They might:
-
create a new service every time;
-
return a shared service every time;
-
create a new service the first time and return that same instance every time thereafter; or,
-
do some combination of the above, depending how the service was defined.
The terms build and create are less-ambiguous, but are much less common.
In comparison, newService() is easily disambiguated from getService().
Ioc-Interop stipulates that former always returns a new instance, and the latter
always returns a shared instance (after creating it if necessary).
Why is IocContainer separate from IocServices?
Whereas IocContainer is for obtaining instances, IocServices is for registering the instances, definitions, and aliases involved in producing the services to be obtained.
This separation allows for containers that are fully "open" by implementing both interfaces on the same class, and for containers that are "closed" in the sense that the services are encapsulated but not publicly modifiable.
Why IocContainer "Factory" and not IocContainer "Builder" ?
"Builder" implies calling public setup methods, then a build method. "Factory"
implies only a new method. Even if there are multiple steps to the factory
process, they are not accessible as public methods.
Why does IocProvider define provide() instead of register() ?
The method name register() is by far the majority choice for service provider
implementations. This standard breaks with that choice for consistency reasons.
Ioc-Interop opines that, unless the result is outright barbarous,
interface names and method names should mimic each other. Given a Provider
interface, its methods should provide(); given a register() method, its
interface should be a Registry or Registrant. Further, as with the other
interfaces herein, the word "service" should be incorporated into the method
name. This leaves few choices:
IocProvider::provide()(closer to the majority class name)IocServicesRegistrant::registerServices()(closer to the majority method name)
Ioc-Interop opts in favor of honoring the class name, and modeling the method name after it.
What about property and setter injection?
TBD: Supported indirectly as extenders. Implementors may add support as desired, perhaps in their IocDefinition implementations.
What about "action", "method", or "invoker" injection?
TBD: "Action" or "method" injection involves using a container to call a method (typically a controller action method) so that the container can injecting services to the typehinted parameters on that method. Implementors are encouraged to add their own implementations.
Why an IocDefinition at all?
TBD: Is a place to collect all building logic: factory, autowiring, extenders. Also a starting point for implementors to add arguments, setter injection, property injection, etc. Could put these on IocServices but that expands the API too much.
Why IocDefinition and not IocServiceDefinition ?
TBD: "Definition" is the only name used in the projects, when such functionality is offered. Ioc-Interop breaks with this in favor of the more-formal design pattern name "Builder".
What about contextual or environmental binding?
TBD: When two different classes need different implementations of the same interface. Relatively rare (only 2 projects). Another variation is that a class needs different implementations in different environemt (e.g. web vs cli vs test). Ioc-Interop finds little to standardize on as far as an API. Implementors are encouraged to implement IocParameterResolver attributes to note the specific service to inject for a specific parameter.
What about setter and property injection?
TBD: Property injection rare; setter injection less rare but introduces other problems (when/how to resolve arguments?). Ioc-Interop favors constructor injection as all projects support it. Implementors encouraged to add setter and property injection on IocDefinition implementations. Consumers may add service extenders for post-instantiation logic.
What about lifetime scopes?
TBD: Ioc-Interop asserts that all services should be shared (aka "singleton" or "request-scoped") services. PHP-DI outlines the case. Consumers needing transient, prototype, or new-every-time service instances are encouraged to depend on shared factory services instead, or to build custom factories that encapsulate an implementation of IocInstanceFactory.
Why a separate IocInstanceFactory ?
TBD: No newService() method, but useful to have object-creation capabality
without having to pass around both a container and a class resolver.