类似Pimple的依赖注入(DI)容器库。

0.2.1 2021-06-05 23:28 UTC

This package is auto-updated.

Last update: 2023-09-06 05:03:27 UTC


README

Latest Version on Packagist Software License Build Status Coverage Status Code Climate Total Downloads

描述

knot-lib/Di 是一个类似 Pimple 风格的依赖注入(DI)容器库。

Pimple

功能

  • 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许可证。

作者

stk2k

免责声明

此软件无保修。

我们不承担因使用此软件而产生的任何结果。请自行承担责任。