phpwatch/simple-container

一个快速且最小的PSR-11兼容的依赖注入容器,具有数组语法,无需自动绑定

v2.1.0 2023-12-30 16:53 UTC

This package is auto-updated.

Last update: 2024-08-30 01:17:47 UTC


README

一个快速且最小的PSR-11兼容的依赖注入容器,具有数组语法,无需自动绑定。

Latest Stable Version CI codecov Scrutinizer Code Quality License

设计目标

  • 只做一件事,做好这件事
  • ~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 类是可扩展的(getgetOffsetContainer 类本身未声明为final)。

贡献

如果您有任何疑问或建议,请随时提出问题或PR。请记住,此容器旨在成为最简单、最快的容器,并遵循SOLID原则。任何超出这些目标(见上面的设计目标)的功能都可能不会被接受。但是,无论其性质如何,您的贡献都将受到赞赏并加以考虑。

致谢和灵感

本项目灵感来源于Pimple项目。它不封闭于新功能和修改,且不支持PSR-11。这激发了这个项目的想法,Pimple因其稳健的架构和最小化特性集(包括对数组语法的支持)而值得赞誉。

尽管是独立的项目,Simple Container与Pimple在很大程度上是兼容的。本项目在PHP.Watch中使用,并且可以替换Pimple直接使用。