pyrsmk/chernozem

面向大众的依赖注入容器

4.2.1 2020-06-21 14:54 UTC

This package is auto-updated.

Last update: 2024-09-22 00:13:06 UTC


README

Chernozem 是一个基于 ArrayAccess 的高级依赖注入容器。它主要设计为可以被另一个类扩展,因此它会为你处理任何选项。但也可以作为一个简单的容器使用。

具体来说,Chernozem 可以注册任何你想要的值并创建服务。服务是简单的闭包,用于初始化对象并返回其实例。这对于例如,在需要时加载日志记录器,以及它可能需要的所有选项和依赖项,非常有用。

如果你想了解更多关于依赖注入的信息,Fabien Potencier 写了一篇关于这个主题的文章: http://fabien.potencier.org/article/11/what-is-dependency-injection

功能

  • container-interop 兼容
  • 工厂闭包
  • inflectors
  • 委托容器
  • 服务提供者
  • 类型提示
  • 等等...

关于 v4 的注意事项

Chernozem 已完全重写,API 也已更改。请在从 v3 升级之前阅读整个文档。

安装

composer require pyrsmk/chernozem

基础知识

让我们快速看看它是如何工作的

$chernozem = new Chernozem\Container();

// Set a value
$chernozem['foo'] = 72;

// Get a value
echo $chernozem['foo'];

// Test a value
if(isset($chernozem['foo'])) {
	// 'foo' value exists
}

// Remove a value
unset($chernozem['foo']);

或者,为了互操作性,你可以通过这个 API 访问 Chernozem

// Set a value
$chernozem->set('foo', 72);

// Get a value
echo $chernozem->get('foo');

// Test a value
if($chernozem->has('foo')) {
	// 'foo' value exists
}

// Remove a value
$chernozem->remove('foo');

或者也可以通过这个 API

// Set a value
$chernozem->setFoo(72);

// Get a value
echo $chernozem->getFoo();

你也可以用一些值实例化 Chernozem

$chernozem = new Chernozem\Container([
	'foo' => 72,
	'bar' => 33
]);

你可以添加值到容器中而不指定键(就像使用正常的数组一样)

$chernozem[] = 'foo';

Chernozem 也支持对象作为键

$myclass = new Stdclass();
$chernozem[$myclass] = 72;
// Print '72'
echo $chernozem[$myclass];

如果你需要清除容器中的所有值,请这样做

$chernozem->clear();

工厂闭包

使用工厂闭包,你可以创建服务。`factory()` 方法创建一个总是返回服务新实例的服务

$chernozem['some_service'] = $chernozem->factory(function($chernozem) {
	return new Some_Service();
});

`service()` 方法创建一个将返回自身相同实例的服务

$chernozem['some_service'] = $chernozem->service(function($chernozem) {
	return new Some_Service();
});

服务提供者

服务提供者是一种使用类组织和重用服务的方法。让我们看看如何编写一个服务提供者

class MyService implements Chernozem\ServiceProviderInterface {

	public function register(Interop\Container\ContainerInterface $container) {
		$container['some_service1'] = new Some_Service();
		$container['some_service2'] = new Another_Service();
		$container['an_option'] = 'my@email.com';
	}

}

服务提供者在注册时直接运行,

$chernozem->register(new MyService());

类型提示

类型提示是 Chernozem 的一个酷特性,它允许你在容器中定义值的类型。这可以避免应用中的问题,因为可能会设置错误类型的值。

// Set a list of fruits
$chernozem['fruits'] = ['apple', 'banana', 'pear'];
// Set type hinting
$chernozem->hint('fruits', 'array');
// Oops! Wrong type!
$chernozem['fruits'] = 72;

支持以下基本类型

  • 布尔值/bool
  • 整数/int
  • 浮点数/双精度
  • 字符串
  • 数组
  • 任何类名

只读值

如果需要,你可以使用以下方法将你的值标记为只读

// Set a 'mailer' service
$chernozem['mailer'] = $chernozem->service(function($chernozem) {
	return new Mailer();
});
// Mark as read onyl
$chernozem->readonly('mailer');

inflectors

所有之前的功能都是通过 inflectors 可用的。inflectors 是一种在设置或获取值时改变值的方式。请注意,setter inflectors 通常用于数据验证,而 getter inflectors 用于数据过滤。对于下一个示例,假设我们之前设置了一些我们不想在应用中进一步覆盖的随机服务,并且我们想在每次检索服务时运行一些操作

$chernozem->setter('foo_service', function($service) {
	throw new Exception("'foo_service' is already set!");
	return $service; // Never run, it's only for the example
});

$chernozem->getter('foo_service', function($service) {
	$service->executeSomeAction();
	$service->executeAnotherAction();
	return $service;
});

现在,设置 'foo_service' 将抛出异常,并且每次检索 'foo_service' 时,将在我们的服务上运行两个操作。就这么简单!

委托和组合容器

委托容器是一种用于加载服务依赖项的容器。您可能会看到 factory()service() 将 Chernozem 作为参数传递。它用于服务在容器中加载一些依赖项。但在大型应用程序中,您可能需要处理许多不同的容器,而您的依赖项可能不在 Chernozem 容器中。

$delegate_container = new SomeVendor\Container();

// ... some stuff ...

$chernozem->delegate($delegate_container);

$chernozem['some_service'] = $chernozem->service(function($delegate_container) {
	$some_service = new Some_Service();
	// Load an option that is registered in the delegate container
	$some_service->setOption('some_option', $delegate_container->get('some_option'));
	return $some_service;
});

如果您有大量的容器要管理,Chernozem 提供了一个 Composite 类来处理这个问题

// Instantiate containers
$container1 = new SomeVendor\Container();
$container2 = new AnotherVendor\Container();

// ... some stuff ...

// Add containers to the composite container
$composite = new Chernozem\Composite();
$composite->add($container1);
$composite->add($container2);

现在所有容器都已注册,您就可以像往常一样获取值

echo $composite['foo'];

在复合类上调用 foo 将获取在已注册容器中找到的第一个 foo 值。您也可以验证键是否存在

if(isset($composite['foo'])){
	// the 'foo' key exists
}

请注意,Composite 类基于 container-interop,并且它还支持 delegate() 方法,以便在需要时进行链式调用。

循环

Chernozem 容器还实现了 IteratorCountable。这意味着您可以在容器上迭代并计算其中的元素数量。

echo count($chernozem);
foreach($chernozem as $id => $value) {
	var_dump($value);
}

获取数据的其他方法

您可以使用以下方法检索所有容器值

$values = $chernozem->toArray();

如果您需要获取一个未经过变换器修改的原始值,例如服务的基闭包,请调用 raw()

$chernozem['some_service'] = $chernozem->service(function($c) {
	// do stuff
});

$closure = $chernozem->raw('some_service');

链式数组

您不能使用 Chernozem 链式数组来修改或检索值,这是由于 PHP 对 ArrayAccess 的限制。处理这种情况的基本方法是

// Get 'foo' array
$foo = $chernozem['foo'];
// Add 'bar' value to the array
$foo['bar'] = 42;
// Update 'foo' value
$chernozem['foo'] = $foo;

但为了简化这一点,您可以将 Chernozem\Container 对象声明为容器值而不是数组,然后您将能够链式调用

$chernozem['foo'] = new Chernozem\Container(['bar' => 72]);
// Print '72'
echo $chernozem['foo']['bar'];
$chernozem['foo']['bar'] = 42;
// Print '42'
echo $chernozem['foo']['bar'];

最后注意事项

如果您想覆盖由 factory()service() 创建的工厂闭包,在设置之前必须先取消设置其值

$chernozem['service'] = $chernozem->service(function() {
	// First service
});

unset($chernozem['service']);

$chernozem['service'] = $chernozem->service(function() {
	// New service
});

许可证

Chernozem 根据 MIT 许可证 发布。