aolbrich / php-di-container
PHP 依赖注入容器
v1.0.8
2023-03-20 17:31 UTC
Requires
- php: >=8.0.0
- psr/container: ^2.0
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
- vimeo/psalm: ^5.7
README
此类实现了一个简单的构造函数依赖注入容器
安装
composer require aolbrich/php-di-container
基本用法
如果类的构造函数实现了可调用的类,则它将自动绑定,无需映射。例如
class ExampleClass
{
public function __construct(private readonly ExampleClass $exampleClass) {}
...
}
实现
use Aolbrich\PhpDiContainer\Container;
// Create new container
$container = new Container();
// Resolve class
$class = $container->get(ExampleClass::class);
返回新类并自动绑定所有带有 @autowire 注解的方法
(注意:新类不会返回相同的类名,甚至可能不是从原始类继承而来的)
$container = new Container();
$container->set(ExampleServiceInterface::class, ExampleService::class);
$class = $container->resolveClass(ExampleClassForFunctionLevelResolve::class);
echo $class->getResponseWithAutowiredParams(); // they will be auto wired
使用数组设置解析定义
也可以通过构造函数传递定义到类中,或者调用 setDefinitions 方法而不是使用 set
方法。这使得可以一次性设置多个定义
示例
$definitions = [
ExampleServiceInterface::class => ExampleService::class,
];
$container = new Container($definitions);
或者
$definitions = [
ExampleServiceInterface::class => ExampleService::class,
];
$container = new Container();
$container->setDefinitions($definitions);
该类
class ExampleClassForFunctionLevelResolve
{
/**
* @Autowire
*/
public function getResponseWithAutowiredParams(
ExampleServiceInterface $exampleService,
ExampleSubService $exampleSubService): string
{
return
$exampleService->getResponse() . ' / ' .
$exampleSubService->getResponse() . PHP_EOL;
}
}
使用接口解析的用法
如果类实现了具有接口类型提示的构造函数注入,则容器无法自动解析依赖项,因此应通过 set 方法提供映射。映射可以通过接口和类名,或接口名和闭包来完成。
以下两个示例说明了这些解决方案
class ExampleClass
{
public function __construct(private readonly ExampleInterface $example) {}
...
}
实现
use Aolbrich\PhpDiContainer\Container;
// Create new container
$container = new Container();
// Configure what to resolve
$container->set(
ExampleInterface::class,
ExampleClass::class
);
$class = $container->get(ExampleClass::class);
使用闭包的相同实现
use Aolbrich\PhpDiContainer\Container;
// Create new container
$container = new Container();
// Configure what to resolve
$container->set(ExampleServiceInterface::class, function(Container $container) {
return $container->get(ExampleService::class);
});
$class = $container->get(ExampleClass::class);
设置器自动绑定
还可以自动绑定设置器,自动绑定的要求是
- 方法必须是公共的
- 方法名必须以 "set" 开头,例如 "setLogger()"
- 要自动绑定,需要添加 @autowire 注解。
可以有任何数量的设置器
示例类在一个设置器中绑定两个依赖项。
class ExampleSetterAutowireClass
{
private ExampleServiceInterface $exampleService;
private ExampleSubService $exampleSubService;
/**
* @Autowire
*/
public function setAutowire(
ExampleServiceInterface $exampleService,
ExampleSubService $exampleSubService
) {
$this->exampleService = $exampleService;
$this->exampleSubService = $exampleSubService;
}
public function getResponse(): string
{
return
$this->exampleService->getResponse() . ' / ' .
$this->exampleSubService->getResponse() . PHP_EOL;
}
}
添加原始参数
可以在类解析中添加额外的参数以绑定
示例
$class = $container->get(ExampleParameterBinding::class, [
'intValue' => 10,
'stringValue' => "Hello String",
'anyValue' => "This is any value"
]);
echo $class->getResponse();
类实现
class ExampleParameterBinding
{
private mixed $anyValue;
public function __construct(
private readonly ExampleServiceInterface $exampleService,
private readonly ExampleSubService $exampleSubService,
private readonly int $intValue,
private readonly string $stringValue,
$anyValue,
) {
$this->anyValue = $anyValue;
}
public function getResponse(): string
{
return
$this->intValue . ' / ' .
$this->stringValue . ' / ' .
$this->anyValue . ' / ' .
$this->exampleService->getResponse() . ' / ' .
$this->exampleSubService->getResponse() . PHP_EOL;
}
}
单例创建支持
可以使用 singleton() 函数创建类为单例,或者使用闭包自动绑定。示例
$container = new Container();
// Resolve as non singleton
$class = $container->get(ExampleSetterAutowireClass::class);
$class2 = $container->get(ExampleSetterAutowireClass::class);
echo $class === $class2 ? "Same class instance created\n" : "Different class instance created\n";
// Resolve as singleton
$class = $container->singleton(ExampleSetterAutowireClass::class);
$class2 = $container->singleton(ExampleSetterAutowireClass::class);
echo $class === $class2 ? "Same class instance created\n" : "Different class instance created\n";
// Autowire as Singleton
$container->set(ExampleService::class, function(Container $container) {
return $container->singleton(ExampleService::class);
});
$class = $container->get(ExampleService::class);
$class2 = $container->get(ExampleService::class);
echo $class === $class2 ? "Same class instance created\n" : "Different class instance created\n";
运行单元测试
./vendor/bin/phpunit test
工具
运行代码质量检查
./vendor/bin/phpstan analyse src test
./vendor/bin/psalm --show-info=true
注意
这不是一个完整的依赖注入容器实现。它只解析构造函数依赖。
缺少的功能/将添加
构造函数注入设置器注入方法注入单例支持属性注入(不会实现,因为不再被视为最佳实践)注入原始参数值- 缓存
- 别名
- 注解(部分通过设置器中的 @autowire 关键字实现)
- 不可变设置器注入
- 仅添加了基本的循环引用检查,将改进以检查例如,如果 ClassA 依赖于 ClassB,而 ClassB 依赖于 ClassC,而 ClassC 又反过来依赖于 ClassA,则代码将不会检测到这种循环引用。这可以通过维护依赖项堆栈并检查堆栈中的循环来解决。
许可证
MIT 许可证