Catching Exceptions in Zend Framework 3

独自空忆成欢 提交于 2019-12-11 15:43:01

问题


I use the ZF3 skeletion application. I wonder where I am supposed to catch exceptions globally.

Example: Right now, if I access an invalid route (mysite.com/invalid-route), the application reports an uncaught expection and HTTP response code 200

Fatal error: Uncaught Zend\View\Exception\RuntimeException: No RouteMatch instance provided

I would expect the build-in 404 error page to be triggered.

What am I missing? Can someone point me to the right direction?

The exception is logged properly using the following code:

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);
     }

回答1:


This is something you can catch using a Listener, triggered on the early event MvcEvent::EVENT_ROUTE.

I would suggest using a dedicated class & Factory to separate concerns over the usage of the onBootstrap function. You'd do this by registering and "activating" a Listener class, like so:

'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,
    ],
],

You can use just the InvokableFactory for this Listener, as there are no special requirements.

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;
    }
}

NOTE: Used an existing class of my own for the above and modified as I think it should work. However, might contain an error or two ;-) Still, should point you in the right direction I think.



来源:https://stackoverflow.com/questions/52804700/catching-exceptions-in-zend-framework-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!