bigbit/oddin

按需依赖注入

v2.1.0 2020-01-20 13:25 UTC

README

关于

有时候编程很痛苦。你的老板需要它现在,或者你只是无聊地一遍又一遍地写甜话...

如果你在项目中使用依赖注入,你必须在构造函数中编写属性声明并初始化它们。你可以使用依赖容器或定义可注入的构造函数参数。这取决于使用的框架。仅限PHP7.4.0,7.4.1破坏了功能,php bug #78904。

class Foo {
    private Dependency1 $dep1;
    
    private Dependency2 $dep2;
    
    private Depencency3 $dep3;
    
    function __construct(Dependency1 $dep1, Dependency2 $dep2, Container $container)
    {
        $this->dep1 = $dep1;
        $this->dep2 = $dep2;
        $this->dep3 = $container->get(Dependency3::class);
    }
    
    public function bar() 
    {
        $this->dep1->doSomething();
    }
    
    protected function baz()
    {
        $this->dep2->doSomethind();
    }
    
    private function bye() 
    {
        $this->dep3->doSomething();
    }
}

使用ODDIN,你可以跳过构造函数部分。只需声明属性,并在需要时访问它们。请注意,这些属性可以通过魔法 __get 方法在任何地方访问。你的首选IDE将帮助你处理这个问题。

class Foo {
    use InjectsOnDemand;
    
    private Dependency1 $dep1;
    
    private Dependency2 $dep2;
    
    private Depencency3 $dep3;

    /**
    * Foo constructor.
    * as of php7.4.1, we have to unset properties in constructor
    * https://bugs.php.net/bug.php?id=78904 
    */
    public function __construct() 
    {
        unset($this->dep1, $this->dep2, $this->dep3);
    }

    public function bar() 
    {
        $this->dep1->doSomething();
    }
    
    private function baz() 
    {
        $this->dep2->doSomething();
    }
    
    private function bye()
    {
        $this->dep3->doSomethind();
    }
}

它的工作原理

PHP类可以有魔法方法。每当你要使用未设置属性时,都会调用__get魔法方法。DIResolver使用解析器从类或过时的属性注释中获取依赖元数据。InjectOnDemand trait定义了一个魔法__get方法,用于处理所有的属性请求。一旦属性通过特质初始化,魔法方法就不再调用。

优点

  • 更少的代码
  • 按需实例化依赖(懒加载 - 不在构造函数之前,如果正确定义在DI容器中)

缺点

  • 所有属性都变成公开的吗?所有可注入的都是“公开”的
  • 反模式?仅用于原型设计,稍后清理代码。

目的

更干净的控制器类,更少的资源需求。但这是你自己的选择,在哪里使用ODDIN。

已知问题

  • 尚未提供代码修复器

快速入门

你可以使用任何实现了Psr\Container\ContainerInterface的DI容器。为了快速入门,你可以使用Bootstrap类,它使用SmartContainer。

use BigBIT\DIBootstrap\Bootstrap;
use Psr\Container\ContainerInterface;

// custom bindings
$bindings = [
    FooInterface::class => function(ContainerInterface $container) {
        return new BarImplementation(
                $container->get(BazDependency::class)
            );
        }
];

$container = Bootstrap::getContainer($bindings);

$app = new SomeApp($container);

$app->run();

其他框架

你可以请求其他框架支持或基于Bootstrap类编写你自己的引导。

PHP-DI比较

@todo

缓存生成器

已添加了缓存生成器的实验性实现。如果你的项目安装了phpstan,建议也安装tracy/tracy。Oddin使用Psr\SimpleCache\CacheInterface实现和Symfony作为默认。

创建缓存不是必需的,但建议在生产环境中创建。

vendor/bin/oddin cache:injectables:create php-files -a oddin -a 0 -a cache

cli命令的参数是从适配器构造函数中推导出来的。

实例化缓存

$bindings[CacheInterface::class] = function(ContainerInterface $container) {
    return new Psr16Cache(new PhpFilesAdapter('oddin', 0, dirname(__DIR__) . '/cache'));
};

@TODO - 代码修复器

cli命令用于修复代码。它将删除类属性注释,声明属性,并在构造函数中添加或获取器和容器。