Zend 表达依赖注入

Zend Expressive Dependency Injection

如果你想在中间件中有另一个 middleware/object 你必须使用像

这样的工厂
namespace App\Somnething;
use Interop\Container\ContainerInterface;
class MyMiddlewareFactory
{
    public function __invoke(ContainerInterface $container, $requestedName)
    {
        return new $requestedName(
            $container->get(\App\Path\To\My\Middleware::class)
        );
    }
}

所以 MyMiddleware 将被注入 \App\Path\To\My\Middleware,我们将能够访问它。

问题: 将中间件与应用程序本身或容器一起注入会是错误的吗?喜欢:

namespace App\Somnething;
use Interop\Container\ContainerInterface;
use Zend\Expressive\Application;
class MyMiddlewareFactory
{
    public function __invoke(ContainerInterface $container, $requestedName)
    {
        return new $requestedName(
            $container->get(Application::class)
        );
    }
}

这样就可以随时得到任何东西。 喜欢

namespace App\Somnething;
use Zend\Expressive\Application;
class MyMiddleware
{
    /** @var Application $app */
    protected $app;

    public function __construct(Application $app)
    {
        $this->app = $app;
    }

    public function __invoke($some, $thing)
    {
        if ($some and $thing) {
            $ever = $this->app
                ->getContainer()
                ->get(\Path\To\What\Ever::class);
            $ever->doSome();
        }
    }
}

您不会将中间件注入其他中间件。您注入服务或存储库等依赖项。每个中间件负责一项特定的任务,如身份验证、授权、本地化协商等。它们一个接一个地执行。他们弄乱请求并将请求传递给下一个中间件。一旦中间件堆栈耗尽,响应将以相反的顺序通过所有中间件一直返回,直到它最终到达显示输出的外层。您可以在 expressive docs.

中找到流程概述

我不建议注入容器,当然也不建议注入应用程序本身。尽管在开发过程中这可能很容易,但您的应用程序变得无法测试。如果您只将需要的服务注入到中间件、操作或服务中,您可以在测试期间轻松地模拟它们。一段时间后,您就会习惯在需要的地方编写工厂,而且速度非常快。

注入实体管理器也是如此(如果您使用学说)。如果您只注入所需的存储库,那么测试应用程序会更容易,您可以轻松模拟这些存储库。

话虽如此,如果您正在寻找一种简单的方法来注入依赖项,zend-servicemanager 可以做到。看看abstract factories。使用抽象工厂,您可以为所有操作创建一个工厂 类:

<?php

namespace App\Action;

use Interop\Container\ContainerInterface;
use ReflectionClass;
use Zend\ServiceManager\Factory\AbstractFactoryInterface;

class AbstractActionFactory implements AbstractFactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        // Construct a new ReflectionClass object for the requested action
        $reflection = new ReflectionClass($requestedName);
        // Get the constructor
        $constructor = $reflection->getConstructor();
        if (is_null($constructor)) {
            // There is no constructor, just return a new class
            return new $requestedName;
        }

        // Get the parameters
        $parameters = $constructor->getParameters();
        $dependencies = [];
        foreach ($parameters as $parameter) {
            // Get the parameter class
            $class = $parameter->getClass();
            // Get the class from the container
            $dependencies[] = $container->get($class->getName());
        }

        // Return the requested class and inject its dependencies
        return $reflection->newInstanceArgs($dependencies);
    }

    public function canCreate(ContainerInterface $container, $requestedName)
    {
        // Only accept Action classes
        if (substr($requestedName, -6) == 'Action') {
            return true;
        }

        return false;
    }
}

我写了一篇关于那个的 blog post

归根结底,这是您自己的决定,但最佳做法是不注入应用程序、容器或实体管理器。如果您需要调试中间件和/或为其编写测试,它会让您的生活更轻松。

在中间件中注入应用程序或容器是可能的,但这根本不是一个好主意:

1) 控制反转 (IoC)

违反了控制反转原则,你的class一定不了解IoC容器。

2)依赖倒置原则(DIP)

依赖倒置原则指出 "high-level modules should not depend on low-level modules",因此您的更高级别的中间件 class 依赖于 infrastructure/framework.

3) 得墨忒耳法则 (LoD)

根据得墨忒耳定律,一个单位对其他单位的了解应该是有限的,它应该只知道与其密切相关的单位。

MyMiddleware::class对其他单位了解太多,首先它知道Application::class,然后它知道Application知道Container ,然后它知道 Container 知道 What\Ever::class 等等。

这种代码违反了一些最重要的 OOP 原则,导致与框架的严重耦合,它具有隐式依赖关系,至少但不是最后,难以阅读和理解。