承接 dakujem/wire-genie 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

dakujem/wire-genie

Composer 安装命令:

composer require dakujem/wire-genie

包简介

Autowiring Tool & Dependency Provider. Wire with genie powers.

README 文档

README

Tests Coverage Status

Autowiring Tool & Dependency Provider for PSR-11 service containers. Wire with genie powers.

💿 composer require dakujem/wire-genie

📒 Changelog

What?

A superpowered call_user_func? Yup! And more.

Wire Genie uses your PSR-11 service container to "magically" provide arguments (dependencies).

Allows you to:

  • invoke any callables
  • construct any objects

... with high level of control over the arguments. 💪

Usage

$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);

That is only the basis, the process is customizable and more powerful.

For each parameter it is possible to:

  • override type-hint and wire an explicit dependency (override the type-hint)
  • construct missing services on demand (resolves cascading dependencies too)
  • skip wiring (treat as unresolvable)
  • override value (bypass the container)
// 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)

How it works

There are two primary methods:

Genie::invoke(  callable $target, ...$pool );
Genie::construct( string $target, ...$pool );

... where the variadic $pool is a list of values that will be used for unresolvable parameters.

The resolution algorithm works like the following. If any step succeeds, the rest is skipped.
For each parameter...

  1. If the parameter name matches a named argument from the pool, use it.
  2. If #[Skip] hint is present, skip steps 3-6 and treat the parameter as unresolvable.
  3. If a #[Wire(Identifier::class)] hint (attribute) is present, resolve the hinted identifier using the container.
  4. Resolve the type-hinted identifier using the container.
  5. If #[Hot] hint is present, attempt to create the type-hinted class. Resolve cascading dependencies.
  6. If #[Make(Name::class)] hint is present, attempt to create the hinted class. Resolve cascading dependencies.
  7. When a parameter is unresolvable, try filling in an argument from the pool.
  8. If a default parameter value is defined, use it.
  9. If the parameter is nullable, use null.
  10. Fail utterly.

Hints / attributes

As you can see, the algorithm uses native attributes as hints to control the wiring.

#[Wire(Identifier::class)] tells Genie to try to wire the service registered as Identifier from the container
#[Wire('identifier')] tells Genie to try to wire service with 'identifier' identifier from the container
#[Hot] tells Genie to try to create the type-hinted class (works with union types too)
#[Make(Service::class, 42, 'argument')] tells Genie to try to create Service class using 42 and 'argument' as the argument pool for the construction
#Skip tells Genie not to use the container at all

Hot and Make work recursively, their constructor dependencies will be resolved from the container or created on the fly too.

What can it be used for?

  • middleware / pipeline dispatchers
  • asynchronous job execution
    • supplying dependencies after a job is deserialized from a queue
  • generic factories that create instances with varying dependencies
  • method dependency injection
    • for controllers, where dependencies are wired at runtime

Examples

A word of caution

Fetching services from the service container on-the-fly might solve an edge case in certain implementations where dependency injection boilerplate can not be avoided or reduced in a different way.

It is also the only way to invoke callables with dependencies not known at the time of compilation.

Normally, however, you want to wire your dependencies when building your app's service container.

Disclaimer 🤚

Improper use of this package might break established IoC principles and degrade your dependency injection container to a service locator, so use the package with caution.

Remember, it is always better to inject a service into a working class, then to fetch the service from within the working class (this is called "Inversion of Control", "IoC").

Integration

As with many other third-party libraries, you should consider wrapping code using Wire Genie into a helper class with methods like the following one:

/**
 * 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);
}

This adds a tiny layer for flexibility, in case you decide to tweak the way you wire dependencies later on.

Static provisioning

Genie::provide() can be used to provision a callable with a fixed list of services without using reflection.

$factory = function( Dependency $dep1, OtherDependency $dep2 ): MyObject {
    return new MyObject($dep1, $dep2);
};
$object = $g->provide( Dependency::class, OtherDependency::class )->invoke($factory);

Limiting access to services

You can limit the services accessible through Genie by using a filtering proxy Limiter:

$repoGenie = new Dakujem\Wire\Genie(
    new Dakujem\Wire\Limiter($container, [
        RepositoryInterface::class,
        // you may whitelist multiple classes or interfaces
    ])
);

The proxy uses the instanceof type operator and throws if the requested service does not match at least one of the whitelisted classes or interface names.

Customization

A custom strategy can be inserted into Genie, and the default AttributeBasedStrategy allows for customization of the resolver mechanism, thus providing ultimate configurability.

Compatibility

Framework agnostic. Any PSR-11 container can be used.

Wonderful lamp

// 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);

Flying carpet

We've already got a lamp 🪔 and a genie 🧞 ... so?

Installation

💿 composer require dakujem/wire-genie

Never heard of Composer? Go get it!

Testing

Run unit tests using the following command:

$ composer test
or
$ php vendor/phpunit/phpunit/phpunit tests

Contributing

Ideas, feature requests and other contribution is welcome. Please send a PR or create an issue.

Now go, do some wiring!

dakujem/wire-genie 适用场景与选型建议

dakujem/wire-genie 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 6.53k 次下载、GitHub Stars 达 2, 最近一次更新时间为 2020 年 05 月 10 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 dakujem/wire-genie 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 dakujem/wire-genie 我们能提供哪些服务?
定制开发 / 二次开发

基于 dakujem/wire-genie 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 6.53k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 3
  • 点击次数: 26
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: Unlicense
  • 更新时间: 2020-05-10