【问题标题】:Zend Expressive Dependency InjectionZend 表达依赖注入
【发布时间】:2016-06-10 13:32:54
【问题描述】:

如果您想在中间件中拥有另一个中间件/对象 你必须使用像

这样的工厂
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();
        }
    }
}

【问题讨论】:

    标签: zend-framework dependency-injection factory mezzio


    【解决方案1】:

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

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

    【讨论】:

    • MiddlewareInterface 实现Action 中间件并让::canCreate 检查return in_array('MiddlewareInterface', class_implements($requestedName), true); 是否更好,谢谢顺便说一句!
    • 这也是可能的。但是据我所知,使用反射非常昂贵。我不想自动初始化我所有的中间件,每个请求只有 1 个动作类对我来说就足够了。
    【解决方案2】:

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

    1) 控制反转 (IoC)

    它违反了控制反转原则,你的班级不能有任何关于IoC容器的知识。

    2)依赖倒置原则(DIP)

    依赖倒置原则指出“高级模块不应依赖于低级模块”,因此您的高级中间件类依赖于基础架构/框架。

    3) 得墨忒耳法则 (LoD)

    根据德米特法则,单位对其他单位的了解应该是有限的,它应该只知道与其密切相关的单位。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-08
      • 1970-01-01
      • 2011-04-08
      • 2014-06-12
      • 2013-04-30
      相关资源
      最近更新 更多