dakujem / wire-genie
自动装配工具 & 依赖提供者。拥有魔力的Wire。
Requires
- php: ^8.0
- psr/container: ^1
Requires (Dev)
- dakujem/sleeve: ^1
- phpunit/phpunit: ^8 || ^9.1
This package is auto-updated.
Last update: 2024-09-09 12:36:47 UTC
README
自动装配工具 & 依赖提供者 用于PSR-11服务容器。拥有魔力的Wire。
💿
composer require dakujem/wire-genie
📒 变更日志
是什么?
一个超级强大的call_user_func
?是的!还有更多。
Wire Genie使用您的PSR-11服务容器来“神奇地”提供参数(依赖)。
允许您
- 调用任何可调用项
- 构造任何对象
...并具有高度控制参数的能力。💪
用法
$container = new Any\Psr11\Container([ Thing::class => new Thing(), MyService::class => new MyService(), ]); $callable = function (MyService $service, Thing $thing){ ... }; class Something { public function __construct(MyService $service, Thing $thing) { ... } } $g = new Dakujem\Wire\Genie($container); // Magic! The dependencies are resolved from the container. $value = $g->invoke($callable); $object = $g->construct(Something::class);
这只是基础,过程可自定义且更强大。
对于每个参数,您可以
- 重写类型提示并连接显式依赖项(重写类型提示)
- 按需构建缺失的服务(也解决级联依赖)
- 跳过连接(视为无法解析)
- 重写值(绕过容器)
// override type-hint(s) $callable = function ( #[Wire(MyService::class)] AnInterface $service, #[Wire(Thing::class)] $thing ){ ... }; $value = $g->invoke($callable); // construct object(s) if not present in the container $callable = function ( #[Hot] Something $some, #[Make(Thing::class)] $thing ){ ... }; $value = $g->invoke($callable); // provide arguments for scalar-type, no-type and otherwise unresolvable parameters $callable = function (string $question, MyService $service, int $answer){ ... }; $g->invoke( $callable, 'The Ultimate Question of Life, the Universe, and Everything.', 42, ); $g->invoke( $callable, answer: 42, question: 'The Ultimate Question of Life, the Universe, and Everything.', ); // skip wiring for a parameter... $callable = function (#[Skip] MyService $service){ ... }; $g->invoke($callable, new MyService(...)); // ...and provide your own argument(s)
工作原理
有两种主要方法
Genie::invoke( callable $target, ...$pool ); Genie::construct( string $target, ...$pool );
...其中变长参数$pool
是用于无法解析的参数的值列表。
解析算法工作如下。如果任何步骤成功,则跳过其余步骤。
对于每个参数...
- 如果参数名与池中的命名参数匹配,则使用它。
- 如果存在
#[Skip]
提示,则跳过步骤3-6,并将参数视为无法解析。 - 如果存在
#[Wire(Identifier::class)]
提示(属性),则使用容器解析所提示的标识符。 - 使用容器解析类型提示的标识符。
- 如果存在
#[Hot]
提示,则尝试创建类型提示的类。解决级联依赖。 - 如果存在
#[Make(Name::class)]
提示,则尝试创建所提示的类。解决级联依赖。 - 当参数无法解析时,尝试填充池中的一个参数。
- 如果定义了默认参数值,则使用它。
- 如果参数是可空的,则使用
null
。 - 彻底失败。
提示/属性
如您所见,算法使用原生属性作为提示来控制连接。
#[Wire(Identifier::class)]
告诉Genie尝试从容器中连接注册为Identifier
的服务
#[Wire('identifier')]
告诉Genie尝试从容器中连接具有'identifier'
标识符的服务
#[Hot]
告诉Genie尝试创建类型提示的类(也适用于联合类型)
#[Make(Service::class, 42, 'argument')]
告诉Genie尝试使用42
和'argument'
作为构建参数池来创建Service
类
#Skip
告诉Genie根本不使用容器
Hot
和Make
是递归的,它们的构造函数依赖项也将从容器中解析或即时创建。
它可以用作什么?
- 中间件/管道分发器
- 异步作业执行
- 在作业从队列反序列化后提供依赖项
- 通用工厂,创建具有不同依赖项的实例
- 方法依赖注入
- 对于控制器,其中依赖项在运行时连接
示例
一句忠告
动态从服务容器中获取服务可能解决某些实现中的边缘情况,在这些情况下,依赖注入的样板代码无法避免或以其他方式减少。
这也是在编译时未知依赖的调用程序的唯一调用方式。
然而,通常在构建应用程序的服务容器时,你希望连接你的依赖。
免责声明 🤚
不恰当地使用此包可能会破坏已建立的IoC原则,并将依赖注入容器降级为服务定位器,因此请谨慎使用该包。
请记住,将服务注入到工作类中总是比在工作类内部获取服务(这被称为“控制反转”,“IoC”)更好的。
集成
与其他许多第三方库一样,你应该考虑将使用Wire Genie的代码包装在一个辅助类中,其中包含以下方法
/** * Invokes a callable resolving its type-hinted arguments, * filling in the unresolved arguments from the static argument pool. * Returns the callable's return value. * Also allows to create objects passing in a class name. */ public function call(callable|string $target, ...$pool): mixed { return Genie::employ($this->container)($target, ...$pool); }
这添加了一个微小的层,以便您稍后决定调整依赖项连接的方式。
静态配置
Genie::provide()
可以用来配置一个具有固定服务列表的可调用对象,而无需使用反射。
$factory = function( Dependency $dep1, OtherDependency $dep2 ): MyObject { return new MyObject($dep1, $dep2); }; $object = $g->provide( Dependency::class, OtherDependency::class )->invoke($factory);
限制对服务的访问
您可以通过使用过滤代理Limiter
来限制通过Genie
可访问的服务
$repoGenie = new Dakujem\Wire\Genie( new Dakujem\Wire\Limiter($container, [ RepositoryInterface::class, // you may whitelist multiple classes or interfaces ]) );
代理使用instanceof
类型操作符,如果请求的服务不匹配白名单中的任何类或接口名称,则抛出异常。
定制
可以将自定义的策略插入到Genie
中,默认的AttributeBasedStrategy
允许自定义解析机制,从而提供最大可配置性。
兼容性
框架无关。可以使用任何PSR-11容器。
神奇的灯泡
// If we happen to find a magical lamp... $lamp = new Dakujem\Wire\Lamp($container); // we can rub it, and a genie might come out! $genie = $lamp->rub(); // My wish number one is... $genie->construct(Palace::class);
飞毯
我们已经有一个灯泡 🪔 和一个精灵 🧞 ... 那么?
安装
💿 composer require dakujem/wire-genie
从未听说过Composer?去获取它!
测试
使用以下命令运行单元测试
$
composer test
或者
$
php vendor/phpunit/phpunit/phpunit tests
贡献
欢迎提供想法、功能请求和其他贡献。请发送PR或创建一个问题。
现在去,做一些连接吧!