knot-lib / di
类似Pimple的依赖注入(DI)容器库。
Requires
- php: >=7.2
- ext-json: *
- knot-lib/annotations: ~0.1
- knot-lib/cache: ~0.1
- knot-lib/config: ~0.3
- psr/container: ^1.0
Requires (Dev)
- mikey179/vfsstream: 1.6.8
- php-coveralls/php-coveralls: ^2.0
- phpunit/phpunit: ^8.5.15
This package is auto-updated.
Last update: 2023-09-06 05:03:27 UTC
README
描述
knot-lib/Di 是一个类似 Pimple 风格的依赖注入(DI)容器库。
功能
- Pimple 风格的 API
- 懒加载以提高性能
- 多容器构建器
- 保护或限制组件
- 可缓存
- 为引导程序动态生成代码
- 依赖注入
- 自动装配
- 构造函数、setter、字段注入
- 作用域:单例/原型
- 自定义注解
- @Inject
- @Scope
- @Singleton
- @Named
- @Prototype
要求
PHP 7.2 或更高版本
安装 knot-lib/di
推荐通过 Composer 安装 knot-lib/di。
composer require knot-lib/di
安装后,您需要引入 Composer 的自动加载器
require 'vendor/autoload.php';
用法
1. 构建 Container
可以通过构建器类直接创建 Container,您可以根据喜好选择容器创建方式。
有几种容器构建方法
- 简单(无容器构建器)
- 引导程序容器构建器
- 编译器容器构建器
- 模块化容器构建器
- 缓存编译器容器构建器
简单(无容器构建器)
您可以在没有任何内置容器构建器的情况下构建容器。
此方法简单快捷,但如果使用许多组件,容器初始化代码将变得复杂。
use knot-lib\Di\Container; use Sample\Person; $container = Container(); $container['david'] = function(Container $c){ return new Person('David Smith', 24, 179.2, function($target){ return 'throws '.$target.'.'; }); }; $david = $container['david']; $david->sayHello(); // David Smith says "Hello! My name is David Smith. I am 24 years old."
引导程序容器构建器
引导程序容器构建器将通过回调函数(引导程序)中编写的代码设置容器。此方法非常快,但可能有些冗余。
use knot-lib\Di\Container; use knot-lib\Di\BootstrapContainerBuilder; use Sample\Person; $bootstrap = function(Container $c){ $c['david'] = function(Container $c){ return new Person('David Smith', 24, 179.2, function($target){ return 'throws '.$target.'.'; }); }; }; $bulder = new BootstrapContainerBuilder($bootstrap); $container = $bulder->build(); $david = $container['david']; $david->sayHello(); // David Smith says "Hello! My name is David Smith. I am 24 years old."
编译器容器构建器
编译器容器构建器将通过配置文件(支持 .json/.yml/.php)设置容器。此容器构建器在缓存目录中动态生成引导程序代码,因此第二次激活容器的激活时间将大大减少。
sample.php
use knot-lib\Di\CompilerContainerBuilder; use knot-lib\Config\Util\ConfigFileUtil; $config = ConfigFileUtil::loadConfigFromFile('components.json'); $container = (new CompilerContainerBuilder($config))->build(); $david = $container['david']; $david->sayHello(); // David Smith says "Hello! My name is David Smith. I am 24 years old."
components.json
{
"compiler": {
"vendor_dir": "../vendor",
"autoloaders": [
"../sample_autoloader2.php"
]
},
"cache": {
"root": "../cache",
"filename": "demo_di"
},
"components": [
{
"id": "david",
"type": "object",
"class_name": "Sample\\Person",
"injections": [
{
"new": [ "David Smith", "@int: 24", "@float: 179.2", "@func: function($target){ return 'throws '.$target.'.'; }" ]
}
]
}
]
}
@ 指令
如上配置文件(components.json),您可以在组件配置文件中使用 '@xxx' 指令。
以下是一些 '@xxx' 指令。
| 指令 | 替换 | 示例 |
|---|---|---|
| @id: [component_id] | 通过 [component_id] 识别的其他组件 | @id: logger |
| @func: [function_code] | [function_code] | @func: function($a) { echo $s; } |
| @int: [integer_value] | [integer_value] | @int: 100 |
| @float: [float_value] | [float_value] | @float: 3.14 |
| @bool: [bool_value] | [bool_value]('yes' 或 '1' 或 'on' 被视为 true) | @bool: yes @bool: true |
| JSON数据: [json_data] | 解码后的JSON | JSON: { 'age': '21', 'name': 'david' } |
| 环境变量: [key] | $_ENV值($_ENV['key']) | 环境变量: USER |
模块化容器构建器
模块化容器构建器将通过安装“容器模块”来设置容器。容器模块是一组容器初始化代码。
BootstrapContainerBuilder仅提供一组初始化代码,但ModularContainerBuilder在需要时可以接受多个模块。
use knot-lib\Di\Container; use knot-lib\Di\ModularContainerBuilder; use knot-lib\Di\ContainerModuleInterface; use Sample\Person; class DavidModule implements ContainerModuleInterface { public function install(Container $container) { $container['david'] = function(){ return new Person('David Smith', 24, 179.2, function($target){ return 'throws '.$target.'.'; }); }; } } $builder = new ModularContainerBuilder([ new DavidModule() ]); $container = $builder->build(); $david = $container['david']; $david->sayHello(); // David Smith says "Hello! My name is David Smith. I am 24 years old."
2. 使用容器
get
// get service $service = $container['my-service']; // do something $service->doSomething();
set/unset
您可以通过相同的方式将实例或值设置到容器中。您还可以设置用于生成实例的工厂代码或用于懒加载的常量值。
// set object $container['my-favorite-fruits'] = new MyService(); // set object factory(lazy creation) $container['my-service'] = function(){ return new MyService(); }; // set constant value $container['my-favorite-fruits'] = [ 'apple', 'banana' ]; // set constant value factory(lazy creation) $container['my-service'] = function(){ return [ 'apple', 'banana' ]; }; // unset entry unset($container['my-favorite-fruits']);
slot
slot是关于容器组件的信息。您可以通过slot对象锁定或限制组件。
// get slot object $slot = $contianer->slot('something'); // lock the slot(in this case, 'something' slot will be locked) $slot->lock(); // type restriction $slot->mustBeTypeOf('array'); // instance-of restriction $slot->mustBeInstanceOf('MyServiceInterface');
提示
每次访问时获取新实例
默认情况下,容器在第二次访问后返回现有对象。但您可以通过调用Container#newInstance()方法在每次访问时检索新对象。
$cart = $container['cart']; echo 'count:' . $cart->countItems(); // count: 0 $cart->addItem('apple'); echo 'count:' . $cart->countItems(); // count: 1 $cart = $container['cart']; echo 'count:' . $cart->countItems(); // count: 1 $cart = $container->newInstance('cart'); echo 'count:' . $cart->countItems(); // count: 0
保护组件
您可以通过使用Slot#lock()方法来保护组件。
$container['cart'] = new MyCart(); // lock slot $container->slot('cart')->lock(); // this will fail becasue the slot is locked $container['cart'] = new AnotherCart(); // throws SlotIsLockedException
限制组件类型
您可以通过使用Slot#mustBeTypeOf()方法来限制组件类型。
$container['something'] = new MyCart(); // restrict slot to array value $container->slot('something')->mustBeTypeOf('array'); // this will fail because AnotherCart is not array $container['something'] = new AnotherCart(); // throws SlotTypeRestrictionViolationException // array value can be set $container['something'] = [ 'apple', 'banana' ];
限制组件类或接口
您可以通过使用Slot#mustBeInstanceOf()方法来限制组件类或接口。
$container['something'] = new MyCart(); // restrict slot to array value $container->slot('something')->mustBeInstanceOf('MyServiceInterface'); // this will fail because AnotherCart does not implement MyServiceInterface $container['something'] = new AnotherCart(); // throws SlotInstanceOfRestrictionViolationException // interface implemented instance can be set in this slot $container['something'] = new MyService; // class MyService implements MyServiceInterface
扩展组件
您可以通过使用Container#extend()方法来扩展组件。
// register my service $container['my_service'] = new MyService(); // extend my serice(set max item count) $container->extend('my_service', function($my_service) { $my_service->setMaxItems(10); }); echo 'max: ' . $container['my_service']->getMaxItems(); // max: 10 // register array $container['my_favorite_fruits'] = ['apple']; // add banana to my favorites array $container->extend('my_favorite_fruits', function(&$favorites) { $favorites[] = 'banana'; }); echo implode(', ',$container['my_favorite_fruits']); // apple, banana
编译器配置文件格式 & 参数
编译器配置文件可以是JSON、YAML和PHP格式。所有参数都必须以数组元素开始。
组件配置文件:JSON格式示例
{
"compiler": {
"vendor_dir": "../vendor",
"autoloaders": [
"../sample_autoloader2.php"
]
},
"cache": {
"root": "../cache",
"filename": "demo_di"
},
"slots": [
{
"id": "david",
"type": "object",
"instanceOf": "\\Sample\\PersonInterface",
"locked": true
}
],
"components": [
{
"id": "david",
"type": "object",
"class_name": "Sample\\Person",
"injections": [
{
"new": [ "David Smith", "@int: 24", "@float: 179.2", "@func: function($target){ return 'throws '.$target.'.'; }" ]
}
]
}
]
}
组件配置文件:YAML格式示例
compiler: vendor_dir: "../vendor" autoloaders: "../sample_autoloader2.php" cache: root: "../cache", filename: demo_di slots: - id: david, type: object, instanceOf: \Sample\PersonInterface, locked: true components: - id: david, type: object, class_name: Sample\Person, injections": new: - "David Smith" - "@int: 24" - "@float: 179.2" - "@func: function($target){ return 'throws '.$target.'.'; }"
组件配置文件:PHP格式示例
<?php return [ 'compiler' => [ 'vendor_dir' => '../vendor', 'autoloaders' => [ '../sample_autoloader2.php' ], ], 'cache' => [ 'root' => '../cache', 'filename' => 'demo_di', ], 'slots' => [ [ 'id' => 'david', 'type' => 'object', 'instanceOf' => '\Sample\PersonInterface', 'locked' => true, ] ], 'components' => [ [ 'id' => 'david', 'type' => 'object', 'class_name' => 'Sample\Person', 'injections' => [ 'new' => [ "David Smith", "@int: 24", "@float: 179.2", "@func: function(\$target){ return 'throws '.\$target.'.'; }", ] ] ] ], ];
组件配置文件:参数
| 参数 | 必填 | 说明 | 示例 |
|---|---|---|---|
| compiler/vendor_dir | 是 | 表示composer供应商目录路径 | vendor_dir: "../vendor" |
| compiler/autoloaders | 否 | 表示用户定义的自动加载器文件。在文件中,使用spl_autoload_register全局注册自己的自动加载器。 | autoloaders: "../my_autoloader.php" |
| cache/root | 否 | 表示缓存目录。编译器将在该目录中输出引导PHP代码。 | root: "../cache" |
| cache/filename | 否 | 表示引导缓存代码的文件基本名。缓存文件的完整名称将通过添加后缀 '.php' 生成。 | 文件名: "my_bootstrap" |
| 槽位 | 否 | 表示槽位数组,它附加到容器组件上以限制它。请参阅槽位项目参数 | - |
| 组件 | 是 | 表示组件数组。请参阅组件项目参数 | - |
组件配置文件:槽位项目参数
| 参数 | 说明 | 示例 |
|---|---|---|
| id | 表示槽位ID | id: "my_service" |
| type | 表示槽位类型 | type: 对象 |
| instanceOf | 表示组件实现或继承的类或接口 | instanceOf: MyServiceInterface |
| locked | 表示槽位是否被锁定。如果槽位被锁定,则无法覆盖它。 | locked: true |
组件配置文件:组件项目参数
| 参数 | 说明 | 示例 |
|---|---|---|
| id | 表示组件ID | id: "my_service" |
| type | 表示组件类型 | type: 对象 |
| class | 表示组件的类 | instanceOf: MyService |
| 注入 | 表示注入数组。请参阅[组件注入参数](#component_injections) | locked: true |
组件配置文件:组件注入参数
| 参数 | 值类型 | 说明 | 示例 |
|---|---|---|---|
| new | 数组 | 表示构造函数注入。值将传递给构造函数 | new: [ "param1", "param2" ] |
| method | 字符串 | 表示方法注入。值表示方法名称。 | method: doSomething |
| property | 字符串 | 表示属性注入。值表示属性名称。该属性必须是公共的。 | property: tax_ratio |
| params | 数组 | 表示方法注入的参数。值必须是数组。 | params: [ "pram1", "param2" ] |
| value | 整数 字符串 布尔值 浮点数 空值 数组 |
表示属性注入的值。 | value: "tomato" |
需求
PHP 7.1或更高版本
安装knot-lib/di
推荐通过 Composer 安装 knot-lib/di。
composer require knot-lib/di
安装后,您需要引入 Composer 的自动加载器
require 'vendor/autoload.php';
许可证
此库使用MIT许可证。
作者
免责声明
此软件无保修。
我们不承担因使用此软件而产生的任何结果。请自行承担责任。