如何将侦听器代码从闭包移动到方法并在 ZF2 中将参数传递给它?

How to move listener code from closure to method and pass arguments to it in ZF2?

我有一个 Module class 在其 onBootstrap(...):

中有多个 "inline" 事件侦听器
...
class Module
{
    public function onBootstrap(MvcEvent $mvcEvent)
    {
        ...
        $foo = ...;
        ...
        $halPlugin = $viewHelperManager->get('Hal');
        $halPlugin->getEventManager()->attach('bar', function ($event) {
            ...
        });
        ...
        $halPlugin->getEventManager()->attach('baz', function ($event) use ($foo)
        {
            ...
        });
        ...
    }
    ...
}

现在,为了 hold/make Module#onBootstrap(...) 苗条,我想将侦听器从闭包移动到单独的方法。 onBar 事件侦听器没有问题。但它不适用于 onBaz,需要额外输入:

...
class Module
{
    public function onBootstrap(MvcEvent $mvcEvent)
    {
        ...
        $halPlugin->getEventManager()->attach('bar', [$this, 'onBar']);
        ...
    }
    ...
    public function onBar()
    {
        ...
    }
    public function onBaz() // <-- How to pass $foo to the method?
    {
        // Some stuff, that needs $foo...
        ...
    }
}

在原始变体中,此输入通过 use 指令传递到闭包中。但是现在怎么办呢?

如何将事件侦听器(附加在 Module#onBootstrap(...) 中)的逻辑从带有 use 语句的闭包移动到单独的方法并将参数传递给它?

您可以做的是将 foo 作为 属性 移动到您的模块 class 然后使用 $this->foo

访问它
class Module
{
    private $foo;

    public function onBootstrap(MvcEvent $mvcEvent)
    {
        $this->foo = ...;
        $halPlugin->getEventManager()->attach('baz', [$this, 'onBaz']);
        ...
    }
    ...
    public function onBaz()
    {
        $this->foo
        // Some stuff, that needs $foo...
        ...
    }
}

您还可以将模块 return 中的函数设为闭包,其中您使用 $foo 随函数一起发送。

class Module
{
    public function onBootstrap(MvcEvent $mvcEvent)
    {
        $foo = ...;
        $halPlugin->getEventManager()->attach('baz', $this->onBaz($foo));
        ...
    }
    ...
    public function onBaz($foo)
    {
        return function ($event) use ($foo) {
            $this->foo
            // Some stuff, that needs $foo...
            ...
        }
    }
}

将您的方法移至侦听器 class。

侦听器只需要 Callable,这意味着在您的 class 中声明 __invoke() 方法并将您拥有的所有代码放在当前的 onBaz() 方法中

<?php

namespace SomeModule\Listener;

class BazListener
{

    protected $foo;

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

    public function __invoke()
    {
         // code from onBaz() method using $this->foo
    }
}

您会注意到 $foo 现在是一个需要构造函数注入的依赖项。为了实现依赖性,为您的侦听器 class 编写一个工厂,在工厂中获取 $foo 并将其注入到那里。

<?php

namespace SomeModule\Factory;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class BazListenerFactory implements FactoryInterface
{
     public function createService(ServiceLocatorInterface $services)
     {
          //fetch foo from wherever ..
          $foo = ;
          return new \SomeModule\Listener\BazListener($foo);
     }
}

现在将侦听器工厂添加到服务管理器,以便您可以在 onBootstrap 中访问它

return array(
     'service_manager' => (
         'factories' => (
             // ..
             'SomeModule\Listener\BazListener' => 'SomeModule\Factory\BazListenerFactory',
             // ..
         ),
    ),
);

最后从 onBootstrap 中的服务管理器获取您的侦听器作为服务并将其附加到您的事件管理器

public function onBootstrap(MvcEvent $mvcEvent)
{
    $bazListener = $mvcEvent->getApplication()
                            ->getServiceManager()
                            ->get('SomeModule\Listener\BazListener');
    $halPlugin->getEventManager()->attach('bar', $bazListener);
    ...
}

这是一些额外的工作,但它更好地分离了关注点,并且您现在有一个独立的侦听器,您可以重用它而不是依赖模块 class 中的 copy/pasted 方法。