lordmonoxide / phi
一个超级最小的控制反转库
Requires
- php: >=5.4.0
Requires (Dev)
- satooshi/php-coveralls: dev-master
This package is auto-updated.
Last update: 2024-09-15 05:34:01 UTC
README
φ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。