gpt4 book ai didi

php - 如何在 Zend Framework 中使用依赖注入(inject)?

转载 作者:IT王子 更新时间:2023-10-29 00:17:45 24 4
gpt4 key购买 nike

目前我正在努力学习Zend Framework,因此我买了《Zend Framework in Action》这本书。

在第 3 章中,介绍了基本模型和 Controller 以及对它们进行的单元测试。基本 Controller 如下所示:

class IndexController extends Zend_Controller_Action 
{
public function indexAction()
{
$this->view->title = 'Welcome';
$placesFinder = new Places();
$this->view->places = $placesFinder->fetchLatest();
}
}

Places 是从数据库中获取最新地点的模型类。这里有什么问题:我应该如何在隔离 中测试 IndexController?由于对 Places 类的引用是“硬编码”的,我无法在 IndexController 中注入(inject)任何 stub 或模拟。

我更想拥有的是这样的东西:

class IndexController extends Zend_Controller_Action 
{
private $placesFinder;

// Here I can inject anything: mock, stub, the real instance
public function setPlacesFinder($places)
{
$this->placesFinder = $places;
}

public function indexAction()
{
$this->view->title = 'Welcome';
$this->view->places = $this->placesFinder->fetchLatest();
}
}

我发布的第一个代码示例绝对不是单元测试友好的,因为 IndexController 不能单独测试。第二个要好得多。现在我只需要一些方法将模型实例注入(inject) Controller 对象。

我知道 Zend Framework 本身没有用于依赖注入(inject)的组件。但是有一些很好的 PHP 框架,可以与 Zend Framework 一起使用吗?或者在 Zend Framework 中是否有其他方法可以做到这一点?

最佳答案

模型逻辑

首先,值得一提的是,尽管所有逻辑都属于模型,但 Controller 应该只需要进行功能测试。

我的实现

这是我的 Action Controller 实现的摘录,它解决了以下问题:

  • 允许对 Action 注入(inject)任何依赖
  • 验证操作参数,例如当需要整数时,您不能在 $_GET 中传递数组

我的完整代码还允许生成基于或要求或处理的操作参数的规范 URL(用于 SEO 或用于统计的唯一页面哈希)。为此,我使用了这个抽象的 Action Controller 和自定义 Request 对象,但这不是我们在这里讨论的情况。

很明显,我使用 Reflections 来自动确定 Action 参数和依赖对象。

这是一个巨大的优势,可以简化代码,但也会对性能产生影响(对于我的应用程序和服务器而言影响很小且不重要),但您可以实现一些缓存来加快速度。计算利弊,然后再决定。

DocBlock 注释正在成为一个众所周知的行业标准,并且出于评估目的对其进行解析变得越来越流行(例如 Doctrine 2)。我将这种技术用于许多应用程序并且效果很好。

写这门课的灵感来自 Actions, now with params!Jani Hartikainen's blog post .

所以,这是代码:

<?php

/**
* Enchanced action controller
*
* Map request parameters to action method
*
* Important:
* When you declare optional arguments with default parameters,
* they may not be perceded by optional arguments,
* e.g.
* @example
* indexAction($username = 'tom', $pageid); // wrong
* indexAction($pageid, $username = 'tom'); // OK
*
* Each argument must have @param DocBlock
* Order of @param DocBlocks *is* important
*
* Allows to inject object dependency on actions:
* @example
* * @param int $pageid
* * @param Default_Form_Test $form
* public function indexAction($pageid, Default_Form_Test $form = null)
*
*/
abstract class Your_Controller_Action extends Zend_Controller_Action
{
/**
*
* @var array
*/
protected $_basicTypes = array(
'int', 'integer', 'bool', 'boolean',
'string', 'array', 'object',
'double', 'float'
);

/**
* Detect whether dispatched action exists
*
* @param string $action
* @return bool
*/
protected function _hasAction($action)
{
if ($this->getInvokeArg('useCaseSensitiveActions')) {
trigger_error(
'Using case sensitive actions without word separators' .
'is deprecated; please do not rely on this "feature"'
);

return true;
}

if (method_exists($this, $action)) {

return true;
}

return false;
}

/**
*
* @param string $action
* @return array of Zend_Reflection_Parameter objects
*/
protected function _actionReflectionParams($action)
{
$reflMethod = new Zend_Reflection_Method($this, $action);
$parameters = $reflMethod->getParameters();

return $parameters;
}

/**
*
* @param Zend_Reflection_Parameter $parameter
* @return string
* @throws Your_Controller_Action_Exception when required @param is missing
*/
protected function _getParameterType(Zend_Reflection_Parameter $parameter)
{
// get parameter type
$reflClass = $parameter->getClass();

if ($reflClass instanceof Zend_Reflection_Class) {
$type = $reflClass->getName();
} else if ($parameter->isArray()) {
$type = 'array';
} else {
$type = $parameter->getType();
}

if (null === $type) {
throw new Your_Controller_Action_Exception(
sprintf(
"Required @param DocBlock not found for '%s'", $parameter->getName()
)
);
}

return $type;
}

/**
*
* @param Zend_Reflection_Parameter $parameter
* @return mixed
* @throws Your_Controller_Action_Exception when required argument is missing
*/
protected function _getParameterValue(Zend_Reflection_Parameter $parameter)
{
$name = $parameter->getName();
$requestValue = $this->getRequest()->getParam($name);

if (null !== $requestValue) {
$value = $requestValue;
} else if ($parameter->isDefaultValueAvailable()) {
$value = $parameter->getDefaultValue();
} else {
if (!$parameter->isOptional()) {
throw new Your_Controller_Action_Exception(
sprintf("Missing required value for argument: '%s'", $name));
}

$value = null;
}

return $value;
}

/**
*
* @param mixed $value
*/
protected function _fixValueType($value, $type)
{
if (in_array($type, $this->_basicTypes)) {
settype($value, $type);
}

return $value;
}

/**
* Dispatch the requested action
*
* @param string $action Method name of action
* @return void
*/
public function dispatch($action)
{
$request = $this->getRequest();

// Notify helpers of action preDispatch state
$this->_helper->notifyPreDispatch();

$this->preDispatch();
if ($request->isDispatched()) {
// preDispatch() didn't change the action, so we can continue
if ($this->_hasAction($action)) {

$requestArgs = array();
$dependencyObjects = array();
$requiredArgs = array();

foreach ($this->_actionReflectionParams($action) as $parameter) {
$type = $this->_getParameterType($parameter);
$name = $parameter->getName();
$value = $this->_getParameterValue($parameter);

if (!in_array($type, $this->_basicTypes)) {
if (!is_object($value)) {
$value = new $type($value);
}
$dependencyObjects[$name] = $value;
} else {
$value = $this->_fixValueType($value, $type);
$requestArgs[$name] = $value;
}

if (!$parameter->isOptional()) {
$requiredArgs[$name] = $value;
}
}

// handle canonical URLs here

$allArgs = array_merge($requestArgs, $dependencyObjects);
// dispatch the action with arguments
call_user_func_array(array($this, $action), $allArgs);
} else {
$this->__call($action, array());
}
$this->postDispatch();
}

$this->_helper->notifyPostDispatch();
}

}

要使用它,只需:

Your_FineController extends Your_Controller_Action {}

并像往常一样为操作提供注释(至少您已经应该这样做了;)。

例如

/**
* @param int $id Mandatory parameter
* @param string $sorting Not required parameter
* @param Your_Model_Name $model Optional dependency object
*/
public function indexAction($id, $sorting = null, Your_Model_Name $model = null)
{
// model has been already automatically instantiated if null
$entry = $model->getOneById($id, $sorting);
}

(需要 DocBlock,但我使用 Netbeans IDE,因此 DocBlock 是根据操作参数自动生成的)

关于php - 如何在 Zend Framework 中使用依赖注入(inject)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3645300/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com