codenamephp/platform.di

基于 www.php-di.org 的简单依赖注入容器

5.2.0 2020-12-05 16:39 UTC

README

Packagist Version Packagist PHP Version Support Lines of code GitHub code size in bytes CI Mutation testing badge Packagist Downloads GitHub

基于 www.php-di.org 的简单依赖注入容器

安装

最简单的方法是通过 composer。只需在您的 CLI 中运行 composer require codenamephp/platform.di 即可为您安装最新版本。

用法

$builder = new de\codenamephp\platform\di\ContainerBuilder();
$container = $builder->build();
$container->get('...');

这创建了一个没有定义的构建器。为了添加定义,我建议使用以下提供程序选项之一,特别是 de\codenamephp\platform\di\definitionsProvider\iArray 提供程序。

从那里,您只需从容器中获取依赖项。

使用提供程序

在模块和库中拥有配置的最佳方式是通过提供程序。这样,提供程序类将被用来添加文件或定义。每次提供程序类更新时,配置也会更新。

所有提供程序都需要实现 de\codenamephp\platform\di\definitionsProvider\* 接口中的一种

use de\codenamephp\platform\di\definitionsProvider\iDefinitionsProvider;

$builder = new de\codenamephp\platform\di\ContainerBuilder();
$builder->addDefinitionsByProvider(new class() implements iDefinitionsProvider{});
$container = $builder->build();
$container->get('...');

数组

可能是性能最好的提供程序,因为定义是在方法中定义的,不需要进行任何额外的文件查找

class DefinitionsProvider implements de\codenamephp\platform\di\definitionsProvider\iArray {

  public function getDefinitions() : array {
    return ['some class' => 'some defintion'];
  }
}

文件

文件提供程序提供定义文件的绝对文件路径

class DefinitionsProvider implements de\codenamephp\platform\di\definitionsProvider\iFiles {

  public function getFiles() : array {
    return [__DIR__ . '/path/to/file'];
  }
}

元提供程序

有时您想将依赖关系分成多个提供程序,这样它们就不会太长,并且/或者将它们分组到逻辑单元中。但是您不想将多个提供程序添加到实际使用提供程序的项目中。这就是 \de\codenamephp\platform\di\definitionsProvider\iMetaProvider 接口的作用。它基本上创建了多个提供程序,并将它们作为数组返回,然后这些数组被容器构建器添加,就像任何其他提供程序一样,包括依赖项检查和嵌套其他元提供程序。

use de\codenamephp\platform\di\definitionsProvider\iArray;
use de\codenamephp\platform\di\definitionsProvider\iFiles;
use de\codenamephp\platform\di\definitionsProvider\iMetaProvider;

class MyArrayProvider implements iArray{
    public function getDefinitions() : array {
     return [];
    }
}
class MyFileProvider implements iFiles {
    public function getFiles() : array {
      return []; 
    }
}
class MyNestedMetaProvider implements iMetaProvider {
    public function getProviders() : array{
      return [new MyFileProvider()]; 
    }
}
class MyMetaProvider implements iMetaProvider {
  public function getProviders() : array {
    return [
        new MyArrayProvider(),
        new MyNestedMetaProvider()
    ];
  }
}

但即使在示例中,这也清楚地表明我们在这里处理的是一个Uncle Ben的情况:随着递归能力的增强,头疼也会变得更糟!仍然会检查依赖关系,因此提供程序需要按正确的顺序排列,如果您过度使用嵌套提供程序,这可能会很快变成真正的痛苦。

我建议不要使用超过一级的嵌套,如果可能的话,尽量避免使用它。毕竟,这只是实现的一个副作用,而不是一个计划中的功能。 ;)

提供程序依赖关系

提供程序可以依赖于其他提供程序,例如,要覆盖它们的定义。如果是这种情况,提供程序可以实现 de\codenamephp\platform\di\definitionsProvider\dependency\* 接口之一。

依赖项提供程序

DependencyFactory

从 5.1 版本开始,添加了一个 \de\codenamephp\platform\di\definitionsProvider\factory\byClassname\iByClassname 接口,可以用于动态创建依赖关系。有一个非常简单的 \de\codenamephp\platform\di\definitionsProvider\factory\byClassname\SimpleNew(因此得名),它只需要一个类名,并“new”它。

iDependsOn

此接口声明提供程序依赖于其他提供程序,并且必须实现 getDependencies() 方法,该方法返回必须添加到容器中才能添加此提供程序的所有提供程序类的名称。

use de\codenamephp\platform\di\definitionsProvider\iDefinitionsProvider;

class MustBeAddedBeforeMe implements iDefinitionsProvider {}

class DefinitionsProvider implements de\codenamephp\platform\di\definitionsProvider\dependency\iDependsOn {

  public function getDependencies() : array {
    return [MustBeAddedBeforeMe::class ];
  }
}

依赖项检查

当您有相互依赖的模块时,通常定义也相互依赖。这就是依赖项收集器的作用。它们收集提供程序(duh)并使用上述接口检查依赖关系。它们实现 \de\codenamephp\platform\di\definitionsProvider\collection\iCollection 并执行不同级别的检查和排序。

SimpleArray

这个集合实际上不执行任何依赖检查,只是收集提供者并将它们存储在数组中。这通常用作其他集合的简单存储。

ClassNamesInArray

这个集合将依赖项的类名收集到一个数组中,并将依赖项与这些类名进行比对。如果提供者没有添加iDependsOn接口,则自动添加提供者的类名,因此如果您的提供者只覆盖其自身的依赖项,您不需要实现该接口。

这是一个非常简单的检查,因此也易于调试。每次添加依赖项时都会检查依赖项,所以如果缺少某些内容,它将早期失败。这个缺点是您必须以正确的顺序添加提供者。

use de\codenamephp\platform\di\ContainerBuilder;
use de\codenamephp\platform\di\definitionsProvider\collection\ClassNamesInArray;
use de\codenamephp\platform\di\definitionsProvider\dependency\iDependsOn;
use de\codenamephp\platform\di\definitionsProvider\iDefinitionsProvider;

class Dependency implements iDefinitionsProvider {}
class Dependant implements iDependsOn { public function getDependencies() : array{ return [Dependency::class]; } }

$collection = new ClassNamesInArray();
$collection->add(new Dependency());
$collection->add(new Dependant()); // would fail if those were reversed
//...

$containerBuilder = new ContainerBuilder();
foreach($collection->get() as $provider) { $containerBuilder->addDefinitionsByProvider($provider); }
$container = $containerBuilder->build();
//...

TopoGraph

这个集合根据提供者的依赖项对提供者进行排序。排序和检查是在获取提供者后进行的。这使得您可以以任何合适的方式添加提供者。但这也意味着存在轻微的性能开销,调试可能稍微困难一些。

这也意味着您除了声明依赖项外,没有其他方式来影响顺序,因此这不仅推荐,几乎是必要的。

use de\codenamephp\platform\di\ContainerBuilder;
use de\codenamephp\platform\di\definitionsProvider\collection\TopoGraph;
use de\codenamephp\platform\di\definitionsProvider\dependency\iDependsOn;
use de\codenamephp\platform\di\definitionsProvider\iDefinitionsProvider;

class Dependency implements iDefinitionsProvider {}
class Dependant implements iDependsOn { public function getDependencies() : array{ return [Dependency::class]; } }

$collection = new TopoGraph();
$collection->add(new Dependant()); // the sequence doesn't matter
$collection->add(new Dependency());
//...

$containerBuilder = new ContainerBuilder();
foreach($collection->get() as $provider) { $containerBuilder->addDefinitionsByProvider($provider); } // Dependency will be returned/added first
$container = $containerBuilder->build();
//...

CreateAndAddDependenciesBeforeProvider

这个集合是为#34创建的。当一个提供者具有\de\codenamephp\platform\di\definitionsProvider\dependency\iDependsOn接口时,所有依赖项都将使用\de\codenamephp\platform\di\definitionsProvider\factory\byClassname\iByClassname(默认为\de\codenamephp\platform\di\definitionsProvider\factory\byClassname\SimpleNew)创建,并添加到底层集合(默认为\de\codenamephp\platform\di\definitionsProvider\collection\SimpleArray)中。

使用时请谨慎,因为您的提供者不能有构造函数,因为工厂无法猜测参数。当然,您可以实现自己的工厂,但在这个阶段,可能直接添加依赖项会更容易。