frogsystem/spawn

Spawn 是一个简单且轻量级的 IoC 应用容器实现,并且完全兼容 container-interop。

1.0.0-rc1 2016-04-17 01:48 UTC

This package is not auto-updated.

Last update: 2024-09-10 02:07:39 UTC


README

Latest Stable Version Total Downloads Latest Unstable Version License Build Status Codacy Badge Codacy Badge SensioLabsInsight

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来执行带有填充依赖的调用者。这两种方法都会使用依赖注入来解析它们的参数。这意味着,如果被调用的调用者或类构造函数有任何参数,这些方法将使用容器来查找合适的实现,并将其注入参数列表中。

通过getArrayAccess从容器中检索到的任何值,如果是一个调用者,将使用相同的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);

代理查找允许跨容器共享条目,并允许构建一个代理队列。查看设计原则了解如何正确利用此功能。

设计原则

  • 实现容器-互操作性
  • 实现代理查找
  • 分离存储'公共'抽象和内部
  • 始终通过相同的方法添加条目;所有其他功能都使用闭包实现
  • 强制用户使用代理查找功能和代理队列