krak/cargo

受pimple启发的轻量级且功能强大的容器库

v0.2.4 2017-04-26 19:33 UTC

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']

自动装配

自动装配允许容器尝试自动实例化未在容器中定义的服务。要启用自动装配,您需要

  1. 安装Auto Args库(composer install krak/auto-args
  2. 使用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);

ab 将解析为各自的类。这仅适用于单例/工厂条目,否则它将像字符串值一样处理服务,而不会尝试自动解析。

服务提供者

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 定义,但回退到委托容器。

ArrayAccessDelegateContainerPsrDelegateContainer 都作为委托容器。第一个将接受任何数组或 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