Zend 2:在插件中获取服务定位器


Zend 2: Get service locator in plugin

我想在我的自定义插件中获得服务定位器。我通过Module.php中的工厂创建它:

public function getControllerPluginConfig() {
    return [
        'factories' => [
            'Application'Plugin'AppPlugin' => function($serviceManager) {
                $serviceLocator = $serviceManager->getServiceLocator();
                $appPlugin = new 'Application'Plugin'AppPlugin();
                $appPlugin->setLocator($serviceLocator);
                return $appPlugin;
            }  
        ]
    ];
}

…我的插件:

<?php
namespace Application'Plugin;
use Zend'Mvc'Controller'Plugin'AbstractPlugin;
class AppPlugin extends AbstractPlugin {
    protected $locator;
    public function getAppInfo() {
        $config = $this->locator->get('Config');
    }
   /**
    * Set Service Locator
    * @param 'Zend'ServiceManager'ServiceManager $locator
    */
    public function setLocator($locator) {
        $this->locator = $locator;
    }
}

然后在控制器中调用getAppInfo():

$appPlugin = new AppPlugin();
$appPlugin->getAppInfo();

…我得到错误:

Fatal error: Call to a member function get() on a non-object in /vagrant/app/module/Application/src/Application/Plugin/AppPlugin.php on line 13
Call Stack
#   Time    Memory  Function    Location
1   0.0027  232104  {main}( )   ../index.php:0
2   0.6238  3166904 Zend'Mvc'Application->run( )    ../index.php:21
3   1.4410  5659112 Zend'EventManager'EventManager->trigger( )  ../Application.php:314
4   1.4410  5659112 Zend'EventManager'EventManager->triggerListeners( ) ../EventManager.php:205
5   1.4411  5660872 call_user_func ( )  ../EventManager.php:444
6   1.4411  5661440 Zend'Mvc'DispatchListener->onDispatch( )    ../EventManager.php:444
7   1.4505  5704600 Zend'Mvc'Controller'AbstractController->dispatch( ) ../DispatchListener.php:93
8   1.4505  5705080 Zend'EventManager'EventManager->trigger( )  ../AbstractController.php:118
9   1.4505  5705080 Zend'EventManager'EventManager->triggerListeners( ) ../EventManager.php:205
10  1.4507  5716656 call_user_func ( )  ../EventManager.php:444
11  1.4507  5716784 Zend'Mvc'Controller'AbstractActionController->onDispatch( ) ../EventManager.php:444
12  1.4508  5717504 Application'Controller'IndexController->indexAction( )  ../AbstractActionController.php:82
13  1.4641  5727768 Application'Plugin'AppPlugin->getAppInfo( ) ../IndexController.php:21

但是如果我从控制器传递服务定位器它工作得很好:

$appPlugin = new AppPlugin();
$appPlugin->setLocator($this->getServiceLocator());
$appPlugin->getAppInfo(); 
如果有人能告诉我我做错了什么,我会很高兴的。谢谢。

在任何可能的情况下,您都不应该尝试访问除工厂之外的任何类中的ServiceLocator。这样做的主要原因是,如果ServiceLocator被注入到你的类中,你现在就不知道那个类的依赖项是什么,因为它现在可能包含任何东西。

关于依赖注入,你有两个基本的选择:构造函数注入或setter注入。根据经验,总是更倾向于构造函数注入。Setter注入应该只用于可选的依赖项,而且它还使代码更加模糊,因为类现在是可变的。如果你使用纯构造函数注入,你的依赖项是不可变的,你总是可以确定它们会在那里。

也不是使用闭包,通常使用具体工厂类更好,因为闭包不能被操作码缓存,而且如果你的配置数组包含闭包,也不能被缓存。

请参阅https://stackoverflow.com/a/18866169/1312094,了解如何建立一个类来实现FactoryInterface

也就是说,为了讨论的目的,让我们继续使用闭包工厂的例子。我们注入的不是ServiceManager,而是config数组(这仍然有点过于依赖;最好注入你需要的配置的特定键,或者更好的是,一个实例化的类)。

public function getControllerPluginConfig() {
    return [
        'factories' => [
            'Application'Plugin'AppPlugin::class => function($serviceManager) {
                $serviceLocator = $serviceManager->getServiceLocator();
                $config = $serviceLocator->get('Config');
                $appPlugin = new 'Application'Plugin'AppPlugin($config);
                return $appPlugin;
            }  
        ]
    ];
}

在这里,插件被修改为接受构造函数中的配置。我认为这段插件代码只是一个概念证明,除了返回配置之外,你还会用这个插件做一些有用的事情,但希望这确实展示了注入插件依赖项的模式。

<?php
namespace Application'Plugin;
use Zend'Mvc'Controller'Plugin'AbstractPlugin;
class AppPlugin extends AbstractPlugin {
    protected $config;
    public function __construct($config){
         $this->config = $config;
    }
    public function getAppInfo() {
        return $this->config;
    }
}

还有一件小事,但假设PHP 5.5+,考虑使用'Application'Plugin'AppPlugin::class,就像我在上面的例子中所做的那样,而不是字符串字面值的配置键

我通过ServiceLocatorAwareInterface创建了它,并使其可调用:

namespace Application'Controller'Plugin;
use Zend'Mvc'Controller'Plugin'AbstractPlugin;
use Zend'ServiceManager'ServiceLocatorAwareInterface;
use Zend'ServiceManager'ServiceLocatorInterface;
class AppPlugin extends AbstractPlugin implements ServiceLocatorAwareInterface {
    /**
     * @var ServiceLocatorInterface
     */
    protected $serviceLocator;
    /**
     * Get system information
     * @return type
     */
    public function getAppInfo() {
        $config = $this->getServiceLocator()->get('Config');
        // to do something...
    }
    /**
     * Retrieve service manager instance
     *
     * @return ServiceLocator
     */
    public function getServiceLocator() {
        return $this->serviceLocator->getServiceLocator();
    }
    /**
     * Set service locator
     *
     * @param ServiceLocatorInterface $serviceLocator
     */
    public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
        $this->serviceLocator = $serviceLocator;
    }
}
在module. application .config中:
'controller_plugins' => [
    'invokables' => [
        'AppPlugin'     => 'Application'Controller'Plugin'AppPlugin::class
         ]
    ]

…现在我可以在控制器中使用我的插件,像这样:

$app = $this->AppPlugin()->getAppInfo();

你不能直接在控制器中实例化类,你需要通过键'Application' plugin 'AppPlugin'从控制器插件管理器中获取它

$pluginManager = $this->getPluginManager();
$appPlugin = $pluginManager->get('Application'Plugin'AppPlugin');
$appPlugin->getAppInfo();

或者您可以将method更改为:

public function getAppInfo() 
{
    $serviceManager = $this->controller->getServiceLocator();
    $config = $serviceManager->get('Config');
}