xref: /webtrees/app/Http/Middleware/HandleExceptions.php (revision 20691a3a6e9b12313102705555b6cbad99d0f94b)
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