pyrsmk / chernozem
面向大众的依赖注入容器
Requires
- php: >=5.4
- container-interop/container-interop: ^1.1
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 容器还实现了 Iterator
和 Countable
。这意味着您可以在容器上迭代并计算其中的元素数量。
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 许可证 发布。