nih/container
Composer 安装命令:
composer require nih/container
包简介
NIH container
README 文档
README
A lightweight PSR-11 dependency injection container for PHP 8.4+ with autowiring, lazy objects, and circular-reference support.
Contents
- Features
- Installation
- Quick Start
- Configuration
- Modes
- Argument Helpers
- Errors and Exceptions
- Testing
- Acknowledgements
- License
Features
- PSR-11 compatible container (
Psr\Container\ContainerInterface). - Autowiring via constructor type hints.
- Configurable object lifecycle:
- non-shared (default): new instance on each
get(); - shared: singleton-like instances.
- non-shared (default): new instance on each
- Lazy initialization modes (
Ghost,Proxy, nested modes). - Circular reference resolution.
- Argument helpers for dynamic wiring (
Arg::get(),Arg::new(),Arg::id()).
Installation
composer require nih/container
Quick Start
<?php use NIH\Container\Container; use NIH\Container\ContainerConfig; $config = new ContainerConfig(shared: false); $container = new Container($config); $service = $container->get(App\Service::class);
By default, the container resolves classes and their dependencies via autowiring.
Configuration
use NIH\Container\ContainerConfig; use NIH\Container\Mode; $config = new ContainerConfig( shared: true, mode: Mode::Ghost, cacheReflections: true, maxDepth: 5 ); $config->manual(App\Contracts\LoggerInterface::class)->to(App\Logger\FileLogger::class); $config->value('api.base_url', 'https://api.example.com'); $config->alias('logger', App\Contracts\LoggerInterface::class);
alias() can reference both service IDs and plain value keys. Aliases are applied in both definition and value resolution paths.
Use add() and replace() for fast batch definition of multiple services, and auto() / manual() for detailed configuration of individual services.
Single Service Definitions: auto() and manual()
Use these methods when you need explicit per-service configuration:
auto(ClassName::class): create/update a definition with autowiring enabled.manual(ClassName::class): create/update a definition with autowiring disabled.
use NIH\Container\ContainerConfig; use NIH\Container\Mode; $config = new ContainerConfig(); $config->auto(App\Service::class) ->shared() ->mode(Mode::Proxy); $config->manual(App\Contracts\LoggerInterface::class) ->to(App\Logger\FileLogger::class) ->argument('channel', 'app');
Argument Configuration: argument() and args()
Both methods configure constructor/factory arguments for the current definition:
argument($nameOrIndex, $value)sets one argument.args(array $arguments)sets the full argument map/list for the definition.
Override rules:
- Repeated
argument()calls for the same argument overwrite the previous value. args()replaces all previously configured arguments for that definition.
Autowiring interaction:
- You can pass arguments for both manual bindings and auto-wired definitions.
- In auto-wiring mode, only arguments not explicitly provided via
argument()/args()are resolved automatically by the container.
$config->auto(App\Service::class) ->argument('dsn', 'sqlite::memory:') ->argument('dsn', 'pgsql:host=db;dbname=app'); // overwrites previous dsn $config->manual(App\Formatter::class) ->argument('prefix', '[api]') ->args(['prefix' => '[worker]', 'suffix' => '!']); // `args()` replaces previously configured arguments for this definition
Batch Definitions via add() and replace()
Use add() to register entries only if they are not already defined. If an entry already exists, the new value is ignored.
Value handling rules in the input array:
stringvalue: treated as a target class name for service definition (tobinding).Closurevalue: treated as a factory callback for service definition.Definitionvalue: stored as-is.- any other value: stored as a plain container value (
value()-like entry).
If you need to store raw string data in the container, use value() explicitly.
String targets are not validated at configuration time; they are resolved and validated when get() / new() is called.
use NIH\Container\ContainerConfig; $config = new ContainerConfig(); $config->value('api.base_url', 'https://api.example.com'); $config->add([ App\Contracts\LoggerInterface::class => App\Logger\FileLogger::class, App\Contracts\CacheInterface::class => App\Cache\RedisCache::class, App\Contracts\MailerInterface::class => App\Mailer\SmtpMailer::class, ]);
Use replace() to overwrite existing entries. If an entry already exists, the old value is replaced with the new one.
It uses the same value handling rules as add().
// initial definitions, added only to demonstrate replace() behavior on existing entries $config->add([ App\Contracts\LoggerInterface::class => App\Logger\FileLogger::class, App\Contracts\CacheInterface::class => App\Cache\RedisCache::class, App\Contracts\MailerInterface::class => App\Mailer\SmtpMailer::class, ]); $config->value('api.base_url', 'https://api.dev.local'); $config->replace([ App\Contracts\LoggerInterface::class => App\Logger\ConsoleLogger::class, App\Contracts\CacheInterface::class => App\Cache\ArrayCache::class, App\Contracts\MailerInterface::class => App\Mailer\NullMailer::class, ]);
For exact branching logic, see ContainerConfig::add() and ContainerConfig::replace().
Resolution priority note:
Input state for $id |
get($id) |
new($id) |
|---|---|---|
| scalar/array value exists | returns value() |
returns value() |
| object value exists + definition/class exists | returns value() object |
ignores object value(), instantiates via definition/class |
| object value exists + no definition/class | returns value() object |
throws ContainerNotFoundException |
| only definition/class exists | instantiates via definition/class | instantiates via definition/class |
Group Definitions: inherit(), namespace(), regex()
These methods let you define shared rules for multiple class entries at once:
inherit(BaseClass::class): applies to classes that extend/implement the given type.namespace('App\\Service'): applies to classes in the given namespace prefix.regex('/^App\\\\Report\\\\.+$/'): applies to classes matching the regex pattern.
use NIH\Container\Arg; use NIH\Container\ContainerConfig; use NIH\Container\Mode; $config = new ContainerConfig(); $config->inherit(App\Contracts\RepositoryInterface::class) ->shared() ->mode(Mode::Proxy); $config->namespace('App\\Handler') ->manual() ->argument('logger', Arg::get(App\Logger\FileLogger::class)); $config->regex('/^App\\\\Report\\\\.+$/') ->callback( static fn(App\Factory\ReportFactory $factory, string $id): object => $factory->create($id) ) ->argument('id', Arg::id());
Group rules are evaluated when a class entry is resolved by id, and the matched rule is used as a template for its definition.
Group-specific note about to() and callback():
- For group definitions, binding directly to a concrete target class is not meaningful.
to()is used to reference an abstract factory via a string callable (for example,App\\Factory\\ReportFactory::create).callback()is used to provide an anonymous factory function.- Edge case: if string
to()is not callable, group definition resolution returnsnull, and containerget()/new()will end with a NotFound error for that id.
Binding Override Behavior
to() and callback() configure the same target binding (Definition::$to), so each subsequent call overrides the previous one.
$config->manual(App\Contracts\LoggerInterface::class) ->to(App\Logger\FileLogger::class) ->callback(static fn(): App\Contracts\LoggerInterface => new App\Logger\ConsoleLogger()); // effective binding: callback, `to()` value is overwritten $config->manual(App\Contracts\CacheInterface::class) ->callback(static fn(): App\Contracts\CacheInterface => new App\Cache\ArrayCache()) ->to(App\Cache\RedisCache::class); // effective binding: `to()`, callback is overwritten
Factories via callback()
Factory callbacks are invoked through the internal instantiator. Parameters can be resolved automatically from type hints, and also passed explicitly via argument() and args().
use NIH\Container\ContainerConfig; use NIH\Container\Mode; $config = new ContainerConfig(shared: true); $config->manual(App\Service::class) ->callback( static function (App\Contracts\LoggerInterface $logger): App\Service { return new App\Service($logger); } ) ->mode(Mode::Proxy);
Abstract factory configuration with Arg::id():
use NIH\Container\Arg; use NIH\Container\ContainerConfig; $config = new ContainerConfig(); $config->namespace('App\\Report') ->callback( static function (App\Factory\ReportFactory $factory, string $id): object { return $factory->create($id); } ) ->argument('id', Arg::id());
In this setup, Arg::id() resolves to the current requested entry id (for example, App\Report\DailyReport::class), and the abstract factory creates that class.
Modes
Mode::Default: uses container default mode; if both container mode and definition mode areMode::Default, it effectively falls back toMode::Instance.Mode::Instance: eager instantiation.Mode::Ghost: lazy ghost object.Mode::Proxy: lazy proxy object.Mode::NestedGhost: laziness for nested dependencies only.Mode::NestedProxy: proxy laziness for nested dependencies only.
Notes:
maxDepthaffects deep dependency graphs inMode::Instance: when current depth is greater thanmaxDepth, container switches nested creation toMode::Ghost.
Argument Helpers
Argument helpers are runtime placeholders for definition arguments. They are resolved through the container when the target entry is instantiated.
Arg::get(string|Arg $id, ?Mode $mode = null): resolves the id and returns$container->get($id).Arg::new(string|Arg $id, ?Mode $mode = null): resolves the id and returns$container->new($id).Arg::id(): returns the current resolving entry id (the id of the service being instantiated).
Basic usage:
use NIH\Container\Arg; $config->manual(App\Handler::class) ->argument('service', Arg::get(App\Service::class)) ->argument('freshService', Arg::new(App\Service::class));
Dynamic id usage:
$config->manual(App\ContextAwareHandler::class) ->argument('entryId', Arg::id());
In this example, entryId receives the id of the currently resolved entry (here: App\ContextAwareHandler::class).
Mode-aware usage:
use NIH\Container\Mode; $config->manual(App\LazyHandler::class) ->argument('service', Arg::get(App\Service::class, Mode::Ghost)) ->argument('freshService', Arg::new(App\Service::class, Mode::Proxy));
Errors and Exceptions
ContainerNotFoundException: thrown byget()/new()when no definition/class can be resolved for the requested id.ContainerException: thrown when instantiation/invocation fails; original exception is wrapped as previous.- Group edge case: if group
to()contains a non-callable string factory reference, group resolution returnsnull, which leads toContainerNotFoundExceptionfor that id.
Testing
composer test
Acknowledgements
Autowiring and dependency resolution are powered by the excellent yiisoft/injector library.
License
This project is licensed under the MIT License. See LICENSE for details.
nih/container 适用场景与选型建议
nih/container 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 10 次下载、GitHub Stars 达 3, 最近一次更新时间为 2026 年 03 月 03 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「container」 「dependency」 「di」 「injection」 「Autowiring」 「injector」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 nih/container 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 nih/container 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 nih/container 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A fast and intuitive dependency injection container.
Dependency injection container for the Monolith framework.
The PSR-11 container bridges
Http Microframework for Hack
PHP Dependency Injection Container
统计信息
- 总下载量: 10
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 3
- 点击次数: 35
- 依赖项目数: 4
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-03-03