此包已被弃用,不再维护。作者建议使用 calgamolib/di 包。

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

0.6.0 2019-11-07 19:18 UTC

README

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

描述

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

Pimple

特性

  • Pimple风格API
  • 懒加载以提高性能
  • 多容器构建器
  • 保护或限制组件
  • 可缓存
  • 为启动生成的动态代码
  • 依赖注入
    • 自动装配
    • 构造函数、setter、字段注入
    • 作用域:单例/原型
    • 自定义注解
      • @Inject
      • @Scope
      • @Singleton
      • @Named
      • @Prototype

要求

PHP 7.1 或更高版本

安装 calgamo/di

安装 calgamo/di 的推荐方式是通过 Composer

composer require calgamo/di

安装后,您需要引入 Composer 的自动加载器

require 'vendor/autoload.php';

使用

1. 构建 Container

Container 可以通过构建器类直接创建。您可以选择您喜欢的创建 Container 的方式。
有几种构建 Container 的方法

  • 简单(无容器构建器)
  • Bootstrap 容器构建器
  • 编译器容器构建器
  • 模块化容器构建器
  • 缓存编译器容器构建器

简单(无容器构建器)

您可以在不使用任何内置容器构建器的情况下构建 Container。
此方法简单且速度快,但使用多个组件时,Container 初始化代码将变得复杂。

use Calgamo\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."

Bootstrap 容器构建器

Bootstrap 容器构建器将通过回调函数(bootstrap)中编写的代码设置 Container。此方法非常快,但可能有些多余。

use Calgamo\Di\Container;
use Calgamo\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)设置 Container。此容器构建器在缓存目录中动态生成启动代码,因此第二次激活 Container 的时间将大大减少。

sample.php
use Calgamo\Di\CompilerContainerBuilder;
use Calgamo\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": "sample_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' 视为真) @bool: yes
@bool: true
@json: [json_data] 解码后的json @json: { 'age': '21', 'name': 'david' }
@env: [key] $_ENV值($_ENV['key']) @env: USER

模块化容器构建器

模块化容器构建器将通过安装 "容器模块" 来设置容器。容器模块是一组容器初始化代码。
BootstrapContainerBuilder只提供一套初始化代码,但ModularContainerBuilder在需要时可以接受多个模块。

use Calgamo\Di\Container;
use Calgamo\Di\ModularContainerBuilder;
use Calgamo\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

槽位是关于容器组件的信息。您可以通过槽位对象锁定或限制组件。

// 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": "sample_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: sample_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' => 'sample_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'来生成 filename: "my_bootstrap"
slots 指示插槽数组,它附加到容器组件上以限制它。参见插槽项目参数 -
组件 指示组件数组。参见组件项目参数 -
组件配置文件:插槽项目参数
参数 说明 示例
id 指示插槽id id: "my_service"
type 指示插槽的类型 type: object
instanceOf 指示组件实现的类或接口 instanceOf: MyServiceInterface
locked 指示插槽是否锁定。如果插槽已锁定,则无法覆盖它。 locked: true
组件配置文件:组件项目参数
参数 说明 示例
id 指示组件id id: "my_service"
type 指示组件的类型 type: object
class 指示组件的类 instanceOf: MyService
injections 指示注入数组。参见[组件注入参数](#component_injections) locked: true
组件配置文件:组件注入参数
参数 值类型 说明 示例
new array 指示构造函数注入。值将被传递给构造函数 new: [ "param1", "param2" ]
method string 指示方法注入。值表示方法名称。 method: doSomething
property string 指示属性注入。值表示属性名称。属性必须是公开的。 property: tax_ratio
params array 指示方法注入的参数。值必须是数组。 params: [ "pram1", "param2" ]
value int
string
bool
float
null
array
指示属性注入的值。 value: "tomato"

许可

此库使用MIT许可。

作者

stk2k

免责声明

此软件无任何保证。

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