talesoft/tale-di 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

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

talesoft/tale-di

Composer 安装命令:

composer require talesoft/tale-di

包简介

A PSR-15 compatible HTTP utility library

README 文档

README

Packagist License CI Coverage

Tale DI

What is Tale DI?

Tale DI is a lightweight implementation of the PSR-11 Dependency Injection spec will full auto-wiring support.

The API might still change here and there.

Installation

composer req talesoft/tale-di

Usage

ContainerBuilder

use Tale\Di\ContainerBuilder;

Using the ContainerBuilder, you can auto-wire a fully working DI container including full PSR-6 cache support.

The container returned will be a Tale\Di\Container which is explained below.

$cachePool = new RedisCachePool();

$builder = new ContainerBuilder($cachePool);

$builder->add(ViewRenderer::class);

$builder->add(ControllerDispatcher::class);

$builder->addInstance(new PDO(...));

$container = $builder->build();

$pdo = $container->get(PDO::class);

Service Locators

use Tale\Di\ServiceLocator\FileServiceLocator;
use Tale\Di\ServiceLocator\DirectoryServiceLocator;
use Tale\Di\ServiceLocator\GlobServiceLocator;

If you don't want to add every single file manually, you can also use one of the three service Locators that come with Tale DI.

$builder->addLocator(
    new FileServiceLocator('src/Classes/MyClass.php')
);

$builder->addLocator(
    new DirectoryServiceLocator('../src')
);

$builder->addLocator(
    new GlobServiceLocator('../src/{Controller,Model}/**/*.php')
);

$container = $builder->build();

Injections and philosophy

Tale DI, by design, only allows constructor injections. There are no optional dependencies that are not covered in a constructor and there are no XyzAware interfaces and no possibility to do it.

This avoids a lot of magic and defensive null checks all over your code. If this is not what you like, Tale DI might not be what you're looking for. I suggest you give it a try anyways.

Injections happen simply by class name or an interface it implements:

class OrderProvider
{
    public function __construct(OrderRepository $repository)
}

$orderProvider = $container->get(OrderProvider::class);

It doesn't matter if you ever added the dependency to the container, it will auto-wire any external (or internal) dependency that has a readable type (and even that can be handled). This is possible because Tale DI works solely based on PHPs existing class mechanisms, interfaces and reflection.

interface ViewRendererInterface
{
}

class ViewRenderer implements ViewRendererInterface
{
}

//...

$renderer = $container->get(ViewRendererInterface::class);
// $renderer is instanceof ViewRenderer

Optional dependencies work as expected and should be defaulted to default/null implementations so no defensive null checks are required.

class AvatarGenerator
{
    /**
     * @var CacheInterface
     */
    private $cache;
    
    public function __construct(CacheInterface $cache = null)
    {
        $this->cache = $cache ?? new RuntimeCache();
    }
}

Iterables and Arrays of interfaces

Tale DI has a notion of Tags specified through plain PHP interfaces. You can inject by using interfaces:

class EntityManager
{
    public function __construct(DbalInterface $dbal)
}

and through proper type-hinting in the doc-comment you can even inject all instances of a specific interface:

class Importer
{
    /**
     * @param iterable<\App\Service\Importer\WorkerInterface>
     */
    public function __construct(iterable $workers)
    {
        foreach ($workers as $worker) {
            $this->initializeWorker($worker);
        }
    }
    
    /**
     * @param array<\App\Service\Importer\WorkerInterface>
     */
    public function __construct(array $workers)
    {
        $this->workers = array_filter($workers, fn($worker) => $worker->canImport());
    }
}

This will inject all known dependencies of type WorkerInterface into the workers argument.

Parameters

Tale DI can't only inject classes and instances, you can specify fixed parameters that are injected based on their names. Notice they should be serializable to make use of the caching mechanisms.

$builder->setParameter('someParameter', 'some value');

//Any class with a parameter 'someParameter' will get 'some value' injected as a string

The second parameter allows you to reduce the parameter to be used on a specific class or interface only.

This also allows creating instances of external types without requiring a specific factory for it:

class UserManager
{
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo; // Fully working PDO instance!
    }
}

$builder->setParameters([
    // These are the parameter names PDO internally uses in its constructor!
    'dsn' => 'mysql:host=localhost',
    'username' => 'root', 
    'passwd' => '', 
    'options' => [PDO::ATTR_ERRMODE => PDO_ERRMODE_EXCEPTION]
], PDO::class);

$builder->add(UserManager::class);
$container = $builder->build();

$userManager = $container->get(UserManager::class);
// PDO will be fully wired in UserManager

Manually construct container

The base setup of the container is pretty simple. Most of the stuff is used for the auto-wiring mechanism, but you can also completely avoid the ContainerBuilder and use containers directly.

Tale DI brings three base containers with it that you can use for their specific purposes.

Container

use Tale\Container;

The Tale Container and the heart of the DI system is a container that resolves values through specific Tale\Di\DependencyInterface instances. This is how it works:

$dependencies = [
    //Value Dependency is just a value. Can be any kind of value.
    'test value' => new ValueDependency('some value'),
    
    //A reference dependency references another value in the container
    'reference' => new ReferenceDependency('test value'),
    
    //A callback dependency only gets resolved when it's requested
    'factory' => new CallbackDependency(function (ContainerInterface $container) {
        return new Something($container->get(SomethingElse::class));
    }),
    
    //Same as CallbackDependency, but will cache the result between each ->get() call
    'lazy factory' => new PersistentCallbackDependency(function () {
        return (new SomeHeavyWorker())->getResult();
    })
];

$container = new Container($dependencies);

$container->get('test value'); //"some value"
$container->get('reference'); //"some value"
// etc.

You can always define own dependency types and how they are resolved by implementing Tale\Di\DependencyInterface:

final class PdoDependency implements DependencyInterface
{
    private $pdo;
    
    public function __construct()
    {
        $this->pdo = new PDO(...);
    }
    
    public function get(ContainerInterface $container)
    {
        return $this->pdo;
    }
}

$container = new Container(['pdo' => new PdoDependency()]);
$pdo = $container->get('pdo');

$stmt = $pdo->prepare(...);

ArrayContainer

use Tale\Di\Container\ArrayContainer;

Mostly useful for testing, this is a really basic implementation of PSR-11 that just maps fixed names to values

$container = new ArrayContainer([
    SomeClass::class => new SomeClass(),
    'test key' => 15
]);

$container->get(SomeClass::class); //SomeClass instance
$container->get('test key'); //15

NullContainer

use Tale\Di\Container\NullContainer;

A Null Container that always returns false when calling ->has() and always throws a NotFoundException when calling ->get().

This is mostly useful as a default container for classes that want to decorate containers as an optional dependency and require a default implementation to avoid defensive null checks.

final class AdapterFactory
{
    public function __construct(ContainerInterface $container = null)
    {
        $this->container = $container ?? new NullContainer();
    }
    
    public function createAdapter(): AdapterInterface
    {
        $adapter = null;
        if ($this->container->has(SomeAdapter::class)) {
            $adapter = $this->container->get(SomeAdapter::class);
        } else if ($this->container->has(SomeOtherAdapter::class)) {
            $adapter = $this->container->get(SomeOtherAdapter::class);
        } else {
            $adapter = new SomeDefaultAdapter();
        }
        return $adapter;
    }
}

Type Information

use Tale\Di\TypeInfoFactory\PersistentTypeInfoFactory;

Notice this will probably end up in an own library at some point.

$typeInfoFactory = new PersistentTypeInfoFactory();

$typeInfo = $typeInfoFactory->get('array<int>');

$typeInfo->isGeneric() //true

$typeInfo->getGenericType()->getName() //array

$typeInfo->getGenericTypeParameters()[0]->getName() //int

Parameter Reader

use Tale\Di\ParameterReader\DocCommentParameterReader;

A parameter reader that also takes into account doc comment @param-annotations

$typeInfoFactory = new PersistentTypeInfoFactory();

$paramReader = new DocCommentParameterReader($typeInfoFactory);

$reflClass = new ReflectionClass(SomeClass::class);

$params = $paramReader->read($reflClass->getMethod('__construct');

var_dump($params);

talesoft/tale-di 适用场景与选型建议

talesoft/tale-di 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 163 次下载、GitHub Stars 达 0, 最近一次更新时间为 2016 年 02 月 27 日, 在 PHP 生态内属于活跃度较高的组件。

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

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

围绕 talesoft/tale-di 我们能提供哪些服务?
定制开发 / 二次开发

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

  • 总下载量: 163
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 8
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 3
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2016-02-27