1cfbf56adSGreg Roach<?php 2cfbf56adSGreg Roach/** 3cfbf56adSGreg Roach * webtrees: online genealogy 4cfbf56adSGreg Roach * Copyright (C) 2019 webtrees development team 5cfbf56adSGreg Roach * This program is free software: you can redistribute it and/or modify 6cfbf56adSGreg Roach * it under the terms of the GNU General Public License as published by 7cfbf56adSGreg Roach * the Free Software Foundation, either version 3 of the License, or 8cfbf56adSGreg Roach * (at your option) any later version. 9cfbf56adSGreg Roach * This program is distributed in the hope that it will be useful, 10cfbf56adSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 11cfbf56adSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12cfbf56adSGreg Roach * GNU General Public License for more details. 13cfbf56adSGreg Roach * You should have received a copy of the GNU General Public License 14cfbf56adSGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 15cfbf56adSGreg Roach */ 16cfbf56adSGreg Roachdeclare(strict_types=1); 17cfbf56adSGreg Roach 18cfbf56adSGreg Roachnamespace Fisharebest\Webtrees\Http\Middleware; 19cfbf56adSGreg Roach 20b7765f6bSGreg Roachuse Fig\Http\Message\RequestMethodInterface; 21f397d0fdSGreg Roachuse Fig\Http\Message\StatusCodeInterface; 22f397d0fdSGreg Roachuse Fisharebest\Webtrees\Http\ViewResponseTrait; 23f397d0fdSGreg Roachuse Fisharebest\Webtrees\Log; 24cfbf56adSGreg Roachuse Psr\Http\Message\ResponseInterface; 25cfbf56adSGreg Roachuse Psr\Http\Message\ServerRequestInterface; 26cfbf56adSGreg Roachuse Psr\Http\Server\MiddlewareInterface; 27cfbf56adSGreg Roachuse Psr\Http\Server\RequestHandlerInterface; 28cfbf56adSGreg Roachuse Symfony\Component\HttpKernel\Exception\HttpException; 29cfbf56adSGreg Roachuse Throwable; 30f397d0fdSGreg Roachuse function dirname; 31f397d0fdSGreg Roachuse function response; 32f397d0fdSGreg Roachuse function str_replace; 33f397d0fdSGreg Roachuse function view; 34f397d0fdSGreg Roachuse const PHP_EOL; 35cfbf56adSGreg Roach 36cfbf56adSGreg Roach/** 37cfbf56adSGreg Roach * Middleware to handle and render errors. 38cfbf56adSGreg Roach */ 39b7765f6bSGreg Roachclass HandleExceptions implements MiddlewareInterface, RequestMethodInterface, StatusCodeInterface 40cfbf56adSGreg Roach{ 41f397d0fdSGreg Roach use ViewResponseTrait; 42cfbf56adSGreg Roach 43cfbf56adSGreg Roach /** 44cfbf56adSGreg Roach * @param ServerRequestInterface $request 45cfbf56adSGreg Roach * @param RequestHandlerInterface $handler 46cfbf56adSGreg Roach * 47cfbf56adSGreg Roach * @return ResponseInterface 48f397d0fdSGreg Roach * @throws Throwable 49cfbf56adSGreg Roach */ 50cfbf56adSGreg Roach public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 51cfbf56adSGreg Roach { 52cfbf56adSGreg Roach try { 53f397d0fdSGreg Roach try { 54cfbf56adSGreg Roach return $handler->handle($request); 55cfbf56adSGreg Roach } catch (HttpException $exception) { 56f397d0fdSGreg Roach $original_exception = $exception; 57f397d0fdSGreg Roach 58f397d0fdSGreg Roach return $this->httpExceptionResponse($request, $exception); 59f397d0fdSGreg Roach } catch (Throwable $exception) { 60f397d0fdSGreg Roach $original_exception = $exception; 61f397d0fdSGreg Roach 62f397d0fdSGreg Roach return $this->unhandledExceptionResponse($request, $exception); 63f397d0fdSGreg Roach } 64f397d0fdSGreg Roach } catch (Throwable $exception) { 65f397d0fdSGreg Roach // If we can't handle the exception, rethrow it. 66f397d0fdSGreg Roach throw $original_exception; 67f397d0fdSGreg Roach } 68cfbf56adSGreg Roach } 69cfbf56adSGreg Roach 70f397d0fdSGreg Roach /** 71f397d0fdSGreg Roach * @param ServerRequestInterface $request 72f397d0fdSGreg Roach * @param HttpException $exception 73f397d0fdSGreg Roach * 74f397d0fdSGreg Roach * @return ResponseInterface 75f397d0fdSGreg Roach */ 76f397d0fdSGreg Roach private function httpExceptionResponse(ServerRequestInterface $request, HttpException $exception): ResponseInterface 77f397d0fdSGreg Roach { 78f397d0fdSGreg Roach if ($request->getHeaderLine('X-Requested-With') !== '') { 79db6e5963SGreg Roach $this->layout = 'layouts/ajax'; 80*20691a3aSGreg Roach } 81db6e5963SGreg Roach 82f397d0fdSGreg Roach return $this->viewResponse('components/alert-danger', [ 83f397d0fdSGreg Roach 'alert' => $exception->getMessage(), 84f397d0fdSGreg Roach 'title' => $exception->getMessage(), 85f397d0fdSGreg Roach ], $exception->getStatusCode()); 86cfbf56adSGreg Roach } 87f397d0fdSGreg Roach 88f397d0fdSGreg Roach /** 89f397d0fdSGreg Roach * @param ServerRequestInterface $request 90f397d0fdSGreg Roach * @param Throwable $exception 91f397d0fdSGreg Roach * 92f397d0fdSGreg Roach * @return ResponseInterface 93f397d0fdSGreg Roach */ 94f397d0fdSGreg Roach private function unhandledExceptionResponse(ServerRequestInterface $request, Throwable $exception): ResponseInterface 95f397d0fdSGreg Roach { 96f397d0fdSGreg Roach // Create a stack dump for the exception 97f397d0fdSGreg Roach $base_path = dirname(__DIR__, 3); 98f397d0fdSGreg Roach $trace = $exception->getMessage() . PHP_EOL . $exception->getTraceAsString(); 99f397d0fdSGreg Roach $trace = str_replace($base_path, '…', $trace); 100f397d0fdSGreg Roach 101f397d0fdSGreg Roach try { 102f397d0fdSGreg Roach Log::addErrorLog($trace); 103f397d0fdSGreg Roach } catch (Throwable $exception) { 104f397d0fdSGreg Roach // Must have been a problem with the database. Nothing we can do here. 105f397d0fdSGreg Roach } 106f397d0fdSGreg Roach 107f397d0fdSGreg Roach if ($request->getHeaderLine('X-Requested-With') !== '') { 108b7765f6bSGreg Roach // If this was a GET request, then we were probably fetching HTML to display, for 109b7765f6bSGreg Roach // example a chart or tab. 110b7765f6bSGreg Roach if ($request->getMethod() === self::METHOD_GET) { 111b7765f6bSGreg Roach $status_code = self::STATUS_OK; 112b7765f6bSGreg Roach } else { 113b7765f6bSGreg Roach $status_code = self::STATUS_INTERNAL_SERVER_ERROR; 114b7765f6bSGreg Roach } 115b7765f6bSGreg Roach 116b7765f6bSGreg Roach return response(view('components/alert-danger', ['alert' => $trace]), $status_code); 117f397d0fdSGreg Roach } 118f397d0fdSGreg Roach 119f397d0fdSGreg Roach return $this->viewResponse('errors/unhandled-exception', [ 120f397d0fdSGreg Roach 'title' => 'Error', 121f397d0fdSGreg Roach 'error' => $trace, 122f397d0fdSGreg Roach ], self::STATUS_INTERNAL_SERVER_ERROR); 123cfbf56adSGreg Roach } 124cfbf56adSGreg Roach} 125