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();
如果有人能解释我做错了什么,我会很高兴。谢谢。
您不能直接在控制器中实例化 class 您需要使用密钥 'Application\Plugin\AppPlugin'
将其从控制器插件管理器中取出
$pluginManager = $this->getPluginManager();
$appPlugin = $pluginManager->get('Application\Plugin\AppPlugin');
$appPlugin->getAppInfo();
或者您可以将方法更改为:
public function getAppInfo()
{
$serviceManager = $this->controller->getServiceLocator();
$config = $serviceManager->get('Config');
}
如果可能的话,您不应该尝试访问除工厂之外的任何 class 内部的 ServiceLocator。这样做的主要原因是,如果将 ServiceLocator 注入到您的 class,您现在不知道 class 的依赖项是什么,因为它现在可能包含任何内容。
关于依赖注入,您有两个基本选择:构造函数或 setter 注入。根据经验,总是更喜欢构造函数注入。 Setter 注入应该只用于可选的依赖项,它也会让你的代码更加模糊,因为 class 现在是可变的。如果你使用纯粹的构造函数注入,你的依赖是不可变的,你总是可以确定它们会在那里。
另外,与其使用闭包,不如使用具体工厂 class,因为闭包无法缓存操作码,而且如果包含闭包,则配置数组也无法缓存。
有关如何设置 class 以实施 FactoryInterface
的详细说明,请参阅
话虽这么说,但为了讨论的目的,让我们坚持使用您的闭包工厂示例。我们没有注入 ServiceManager,而是注入了配置数组(这仍然有点过于依赖上帝;最好注入您需要的配置的特定键,或者更好的是,实例化 class)。
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.appplication.config:
'controller_plugins' => [
'invokables' => [
'AppPlugin' => \Application\Controller\Plugin\AppPlugin::class
]
]
...现在我可以像这样在控制器中使用我的插件了:
$app = $this->AppPlugin()->getAppInfo();
我想在我的自定义插件中获取服务定位器。我通过 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();
如果有人能解释我做错了什么,我会很高兴。谢谢。
您不能直接在控制器中实例化 class 您需要使用密钥 'Application\Plugin\AppPlugin'
将其从控制器插件管理器中取出$pluginManager = $this->getPluginManager();
$appPlugin = $pluginManager->get('Application\Plugin\AppPlugin');
$appPlugin->getAppInfo();
或者您可以将方法更改为:
public function getAppInfo()
{
$serviceManager = $this->controller->getServiceLocator();
$config = $serviceManager->get('Config');
}
如果可能的话,您不应该尝试访问除工厂之外的任何 class 内部的 ServiceLocator。这样做的主要原因是,如果将 ServiceLocator 注入到您的 class,您现在不知道 class 的依赖项是什么,因为它现在可能包含任何内容。
关于依赖注入,您有两个基本选择:构造函数或 setter 注入。根据经验,总是更喜欢构造函数注入。 Setter 注入应该只用于可选的依赖项,它也会让你的代码更加模糊,因为 class 现在是可变的。如果你使用纯粹的构造函数注入,你的依赖是不可变的,你总是可以确定它们会在那里。
另外,与其使用闭包,不如使用具体工厂 class,因为闭包无法缓存操作码,而且如果包含闭包,则配置数组也无法缓存。
有关如何设置 class 以实施 FactoryInterface
话虽这么说,但为了讨论的目的,让我们坚持使用您的闭包工厂示例。我们没有注入 ServiceManager,而是注入了配置数组(这仍然有点过于依赖上帝;最好注入您需要的配置的特定键,或者更好的是,实例化 class)。
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.appplication.config:
'controller_plugins' => [
'invokables' => [
'AppPlugin' => \Application\Controller\Plugin\AppPlugin::class
]
]
...现在我可以像这样在控制器中使用我的插件了:
$app = $this->AppPlugin()->getAppInfo();