PHPackages                             ioc-interop/ioc-interop - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. ioc-interop/ioc-interop

ActiveLibrary[Framework](/categories/framework)

ioc-interop/ioc-interop
=======================

Interoperable IOC container, factory, and registry interfaces for PHP.

1.x-dev(2y ago)12MITPHP

Since Feb 1Pushed 2y ago1 watchersCompare

[ Source](https://github.com/ioc-interop/ioc-interop)[ Packagist](https://packagist.org/packages/ioc-interop/ioc-interop)[ RSS](/packages/ioc-interop-ioc-interop/feed)WikiDiscussions 1.x Synced 1mo ago

READMEChangelog (1)Dependencies (4)Versions (2)Used By (0)

`ioc-interop`
=============

[](#ioc-interop)

The `ioc-interop` package defines a common set of interfaces for inversion-of-control (IOC) container functionality, including factories and registries, used by dependency injection and service locator libraries.

Motivation
----------

[](#motivation)

The widely-used [container-interop](https://github.com/container-interop/container-interop) project started [11 years ago](https://github.com/container-interop/container-interop/commit/81dd0afe8346cf5956a1570dea6dd64d68575d31). It was finalized as [PSR-11](https://www.php-fig.org/psr/psr-11/) and packaged as psr/container [7 years ago](https://groups.google.com/g/php-fig/c/bU_iHdk38nE/m/bPlNocmICAAJ).

This was well before the advent of static analysis in PHP userland, with such tools as [PHPStan](https://phpstan.org/). However, even with its latest update, the PSR-11 [ContainerInterface](https://github.com/php-fig/container/blob/master/src/ContainerInterface.php) is resistant to static analysis because `get()` returns `mixed`.

That is, `ContainerInterface::get()` can return *anything*. The documentation is clear if indirect about this. It states that multiple calls to `get()` using the same entry identifier "SHOULD return the same value" but "different values might be returned."

This flexibility is useful, but it creates a great deal of uncertainty. Will the return be an object, array, string, resource, or something else? If an object, will it be a new standlone instance, or will it be an instance shared throughout the rest of the system? The `ContainerInterface::get()` consumer simply cannot tell.

Comparison and Overview
-----------------------

[](#comparison-and-overview)

The `ioc-interop` package remedies these and other issues revealed over the lifetime of PSR-11 as follows:

PSR-11`ioc-interop`Stores any kind of value (`mixed`).Stores only `object` values.Entry IDs are any non-empty string.Entry specifications are strings resolvable to a class/interface strings by static analysis. `*`The `get()` method may return anything.The `get()` method always returns a shared object instance.`*` This allows both for fully-qualified class names and for arbitrary strings that static analysis can map to fully-qualified class names.

Further, `ioc-interop` composes its [*IocContainer*](./src/IocContainer.php) interface from two other interfaces:

- [*IocFactory*](./src/IocFactory.php), with a `new()` method that always returns a **new** instance; and,
- [*IocRegistry*](./src/IocRegistry.php), with a `get()` method that always returns a **shared** instance.

These constraints allow for easier static analysis and better predictability. The separated interfaces also allow custom type-restricted factory and registry classes to use an *IocContainer* to delegate their creation and retrieval logic.

> N.b.: The `new()` method merely exposes the entry-creation logic that has to occur anyway when you `get()` an entry for the first time.

As with PSR-11, `ioc-interop` deals only with the aspects of containers, not the setting/definition/provider aspects.

Example: Static Analysis Annotation
-----------------------------------

[](#example-static-analysis-annotation)

Because different static analyzers currently use different annotations for templates and generics, `ioc-interop` does not include any static analysis annotations. However, `ioc-interop` implementations may add annotations using their preferred static analyzer.

Below is an example of how to add [PHPStan](https://phpstan.org/) annotations to an *IocContainer* implementation:

```
declare(strict_types=1);

use IocInterop\IocContainer;

class Container immplements IocContainer
{
    /**
     * @var array
     */
    protected array $instances = [];

    /**
     * Returns a shared instance of the specified class/interface.
     *
     * @template T
     * @param class-string $spec
     * @return T
     * @throws IocException when the shared instance cannot be returned.
     */
    public function get(string $spec) : object
    {
        if (! $this->has($spec)) {
            throw new ContainerException("Class/interface {$spec} does not exist.");
        }

        if (! $this->instances[$spec]) {
            $this->instances[$spec] = $this->new($spec);
        }

        /** @var T */
        return $this->instances[$spec];
    }

    /**
     * Can this container return an instance of the specified class/interface?
     *
     * @param class-string $spec
     */
    public function has(string $spec) : bool
    {
        return class_exists($spec) || interface_exists($spec);
    }

    /**
     * Returns a new instance of the specified class/interface.
     *
     * @template T
     * @param class-string $spec
     * @return T
     * @throws IocException when the new instance cannot be created.
     */
    public function new(string $spec) : object
    {
        if (! $this->has($spec)) {
            throw new ContainerException("Class/interface {$spec} does not exist.");
        }

        // logic to create $instance; may include binding of
        // interfaces and abstracts to concretes, delegation
        // of creation logic to other providers, etc.

        /** @var T */
        return $instance;
    }
}
```

```
declare(strict_types=1);

use Exception;
use IocInterop\IocException;

class ContainerException extends Exception implements IocException
{
}
```

Example: Type-Restricted Factory
--------------------------------

[](#example-type-restricted-factory)

For a type-restricted factory, inject and retain an *IocFactory* implementation, then typehint the return on your own creation method, and use the *IocFactory* to create the instance.

```
use IocInterop\IocFactory;

class CommandFactory
{
    public function __construct(protected IocFactory $iocFactory)
    {
    }

    public function newCommand(string $commandName) : Command
    {
        $class = ucfirst($commandName) . 'Command';
        return $this->iocFactory->new($class);
    }
}
```

Now, when instantiating the type-restricted factory, you may inject an *IocContainer* implementation, since the *IocContainer* interface extends the *IocFactory* interface.

The object creation logic of the *IocContainer* will be used for the type-restricted factory, and will not pollute the registry aspects of the *IocContainer*.

Example: Type-Restricted Registry
---------------------------------

[](#example-type-restricted-registry)

> N.b.: This might also be called a type-restricted service locator.

For a type-restricted registry, inject and retain an *IocRegistry* implementation, then typehint the return on your own retrieval method, and use the *IocRegistry* to retrieve the instance:

```
use IocInterop\IocRegistry;

class HelperRegistry
{
    public function __construct(protected IocRegistry $iocRegistry)
    {
    }

    /**
     * @param class-string $helperClass
     */
    public function getHelper(string $helperClass) : Helper
    {
        return $this->iocRegistry->get($helperClass);
    }
}
```

Now, when instantiating the type-restricted registry, you may inject an *IocContainer* implementation, since the *IocContainer* interface extends the *IocRegistry* interface.

The very same object instances used by the *IocContainer* will be used for the type-restricted registry.

###  Health Score

18

—

LowBetter than 8% of packages

Maintenance31

Infrequent updates — may be unmaintained

Popularity4

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity27

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~72 days

Total

2

Last Release

755d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/25754?v=4)[Paul M. Jones](/maintainers/pmjones)[@pmjones](https://github.com/pmjones)

---

Top Contributors

[![pmjones](https://avatars.githubusercontent.com/u/25754?v=4)](https://github.com/pmjones "pmjones (5 commits)")

###  Code Quality

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ioc-interop-ioc-interop/health.svg)

```
[![Health](https://phpackages.com/badges/ioc-interop-ioc-interop/health.svg)](https://phpackages.com/packages/ioc-interop-ioc-interop)
```

###  Alternatives

[laravel/telescope

An elegant debug assistant for the Laravel framework.

5.2k67.8M190](/packages/laravel-telescope)[spiral/roadrunner

RoadRunner: High-performance PHP application server and process manager written in Go and powered with plugins

8.4k12.2M84](/packages/spiral-roadrunner)[nolimits4web/swiper

Most modern mobile touch slider and framework with hardware accelerated transitions

41.8k177.2k1](/packages/nolimits4web-swiper)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k36.7M255](/packages/laravel-dusk)[laravel/prompts

Add beautiful and user-friendly forms to your command-line applications.

708181.8M591](/packages/laravel-prompts)[cakephp/chronos

A simple API extension for DateTime.

1.4k47.7M119](/packages/cakephp-chronos)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
