Unbox
Fast, simple, easy-to-use DI container
Install / Use
/learn @mindplay-dk/UnboxREADME

Unbox is a fast, simple, opinionated dependency injection container, with a gentle learning curve.
Compatible with PSR-11.
To upgrade from an older (pre-3.x) version, please see the upgrade guide.
Installation
With Composer: require mindplay/unbox
Introduction
This library implements a dependency injection container with a very small footprint, a small number of concepts and a reasonably short learning curve, good performance, and quick and easy configuration relying mainly on the use of closures for IDE support.
The container is capable of resolving constructor arguments, often automatically, with as little
configuration as just the class-name. It will also resolve arguments to any callable, including
objects that implement __invoke(). It can also be used as a generic factory class, capable of
creating any object for which the constructor arguments can be resolved - the common use-case
for this is in your own factory classes, e.g. a controller factory or action dispatcher.
Quick Overview
Below, you can find a complete guide and full documentation - but to give you an idea of what this library does, let's open with a quick code sample.
For this basic example, we'll assume you have the following related types:
interface CacheInterface {
// ...
}
class FileCache implements CacheInterface {
public function __construct($path) { ... }
}
class UserRepository {
public function __construct(CacheInterface $cache) { ... }
}
Unbox has a two-stage life-cycle. The first stage is the creation of a ContainerFactory - this
class provides bootstrapping and configuration facilities. The second stage begins with a call
to ContainerFactory::createFactory() which creates the actual Container instance, which
provides the facilities enabling client-code to invoke functions and constructors, etc.
Let's bootstrap a ContainerFactory with those dependencies, in a "bootstrap" file somewhere:
use mindplay\unbox\ContainerFactory;
$factory = new ContainerFactory();
// register a component named "cache":
$factory->register("cache", function ($cache_path) {
return new FileCache($cache_path);
});
// register "CacheInterface" as a component referencing "cache":
$factory->alias(CacheInterface::class, "cache");
// register "UserRepository" as a component:
$factory->register(UserRepository::class);
Then configure the missing $cache_path for the cache component, add that to a "config" file somewhere:
$factory->set("cache_path", "/tmp/cache");
Now that the ContainerFactory is fully bootstrapped, we're ready to create a Container:
$container = $factory->createContainer();
In this simple example, we're now done with ContainerFactory, which can simply fall out of
scope. (In more advanced scenarios, such as long-running React or
PHP-PM applications, you might want to maintain a
reference to ContainerFactory, so you can create a fresh Container for each request.)
You can now take your UserRepository out of the Container, either by asking for it directly:
$users = $container->get(UserRepository::class);
Or, by using a type-hinted closure for IDE support:
$container->call(function (UserRepository $users) {
$users->...
});
To round off this quick example, let's say you have a controller:
class UserController
{
public function __construct(UserRepository $users)
{
// ...
}
public function show($user_id, ViewEngine $view, FormHelper $form, ...)
{
// ...
}
}
Using the container as a factory, you can create an instance of any controller class:
$controller = $container->create(UserController::class);
Finally, you can dispatch the show() action, with dependency injection - as a naive example,
we're simply going to inject $_GET directly as parameters to the method:
$container->call([$controller, "show"], $_GET);
Using $_GET as parameters to the call, the $user_id argument to UserController:show() will
be resolved as $_GET['user_id'].
That's the quick, high-level overview.
API
If you're already comfortable with dependency injection, and just want to know what the API looks
like, below is a quick overview of the ContainerFactory API:
register(string $type) # register a component (for auto-creation)
register(string $type, array $map) # ... with custom constructor arguments
register(string $name, string $type) # ... with a specific name for auto-creation
register(string $name, string $type, array $map) # ... and custom constructor arguments
register(string $name, callable $func) # ... with a custom creation function
register(string $name, callable $func, array $map) # ... and custom arguments to that closure
set(string $name, mixed $value) # directly insert an existing component
add(ProviderInterface $provider) # register a configuration provider
alias(string $new_name, string $ref_name) # make $ref_name available as $new_name
configure(callable $func) # manipulate a component upon creation
configure(callable $func, array $map) # ... with custom arguments to the closure
configure(string $name, callable $func) # ... for a component with a specific name
configure(string $name, callable $func, array $map) # ... with custom arguments
ref(string $name) : BoxedValueInterface # create a boxed reference to a component
registerFallback(ContainerInterface $container) # register a fallack container
requires(string $requirement, string $description) # defines a Requirement
provides(string $requirement, string $description) # fulfills an abstract Requirement
createContainer() : Container # create a bootstrapped Container instance
The following provides a quick overview of the Container API:
get(string $name) : mixed # unbox a component
has(string $name) : bool # check if a component is defined/exists
isActive(string $name) : bool # check if a component has been unboxed
call(callable $func) : mixed # call any callable an inject arguments
call(callable $func, array $map) : mixed # ... and override or add missing params
create(string $class_name) : mixed # invoke a constructor and auto-inject
create(string $class_name, array $map) : mixed # ... and override or add missing params
If you're new to dependency injection, or if any of this baffles you, don't panic - everything is covered in the guide below.
Terminology
The following terminology is used in the documentation below:
-
Callable: refers to the
callablepseudo-type as defined in the PHP manual. -
Component: any object or value registered in a container, whether registered by class-name, interface-name, or some other arbitrary name.
-
Singleton: when we say "singleton", we mean there's only one component with a given name within the same container instance; of course, you can have multiple container instances, so each component is a "singleton" only within the same container.
-
Dependency: in our context, we mean any registered component that is required by another component, by a constructor (when using the container as a factory) or by any callable.
Dependency Resolution
Any argument, whether to a closure being manually invoked, or to a constructor being automatically invoked as part of resolving a longer chain of dependencies, is resolved according to a consistent set of rules - in order of priority:
-
If you provide the argument yourself, e.g. when registering a component (or configuration function, or when invoking a callable) this always takes precedence. Arguments can include boxed values, such as (typically) references to other components, and these will be unboxed as late as possible.
-
Type-hints is the preferred way to resolve singletons, e.g. types of which you have only one instance (or one "preferred" instance) in the same container. Singletons are usually registered under their class-name, or interface-name, or sometimes both.
-
Parameter names, e.g. components matching the precise argument name (without
$) - this works only when it's safe, which it is in most cases, the only exception being constructors invoked viacreate()where component names in the Container happen to match parameter names in the constructor. (constructor arguments given via the$maparguments are of course safe, too.) -
A default parameter value, if provided, will be used as a last resort - this can be useful in cases such as
function ($db_port = 3306) { ... }, which allows for optional configuration of simple values with defaults.
For dependencies resolved using type-hints, the parameter name is ignored - and vice-versa: if a dependency i
Related Skills
node-connect
346.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.8kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。

