一个超级最小的控制反转库

1.2.0 2015-05-13 22:45 UTC

This package is auto-updated.

Last update: 2024-09-15 05:34:01 UTC


README

Build Status Coverage Status License

φhi

一个高效、易于使用、开源的PHP依赖注入容器,具有小体积、强大功能、100%单元测试覆盖率和优秀的文档。Phi与PSR-0和PSR-4自动加载标准兼容,欢迎任何认为可以改进的人进行合作。

安装

Composer

Composer是安装Phi的推荐方法。

$ composer require lordmonoxide/phi

GitHub

可以从GitHub下载Phi。

功能

Phi支持多种注入依赖的方式,这些方式可以单独使用或相互结合。

自动注入

假设你有一个名为Foo的类,它依赖于另一个名为Bar的类

class Foo {
  public $bar = null;
  
  public function __construct(Bar $bar) {
    $this->bar = $bar;
  }
}

你可以通过以下方式轻松获取带有所有必需依赖项的新Foo实例

$foo = $phi->make('Foo');
// $foo->bar = new Bar

你将获得一个带有自动注入到构造函数中的新Bar实例的Foo实例。这当然是递归的。如果Bar依赖于Baz,则将向Bar注入Baz的实例,依此类推。

传递参数

有许多情况需要将参数传递给构造函数。考虑以下类(注意参数的顺序)

class Foo {
  public $a = null;
  public $b = null;
  public $first_name = null;
  public $last_name  = null;
  
  public function __construct(B $b, A $a, $first_name = null, $last_name = null) {
    $this->a = $a;
    $this->b = $b;
    $this->first_name = $first_name;
    $this->last_name  = $last_name;
  }
}

你可以通过以下几种方式从Phi请求此类

$foo = $phi->make('Foo');
// $foo->a == new A
// $foo->b == new B
// $foo->first_name == null
// $foo->last_name  == null
$foo = $phi->make('Foo', ['John', 'Doe']);
// $foo->a == new A
// $foo->b == new B
// $foo->first_name == 'John'
// $foo->last_name  == 'Doe'
$foo = $phi->make('Foo', ['John']);
// $foo->a == new A
// $foo->b == new B
// $foo->first_name == 'John'
// $foo->last_name  == null

你可能想覆盖自动注入的参数

$a = new A;
$foo = $phi->make('Foo', ['John', 'Doe', $a]);
// $foo->a == $a
// $foo->b == new B
// $foo->first_name == 'John'
// $foo->last_name  == 'Doe'

请注意,在先前的示例中,$a是最后一个传入的。Phi足够智能,可以确定非标量类型参数的注入顺序。

同一类型的多个依赖项

考虑以下类

class Foo {
  public function __construct(BarInterface $bar, A $a, BarInterface $baz, B $b) {
    // ...
  }
}
$bar = new Bar; // implements BarInterface
$baz = new Baz; // implements BarInterface
$foo = $phi->make('Foo', [$bar, $baz]);
// $foo->bar == $bar
// $foo->baz == $baz
// $foo->a   == new A
// $foo->b   == new B

相同类型的参数将按Phi接收到的顺序传递给构造函数。如果您想按不同顺序传递它们,请参阅命名注入部分。

命名注入

在某些情况下,明确指定传入的参数很有用。Phi使这变得简单。考虑“传递参数”部分中的类

class Foo {
  public function __construct(A $a, B $b, $first_name = null, $last_name = null) {
    // ...
  }
}
$a = new A;
$b = new B;
$foo = $phi->make('Foo', [$b, 'last_name' => 'Doe', $a]);
// $foo->a == $a
// $foo->b == $b
// $foo->first_name == null
// $foo->last_name  == 'Doe'

绑定

许多现代应用程序都有可能被替换的部分。这是通过使用接口来完成的。Phi允许使用绑定自动注入接口

interface BarInterface {

}

class Bar implements BarInterface {

}

class Foo {
  public function __construct(BarInterface $bar) {
    // ...
  }
}

$phi->bind('BarInterface', 'Bar');
$foo = $phi->make('Foo');
// $foo->bar == new Bar
$bar = $phi->make('BarInterface');
// $bar = new Bar

绑定甚至允许你用一个具体的实例替换另一个具体的实例

$phi->bind('A', 'B');

$a = $phi->make('A');
// $a == new B

带有参数的依赖项

有时你可能有一个具有必需参数的依赖项。这可以通过将类绑定到可调用对象来实现

$phi->bind('Person', function() {
  return new Person('John', 'Doe');
});

$person = $phi->make('Person');
// $person->first_name == 'John'
// $person->last_name  == 'Doe'

这也适用于你需要执行实例化类时逻辑的情况

$id = 0;

$phi->bind('Person', function() use(&$id) {
  return new Person(++$id);
});

$person1 = $phi->make('Person'); // id == 1
$person2 = $phi->make('Person'); // id == 2

传递给Phi的任何参数都将直接传递给可调用对象

$id = 0;

$phi->bind('Person', function($name, $age) use(&$id) {
  $names = explode(' ', $name);
  return new Person(++$id, $name[0], $name[1], $age);
});

$person = $phi->make('Person', ['John Doe', 21]);
// $person->id == 1
// $person->first_name == 'John'
// $person->last_name  == 'Doe'
// $person->age == 21

单例

Phi还允许将类绑定到真实实例。这可以用来创建单例

$default_pdo = new PDO(...); // The default database
$stats_pdo   = new PDO(...); // PDO pointing to a different database

$phi->bind('PDO', $default_pdo);
$pdo = $phi->make('PDO');
// $pdo == $default_pdo
class Table {
  public function __construct(PDO $pdo, $table_name) {
    // ...
  }
}

$users_table = $phi->make('Table', ['users']);
// $users_table->pdo   == $default_pdo
// $users_table->table == 'users'

$stats_table = $phi->make('Table', [$stats_pdo, 'stats']);
// $stats_table->pdo   == $stats_pdo
// $stats_table->table == 'stats'

别名

有时,代码库中将有很多常用的类,它们具有难以记住的名称。例如,Vendor\Package\Core\Logging\Log。给这样的类起一个较短且易于输入的名称可能很有用

$phi->bind('core.log', 'Vendor\Package\Core\Logging\Log');

$log = $phi->make('core.log');
// $log == new Vendor\Package\Core\Logging\Log

您还可以将别名绑定到可调用对象或单例。

自定义解析器

有时你可能需要匹配多个别名,而不是单个别名。自定义解析器正是为此目的而设计的。当从Phi请求绑定时,任何已注册的自定义解析器都将按添加顺序依次执行,并返回非空值的第一个解析器将被使用。如果所有自定义解析器都返回null,Phi将按正常方式解析绑定。

use LordMonoxide\Phi\ResolverInterface;

class CustomResolver implements ResolverInterface {
  public function make($alias, array $arguments = []) {
    if($alias == 'A') {
      return new B(new A());
    }
  }
}

$phi->addResolver(new CustomResolver());
$b = $phi->make('A');
//$b == new B

使用自定义解析器的另一个原因是封装其他IoC容器。例如,如果你正在使用Laravel,你可以将Laravel容器与Phi结合使用

use Illuminate\Support\Facades\App;
use LordMonoxide\Phi\ResolverInterface;

class LaravelResolver implements ResolverInterface {
  public function make($alias, array $arguments = []) {
    if(App::bound($alias)) {
      return App::make($alias, $arguments);
    }
  }
}

$phi->addResolver(new LaravelResolver());

这样,任何在Laravel IoC容器中注册的绑定都将由它解析。其余的将传递给Phi。