phpwatch / simple-container
一个快速且最小的PSR-11兼容的依赖注入容器,具有数组语法,无需自动绑定
Requires
- php: ^8.3
- psr/container: ^2.0
Requires (Dev)
- phpunit/phpunit: ^10.5.5
Provides
- psr/container-implementation: ^1.1 || ^2.0
This package is auto-updated.
Last update: 2024-08-30 01:17:47 UTC
README
一个快速且最小的PSR-11兼容的依赖注入容器,具有数组语法,无需自动绑定。
设计目标
- 只做一件事,做好这件事
- ~100 LOC
- 设计上不包含自动绑定
- 服务声明为闭包
- 使用数组语法访问和设置服务 (
$container['database']
) - 完全符合PSR-11规范
- 支持受保护的服务(直接返回闭包)
- 支持工厂服务(返回新实例而不是返回相同的实例)
- 100% 测试覆盖率
- 服务可以后来重写,标记为工厂/受保护
- 无需编译步骤,以全速运行
安装
composer require phpwatch/simple-container
使用方法
Simple Container 支持使用数组语法设置和获取服务和值。您也可以稍后标记某些服务为工厂或受保护。
声明服务和值
<?php use Psr\Container\ContainerInterface; $container = new PHPWatch\SimpleContainer\Container(); $container['database.dsn'] = 'mysql:host=localhost;database=test'; $container['database'] = static function(ContainerInterface $container): \PDO { return new \PDO($container->get('database.dsn')); }; $container['api.ipgeo'] = 'rhkg3...';
获取服务
不要将此类用作服务定位器。您为每个服务声明的闭包将获得 Container
实例,您可以使用数组语法从中获取服务
<?php $container['database']; // \PDO // OR $container->get('database'); // \PDO
从定义创建容器
<?php use Psr\Container\ContainerInterface; use PHPWatch\SimpleContainer\Container; $services = [ 'database' => [ 'dsn' => 'sqlite...' ], 'prefix' => 'Foo', 'csprng' => static function (ContainerInterface $container) { return $container->get('prefix') . bin2hex(random_bytes(16)); } ]; $container = new Container($services); $container->get('prefix'); // Foo
工厂和受保护服务
如果服务定义是闭包(类似于上面的 database
示例),则返回值将被缓存,并在后续调用中返回,而无需再次实例化。这通常是数据库和其他可重用服务的期望行为。
工厂服务
要每次请求服务时执行提供的闭包(例如,返回HTTP客户端),可以使用 工厂。
<?php $container->setFactory('http.client', static function(ContainerInterface $container) { $handler = new curl_init(); curl_setopt($handler,CURLOPT_USERAGENT, $container->get('http.user-agent')); return $handler; };
上面的示例将始终在每次调用 $container->get('http.client')
时返回一个新的curl处理程序资源,并将User-Agent字符串设置为容器中的 http.user-agent
值。
您也可以在已设置容器服务的情况下将其标记为工厂方法
<?php $container->setFactory('http.client'); // Mark existing definition as a factory.
如果已经声明了 http.client
服务,现在它将被标记为工厂。如果现有声明未设置或不是 callable
,则将抛出 PHPWatch\SimpleContainer\Exception\BadMethodCallExceptionTest
异常。
受保护服务
Simple Container 预期服务声明为闭包,并且它将自行执行闭包以返回服务。然而,在某些情况下,您需要将闭包本身作为服务返回。
<?php $container['csprng'] = static function(): string { return bin2hex(random_bytes(32)); }; $container['csprng']; // "eaa3e95d4102..." $container['csprng']; // "eaa3e95d4102..." $container['csprng']; // "eaa3e95d4102..."
这种行为可能不是您想要的。您可以将服务标记为 工厂 以在每次调用时获取不同的值。您也可以将其标记为 受保护,这将返回 闭包本身,因此您可以在代码中调用它
<?php $container->setProtected('csprng', static function(): string { return bin2hex(random_bytes(32)); }); $csprng = $container->get('csprng'); echo $csprng(); // eaa3e95d4102... echo $csprng(); // b857ce87400b... echo $csprng(); // a833e3db880...
扩展容器
只需使用数组语法并添加/删除服务
<?php // Remove: unset($container['secret.service']); // Extend: $container['secret.service'] = static function(): void { throw new \Exception('You are not allowed to use this');}
冻结容器
根据设计,容器不允许被冻结。如果您绝对需要此功能,则 Container
类是可扩展的(get
、getOffset
或 Container
类本身未声明为final)。
贡献
如果您有任何疑问或建议,请随时提出问题或PR。请记住,此容器旨在成为最简单、最快的容器,并遵循SOLID原则。任何超出这些目标(见上面的设计目标)的功能都可能不会被接受。但是,无论其性质如何,您的贡献都将受到赞赏并加以考虑。
致谢和灵感
本项目灵感来源于Pimple项目。它不封闭于新功能和修改,且不支持PSR-11。这激发了这个项目的想法,Pimple因其稳健的架构和最小化特性集(包括对数组语法的支持)而值得赞誉。
尽管是独立的项目,Simple Container与Pimple在很大程度上是兼容的。本项目在PHP.Watch中使用,并且可以替换Pimple直接使用。