问题
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