PHPackages                             kelvinmo/lightcontainer - 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. [PSR &amp; Standards](/categories/psr-standards)
4. /
5. kelvinmo/lightcontainer

ActiveLibrary[PSR &amp; Standards](/categories/psr-standards)

kelvinmo/lightcontainer
=======================

A lightweight, autowiring, PSR-11 compliant container

v0.3.0(2y ago)317[2 issues](https://github.com/kelvinmo/lightcontainer/issues)BSD-3-ClausePHPPHP ^7.2 || ^8.0CI passing

Since Sep 4Pushed 4mo ago1 watchersCompare

[ Source](https://github.com/kelvinmo/lightcontainer)[ Packagist](https://packagist.org/packages/kelvinmo/lightcontainer)[ RSS](/packages/kelvinmo-lightcontainer/feed)WikiDiscussions master Synced 5d ago

READMEChangelogDependencies (3)Versions (9)Used By (0)

LightContainer
==============

[](#lightcontainer)

LightContainer is a simple [PSR-11](https://www.php-fig.org/psr/psr-11/) compliant dependency injection container that supports autowiring.

[![Latest Stable Version](https://camo.githubusercontent.com/598a2e01c9ff06fab23f772ebe35c51f0adb650ead254692d4b5d2348feab774/68747470733a2f2f706f7365722e707567782e6f72672f6b656c76696e6d6f2f6c69676874636f6e7461696e65722f762f737461626c65)](https://packagist.org/packages/kelvinmo/lightcontainer)[![build](https://github.com/kelvinmo/lightcontainer/workflows/CI/badge.svg)](https://github.com/kelvinmo/lightcontainer/actions?query=workflow%3ACI)

Table of contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Configuration](#configuration)
    - [Introduction](#introduction)
    - [Instantiation options](#instantiation-options)
        - [Aliases](#aliases)
        - [Constructor arguments](#constructor-arguments)
        - [Setter injection](#setter-injection)
        - [Modifying resolved instances](#modifying-resolved-instances)
        - [Shared instances](#shared-instances)
        - [Options for autowired resolvers](#options-for-autowired-resolvers)
    - [Global aliases](#global-aliases)
    - [Multiple shared instances](#multiple-shared-instances)
    - [Custom instantiation](#custom-instantiation)
    - [Storing arbitrary values](#storing-arbitrary-values)
    - [Configuring services](#configuring-services)
    - [Loading configurations](#loading-configurations)
- [Reference](#reference)
    - [Resolvers](#resolvers)
    - [Autowiring](#autowiring)
    - [Instantiation options reference](#instantiation-options-reference)
- [Licence](#licence)

Requirements
------------

[](#requirements)

- PHP 7.2 or later

Installation
------------

[](#installation)

You can install via [Composer](http://getcomposer.org/).

```
composer require kelvinmo/lightcontainer
```

Basic Usage
-----------

[](#basic-usage)

Consider the following set of classes:

```
class A {}

class B {
    public function __construct(A $a) {}
}

class C {
    public function __construct(B $b) {}
}
```

Normally, if you want to create `C`, you will need to do the following:

```
$c = new C(new B(new A()));
```

However, with LightContainer you can do this:

```
$container = new LightContainer\Container();
$c = $container->get(C::class);
```

Note that absolutely no configuration of the container is required. LightContainer figures out the dependencies based on the type hints provided by the constructor parameters, and then configures itself to create the necessary objects. This is *autowiring*.

Further details on how autowiring works can be found in the [reference](#autowiring).

Configuration
-------------

[](#configuration)

### Introduction

[](#introduction)

You can configure the container through the `set` method. This method can be called with up to two parameters: a string *entry identifier*, and an optional value. The main ways you can use this method is used to configure the container are set out in this section, with further details can be found in the [reference](#reference) section below.

### Instantiation options

[](#instantiation-options)

You can set instantiation options for a particular class by calling the `set`method with the name of the class. This returns a *class resolver* object, which then allows you to specify options.

```
$container->set(D::class)->shared();
```

Multiple options can be set by chaining up the methods.

```
$container->set(D::class)
    ->alias(FooInterface::class, FooInterfaceImpl::class)
    ->shared();
```

Certain instantiation options can be set using attributes.

Instantiation options are cleared every time you call the `set` method on the container object. To set additional options on the same resolver, use the `getResolver` method to retrieve the existing resolver.

```
$container->set(D::class)->shared();

// Correct
$container->getResolver(D::class)->alias(FooInterface::class, FooInterfaceImpl::class);

// Incorrect - shared() will disappear
$container->set(D::class)->alias(FooInterface::class, FooInterfaceImpl::class);
```

#### Aliases

[](#aliases)

Consider the following declarations:

```
interface FooInterface {}

class FooInterfaceImpl implements FooInterface {}

class FooInterfaceSubclass extends FooInterfaceImpl {}

class D {
    public function __construct(FooInterface $i) {}
}
```

`D` cannot be instantiated by autowiring as `FooInterface` is an interface. We need to specify which concrete class that implements the interface we want. We can do this by using the `alias` method on the resolver.

```
$container->set(D::class)->alias(FooInterface::class, FooInterfaceImpl::class);

$d = $container->get(D::class);
// This is equivalent to new D(new FooInterfaceImpl())
```

You can set multiple aliases with a single call by passing an array.

```
$container->set(E::class)->alias([
    FooInterface::class => FooInterfaceImpl::class,
    BarInterface::class => BarInterfaceImpl::class
]);
```

Aliases can refer to other aliases. In the example below, when the container looks for `FooInterface`, it finds `FooInterfaceImpl` as an alias, but that in turn references `FooInterfaceSubclass`. In the end, `FooInterfaceSubclass`is created. It can also be seen that aliases can be made for classes as well as interfaces.

```
$container->set(D::class)
    ->alias(FooInterface::class, FooInterfaceImpl::class)
    ->alias(FooInterfaceImpl::class, FooInterfaceSubclass::class);

$d = $container->get(D::class);
// This is equivalent to new D(new FooInterfaceSubclass())
```

If you want to define an alias applicable for all classes in the container, consider using a [global alias](#global-aliases).

#### Constructor arguments

[](#constructor-arguments)

Consider the following set of classes:

```
class F {}

class G {
    public function __construct(F $f, string $host, int $port = 80) {}
}
```

`G` cannot be instantiated by autowiring as we need to, as a minimum, specify `$host`. We can do this by using the `args` method on the resolver.

```
$container->set(G::class)->args('example.com', 8080);

// Multiple calls also work
$container->set(G::class)
    ->args('example.com')
    ->args(8080);

// Optional parameters can be omitted
$container->set(G::class)->args('example.com');
```

The `args` method can only be used to specify parameters that:

- do not have a type hint; or
- have a type hint for an internal type (e.g. int, string, bool); or
- for PHP 8, have a type hint that is a union type.

Other parameters (i.e. those with type hints for classes or interfaces) are ignored when processing the `args` method. To manipulate how these parameters are treated, use [aliases](#aliases) or [global aliases](#global-aliases).

```
class H {
    // PHP 8 is required for this declaration to work
    public function __construct(F $f, int|string $bar, A $a, $baz) {}
}

// This sets $bar to 'one' and $baz to 'two'
$container->set(H::class)->args('one', 'two');
```

There may be times where you need to use something from the container as an argument. This may occur if the parameter in the declaration is not type hinted (and so you can't use `alias`), or if you want to specify a particular [named instance](#multiple-shared-instances). In these cases you will need to wrap the entry identifier with `Container::ref()`.

```
class I {
    /**
     * @param FooInterface $foo
     */
    public function __construct($foo) {}
}

// See $foo to the FooInterfaceImpl from the container (which may be
// instantiated if required)
$container->set(I::class)->args(LightContainer\Container::ref(FooInterfaceImpl::class));

// Set $foo to named instance @foo from the container
$container->set(I::class)->args(LightContainer\Container::ref('@foo'));
```

#### Setter injection

[](#setter-injection)

In addition to dependency injection via the constructor, LightContainer also supports injecting dependencies via setter methods. Consider this declaration:

```
class J {
    public function setA(A $a) {}
}
```

To get the container to call `setA` to inject `A` whenever `J` is created, use the `call` method to specify the method to call. [Autowiring](#autowiring)works in the same way as for constructor injection.

```
$container->set(J::class)->call('setA');
```

The `call` method also takes additional arguments, which will be passed on to the setter method. The rules for specifying additional arguments are the same as for [constructors](#constructor-arguments). In addition, [aliases](#aliases) and [global aliases](#global-aliases) are also resolved in the same way as for constructor injection.

```
class K {
    public function setFoo(FooInterface $f, bool $debug);
}

$container->set(K::class)
    ->alias(FooInterface::class, FooInterfaceImpl::class)
    ->call('setFoo', false);
```

**NOTE.** Aliases apply to the constructor and *all* setter methods. You cannot define aliases that only applies to a particular setter method.

#### Modifying resolved instances

[](#modifying-resolved-instances)

In addition to setter injection, LightContainer supports other forms of modifying the resolved object before passing it back to the caller.

To do this, create an instance of a class that implements `LightContainer\InstanceModifierInterface`, then pass it to the container using the `modify` method.

```
class Modifier implements InstanceModifierInterface {
    public function modify(object $obj, LightContainerInterface $container): object {}
}

class ModifyMe {}

$modifier = new Modifier();
$container->set(ModifyMe::class)->modify($modifier);

// The container will call $modifier->modify() before returning the instance
$modify_me = $container->get(ModifyMe::class);
```

#### Shared instances

[](#shared-instances)

There may be times where you want the same instance of a class to be returned by the container no matter how many times it is resolved. This may be the case if the object is meant to be used as a singleton.

To do this, call the `shared` method on the resolver.

```
$container->set(A::class)->shared();

// $a1 and $a2 are the same instance
$a1 = $container->get(A::class);
$a2 = $container->get(A::class);
```

You can also switch off this behaviour by calling `shared(false)`. However, this only works if the shared instance has *not* been created (i.e. if `get` hasn't been called). Otherwise this will throw an exception.

You can also set this option using the `LightContainer\Attributes\Shared`attribute:

```
#[Shared]
class A {}
$container->set(A::class);

// This is equivalent to:
// $container->set(A::class)->shared();
```

#### Options for autowired resolvers

[](#options-for-autowired-resolvers)

*Autowired resolvers* are created automatically by the container as part of the autowiring process. As autowired resolvers do not have an explicit entry in the container, they inherit the instantiation options for the immediate ancestor class that have an entry in the container.

For example, in the declaration below, the resolver for `N` is autowired as there is no explicit entry in the container for `N`. Because `L` is an ancestor class for `N` and it has an entry in the container, `N`inherits all the instantiation options from `L`.

```
class L {}
class M extends L {}
class N extends M {}

$container->set(L::class)->shared();
// $n1 and $n2 are the same instance because
// N inherited 'shared' from L
$n1 = $container->get(N::class);
$n2 = $container->get(N::class);
```

To control this behaviour, call `propagate(false)` on the resolver. This will stop instantiation options from being propagated to autowired resolvers created for its subclasses. For example, in the example below, `N` is an autowired resolver, but does not inherit the options from `L` because `propagate` for `L` is set to false. Therefore `L` retains the default behaviour of not creating shared instances.

```
$container->set(L::class)->shared()->propagate(false);
// $n1 and $n2 are the different instances because
// L does not propagate its options to autowired
// subclasses
$n1 = $container->get(N::class);
$n2 = $container->get(N::class);
```

You can also set this option using the `LightContainer\Attributes\Propagate`attribute:

```
#[Propagate(false)]
class L {}
```

To set instantiation options for *all* autowired resolvers, you can use the special wildcard resolver `*`.

```
// Set 'shared' to true for all autowired resolvers
$container->set('*')->shared();
```

### Global aliases

[](#global-aliases)

Instead of defining [aliases](#aliases) at the class level, you can define a *global alias*, which applicable for all classes created by the container. This can be done by calling the `set` method with the name of the class or interface to be replaced in the first argument, and the name of the concrete class as the second argument.

Note that the class or interface does not actually need to exist. LightContainer only checks whether the first argument only contains characters that can be used as the fully qualified name of a type (i.e. including the namespace), and if it does, it treats the entry as a global alias. Otherwise it treats the entry as a [named instance](#multiple-shared-instances).

```
$container->set(FooInterface::class, FooInterfaceImpl::class);
```

If an alias is also defined at the class level, that definition takes precedence over the global alias.

```
class FooInterfaceImplGlobal implements FooInterface {}
class FooInterfaceImplForD implements FooInterface {}

$container->set(FooInterface::class, FooInterfaceImplGlobal::class);
$container->set(D::class)->alias(FooInterface::class, FooInterfaceImplForD::class);

// D uses FooInterfaceImplForD instead of FooInterfaceImplGlobal
$container->get(D::class);
```

Unlike aliases defined at the class level, you can set instantiation options for global aliases. These are applied to objects instantiated by referring to the identifier of the global alias instead of the concrete class. The following instantiation options are supported:

- `alias` (additional [class aliases](#aliases))
- `args` ([constructor arguments](#constructor-arguments))
- `call` ([setters](#setter-injection))
- `shared` ([shared instances](#shared-instances))

Instantiation options specified in the global alias takes precedence over the options defined for the concrete class.

### Multiple shared instances

[](#multiple-shared-instances)

The [`shared` instantiation option](#shared-instances) will provide a single shared instance of a class for the entire container. However, you may want to have multiple instances of the same class that are shared across the container.

```
class O {
    public function __construct(\PDO $db) {}
}

$container->set('@prod_db', \PDO::class)
    ->args('mysql:host=example.com;dbname=prod');
$container->set('@dev_db', \PDO::class)
    ->args('mysql:host=example.com;dbname=dev');
$container->set(O::class)->alias(\PDO::class, '@prod_db');
```

Named instances are simply a special kind of [global aliases](#global-aliases), so they can be used anywhere where an alias is accepted. In particular, they are injected into constructors and setter methods using `alias`, not `args`.

```
// Correct
$container->set(O::class)->alias(\PDO::class, '@prod_db');

// Incorrect
$container->set(O::class)->args('@prod_db');
```

Furthermore, because named instances are simply a special kind of global aliases, you can set instantiation options for them (other than `shared`).

### Custom instantiation

[](#custom-instantiation)

Instead of using LightContainer's default instantiation function with autowiring, you can specify a custom factory function to create objects of a particular class. This is done by calling `set`method with a callable pointing to the factory function as the second argument.

The container is passed on as the first parameter to the factory function.

```
$container->set(CustomClass::class, function ($container) {
    $a = new CustomClass();
    // Custom code here
    return $a;
});
```

[Instantiation options](#instantiation-options) are not available to custom factory functions. [Named instances](#multiple-shared-instances) are also not available, as sharing is not supported for factory functions. Calls to named instances will simply result in the factory function being called, which may result in a different instance of the object being returned.

### Storing arbitrary values

[](#storing-arbitrary-values)

You can store arbitrary values in the container by calling the `set`method with a non-string, non-callable value as the second argument.

```
$container->set('@port', 8080);
$container->set('@config', ['foo' => 'bar']);

// Returns 8080
$container->get('@port');
```

It is recommended that you use an identifier that cannot be confused with a class name (e.g. by adding an invalid character for a class name, like `@`in the example above).

**WARNING.** If you want to store `null`, a string or a callable, you will need to wrap it with using `Container::value()`. Otherwise LightContainer will treat it as a [global alias](#global-aliases) or a [custom instantiation function](#custom-instantiation).

```
// Correct
$container->set('@host',
LightContainer\Container::value('example.com'));

// Incorrect
$container->set('@host', 'example.com');
```

### Configuring services

[](#configuring-services)

In certain situations, concrete classes are used to implement services, which are defined using interfaces. Containers are then used to link the services to their implementations.

Services are identified by either:

- using the `LightContainer\Attributes\Service` attribute; or
- extending `LightContainer\ServiceInterface`.

```
// PHP 8
#[Service]
interface FooService {}
#[Service]
interface BarService {}

// PHP 7
interface FooService extends ServiceInterface {}
interface BarService extends ServiceInterface {}

class Baz implements FooService, BarService {}
```

Instead of calling the `set` method for each service separately, you can call the `populate` method on the concrete class. This method uses reflection to determine which services the class implements, and then creates container entries accordingly.

```
// This:
$container->populate(Baz::class);

// is equivalent to:
$container->set(FooService::class, Baz::class);
$container->set(BarService::class, Baz::class);
```

### Loading configurations

[](#loading-configurations)

Instead of configuring the container programmatically, you can also load a pre-defined configuration directly into the container using the `load` method. This method takes the container configuration as a plain PHP array, which you can populate using whichever method you want (e.g. loading from a JSON or YAML file).

```
try {
    $config = [
        'FooInterface' => 'FooInterfaceImpl',
        '@host' => [ '_value' => 'example.com' ]
    ];
    $container->load($config);
} catch (LightContainer\Loader\LoaderException $e) {

}
```

See the [configuration format](docs/configuration_format.md) documentation for details on how the configuration array should be structured. Note that only a subset of LightContainer's features are supported by this format.

Reference
---------

[](#reference)

### Resolvers

[](#resolvers)

At its core, LightContainer stores a mapping between an entry identifier and a resolver. A *resolver* is an object that implements `LightContainer\Resolvers\ResolverInterface`, which the container uses to resolve to an object or some other value whenever the `get` method is called.

The main kind of resolver is a *class resolver*, which resolves to an object by instantiating it from the specified class. Other key kinds of resolvers include a *reference resolver*, which looks up another entry in the container, and a *value resolver*, which simply returns a specified value.

The `set` method takes the string entry identifier and the specified value, creates and registers a resolver, and then returns it to the user. The kind of resolver the method creates depends on the format of the entry identifier, and the type of value that is specified. These are set out in the table below.

IdentifierValueResolverDescriptionName of an existing classNoneClass resolverSets [instantiation options](#instantiation-options) for that classA valid type name (whether or not it exists)A stringReference resolverSets a [global alias](#global-aliases)`*`NoneClass resolver\*Sets default [instantiation options](#instantiation-options) for autowired resolversAny other stringA stringReference resolverCreates a [named instance](#multiple-shared-instances)Any other stringA callableFactory resolverSets a [custom instantiation function](#custom-instantiation)Any other stringAny other valueValue resolverStores an [arbitrary value](#storing-arbitrary-values) in the containerThe class resolver returned for `*` is a special kind of class resolver. It cannot be called to resolve to an actual object.

### Autowiring

[](#autowiring)

Autowiring works by using PHP reflection to examine the parameters of the constructor (or setter method). If any parameter has a type hint, and the hinted type is not a built-in literal type, it looks up the container to see if there is an existing entry with the type as the entry identifier. If an entry does not exist, but the specified type is a class and the class exists, the container then creates a class resolver (the *autowired resolver*) for that type automatically. The instantiation options for this autowired resolver are set based on the [propagation rules](#options-for-autowired-resolvers) described above.

Note that when injecting parameters into the constructor or setter method, the nullable market (`?`) and default value declarations are initially ignored. The container will try to create and inject an instance of a particular type even it is declared nullable or contain a default value. Only if the container cannot create an instance will a null or default value be used.

```
interface ImplementedInterface {}
class InterfaceImplementation implements ImplementedInterface {}

interface UnimplementedInterface {}

class P1 {
    function __construct(?ImplementedInterface $i) {}
}
class P2 {
    function __construct(ImplementedInterface $i = null) {}
}

class Q1 {
    function __construct(?UnimplementedInterface $i) {}
}
class Q2 {
    function __construct(UnimplementedInterface $i = null) {}
}

$container->set(ImplementedInterface::class, InterfaceImplementation::class);

// $p1->i and $p2->i will both contain a InterfaceImplementation,
// as an alias is registered for ImplementedInterface and the aliased class
// exists
$p1 = $container->get(P1::class);
$p2 = $container->get(P2::class);

// $q1->i and $q2->i will both be null, as there is no alias registered for
// UnimplementedInterface
$q1 = $container->get(Q1::class);
$q2 = $container->get(Q2::class);
```

In order to pass a null as an argument to a nullable type, you have to use `alias` to explicitly declare an alias to null.

```
$container->set(P1::class)->alias(ImplementedInterface::class, null);
$container->set(P2::class)->alias(ImplementedInterface::class, null);

// $p1->i and $p2->i are now both null
$p1 = $container->get(P1::class);
$p2 = $container->get(P2::class);
```

### Instantiation options reference

[](#instantiation-options-reference)

The following instantiation options are available for class resolvers. These options (apart from `propagate`) are also available for [global aliases](#global-aliases).

OptionDefaultDescription[`alias`](#aliases)noneAliases for type hinted arguments for the constructor and setter methods[`args`](#constructor-arguments)noneSet other arguments for the constructor[`call`](#setter-injection)noneCall setter methods[`modify`](#modifying-resolved-instances)noneModify resolved instances[`propagate`](#options-for-autowired-resolvers)trueWhether instantiation options will propagate to autowired resolvers for subclasses[`shared`](#shared-instances)false (except for [named instances](#multiple-shared-instances))Whether the instance resolved is shared across the containerLicence
-------

[](#licence)

BSD 3 clause

###  Health Score

29

—

LowBetter than 59% of packages

Maintenance38

Infrequent updates — may be unmaintained

Popularity9

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 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 ~90 days

Recently: every ~152 days

Total

8

Last Release

1083d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/467c1767396cee916f7dfb3a04f0dcef1714b72940a27fee0b58f96a8aa3b608?d=identicon)[kelvinmo](/maintainers/kelvinmo)

---

Top Contributors

[![kelvinmo](https://avatars.githubusercontent.com/u/1594601?v=4)](https://github.com/kelvinmo "kelvinmo (179 commits)")

---

Tags

containerdependency-injectionpsr-11containerPSR-11dependency-injectiondi

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/kelvinmo-lightcontainer/health.svg)

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

###  Alternatives

[php-di/php-di

The dependency injection container for humans

2.8k48.9M994](/packages/php-di-php-di)[slince/di

A flexible dependency injection container

20260.4k6](/packages/slince-di)

PHPackages © 2026

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