krak / cargo
受pimple启发的轻量级且功能强大的容器库
Requires
- krak/array: ^0.4.0
- psr/container: ^1.0
Requires (Dev)
- krak/auto-args: ^0.3.1
- peridot-php/peridot: ^1.18
- pimple/pimple: ^3.0
- symfony/var-dumper: ^3.2
This package is auto-updated.
Last update: 2024-09-18 17:53:34 UTC
README
Cargo是另一个容器库。它的特性和语义紧密遵循Pimple;然而,它的设计更加模块化,因此可以扩展。它被设计成与Pimple兼容;因此,您可以轻松地使用Cargo与任何Pimple服务提供者。
Pimple是一个出色的服务容器;然而,它有一个问题...可扩展性。Pimple从未被设计为正确扩展或装饰,这使得在不修改核心的情况下添加功能变得非常困难。Cargo是一个容器,它设法保持了Pimple的简单性,同时允许强大的扩展。
安装
使用composer安装krak/cargo
使用方法
创建容器
创建cargo容器有几种方法。最简单的方法是创建默认容器,如下所示
<?php use Krak\Cargo; $c = Cargo\container();
这和这样做是一样的
$c = new Container\BoxContainer(); $c = new Container\SingletonContainer($c); $c = new Container\BoxFactoryContainer($c); $c = new Container\FreezingContainer($c); $c = new Container\AliasContainer($c);
Cargo被设计成可扩展和灵活的,因此每个容器装饰器都添加了一个可以删除的功能。
如果您只想有一个裸骨容器,您可以使用以下内容
<?php $c = Cargo\liteContainer($values = [], $box_container = null);
这仅仅实现了Box和BoxFactory容器,默认情况下将服务作为单例缓存。
定义服务
服务可以通过多种方式定义和配置。
$c['a'] = function($c) { return new ServiceA(); }; // or $c->add('b', function($c) { return new ServiceB($c['a']); });
由于BoxFactoryContainer,所有闭包都被视为懒服务。这意味着它们在需要时才被调用。单例容器也将所有服务默认为单例,因此服务定义闭包的结果被缓存,以确保它不会两次调用。这些语义模仿了Pimple容器的行为;
访问容器
您可以使用ArrayAccess方法或get
方法来检索值和调用服务。
$c['a'] == $c->get('a');
工厂或单例服务
您可以使用这两个辅助方法指定是否将服务定义为工厂或单例。
$c->singleton('a', function() { return new ServiceA(); }); $c->factory('b', function() { return new ServiceB(); }); // $c['a'] === $c['a'] - same instance each time // $c['b'] !== $c['b'] - different instance each time
参数/值
添加到容器中的任何不是服务的内容都定义为值。
$c['a.parameter'] = 'value'; $c['a'] = function($c) { return new ServiceA($c['a.parameter']); };
值将按原样存储和检索。不对其进行任何处理。
如果您想将闭包用作参数,您可以使用protect
方法
$func = function() {}; $c->protect('a.closure_parameter', $func); // it returns the same instance because values are just stored as is. // $c['a.closure_parameter'] === $func
环境参数
您可以使用env
方法注册要从中读取的参数
$c->env('APP_KEY', $alias = 'application.key'); // $c['APP_KEY'] === $c['application.key'] are read from the env
封装服务
类似于Pimple的extend
,Cargo允许您封装服务定义进行装饰。
如果您想替换一个定义,您只需重新定义它;但是,如果您想装饰或修改一个定义,您只需封装它
$c['logger'] = function() { return new Logger(); }; $c->wrap('logger', function($logger, $c) { $logger->setValue($c['value']); return new MyLogger($logger); }); // $c['logger'] instanceof MyLogger == true
服务冻结
由于FreezingContainer,服务默认将冻结。您可以多次重新定义条目,但一旦服务被调用,它就被认为是冻结的,如果尝试重新定义它,将抛出异常。
$c['a'] = function() {}; // ok to redefine because we haven't invoked 'a' yet. $c['a'] = function() {}; $service = $c['a']; // this will throw an exception because the service was frozen $c['a'] = function() {};
条目别名
通常使用类名作为标识符很有用,但也可以提供别名以供快速参考。
$c[Acme\ServiceA::class] = function() { return new Acme\ServiceA(); }; $c->alias(Acme\ServiceA::class, 'acme.service_a', 'a'); // $c[Acme\ServiceA::class] === $c['service_a'] === $c['a']
自动装配
自动装配允许容器尝试自动实例化未在容器中定义的服务。要启用自动装配,您需要
- 安装Auto Args库(
composer install krak/auto-args
) - 使用AutoWireContainer
// the second parameter as true will include the auto wiring $c = Cargo\container([], $auto_wire = true); $stack = $c->get('SplStack'); // will return an instance of SplStack as a singleton. // defines 'StdClass' as a factory instance and will set it up for auto-wiring since no definition was given. $c->factory('StdClass'); // $c['StdClass'] !== $c['StdClass']
此外,您可以绑定任何类来自动装配。
$c->singleton('a', SplStack::class); $c->factory('b', ArrayObject::class);
a
和 b
将解析为各自的类。这仅适用于单例/工厂条目,否则它将像字符串值一样处理服务,而不会尝试自动解析。
服务提供者
Cargo\ServiceProvider
为定义多个相关服务提供了一个简单的接口。
interface ServiceProvider { public function register(Cargo\Container $c); }
您可以使用 register
方法在给定的容器中注册服务提供者。
$c->register(new FooProvider(), [ 'foo.parameters' => 1, ]); // or Cargo\register($c, new FooProvider(), [])
容器互操作
Krak\Cargo\Container
默认不兼容 ContainerInterop
接口。但是,您可以使用 toInterop
函数轻松地将容器导出到互操作容器。
$interop = Cargo\toInterop($c); // or $c->toInterop // $interop instanceof Psr\Container\ContainerInterface
Pimple 互操作
使用 toPimple
函数实现 Pimple 兼容性非常简单。
$pimple = Cargo\toPimple($c); // or $c->toPimple() $pimple['a'] = function() {}; $pimple->extend('a', function() {}); $pimple['b'] = $pimple->protect(function() { }); // $c has access to all services defined in pimple $c['b'];
委托容器
为了更好地与其他容器集成,我们提供了委托容器,允许您默认使用 cargo 定义,但回退到委托容器。
ArrayAccessDelegateContainer
和 PsrDelegateContainer
都作为委托容器。第一个将接受任何数组或 ArrayAccess
对象(如 Pimple),而另一个将接受任何 Psr 容器。
<?php $pimple = new Pimple\Container(); $pimple['a'] = 1; $pimple['b'] = 1; $c = Cargo\container(); $c = new Cargo\Container\ArrayAccessDelegateContainer($c, $pimple); $c['b'] = 2; assert($c['b'] == 2 && $c['a'] == 1);
Cargo 设计
用于装饰的容器接口
待完成...
盒子
待完成...
API
function alias(Container $c, $id, ...$aliases)
为容器 $c
中的条目 $id
在 $aliases
中创建别名。每个别名将与原始条目共享相同的盒子引用。
function env(Container $c, $var_name, $id = null)
将带有 $var_name
的 EnvBox 条目添加到容器 $c
中,其中 $var_name
是环境变量的名称,$id
是条目名称。如果 $id
被留为 null,则默认为 $var_name
。