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