selective/container

一个简单的 PSR-11 容器实现,具有自动装配功能。

1.3.0 2023-09-09 16:52 UTC

This package is auto-updated.

Last update: 2024-09-09 18:57:15 UTC


README

Latest Version on Packagist Software License Build Status Coverage Status Quality Score Total Downloads

描述

一个可选的 自动装配 的 PSR-11 容器实现。

要求

  • PHP 8.1+

安装

composer require selective/container

使用

use Selective\Container\Container;

$container = new Container();
// ...

$myService = $container->get(MyService::class);

启用自动装配

容器能够自动为您创建和注入依赖项。这称为“自动装配”。

要启用自动装配,您必须添加 ConstructorResolver

<?php

use Selective\Container\Container;
use Selective\Container\Resolver\ConstructorResolver;

$container = new Container();

// Enable autowiring
$container->addResolver(new ConstructorResolver($container));

//...

定义 DI 容器定义

您可以使用工厂(闭包)来定义注入。

<?php

use App\Service\MyService;
use Selective\Container\Container;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;

$container = new Container();

// Add definition
$container->factory(MyService::class, function (ContainerInterface $container) {
    return new MyService();
});

定义多个 DI 容器定义

use Psr\Container\ContainerInterface;
// ...

$entries = [
    MyService::class => function (ContainerInterface $container) {
        return new MyService();
    },
    
    PDO::class => function (ContainerInterface $container) {
        return new PDO('sqlite:example.db');
    },
    
    // and so on...
];

$container->factories($entries);

服务提供者

服务提供者可以在大型应用程序中通过懒加载注册容器定义来组织您的容器定义,从而提高性能。

要构建服务提供者,创建一个可调用的类,并返回您想要注册的定义(工厂)。

<?php

use App\Service\MyService;
use Selective\Container\Container;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;

final class MyServiceFactoryProvider
{
    /**
     * @return array<string, callable>
     */
    public function __invoke(): array
    {
        return [
            MyService::class => function (ContainerInterface $container) {
                return new MyService($container->get(LoggerInterface::class));
            },
        ];
    }
}

$container->factories((new MyServiceFactoryProvider())());

直接设置定义

除了在工厂/回调数组的数组中定义条目之外,您还可以直接设置值,如下所示

$container->set(\App\Domain\MyService::class, new \App\Domain\MyService());

获取 DI 容器条目

要获取值,请使用 get 方法

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

测试

  • 请确保您的容器将为每个测试重新创建。您可以使用 phpunit 的 setUp() 方法初始化容器定义。
  • 您可以使用 set() 方法覆盖现有的容器条目。

模拟

也可以使用 set 方法将模拟对象直接设置到容器中。

此示例需要 phpunit

<?php

$class = \App\Domain\User\Repository\UserRepository::class;

$mock = $this->getMockBuilder($class)
    ->disableOriginalConstructor()
    ->getMock();

$mock->method('methodToMock1')->willReturn('foo');
$mock->method('methodToMock2')->willReturn('bar');

$container->set($class, $mock);

Slim 4 集成

使用容器引导 Slim 4 应用程序的示例

<?php

use Selective\Container\Container;
use Selective\Container\Resolver\ConstructorResolver;
use Slim\App;
use Slim\Factory\AppFactory;

require_once __DIR__ . '/../vendor/autoload.php';

$container = new Container();

// Enable autowiring
$container->addResolver(new ConstructorResolver($container));

// Load container definitions
$container->factories(require __DIR__ . '/container.php');

// Create slim app instance
AppFactory::setContainer($container);
$app = AppFactory::create();

// Add routes, middleware etc...

$app->run();

container.php 文件必须返回一个工厂(闭包)数组

<?php

use Monolog\Logger;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;

return [
    'settings' => function () {
        return require __DIR__ . '/settings.php';
    },

    LoggerInterface::class => function (ContainerInterface $container) {
        $logger = new Logger('name');
        
        // ...
        
        return $logger;
    },
    
    // Add more definitions here...
]

PhpStorm 集成

如果您使用 PhpStorm,请在项目根目录中创建一个新的文件 .phpstorm.meta.php 并粘贴以下内容

<?php

namespace PHPSTORM_META;

override(\Psr\Container\ContainerInterface::get(0), map(['' => '@']));

性能比较

selective/container 的性能

  • php-di/php-di 快 11%。
  • league/container 快 5.4%。

所有测试都是在启用自动装配的情况下进行的。

从 PHP-DI 迁移

此 PSR-11 容器实现模仿了 PHP-DI 的行为。

如果您已经使用 工厂 为您的容器定义,则切换应非常简单。

将以下内容替换为

<?php
use DI\ContainerBuilder;

// ...

$containerBuilder = new ContainerBuilder();

$containerBuilder->addDefinitions(__DIR__ . '/container.php');

$container = $containerBuilder->build();

... 与此

<?php
use Selective\Container\Container;
use Selective\Container\Resolver\ConstructorResolver;
// ...

$container = new Container();

// Enable auto-wiring
$container->addResolver(new ConstructorResolver($container));

// Add definitions
$container->factories(require __DIR__ . '/container.php');

就是这样。

鸣谢

  • Dominik Zogg (chubbyphp)

类似库

许可

MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件