在ZF2模块中定义自定义ExceptionStrategy

And*_*ris 9 php exception-handling module zend-framework2

大家好,

我已经在这个问题上苦苦挣扎了一个多星期,最后决定寻求帮助,希望有人知道答案.

我正在开发一个使用Google协议缓冲区作为数据交换格式的应用程序.我正在使用DrSlump的PHP 实现,它允许您使用数据填充类实例,然后将它们序列化为二进制字符串(或将二进制字符串解码为PHP对象).

我已经设法实现我的自定义,ProtobufStrategyselectRenderer(ViewEvent $e)返回一个实例,ProtobufRenderer以防事件包含一个实例ProtobufModel.然后,渲染器通过调用$model->getOptions()确定哪个消息需要发送回客户端,序列化数据并将二进制字符串输出到php:// output,从模型中提取我的自定义参数.

为了让它更有意义,让我们看一下以下示例消息:

message SearchRequest {
    required string query = 1;
    optional int32 page_number = 2;
    optional int32 result_per_page = 3;
}
Run Code Online (Sandbox Code Playgroud)

如果我想用这条消息回复客户端,我会从我的行动中返回这样的内容:

public function getSearchRequestAction()
{
    [..]
    $data = array(
        'query'           => 'my query',
        'page_number'     => 3,
        'result_per_page' => 20,
    );
    return new ProtobufModel($data, array(
        'message' => 'MyNamespace\Protobuf\SearchRequest',
    ));
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我正在利用ViewModel第二个参数$ options来判断哪些消息需要序列化.然后,如前所述,可以通过调用在渲染器中提取$model->getOptions().

到现在为止还挺好.我的控制器动作按预期输出二进制数据.

但是,我遇到处理异常的问题.我的计划是捕获所有异常并使用我的Exception消息的实例响应客户端,如下所示:

message Exception {
    optional string message = 1;
    optional int32 code = 2;
    optional string file = 3;
    optional uint32 line = 4;
    optional string trace = 5;
    optional Exception previous = 6;
}
Run Code Online (Sandbox Code Playgroud)

理论上它应该开箱即用,但事实并非如此.问题是Zend\Mvc\View\Http\ExceptionStrategy::prepareExceptionViewModel(MvcEvent $e)返回一个实例ViewModel,显然不包含我需要的额外$选项信息.

它也返回ViewModel而不是ProtobufModel,这意味着Zend调用默认值ViewPhpRenderer并将异常作为HTML页面输出.

我想要做的是用我自己的类替换默认值ExceptionStrategy(最终也是RouteNotFoundStrategy),这将返回如下内容:

$data = array(
    'message'  => $e->getMessage(),
    'code'     => $e->getCode(),
    'file'     => $e->getFile(),
    'line'     => $e->getLine(),
    'trace'    => $e->getTraceAsString(),
    'previous' => $e->getPrevious(),
);
return new ProtobufModel($data, array(
    'message' => 'MyNamespace\Protobuf\Exception',
));
Run Code Online (Sandbox Code Playgroud)

......我找不到办法......

我尝试创建自己的ExceptionStrategy类并将其别名为现有的ExceptionStrategy服务,但Zend抱怨已存在具有此类名称的服务.

我怀疑我在自定义策略扩展的正确路径上我无法找到覆盖默认路径的方法.

我注意到默认ExceptionStrategy和控制台注册了Zend/Mvc/View/Http/ViewManager.我希望我不必添加自定义视图管理器来实现这么简单的事情,但如果我错了,请纠正我.

任何帮助将不胜感激!

wei*_*ney 10

最简单的方法是做一点捏造.

首先,注册您的侦听器以比ExceptionStrategy更高的优先级运行; 因为它以默认优先级注册,这意味着任何优先级高于1.

然后,在您的监听器中,在返回之前,请确保将MvcEvent中的"错误"设置为假值:

$e->setError(false);
Run Code Online (Sandbox Code Playgroud)

完成后,默认的ExceptionStrategy会在使用ViewModel做任何事情之前说"在这里无事可做,继续前进"并提前返回.

当你在它时,你还应该确保在事件中更改结果实例:

$e->setResult($yourProtobufModel)
Run Code Online (Sandbox Code Playgroud)

因为这将确保这是其他听众检查的内容.