IoC, 构造器依赖注入

4.0.0 2024-06-03 15:00 UTC

README

Build Status codecov Installs Releases

配置更少,完成更多。

框架无关的依赖注入工具和PSR-11实现提供者。

要求

PHP 7.4|8+

安装

composer require free-elephants/di

使用

您的入口php脚本(index.php或某些后台作业运行器)

$components = require 'components.php';
$di = (new \FreeElephants\DI\InjectorBuilder)->buildFromArray($components);
$app = $di->createInstance(\YourApplication::class);
$app->run();

您的components.php文件应包含依赖描述,如下所示

<?php

return [
    'instances' => [
        \PDO::class => new \PDO(getenv('DB_DNS'), getenv('DB_USER'), getenv('DB_PASS')),
    ],
    'register' => [
        \YourApplication::class,
        \ControllerFactory::class,
        \SomeService::class,
        \AnotherService::class,
        \Psr\Log\LoggerInterface::class => \Symfony\Component\Console\Logger\ConsoleLogger::class,
    ],
    'callable' => [ 
        // if function provided as key value
        // first argument passed to callable is psr container
        // second is key
        Foo::class => function(\Psr\Container\ContainerInterface $container, string $key) {
           return (new Foo())->setSomething($container->get('something'));
        },
        // if array provided as key value
        // first argument passed to callable is psr container
        // remaining element as ...args tail
        Bar::class => [ // array where first element is callable, other is values for last arguments
            function(\Psr\Container\ContainerInterface $container, $firstArg, string $secondArg) {
                return new Bar($firstArg, $secondArg);
            },
            100,
            500,
        ],       
    ],
    'loggers' => [
       // For suitable logger injections use map, where keys are your services, that implement LoggerAwareInterface
       // and value is logger instances
       LoggerAwareClass::class        => $logger,
       AnotherLoggerAwareClass::class => $anotherLogger,
    ],   
];

主要思想:所有组件都应将所有依赖项作为构造器参数预期。所有其他工作委托给注入器。您不需要在代码中直接实例化任何类。您必须注入一些工厂。

通过环境覆盖组件

<?php
// getenv('ENV') -> 'test'
$components = (new \FreeElephants\DI\EnvAwareConfigLoader(__DIR__ . '/config', 'ENV'))->readConfig('components');
$di = (new \FreeElephants\DI\InjectorBuilder)->buildFromArray($components);

EnvAwareConfigLoader 加载 config/components.php 并与(如果存在)合并 config/components.test.php

选项

allowNullableConstructorArgs

默认值是 false

allowInstantiateNotRegisteredTypes

默认值是 false。当您将其设置为 true 时,您只能注册特定接口的实例。所有最终类型依赖项将通过链式懒实例化!

useIdAsTypeName

默认值是 true

enableLoggerAwareInjection

默认值是 false

允许在构造后设置 LoggerInterface 到 LoggerAwareInterface 实例中,或如果存在类型,则使用日志记录器映射。

概念

(俄语)为PHP的简单构造器依赖注入

在面向对象中,可以根据其责任将类分为两大组:实体和服务的。

实体 包含数据和操作数据的方法。它们通常具有状态,并且在程序执行期间需要创建多个实例。例如

  • 领域对象(例如 Doctrine 中的 Entities,Propel 中的 Models)
  • 值对象
  • 数据传输对象
  • PSR 7 的 Request / Response

服务 负责其他所有事情

  • 处理用户请求(控制器、命令)
  • 操作实体
  • 确保系统组件之间的通信
  • 实例化其他实体和服务(工厂、定位器)
  • 提供应用功能(协议、存储、路由)

服务通常需要单个实例,并且很少在执行期间改变其状态。服务可以在执行阶段之前描述,例如在静态文件中,并在代码中按需获取或创建一次。

实体不应依赖于服务。相反,服务通常操作实体实例,并且可能使用其他服务。

注入依赖的最明显和可靠的方式是构造器注入

  • 无法创建一个未准备好的实例以供使用
  • 类的依赖关系通过一个地方的定义合同

类型提示和 PHP 中的反射允许根据其构造器签名收集一个准备好的服务,而不需要额外的配置文件和魔法。这种方法在 free-elephants/di 中使用。这种方法很好地支持重构,因为它只使用原生的 PHP 代码,不需要在 yml、xml 或注释中静态描述依赖关系。