承接 pwm/sfw-container 相关项目开发

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

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

pwm/sfw-container

Composer 安装命令:

composer require pwm/sfw-container

包简介

A simple Container that ensures a cycle-free dependency graph

README 文档

README

Build Status codecov Maintainability Test Coverage License: MIT

A minimalistic DI container with cycle detection, cacheable instance resolution and dynamic loading.

Table of Contents

Why

There are many DI containers out there. My design goals were:

  • Minimalism
  • Cycle detection in the dependency graph
  • Instance caching by default
  • Dynamic loading

Requirements

PHP 7.1+

Installation

composer require pwm/sfw-container

Usage

Basic usage:

// Have some classes, some of them depend on others
class A { public function __construct(int $x) {} }
class B { public function __construct(string $s) {} }
class C { public function __construct(A $a, B $b) {} }

// Create a container
$c = new Container();

// Add resolver functions to the container that will instantiate your classes
$c->add(A::class, function (): A {
    return new A(1);
});
$c->add(B::class, function (): B {
    return new B('x');
});

// Resolving from within resolvers is easy as the Container is passed to the functions as the 1st parameter
$c->add(C::class, function (Container $c): C {
    return new C(
        $c->resolve(A::class),
        $c->resolve(B::class)
    );
});

// Resolve your classes
assert($c->resolve(A::class) instanceof A);
assert($c->resolve(B::class) instanceof B);
assert($c->resolve(C::class) instanceof C);

Cycle detection:

// X depends on Y and Y depends on X ...
class X { public function __construct(Y $y) {} }
class Y { public function __construct(X $x) {} }

$c = new Container();

$c->add(X::class, function (Container $c): X {
    return new X($c->resolve(Y::class));
});
$c->add(Y::class, function (Container $c): Y {
    return new Y($c->resolve(X::class));
});

try {
    $c->resolve(X::class);
} catch (CycleDetected $e) {
    assert('X -> Y -> X' === $e->getMessage());
}

Factory vs. cached instances:

// Simple class that saves a timestamp
class TS {
    private $timestamp;
    public function __construct(int $timestamp) {
        $this->timestamp = $timestamp;
    }
    public function getTimestamp(): int {
        return $this->timestamp;
    }
}

$c = new Container();

// Add our TS class both cached and as a factory (using different keys)
$c->add('cachedTS', function (): TS {
    return new TS(time());
});
$c->factory('factoryTS', function (): TS {
    return new TS(time());
});

// Get an instance for both
$cTS = $c->resolve('cachedTS'); // instantiate and cache
$fTS = $c->resolve('factoryTS'); // just instantiate

// Wait a sec ...
sleep(1);

// For the cached ones timestamps will match
assert($cTS->getTimestamp() === $c->resolve('cachedTS')->getTimestamp());

// Factory instantiates again, hence timestamps will differ
assert($fTS->getTimestamp() !== $c->resolve('factoryTS')->getTimestamp());

Dynamic loading:

interface Reader {
    public function read(): string;
}
class XmlReader implements Reader {
    public function read(): string {
        return 'Reading Xml...';
    }
}
class JsonReader implements Reader {
    public function read(): string {
        return 'Reading Json...';
    }
}

$c = new Container();

// Reader's resolver has to be added via factory otherwise
// it will be bound to whatever it first resolves to
$c->factory(Reader::class, function (Container $c, string $strategy): Reader {
    switch ($strategy) {
        case 'xml':
            return new XmlReader();
        case 'json':
            return new JsonReader();
        default:
            throw new RuntimeException(sprintf('No reader found for %s', $strategy));
    }
});

assert('Reading Xml...' === $c->resolve(Reader::class, 'xml')-> read());
assert('Reading Json...' === $c->resolve(Reader::class, 'json')-> read());

try {
    $c->resolve(Reader::class, 'csv');
} catch (RuntimeException $e) {
    assert('No reader found for csv' === $e->getMessage());
}

How it works

A container is just a map where keys are ids of classes defined by us and values are functions, called resolvers, that know how to instantiate these classes. Resolving a class simply means executing its resolver which will return an instance of the class. It's good practice to use full class names namepsace included as ids but we are free to use any string we like.

All resolvers get the container itself as their first argument which makes it easy to resolve from within a resolver, making the resolution process recursive. This is very useful as classes may have other classes as dependencies.

While we are free to build any dependency graph we like, the resolution process will stop when it encounters a cycle while traversing it. This ensures that only acyclic graphs (ie. DAGs) are handled and saves us from blowing the callstack.

The container caches resolved instances by default meaning that any subsequent resolution will return the same instance. This is good and usually what we want. However, if a resolver was added via the factory() method then every resolution will return a new instance of that class.

We can also pass extra parameters to resolvers. This, combined with factory(), makes it possible to implement dynamic loading, ie. to instantiate an interface in various different ways. An example of this would be what is known as the Strategy pattern, ie. using runtime information, eg. a user supplied CLI parameter, to instantiate the appropriate implementation of some interface.

Tests

$ vendor/bin/phpunit
$ composer phpcs
$ composer phpstan
$ composer infection

Changelog

Click here

Licence

MIT

pwm/sfw-container 适用场景与选型建议

pwm/sfw-container 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2.46k 次下载、GitHub Stars 达 5, 最近一次更新时间为 2017 年 04 月 27 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「container」 「dependency-injection」 「DAG」 「sfw」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

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

围绕 pwm/sfw-container 我们能提供哪些服务?
定制开发 / 二次开发

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

  • 总下载量: 2.46k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 5
  • 点击次数: 16
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2017-04-27