0

如果你想在中间件的另一中间件/对象 你必须使用一个工厂一样Zend的表现依赖注入

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) 
     ); 
    } 
} 

这样就可以得到任何东西〜在飞行中。 Like

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(); 
     } 
    } 
} 

回答

4

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

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

+0

通过MiddlewareInterface实现Action中间件并让':: canCreate'检查是否返回i​​n_array('MiddlewareInterface',class_implements($ requestedName),true);'谢谢btw! – cottton

+0

这也是可以的。但据我所知,使用Reflection的代价非常昂贵。我不想自动初始化所​​有中间件,每个请求只有1个动作类对我来说就足够了。 – xtreamwayz

0

注入您的中间件应用程序或容器是可能的,但它不是好主意都:

1)反转控制(IOC)

它侵犯的控制原理的反转,你的类不能有任何关于IoC容器的知识。

2)依赖倒置原则(DIP)

依赖倒置原则规定:“高层模块不应该依赖于低层模块”,让你更高层次的中间件类依赖于基础设施/框架。

3)(LOD)

根据迪米特法则得墨忒耳定律,单位有关于其他单位的知识有限,它应该只知道关于它密切相关的单位。

MyMiddleware::class有太多的知识等单位,首先,它知道的Application::class,那么它知道Application知道的Container,那么它知道Container知道的What\Ever::class等。

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