fiberphp / container
💉 FiberPHP 依赖注入容器 —— PSR-11 规范实现,支持单例绑定、上下文绑定、可调用注入,零框架依赖,开箱即用。
dev-master
2026-09-09 05:54 UTC
Requires
- php: >=8.3
- fiberphp/contract: dev-master
- psr/container: ^2.0
- psr/log: ^3.0
Requires (Dev)
- phpunit/phpunit: ^11.0
Suggests
None
Provides
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-09 05:54:14 UTC
README
FiberPHP 框架的 PSR-11 依赖注入容器。无外部依赖(除 PSR-11 接口),支持单例绑定、上下文绑定、callable 注入、构造注入与
#[Inject] 属性注入,并内置循环依赖检测。
环境要求
- PHP >= 8.3
psr/container^2.0
安装
composer require fiberphp/container
基本用法
use FiberPHP\Container\Container;
use Psr\Log\LoggerInterface;
// 获取全局容器单例
$container = Container::getInstance();
// 绑定接口到闭包实现
$container->bind(LoggerInterface::class, function (Container $c) {
return new FileLogger('/var/log/app.log');
});
// 单例绑定:解析一次后缓存
$container->singleton(Database::class, function (Container $c) {
return new MySQLDatabase('localhost', 'root', '');
});
// 获取已绑定实例(PSR-11 接口)
$db = $container->get(Database::class);
// 不存在时返回 null 而非抛异常(哨兵值)
$cache = $container->get('cache', Container::NULL_ON_INVALID_REFERENCE);
// has():检查是否已绑定/解析
if ($container->has(LoggerInterface::class)) {
// ...
}
// bound():与 has() 等价
$container->bound(Database::class);
// make():每次创建新实例(跳过单例缓存),可显式传参
$controller = $container->make(Controller::class, ['request' => $request]);
$fresh = $container->make(Database::class, [], true); // 强制重建
// call():调用可调用对象并自动注入方法参数
$result = $container->call(function (UserService $service) {
return $service->findAll();
});
// 也支持 [类名|实例, 方法名] 形式
$result = $container->call([$controller, 'action']);
上下文绑定
不同消费方需要同一个抽象的不同实现时使用:
use FiberPHP\Container\Container;
$container = Container::getInstance();
// 当解析 OrderController 的依赖时,遇到 LoggerInterface 注入 FileLogger
$container->addContextual(OrderController::class, LoggerInterface::class, FileLogger::class);
// 当解析 PaymentController 的依赖时,遇到 LoggerInterface 注入 CloudLogger
$container->addContextual(PaymentController::class, LoggerInterface::class, CloudLogger::class);
// 解析时按消费方类型自动选择实现
$container->make(OrderController::class); // 内部依赖 FileLogger
$container->make(PaymentController::class); // 内部依赖 CloudLogger
属性注入
通过 #[Inject] 标注非 readonly 的实例属性,由容器在实例化后自动解析并赋值,省去构造函数样板代码。
use FiberPHP\Container\Attribute\Inject;
use FiberPHP\Container\Container;
class UserController
{
// 按属性类型解析
#[Inject]
private UserService $service;
// 显式指定实现类
#[Inject(UserServiceImpl::class)]
private $service2;
}
$container = Container::getInstance();
$controller = $container->make(UserController::class);
// $service 与 $service2 已自动注入
限制:
- 不可用于
readonly属性(readonly 仅能在构造期赋值),否则解析时抛ContainerException。 - 不可用于
static属性。 - 属性必须有类型声明,或在
#[Inject(ClassName::class)]中显式指定类。
ServiceProvider 基类
FiberPHP\Container\ServiceProvider 为组件包 Provider 提供统一基类(实现 FiberPHP\Contract\ProviderInterface),收敛注册样板。组件包 Provider 应继承它而非裸实现接口:
use FiberPHP\Container\ServiceProvider;
class RedisProvider extends ServiceProvider
{
// boot 失败是否终止启动(true=抛异常;false=warning 后跳过)
protected bool $critical = true;
// boot 是否可能发起网络建连(配合 bootTimeout 启用超时保护)
protected bool $network = true;
// boot 超时秒数;0/null 不启用超时
protected int|float|null $bootTimeout = 3.0;
public function register(): void
{
// singleton():一次完成 实现类 + 别名 + 契约 的单例绑定,共享同一实例
$this->singleton(
RedisManager::class,
fn () => new RedisManager($this->configValue('redis', [])),
'redis',
RedisFactory::class,
);
}
public function boot(?object $worker = null): void
{
$enable = $this->configValue('redis.enable', true); // 读配置(进程内 memoize)
$this->logger()->info('redis provider booting'); // 取日志器(未绑定兜底 NullLogger)
}
}
| 成员 | 说明 |
|---|---|
$critical | boot 失败是否终止启动 |
$network / $bootTimeout | boot 是否网络建连 / 超时秒数;两者满足才启用 pcntl 超时保护 |
register() / boot() | 均有空实现,按需重写;bootMeta() 由三个属性自动派生,无需手写 |
container() | 容器实例(Container::getInstance()) |
config() / configValue($key, $default) | 配置仓库及点语法读取,register/boot 共享一次解析 |
logger() | 日志器,容器绑定优先、脱离框架兜底 NullLogger |
singleton($concrete, $factory, ...$keys) | 绑定单例并把多个别名/契约键指向同一实例,杜绝漏绑 |
异常说明
所有异常都实现 PSR-11 异常接口,便于在框架/应用层统一捕获:
FiberPHP\Container\Exception\ClassNotFoundException:要解析的类不存在。实现Psr\Container\NotFoundExceptionInterface,对应 PSR-11get()找不到条目的场景。FiberPHP\Container\Exception\ContainerException:其他容器错误(如循环依赖、#[Inject]误用、不可解析参数、类不可实例化)。实现Psr\Container\ContainerExceptionInterface。