suramon/auto-pimple

自动服务加载的Pimple

2.2.5 2020-05-09 22:21 UTC

README

使用自动注入功能的 Pimple DI 容器扩展

PHP Composer Build Status

快速介绍

如果您不知道什么是 Pimple,或者您不是使用 Pimple v1,这可能对您没有什么帮助。AutoPimple 是 Pimple(AutoPimple 扩展 Pimple)的即插即用替代品,可以自动注入您的服务,从而减少配置。本介绍假设您已经了解 Pimple v1 API。

典型的旧式 Pimple 设置如下所示

namespace MyLittleProject;

class A {}
class B {}
class C { function __construct(A $a, B $b) {} }

$c = new \Pimple();
$c['my_little_project.a'] = $c->share(function($c) { return new A; });
$c['my_little_project.b'] = $c->share(function($c) { return new B; });
$c['my_little_project.c'] = $c->share(function($c) {
    return new C($c['my_little_project.a'], $c['my_little_project.b']);
});
echo get_class($c['my_little_project.c']); // outputs MyLittleProject\C

如果您使用 AutoPimple,则可以删除上述所有配置

$c = new \AutoPimple\AutoPimple();
echo get_class($c['my_little_project.c']); // outputs MyLittleProject\C

AutoPimple 将自动将 snake case 服务名称(abc_def.hij)转换为 camel case 名称(AbcDef\Hij),并根据构造函数参数的类型提示尝试注入服务。

删除命名空间前缀

每个类都可以通过将完整的类名转换为 snake case(如果自动注入没有失败)来自动地从 AutoPimple 容器中检索。这可能会导致相当长且不方便的服务名称。您可以像这样删除(或替换)命名空间的前缀

namespace MyCompany\MyProject\RatherLong\Namespace;
class A {}

$c = new \AutoPimple\AutoPimple(array(
    'my_company.my_project.' => '',
    'my_company.my_project.rather_long.namespace.' => '',
    'my_company.my_project.rather_long.' => 'blieblabloe.',
));
// Full path will always work
$c['my_company.my_project.rather_long.namespace.a'];
// We mapped 'my_company.my_project.' to ''
$c['rather_long.namespace.a'];
// We mapped 'my_company.my_project.rather_long.namespace.' to ''
$c['a'];
// We mapped 'my_company.my_project.rather_long.' to 'blieblabloe.'
$c['blieblabloe.a'];
// Even though the service name is different, they all point to the same service instance
var_dump($c['a'] === $c['blieblabloe.a']); // true

别名

AutoPimple 提供了一个简单的别名功能。别名的服务始终指向相同的数据。

$c['default_timeout'] = 5;
$c->alias('http_timeout', 'default_timeout');

echo $c['http_timeout']; // outputs 5

处理具有非自动注入构造函数参数的服务

有时您服务的构造函数参数无法进行类型提示,或者被类型提示为接口。前者可以解决一次,但后者应该为每个单独的服务进行解决。例如,假设我们有以下类设置

namespace MilkFactory;

interface Animal { }
class Cow implements Animal {}
class Sheep implements Animal {}

class Milker {
    function __construct(Animal $aminal, $milkingFrequency) { }
}

$c = new \AutoPimple\AutoPimple(array('milk_factory.' => ''));

由于 $animal 和 $milkingFrequency 都不能自动注入,我们可以以多种方式解决这个问题

// 1) old-style resolution (AutoPimple)
$c['milker'] = $c->share(function($c) {
    return new \MilkFactory\Milker($['cow'], 5);
});

// 2) AutoPimple style using shortened service name
$c->alias('milker.animal', 'cow');
$c['milker.frequency'] = 5;

// 3) AutoPimple style using full service name
$c->alias('milk_factory.milker.animal', 'cow');
$c['milk_factory.milker.frequency'] = 5;

// 4) By usings cows as default
$c->alias('animal', 'cow');
$c['milker.frequency'] = 5;

最后一种方法可能最难理解。通过将 'animal'(简称 milk_factory.animal,这是 MilkFactory\Animal 接口名称的 snake case)别名为 cow,我们告诉 AutoPimple 所有 Animal 接口都可以自动注入一个 Cow。因此,我们为 Animal 接口定义了一个默认类。我们可以始终使用方法(2)来覆盖默认设置。

测试自动注入的服务

当您想测试自动注入的服务但想模拟服务的某些依赖项时,AutoPimple 有一个非常简单的方法来帮助您。假设我们有以下设置

class A {
    function __construct(B $b, C $c, D $d, E $e) {}
}

如果您想获取服务 A 但想用 MockD 类模拟参数 $d,则可以使用 getModified 方法这样做

$c->getModified('a', array('d' => new MockD()));

这将创建 A 的新实例(getModified 总是创建新实例)并替换依赖项 $d 为 MockD。getModified 的第二个参数应该是一个关联数组,其中以 snake cased 参数名称(而不是参数类名称)为键,以模拟依赖项实例为值。