首页 > 解决方案 > 在 Zend Framework 3 中捕获异常

问题描述

我使用 ZF3 骨架应用程序。我想知道我应该在哪里全局捕获异常。

示例:现在,如果我访问无效路由 (mysite.com/invalid-route),应用程序会报告未捕获的期望和 HTTP 响应代码 200

致命错误:未捕获 Zend\View\Exception\RuntimeException:未提供 RouteMatch 实例

我希望触发内置的 404 错误页面。

我错过了什么?有人可以指出我正确的方向吗?

使用以下代码正确记录异常:

class Module implements ConfigProviderInterface
{
     const VERSION = '3.0.3-dev';

     public function onBootstrap()
     {
         $logger = new Logger();
         $writer = new Writer\Stream(__DIR__ . '/../../../data/log/error.log');
         $logger->addWriter($writer);

         // Log PHP errors
         Logger::registerErrorHandler($logger, true);

         // Log exceptions
         Logger::registerExceptionHandler($logger);
     }

标签: phpzend-frameworkzend-framework3

解决方案


这是您可以使用侦听器捕获的东西,在 early event 上触发MvcEvent::EVENT_ROUTE

我建议使用专门的类和工厂来分离对onBootstrap函数使用的关注。您可以通过注册和“激活”一个 Listener 类来做到这一点,如下所示:

'listeners'       => [
    // This makes sure it "gets listened to" from the very start of the application (onBootstrap)
    RouteExistsListener::class,     
],
'service_manager' => [
    'factories' => [
        // This is just what you think it is
        RouteExistsListener::class  => InvokableFactory::class,
    ],
],

你可以只使用InvokableFactory这个监听器,因为没有特殊要求。

class RouteExistsListener implements ListenerAggregateInterface
{
    /**
     * @var array
     */
    protected $listeners = [];

    /**
     * @param EventManagerInterface $events
     */
    public function detach(EventManagerInterface $events)
    {
        foreach ($this->listeners as $index => $listener) {
            if ($events->detach($listener)) {
                unset($this->listeners[$index]);
            }
        }
    }

    /**
     * @param EventManagerInterface $events
     */
    public function attach(EventManagerInterface $events, $priority = 1)
    {
        $events->attach(MvcEvent::EVENT_ROUTE, [$this, 'doesRouteExist'], 100);
    }

    /**
     * @param MvcEvent $event
     *
     * @return void|Response
     * @throws Exception
     */
    public function doesRouteExist(MvcEvent $event)
    {
        /** @var TranslatorAwareTreeRouteStack|TreeRouteStack $router */
        $router = $event->getRouter();
        /** @var Request $request */
        $request = $event->getRequest();
        /** @var RouteMatch|null $routeExists */
        $routeExists = $router->match($request); // Return RouteMatch|null

        if ($routeExists instanceof RouteMatch) {
            return; // Route exists - nothing to do
        }

        $url = $router->assemble([], ['name' => 'home']); // Name of your redirect route (ie. not_found/404, or something)

        /** @var Response $response */
        $response = $event->getResponse();
        $response->getHeaders()->addHeaderLine('Location', $url);
        $response->setStatusCode(302);
        $response->sendHeaders();

        $event->getApplication()->getEventManager()->attach(
            MvcEvent::EVENT_ROUTE,
            function (MvcEvent $event) use ($response) {
                $event->stopPropagation();

                return $response;
            },
            -10000
        );

        return $response;
    }
}

注意:为上述内容使用了我自己的现有类,并按照我认为应该可以使用的方式进行了修改。但是,可能包含一两个错误;-) 不过,我认为应该为您指明正确的方向。


推荐阅读