{"id":16676101,"url":"https://github.com/mindplay-dk/unbox","last_synced_at":"2025-08-20T18:33:09.619Z","repository":{"id":35057954,"uuid":"39192985","full_name":"mindplay-dk/unbox","owner":"mindplay-dk","description":"Fast, simple, easy-to-use DI container","archived":false,"fork":false,"pushed_at":"2024-05-28T15:35:00.000Z","size":282,"stargazers_count":46,"open_issues_count":0,"forks_count":7,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-12-07T08:50:21.856Z","etag":null,"topics":["dependency-injection","di-container","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mindplay-dk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-07-16T11:22:35.000Z","updated_at":"2024-05-31T13:59:16.000Z","dependencies_parsed_at":"2023-12-05T10:27:37.736Z","dependency_job_id":"abeb941a-9d1f-4651-863f-ba6e93f01a5b","html_url":"https://github.com/mindplay-dk/unbox","commit_stats":{"total_commits":131,"total_committers":4,"mean_commits":32.75,"dds":"0.48091603053435117","last_synced_commit":"5a54bb16ccd771f1c30fdf1642d98c5c2f26fae2"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindplay-dk%2Funbox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindplay-dk%2Funbox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindplay-dk%2Funbox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindplay-dk%2Funbox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mindplay-dk","download_url":"https://codeload.github.com/mindplay-dk/unbox/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230445926,"owners_count":18227060,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","di-container","php"],"created_at":"2024-10-12T13:09:11.905Z","updated_at":"2024-12-19T14:07:27.175Z","avatar_url":"https://github.com/mindplay-dk.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Unbox](.github/assets/unbox-logo.png)\n\n[![PHP Version](https://img.shields.io/badge/php-8.0%2B-blue.svg)](https://packagist.org/packages/mindplay/unbox)\n[![PHPStan](https://img.shields.io/badge/PHPStan-Level_8-blue)](https://phpstan.org/user-guide/rule-levels)\n[![Build Status](https://github.com/mindplay-dk/unbox/actions/workflows/ci.yml/badge.svg)](https://github.com/mindplay-dk/unbox/actions/workflows/ci.yml)\n[![Code Coverage](https://scrutinizer-ci.com/g/mindplay-dk/unbox/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/mindplay-dk/unbox/?branch=master)\n\nUnbox is a [fast](#benchmark), simple, [opinionated](#opinionated) dependency injection container,\nwith a gentle learning curve.\n\nCompatible with [PSR-11](https://www.php-fig.org/psr/psr-11/).\n\nTo upgrade from an older (pre-3.x) version, please see the [upgrade guide](UPGRADING.md).\n\n## Installation\n\nWith Composer: `require mindplay/unbox`\n\n## Introduction\n\nThis library implements a dependency injection container with a very small footprint, a small number\nof concepts and a reasonably short learning curve, good performance, and quick and easy configuration\nrelying mainly on the use of closures for IDE support.\n\nThe container is capable of resolving constructor arguments, often automatically, with as little\nconfiguration as just the class-name. It will also resolve arguments to any callable, including\nobjects that implement `__invoke()`. It can also be used as a generic factory class, capable of\ncreating any object for which the constructor arguments can be resolved - the common use-case\nfor this is in your own factory classes, e.g. a controller factory or action dispatcher.\n\n### Quick Overview\n\nBelow, you can find a complete guide and full documentation - but to give you an idea of what\nthis library does, let's open with a quick code sample.\n\nFor this basic example, we'll assume you have the following related types:\n\n```php\ninterface CacheInterface {\n    // ...\n}\n\nclass FileCache implements CacheInterface {\n    public function __construct($path) { ... }\n}\n\nclass UserRepository {\n    public function __construct(CacheInterface $cache) { ... }\n}\n```\n\nUnbox has a two-stage life-cycle. The first stage is the creation of a `ContainerFactory` - this\nclass provides bootstrapping and configuration facilities. The second stage begins with a call\nto `ContainerFactory::createFactory()` which creates the actual `Container` instance, which\nprovides the facilities enabling client-code to invoke functions and constructors, etc.\n\nLet's bootstrap a `ContainerFactory` with those dependencies, in a \"bootstrap\" file somewhere:\n\n```php\nuse mindplay\\unbox\\ContainerFactory;\n\n$factory = new ContainerFactory();\n\n// register a component named \"cache\":\n$factory-\u003eregister(\"cache\", function ($cache_path) {\n    return new FileCache($cache_path);\n});\n\n// register \"CacheInterface\" as a component referencing \"cache\":\n$factory-\u003ealias(CacheInterface::class, \"cache\");\n\n// register \"UserRepository\" as a component:\n$factory-\u003eregister(UserRepository::class);\n```\n\nThen configure the missing `$cache_path` for the `cache` component, add that to a \"config\" file somewhere:\n\n```php\n$factory-\u003eset(\"cache_path\", \"/tmp/cache\");\n```\n\nNow that the `ContainerFactory` is fully bootstrapped, we're ready to create a `Container`:\n\n```php\n$container = $factory-\u003ecreateContainer();\n```\n\nIn this simple example, we're now done with `ContainerFactory`, which can simply fall out of\nscope. (In more advanced scenarios, such as long-running [React](http://reactphp.org/) or\n[PHP-PM](https://github.com/php-pm/php-pm) applications, you might want to maintain a\nreference to `ContainerFactory`, so you can create a fresh `Container` for each request.)\n\nYou can now take your `UserRepository` out of the `Container`, either by asking for it directly:\n\n```php\n$users = $container-\u003eget(UserRepository::class);\n```\n\nOr, by using a type-hinted closure for IDE support:\n\n```php\n$container-\u003ecall(function (UserRepository $users) {\n    $users-\u003e...\n});\n```\n\nTo round off this quick example, let's say you have a controller:\n\n```php\nclass UserController\n{\n    public function __construct(UserRepository $users)\n    {\n        // ...\n    }\n\n    public function show($user_id, ViewEngine $view, FormHelper $form, ...)\n    {\n        // ...\n    }\n}\n```\n\nUsing the container as a factory, you can create an instance of any controller class:\n\n```php\n$controller = $container-\u003ecreate(UserController::class);\n```\n\nFinally, you can dispatch the `show()` action, with dependency injection - as a naive example,\nwe're simply going to inject `$_GET` directly as parameters to the method:\n\n```php\n$container-\u003ecall([$controller, \"show\"], $_GET);\n```\n\nUsing `$_GET` as parameters to the call, the `$user_id` argument to `UserController:show()` will\nbe resolved as `$_GET['user_id']`.\n\nThat's the quick, high-level overview.\n\n#### API\n\nIf you're already comfortable with dependency injection, and just want to know what the API looks\nlike, below is a quick overview of the `ContainerFactory` API:\n\n```php\nregister(string $type)                                 # register a component (for auto-creation)\nregister(string $type, array $map)                     # ... with custom constructor arguments\nregister(string $name, string $type)                   # ... with a specific name for auto-creation\nregister(string $name, string $type, array $map)       # ... and custom constructor arguments\nregister(string $name, callable $func)                 # ... with a custom creation function\nregister(string $name, callable $func, array $map)     # ... and custom arguments to that closure\n\nset(string $name, mixed $value)                        # directly insert an existing component\n\nadd(ProviderInterface $provider)                       # register a configuration provider\n\nalias(string $new_name, string $ref_name)              # make $ref_name available as $new_name\n\nconfigure(callable $func)                              # manipulate a component upon creation\nconfigure(callable $func, array $map)                  # ... with custom arguments to the closure\nconfigure(string $name, callable $func)                # ... for a component with a specific name\nconfigure(string $name, callable $func, array $map)    # ... with custom arguments\n\nref(string $name) : BoxedValueInterface                # create a boxed reference to a component\n\nregisterFallback(ContainerInterface $container)        # register a fallack container\n\nrequires(string $requirement, string $description)     # defines a Requirement\nprovides(string $requirement, string $description)     # fulfills an abstract Requirement\n\ncreateContainer() : Container                          # create a bootstrapped Container instance\n```\n\nThe following provides a quick overview of the `Container` API:\n\n```php\nget(string $name) : mixed                              # unbox a component\nhas(string $name) : bool                               # check if a component is defined/exists\nisActive(string $name) : bool                          # check if a component has been unboxed\n\ncall(callable $func) : mixed                           # call any callable an inject arguments\ncall(callable $func, array $map) : mixed               # ... and override or add missing params\n\ncreate(string $class_name) : mixed                     # invoke a constructor and auto-inject\ncreate(string $class_name, array $map) : mixed         # ... and override or add missing params\n```\n\nIf you're new to dependency injection, or if any of this baffles you, don't panic - everything is\ncovered in the guide below.\n\n## Terminology\n\nThe following terminology is used in the documentation below:\n\n  * **Callable**: refers to the `callable` pseudo-type\n    as [defined in the PHP manual](http://php.net/manual/en/language.types.callable.php).\n\n  * **Component**: any object or value registered in a container, whether registered by class-name,\n    interface-name, or some other arbitrary name.\n\n  * **Singleton**: when we say \"singleton\", we mean there's only one component with a given name\n    within the same container instance; of course, you can have multiple container instances, so\n    each component is a \"singleton\" only within the same container.\n\n  * **Dependency**: in our context, we mean any registered component that is required by another\n    component, by a constructor (when using the container as a factory) or by any callable.\n\n## Dependency Resolution\n\nAny argument, whether to a closure being manually invoked, or to a constructor being automatically\ninvoked as part of resolving a longer chain of dependencies, is resolved according to a consistent\nset of rules - in order of priority:\n\n  1. If you provide the argument yourself, e.g. when registering a component (or configuration\n     function, or when invoking a callable) this always takes precedence. Arguments can include\n     boxed values, such as (typically) references to other components, and these will be unboxed\n     as late as possible.\n\n  2. Type-hints is the preferred way to resolve singletons, e.g. types of which you have only one\n     instance (or one \"preferred\" instance) in the same container. Singletons are usually registered\n     under their class-name, or interface-name, or sometimes both.\n\n  3. Parameter names, e.g. components matching the precise argument name (without `$`) - this works\n     only when it's safe, which it is in most cases, the only exception being constructors invoked\n     via `create()` where component names in the Container happen to match parameter names in the\n     constructor. (constructor arguments given via the `$map` arguments are of course safe, too.)\n\n  4. A default parameter value, if provided, will be used as a last resort - this can be useful\n     in cases such as `function ($db_port = 3306) { ... }`, which allows for optional\n     configuration of simple values with defaults.\n\nFor dependencies resolved using type-hints, the parameter name is ignored - and vice-versa: if a\ndependency is resolved by parameter name, the type-hint is ignored, but will of course be checked\nby PHP when the function/method/constructor is invoked. Note that using type-hints either way is\ngood practice (when possible) as this provides self-documenting configurations with IDE support.\n\n## Guide\n\nIn the following sections, we'll assume that a `ContainerFactory` instance is in scope, e.g.:\n\n```php\nuse mindplay\\unbox\\ContainerFactory;\n\n$factory = new ContainerFactory();\n```\n\n### Bootstrapping\n\nThe most commonly used method to bootstrap a container is `register()` - this is the method\nthat lets you register a component for dependency injection.\n\nThis method generally takes one of the following forms:\n\n```php\nregister(string $type)                                 # register a component (for auto-creation)\nregister(string $type, array $map)                     # ... with custom constructor arguments\nregister(string $name, string $type)                   # ... with a specific name for auto-creation\nregister(string $name, string $type, array $map)       # ... and custom constructor arguments\nregister(string $name, callable $func)                 # ... with a custom creation function\nregister(string $name, callable $func, array $map)     # ... and custom arguments to that closure\n```\n\nWhere:\n\n  * `$name` is a component name\n  * `$type` is a fully-qualified class-name\n  * `$map` is a mixed list/map of parameters (see below)\n  * `$func` is a custom factory function\n\nWhen `$type` is used without `$name`, the component name is assumed to also be the name\nof the type being registered.\n\nThe `$map` argument is mixed list and/or map of parameters. That is, if you include\nparameters without keys (such as `['apple', 'pear']`) these are taken as being positional\narguments, while parameters with keys (such as `['lives' =\u003e 9]`) are matched against\nthe parameter name of the callable or constructor being invoked.\n\nWhen supplying custom arguments via `$map`, it is common to use `$factory-\u003eref('name')`\nto obtain a \"boxed\" reference to a component - when the registered component is created\n(on first use) any \"boxed\" arguments will be \"unboxed\" at that time. In other words, this\nenables you to supply other components as arguments \"lazily\", without activating them\nuntil they're actually needed.\n\nIf the callable `$func` is supplied, this is registered as your custom component creation\nfunction - dependency injection is done for this closure, so this is usually the best way\nto specify how a component should be created, if you care about IDE support. (You should!)\n\n#### Examples\n\nThe following examples are all valid use-cases of the above forms:\n\n  * `register(Foo::class)` registers a component by it's class-name, and will try to\n    automatically resolve all of it's constructor arguments.\n\n  * `register(Foo::class, ['bar'])` registers a component by it's class-name, and will\n    use `'bar'` as the first constructor argument, and try to resolve the rest.\n\n  * `register(Foo::class, [$factory-\u003eref(Bar::class)])` creates a boxed reference to\n    a registered component `Bar` and provides that as the first argument.\n\n  * `register(Foo::class, ['bat' =\u003e 'zap'])` registers a component by it's class-name\n    and will use `'zap'` for the constructor argument named `$bat`, and try to resolve\n    any other arguments.\n\n  * `register(Bar::class, Foo::class)` registers a component `Foo` under another name\n    `Bar`, which might be an interface or an abstract class.\n\n  * `register(Bar::class, Foo::class, ['bar'])` same as above, but uses `'bar'` as the\n    first argument.\n\n  * `register(Bar::class, Foo::class, ['bat' =\u003e 'zap'])` same as above, but, well, guess.\n\n  * `register(Bar::class, function (Foo $foo) { return new Bar(...); })` registers a\n    component with a custom factory function.\n\n  * `register(Bar::class, function ($name) { ... }, [$factory-\u003eref('db.name')]);`\n    registers a component creation function with a reference to a component \"db.name\"\n    as the first argument.\n\nIn effect, you can think of `$func` as being an optional argument.\n\nThe provided parameter values may include any `BoxedValueInterface`, such as (commonly) the boxed\ncomponent reference created by `ContainerFactory::ref()` - these will be unboxed as late as possible.\n\n#### Aliasing\n\nSometimes you need to register the same component under two different names - one common\nuse-case, is to register the same component both for a concrete and abstract type, e.g.\nfor a class and an interface.\n\nFor example, it's ordinary to register a cache component twice:\n\n```php\n$factory-\u003eregister(CacheInterface::class, function () {\n    return new FileCache();\n});\n\n$factory-\u003ealias(\"db.cache\", CacheInterface::class); // \"db.cache\" becomes an alias!\n\n$container = $factory-\u003ecreateContainer();\n\nvar_dump($container-\u003eget(\"db.cache\") === $container-\u003eget(CacheInterface::class)); // =\u003e bool(true)\n```\n\nUsing an alias, in this example, means that `\"db.cache\"` by default will resolve as\n`CacheInterface`, but gives us the ability to [override](#overrides) the definition of\n`\"db.cache\"` with a different implementation, without affecting other components which\nmight also be using `CacheInterface` as a default.\n\n#### Direct Insertion\n\nNot all dependencies are expensive to create - simple values (such as host-names and port-numbers)\ndo not benefit from deferred initialization with `register()`, and instead should be inserted\ninto the container directly:\n\n```php\n$factory-\u003eset(\"db.host\", \"localhost\");\n$factory-\u003eset(\"db.port\", \"12345\");\n```\n\nAnother common use-case for `set()` is to inject objects for which you can't defer creation.\n\n#### Overrides\n\nTo override an existing component, simply call `register()` with an already-registered\ncomponent name - this will completely replace an existing component definition.\n\nNote that overriding a component does *not* affect any registered configuration functions -\nit is therefore important that, if you do override a component, the new component must be\ncompatible with the replaced component. Configuration in general is covered below.\n\n#### Configuration\n\nTo perform additional configuration of a registered component, use the `configure()` method.\n\nThis method takes one of the following forms:\n\n```php\nconfigure(callable $func)\nconfigure(callable $func, array $map)\nconfigure(string $name, callable $func)\nconfigure(string $name, callable $func, array $map)\n```\n\nWhere:\n\n  * `$name` is the name of a component being configured\n  * `$func` is a function that configures the component in some way\n  * `$map` is a mixed list/map of parameters (as explained [above](#bootstrapping))\n\nThe callable `$func` will be called with dependency injection - the first argument of\nthis function is the component being configured; you should type-hint it (if possible, for\nIDE support) although you're not strictly required to. Any additional arguments will be\nresolved as well.\n\nThe optional array `$map` is a mixed list/map of parameters, as covered [above](#bootstrapping).\n\nIf no `$name` is supplied, the first argument from the given `$func` is used to infer the\ncomponent name from the type-hint.\n\nAs an example, let's say you've configured a `PDO` component:\n\n```php\n$factory-\u003eregister(PDO::class, function ($db_host, $db_name, $db_user, $db_password) {\n    $connection = \"mysql:host={$db_host};dbname={$db_name}\";\n\n    return new PDO($connection, $db_user, $db_password);\n});\n```\n\nIn a configuration file, simple values like `$db_host` can be inserted directly, e.g. with\n`$factory-\u003eset(\"db_host\", \"localhost\")` - but suppose you need to do something *after*\nthe connection is created? Here's where `configure()` comes into play:\n\n```php\n$factory-\u003econfigure(function (PDO $db) {\n    $db-\u003eexec(\"SET NAMES utf8\");\n});\n```\n\nNote that, in this example, `configure()` will infer the component name `\"PDO\"` from the\ntype-hint - in a scenario with multiple named `PDO` instances, you must explicitly specify\nthe component name as the first argument, e.g.:\n\n```php\n$factory-\u003econfigure(\"logger.pdo\", function (PDO $db) {\n    $db-\u003eexec(\"SET NAMES utf8\");\n});\n```\n\n##### Property or Setter Injection\n\nThis library doesn't support neither property nor setter injection, but both can be accomplished\nby just doing those things in a call to `configure()` - for example:\n\n```php\n$factory-\u003econfigure(function (Connection $db, LoggerInterface $logger) {\n    $db-\u003esetLogger($logger);\n});\n```\n\nIn this example, upon first use of `Connection`, a dependency `LoggerInterface` will be\nunboxed and injected via setter-injection. (We believe this approach is much safer than\noffering a function that accepts the method-name as an argument - closures are more powerful,\nmuch safer, and provide full IDE support, inspections, automated refactoring, etc.)\n\n##### Modification\n\nYou can use `configure()` to modify values (such as strings, numbers or arrays) in the container.\n\nFor example, let's say you have a middleware stack defined as an array:\n\n```php\n$factory-\u003eset(\"app.middleware\", function () {\n    return [new RouterMiddleware, new NotFoundMiddleware];\n);\n```\n\nIf you need to append to the stack, you can do this:\n\n```php\n$factory-\u003econfigure(\"app.middleware\", function ($middleware) {\n    $middleware[] = new CacheMiddleware();\n\n    return $middleware;\n});\n```\n\nNote the `return` statement - this is what causes the value to get updated in the container.\n\n##### Decoration\n\nThe [decorator](https://en.wikipedia.org/wiki/Decorator_pattern) pattern is another pattern\nthat can be implemented with `configure()` - for example, lets say you bootstrapped your\ncontainer with a product repository implementation and interface:\n\n```php\n$factory-\u003eregister(ProductRepository::class, function () { ... });\n\n$factory-\u003ealias(ProductRepositoryInterface::class, ProductRepository::class);\n```\n\nNow lets say you implement a cached product repository decorator - you can bootstrap this\nby creating and returning the decorator instance like this:\n\n```php\n$factory-\u003econfigure(function (ProductRepositoryInterface $repo) {\n    return new CachedProductRepository($repo);\n});\n```\n\nNote that, when replacing components in this manner, of course you must be certain that the\nreplacement has a type that can pass a type-check in the recipient constructor or method.\n\n#### Packaged Providers\n\nYou can package a set of `register()` and `configure()` calls for convenient reuse, by\nimplementing `ProviderInterface` - for example:\n\n```php\nclass MyProvider implements ProviderInterface\n{\n    public function register(ContainerFactory $factory)\n    {\n        $factory-\u003eregister(...);\n        $factory-\u003econfigure(...);\n        // ...\n    }\n}\n```\n\nYou can then easily bootstrap your projects with providers, e.g.:\n\n```php\n$factory-\u003eadd(new MyProvider);\n$factory-\u003eadd(new TestDependenciesProvider);\n$factory-\u003eadd(new DevelopmentDebugProvider);\n// ...\n```\n\nProviders of course can also call `ContainerFactory::add()` to bootstrap other providers - with\nthis in mind, you can make e.g. development or production setup for your app as easy as\ncalling e.g. `$container-\u003eadd(new DevelopmentProvider)` to provide complete bootstrapping\nfor a quick development setup. Even if somebody wanted to override some of the registrations\nin e.g. your default development setup, they can of course still do that, e.g. by calling\n`register()` again to override components as needed.\n\n##### Provider Requirements\n\nIn large, modular architectures, you may have many Providers with inter-dependencies, which\ncan become difficult to manage at scale.\n\nSince Providers exist *outside* the realm of the Container, the concept of Requirements can\nbe used to define verifiable provider interdependencies, which will be checked at the time\nwhen `createContainer()` is called.\n\nRequirements may be defined by calling `requires()`, and more than one Provider may specify the\nsame Requirement - possibly for different reasons, which may be described using the optional\n`$description` argument.\n\nRequirements may be fulfilled by calling either `register()` or `provides()`.\n\n###### Component Requirements\n\nProviders may depend on the consumer to manually register a component.\n \nFor example, the following Provider requires you to register a `PDO` connection instance:\n\n```php\nclass MyProvider implements ProviderInterface\n{\n    public function register(ContainerFactory $factory)\n    {\n        $factory-\u003erequires(PDO::class, \"a PDO instance connected to a MySQL database\");\n        \n        // ...\n    }\n}\n```\n\nAttempting to bootstrap this Provider, without manually registering the `PDO` instance, will\ngenerate an Exception, as soon as `createContainer()` is called - which is much easier to debug\nthan the `NotFoundException` you would otherwise get, and which might not occur until you try\nto resolve a component that actually depends on the database connection.\n\n###### Abstract Requirements\n\nProviders may have abstract Requirements - something that can't be expressed by a simple\ncomponent dependency.\n\nFor example, the following Provider requires you to simply indicate that you've bootstrapped\na \"payment gateway\" - whatever that means to the Provider in question:\n\n```php\nclass MyProvider implements ProviderInterface\n{\n    public function register(ContainerFactory $factory)\n    {\n        $factory-\u003erequires(\"acme.payment-gateway\", \"please refer to acme's documentation\");\n        \n        // ...\n    }\n}\n```\n\nAnother provider needs to explicitly indicate fulfillment of this abstract Requirement:\n\n```php\nclass MyPaymentProvider implements ProviderInterface\n{\n    public function register(ContainerFactory $factory)\n    {\n        $factory-\u003eprovides(\"acme.payment-gateway\");\n        \n        // ...\n    }\n}\n```\n\nNote that abstract Requirements should be a last resort - component dependencies are\n*generally* simpler and easier to understand. This feature exists primarily to support\ncomplex, large-scale modular frameworks.\n\n### Fallback Containers\n\nYou can use this feature to build layered architecture with different component life-cycles.\n\nNote that this type of architecture is less about reuse (which in most cases could be\nachieved more simply by just reusing providers) and more about separating dependencies\ninto architectural layers.\n\nThe most common use-case for this feature is in long-running \"daemons\", such as web-hosts,\nwhere this feature can be used to achieve separation of short-lived, request-specific\ncomponents from long-lived services. For example, controllers or session-models might be\nregistered in containers that get created and disposed with each request - while a database\nconnection or an SMTP client might be registered in a single fallback container that exists\nfor as long as the application is running, eliminating redundant start-up overhead.\n\nThis kind of separation is also useful in terms of architecture, where it forces you to be\ndeliberate and aware of dependencies on request-specific components, since these will not\nbe available in the long-lived container. Similarly, maybe your project has a console-based\nfront-end as well, where this type of architecture can be used to ensure your command-line\ndependencies are not available to the components of your web-host - and so on.\n\nIn practical terms, to register a fallback container, use the `registerFallback` method on\na `ContainerFactory` instance. Containers created by a factory with one or more registered\nfallbacks, will internally query fallbacks (in the order they were added) for any components\nthat haven't been registered in the container itself - effectively, this means that calls\nto `has` and `get` will propagate to any registered fallback containers.\n\nA typical approach is to register the container factory for short-lived services as a component\nin the long-lived main service container - for example:\n\n```php\n$app_factory = new ContainerFactory();\n\n// components we can reuse across many requests:\n\n$app_factory-\u003eregister(DatabaseConnection::class);\n\n// factory for containers for individual requests:\n\n$app_factory-\u003eregister(\"request-context\", function (ContainerInterface $app_container) {\n    $request_container_factory = new ContainerFactory();\n\n    // enable request-specific containers to look up long-lived services in the main container:\n\n    $request_container_factory-\u003eregisterFallback($app_container);\n\n    return $request_container_factory;\n});\n\n// we can now register short-lived components against the `request-context` container factory:\n\n$app_factory-\u003econfigure(\"request-context\", function (ContainerFactory $request_container_factory) {\n    $request_container_factory-\u003eregister(LoginController::class); // depends on DatabaseConnection\n});\n\n// now create the long-lived app container, e.g. in your \"index.php\" or CLI daemon script:\n\n$app_container = $app_factory-\u003ecreateContainer();\n```\n\nWith this bootstrapping in place, you can now create instances of the `request-context` container\nas needed, e.g. in a long-lived component that handles incoming web-requests:\n\n```php\n$request_container = $app_container-\u003eget(\"request-context\")-\u003ecreateContainer();\n\n$controller = $request_container-\u003eget(LoginController::class);\n```\n\nWhen the `$request_container` falls out of scope, any short-lived components such as the `LoginController`\nwill be released along with the container - while any long-lived components such as `DatabaseConnection`\nwill remain in the `$app_container`, with the same instance being passed to every new instance of the\ncontroller.\n\n### Using Containers\n\nObtaining the contents of a container by simply pulling components out of it can *seem* very convenient, and is therefore\ntempting - but usually wrong! You should [inform yourself](http://stackoverflow.com/questions/11316688/inversion-of-control-vs-dependency-injection-with-selected-quotes-is-my-unders/11319026#11319026)\nabout the difference and **avoid** using the container as a [service locator](https://en.wikipedia.org/wiki/Service_locator_pattern).\n\n**Rule of Thumb:**\n\n\u003e ***Never* use a Container to look up a component's own *direct* dependencies.**\n\nConversely, using a Container to look up dependencies on behalf of *other* components is usually okay.\n\nIn the following sections, we'll assume that a `Container` instance is in scope, e.g.:\n\n```php\n$factory = new ContainerFactory();\n\n// ... bootstrapping ...\n\n$container = $factory-\u003ecreateContainer();\n```\n\nThe most basic form of component access, is a direct lookup:\n\n```php\n$cache = $container-\u003eget(CacheInterface::class);\n$db_name = $container-\u003eget(\"db_name\");\n```\n\nThe more indirect form of component access, is an indirect lookup, by resolving parameters:\n\n```php\n$container-\u003ecall(function (CacheInterface $cache, $db_name) {\n    // ...\n});\n```\n\nThe result in these two examples, is the same - but it's important to note that, in the `call()`\nexample, the two arguments are being resolved in two different ways: the `CacheInterface` param\nis resolved by class-name, whereas the `$db_name` param is being resolved by parameter name.\n\nThe latter only works because the `$db_name` component is registered under that precise name -\nif it had been registered under a name such as `\"db.name\"`, the container would be unable to\nresolve this argument automatically; instead, you would have had to write:\n\n```php\n$container-\u003ecall(function (CacheInterface $cache, $name) {\n    // ...\n}, [\"name\" =\u003e $container-\u003eref(\"db.name\")]);\n```\n\nNote that `call()` will accept [any type of callable](http://php.net/manual/en/language.types.callable.php).\n\n#### Factory Facet\n\nThe `create()` method can be used to invoke a constructor, to create an instance of any\nclass, on demand.\n\nAn important thing to understand, is that e.g. `register()` and `configure()` have *no*\nbearing on this functionality - the purpose of this method, is to create instance of types\nthat *aren't* registered as components in the container, but (likely) have *dependencies*\nwhich can be *provided* by the container.\n\nControllers are a great example - you most likely don't want to register every individual\ncontroller class as a component in the container; rather, you probably want a controller\nfactory, capable of creating any controller.\n\nAs an example, here's a simple implementation of a controller factory that resolves the\ntypical `\"foo/bar\"` route string as e.g. `FooController::bar()` - like so:\n\n```php\nclass Action\n{\n    public function __construct(Controller $controller, $action, array $params) { ... }\n}\n\nclass ControllerFactory\n{\n    /** @var FactoryInterface */\n    private $factory;\n\n    public function __construct(FactoryInterface $factory) { ... }\n\n    public function create($route, array $params)\n    {\n        list($controller_name, $action_name) = explode(\"/\", $route);\n\n        $controller_class = ucfirst($controller_name) . \"Controller\";\n\n        $controller = $this-\u003efactory-\u003ecreate($controller_class);\n\n        return new Action($controller, $action_name, $params);\n    }\n}\n```\n\nNote the `FactoryInterface` type-hint in the constructor - in situations where you only\ncare about using the container as a factory, you should type-hint against this facet.\n\n#### Inspection\n\nYou can inspect the state of components in a container using `has()` and `isActive()`.\n\nTo check if a component is defined, use `has()` - for example:\n\n```php\nvar_dump($container-\u003ehas(\"foo\")); // =\u003e bool(false)\n\n$container-\u003eset(\"foo\", \"bar\");\n\nvar_dump($container-\u003ehas(\"foo\")); // =\u003e bool(true)\n```\n\nWhether a component is directly inserted with `set()`, or defined using `register()`, the\n`has()` method will return `true`.\n\nTo check if a component has been activated, use `isActive()` - for example:\n\n```php\n$container-\u003eregister(\"foo\", function () { return \"bar\"; });\n\nvar_dump($container-\u003eisActive(\"foo\")); // =\u003e bool(false)\n\n$foo = $container-\u003eget(\"foo\"); // component activates on first use\n\nvar_dump($container-\u003eisActive(\"foo\")); // =\u003e bool(true)\n```\n\nA component is considered \"active\" when it has been used for the first time - components\nmay get activated directly by calls to `get()`, or may get indirectly activated by\ncascading activation of dependencies.\n\n## Opinionated\n\nLess is more. We support only what's actually necessary to create beautiful architecture - we do\nnot provide a wealth of \"convenience\" features to support patterns we wouldn't use, or patterns\nthat aren't very common and can easily be implemented with the features we do provide.\n\nFeatures:\n\n  * **Productivity-oriented** - favoring heavy use of **closures** for full IDE support:\n    refactoring-friendly definitions with auto-complete support, inspections and so on.\n\n  * **Performance-oriented** only to the extent that it doesn't encumber the API.\n\n  * **Versatile** - supporting many different options for registration and configuration\n    using the same, low number of public methods, including value modifications, decorators, etc.\n\n  * Zero configuration - we don't include any optional features or configurable behavior: the\n    container always behaves consistently, with the same predictable performance and interoperability.\n\n  * **PHP 5.5+** for `::class` support, and because you really shouldn't be using anything older.\n\nNon-features:\n\n  * **NO annotations** - because sprinkling bits of your container configuration across\n    your domain model is a really terrible idea.\n\n  * **NO auto-wiring** - because `$container-\u003eregister(Foo::name)` isn't a burden, and explicitly\n    designates something as being a service; unintentionally treating a non-singleton as a singleton\n    can be a weird experience.\n\n  * **NO caching** - because configuring a container really shouldn't be so much overhead as to\n    justify the need for caching. Unbox is fast.\n\n  * **NO property/setter injections** because it blurs your dependencies - use constructor injection,\n    and for optional dependencies, use optional constructor arguments; you don't, after all, need to\n    count the number of arguments anymore, since everything will be injected. (if you do have a good\n    reason to inject something via properties or setters, you can do that from inside a closure, in\n    a call to `configure()`, with full IDE support.)\n\n  * **NO syntax** - we don't invent or parse any special string syntax, anywhere, period. Any problem\n    that can be solved with custom syntax can also be solved with clean, simple PHP code.\n\n  * No chainable API, because call chains (in PHP) don't play nice with source-control.\n\n  * All registered components are singletons - we do not support factory registrations; if you\n    need to register a factory, the proper way to do that, is to either implement an actual\n    factory class (which is usually better in the long run), or register the factory closure\n    itself as a named component.\n\n## Benchmark\n\nThis is not intended as a competitive benchmark, but more to give you an idea of the performance\nimplications of choosing from three very different DI containers with very different goals and\ndifferent qualities - from the smallest and simplest to the largest and most ambitious:\n\n  * [pimple](http://pimple.sensiolabs.org/) is as simple as a DI container can get, with absolutely\n    no bell and whistles, and barely any learning curve, totalling around **250 LOC**.\n\n  * **unbox** with just a few classes and interfaces - more concepts and a bit more learning\n    curve than pimple, totalling around **350 LOC**.\n\n  * [php-di](http://php-di.org/) is a pristine dependency injection framework with all the bells and\n    whistles - rich with features, but also has more concepts and learning curve, more overhead, and\n    totalling around **3000 LOC**.\n\nThe included [simple benchmark](test/benchmark-all.php) generates the following benchmark results on\na WSL2 under Windows 11 with PHP 8.2.10.\n\nTime to configure the container:\n\n    unbox: configuration ............................. 0.006 msec ....... 41.78% ......... 1.00x\n    pimple: configuration ............................ 0.008 msec ....... 51.25% ......... 1.23x\n    php-di: configuration ............................ 0.013 msec ....... 89.02% ......... 2.13x\n    php-di: configuration [compiled] ................. 0.015 msec ...... 100.00% ......... 2.39x\n\nTime to resolve the dependencies in the container, on first access:\n\n    pimple: 1 repeated resolutions ................... 0.002 msec ........ 9.14% ......... 1.00x\n    php-di: 1 repeated resolutions [compiled] ........ 0.004 msec ....... 18.88% ......... 2.06x\n    unbox: 1 repeated resolutions .................... 0.006 msec ....... 26.47% ......... 2.90x\n    php-di: 1 repeated resolutions ................... 0.022 msec ...... 100.00% ........ 10.94x\n\nTime for multiple subsequent lookups:\n\n    pimple: 3 repeated resolutions ................... 0.003 msec ....... 11.44% ......... 1.00x\n    php-di: 3 repeated resolutions [compiled] ........ 0.004 msec ....... 18.55% ......... 1.62x\n    unbox: 3 repeated resolutions .................... 0.006 msec ....... 27.24% ......... 2.38x\n    php-di: 3 repeated resolutions ................... 0.023 msec ...... 100.00% ......... 8.74x\n\n    pimple: 5 repeated resolutions ................... 0.003 msec ....... 13.36% ......... 1.00x\n    php-di: 5 repeated resolutions [compiled] ........ 0.005 msec ....... 19.68% ......... 1.47x\n    unbox: 5 repeated resolutions .................... 0.007 msec ....... 28.02% ......... 2.10x\n    php-di: 5 repeated resolutions ................... 0.023 msec ...... 100.00% ......... 7.48x\n\n    pimple: 10 repeated resolutions .................. 0.004 msec ....... 17.48% ......... 1.00x\n    php-di: 10 repeated resolutions [compiled] ....... 0.005 msec ....... 22.15% ......... 1.27x\n    unbox: 10 repeated resolutions ................... 0.007 msec ....... 29.83% ......... 1.71x\n    php-di: 10 repeated resolutions .................. 0.024 msec ...... 100.00% ......... 5.72x\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmindplay-dk%2Funbox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmindplay-dk%2Funbox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmindplay-dk%2Funbox/lists"}