frogsystem /spawn
Spawn 是一个简单且轻量级的 IoC 应用容器实现,并且完全兼容 container-interop。
Requires
- php: >=5.6.0
- container-interop/container-interop: 1.1.0
Requires (Dev)
- codacy/coverage: ^1.0
- phpunit/phpunit: ^5.2
Provides
This package is not auto-updated.
Last update: 2024-09-10 02:07:39 UTC
README
Spawn
Spawn 是一个简单且轻量级的 IoC 应用容器实现,并且完全兼容 container-interop 标准。
安装
您可以通过 Composer 安装此包
composer require frogsystem/spawn
此包遵循语义版本规范,并且次要版本之间将保持完全向后兼容。
文档
通过创建一个新的实例启动容器
$app = new Container();
Spawn 会自行管理;只要您使用依赖注入和提供的工厂方法,您将始终得到相同的实例。
获取条目
使用标准化的 get
方法从容器中检索条目;使用数组访问方便起见
print $app->get('MyEntry'); // will print whatever value 'MyEntry' has print $app['MyEntry']; // will do the same
然而,如果条目被设置为可调用对象,则会调用该可调用对象并返回其结果。您会发现这种行为的用途来达到不同的目标。
$app->set('MyEntry', function() { return 'Called!' }); print $app->get('MyEntry'); // will print 'Called!'
设置条目
要将条目注册到容器中,请使用提供的 set
方法或数组访问
$app->set('MyEntry', $value); $app['MyEntry'] = $value;
工厂(实现)
根据设计,容器的目的是为您提供抽象的实现。为此,您必须将抽象绑定到工厂闭包
$app['ACME\MyContract'] = function() use ($app) { return $app->make('MyImplementation'); };
对于这种情况和其他常见用例,存在简写方式
$app['ACME\MyContract'] = $app->factory('MyImplementation'); // shorthand for the statement above (roughly)
赋值(实例)
可以通过正常赋值将特定实例绑定到抽象
$app['ACME\MyContract'] = new MyImplementation();
一次(延迟执行)
如果您想将可调用对象的执行延迟到实际请求时(例如,因为它昂贵但并不总是使用),请使用 once
$app['ACME\MyContract'] = $app->once(function() { return very_expensive_call(); // will be executed once when 'ACME\MyContract' is requested; returns its result afterwards });
它将存储结果,并且任何对 ACME\MyContract
的进一步请求都将返回存储的结果而不是调用可调用对象。
单例(Singleton)
这允许我们注册表现类似于单例的实现
$app['ACME\MyContract'] = $app->one('ACME\MyClass'); // instantiated on first request; returns the same object every time
保护可调用对象
如果您想在容器中存储闭包或其他可调用对象,可以保护它们在检索时不会被调用
$app['MyCallable'] = $app->protect(function() { print 'Called!'; }); $printer = $app->get('MyCallable'); // will do nothing $printer(); // will print 'Called!'
工厂工厂
将所有这些放在一起,您可以轻松地创建所谓的 FactoryFactory
- 一个在您需要时为您提供特定工厂的工厂。
$app['UserFactory'] = $this->protect(function($username) use ($app) { $user = $app->one('User')->getByName($username); return $user; }); $userFactory = $app->get('UserFactory'); print $userFactory('Alice')->getName(); // will print 'Alice' print $userFactory('Bob')->getName(); // will print 'Bob'
检查条目
使用 has
方法检查条目是否存在
$app->has('MyEntry'); // true or false
内部实现
您必须仅使用容器来定义您的抽象。它们旨在与其他容器共享,并且实现可以在运行时由另一个实现替换。但是,您会遇到您的代码依赖于特定实例的情况。这些内部实现与其他容器的一部分分开保存,因此必须作为属性设置
$app->config = $app->make('MyConfig');
使用魔法设置器同样可以提供与上面所述相同的 API。您还可以将内部显式定义为类属性,但是以这种方式设置的可调用对象在检索时不会被调用。
通过属性获取您的内部实现
print $app->version;
要同时为内部和常规容器条目设置值,请简单地链式赋值
$app->config = $app['ConfigContract'] = $this->factory('MyConfig');
依赖注入
Spawn为您提供了两种使用依赖注入和反转控制模式的方法。使用make
来创建抽象类的新实例;使用invoke
来执行带有填充依赖的调用者。这两种方法都会使用依赖注入来解析它们的参数。这意味着,如果被调用的调用者或类构造函数有任何参数,这些方法将使用容器来查找合适的实现,并将其注入参数列表中。
通过get
或ArrayAccess
从容器中检索到的任何值,如果是一个调用者,将使用相同的invoke
方法调用。因此,它们也将注入依赖。
使用make
从具体类创建对象
class MyClass { __construct(OtherClass $other); } $app->make('MyClass');
当使用调用者作为参数调用invoke
时,Spawn将尝试解析任何参数
class MyObject { function print() { print 'Found!!' } } $callable = function(MyObject $object) { $object->print(); } $app->invoke($callable); // will print 'Found!'
额外参数
您还可以将这些方法作为数组传递额外的参数。这允许您根据每个案例覆盖依赖查找。在参数选择期间,条目将首先在数组中查找,匹配参数类和名称与数组键。
class MyClass { __construct(OtherClass $other, $id); function do($name) { print $name; } } $object = $app->make('MyClass', ['id' => 42]); // $id will be 42, $other will be resolved through the container $app->invoke([$object, 'do'], ['name' => 'Spawn']); // will print 'Spawn'
如上所述,get
也会在返回之前调用一个调用者。因此,您也可以将额外的参数传递给此方法。
代理查找
代理查找是容器互操作性标准引入的功能。对容器的请求在容器内部执行。但是,如果获取的条目有依赖关系,而不是在容器中执行依赖查找,而是在代理容器中进行查找。换句话说:每当发生依赖注入时,依赖关系将通过代理容器解决。
Spawn中的依赖查找始终是代理的。默认情况下,容器将查找委托给自己。通过构造函数参数设置不同的代理容器或使用delegate
方法。
$app = new Container($delegateContainer); $app->delegate($delegateContainer);
代理查找允许跨容器共享条目,并允许构建一个代理队列。查看设计原则了解如何正确利用此功能。
设计原则
- 实现容器-互操作性
- 实现代理查找
- 分离存储'公共'抽象和内部
- 始终通过相同的方法添加条目;所有其他功能都使用闭包实现
- 强制用户使用代理查找功能和代理队列