ErrorController.php 6.89 KB
Newer Older
1
<?php
2

3
declare(strict_types=1);
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

18
19
namespace TYPO3\CMS\Frontend\Controller;

20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use TYPO3\CMS\Core\Controller\ErrorPageController;
23
use TYPO3\CMS\Core\Error\Http\InternalServerErrorException;
24
25
use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
use TYPO3\CMS\Core\Error\Http\ServiceUnavailableException;
26
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
27
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException;
28
use TYPO3\CMS\Core\Http\HtmlResponse;
29
use TYPO3\CMS\Core\Http\JsonResponse;
30
use TYPO3\CMS\Core\Site\Entity\Site;
31
32
33
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
34
 * Handles error requests,
35
36
37
38
39
 * returns a response object.
 */
class ErrorController
{
    /**
40
     * Used for creating a 500 response ("Internal Server Error"), usually due some misconfiguration
41
42
     * but if configured, a RedirectResponse could be returned as well.
     *
43
     * @param ServerRequestInterface $request
44
45
46
     * @param string $message
     * @param array $reasons
     * @return ResponseInterface
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
     * @throws InternalServerErrorException
     */
    public function internalErrorAction(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
    {
        if (!$this->isPageUnavailableHandlerConfigured()) {
            throw new InternalServerErrorException($message, 1607585445);
        }
        $errorHandler = $this->getErrorHandlerFromSite($request, 500);
        if ($errorHandler instanceof PageErrorHandlerInterface) {
            return $errorHandler->handlePageError($request, $message, $reasons);
        }
        return $this->handleDefaultError($request, 500, $message ?: 'Internal Server Error');
    }

    /**
     * Used for creating a 503 response ("Service Unavailable"), to be used for maintenance mode
     * or when the server is overloaded, a RedirectResponse could be returned as well.
     *
     * @param ServerRequestInterface $request
     * @param string $message
     * @param array $reasons
     * @return ResponseInterface
69
70
     * @throws ServiceUnavailableException
     */
71
    public function unavailableAction(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
72
73
74
75
    {
        if (!$this->isPageUnavailableHandlerConfigured()) {
            throw new ServiceUnavailableException($message, 1518472181);
        }
76
        $errorHandler = $this->getErrorHandlerFromSite($request, 503);
77
        if ($errorHandler instanceof PageErrorHandlerInterface) {
78
            return $errorHandler->handlePageError($request, $message, $reasons);
79
        }
80
        return $this->handleDefaultError($request, 503, $message ?: 'Service Unavailable');
81
82
83
84
85
86
    }

    /**
     * Used for creating a 404 response ("Page Not Found"),
     * but if configured, a RedirectResponse could be returned as well.
     *
87
     * @param ServerRequestInterface $request
88
89
90
91
92
     * @param string $message
     * @param array $reasons
     * @return ResponseInterface
     * @throws PageNotFoundException
     */
93
    public function pageNotFoundAction(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
94
    {
95
96
        $errorHandler = $this->getErrorHandlerFromSite($request, 404);
        if ($errorHandler instanceof PageErrorHandlerInterface) {
97
            return $errorHandler->handlePageError($request, $message, $reasons);
98
        }
99
100
101
        try {
            return $this->handleDefaultError($request, 404, $message);
        } catch (\RuntimeException $e) {
102
103
104
105
106
107
108
109
            throw new PageNotFoundException($message, 1518472189);
        }
    }

    /**
     * Used for creating a 403 response ("Access denied"),
     * but if configured, a RedirectResponse could be returned as well.
     *
110
     * @param ServerRequestInterface $request
111
112
113
     * @param string $message
     * @param array $reasons
     * @return ResponseInterface
114
     * @throws PageNotFoundException
115
     */
116
    public function accessDeniedAction(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
117
    {
118
119
        $errorHandler = $this->getErrorHandlerFromSite($request, 403);
        if ($errorHandler instanceof PageErrorHandlerInterface) {
120
            return $errorHandler->handlePageError($request, $message, $reasons);
121
        }
122
123
124
125
126
        try {
            return $this->handleDefaultError($request, 403, $message);
        } catch (\RuntimeException $e) {
            throw new PageNotFoundException($message, 1518472195);
        }
127
128
129
    }

    /**
130
131
     * Checks whether the devIPMask matches the current visitor's IP address.
     * Note: the name of this method is a misnomer (legacy code),
132
     *
133
     * @return bool True if the server error handler should be used.
134
135
136
     */
    protected function isPageUnavailableHandlerConfigured(): bool
    {
137
        return !GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']);
138
    }
139
140
141
142
143
144
145
146
147
148
149

    /**
     * Checks if a site is configured, and an error handler is configured for this specific status code.
     *
     * @param ServerRequestInterface $request
     * @param int $statusCode
     * @return PageErrorHandlerInterface|null
     */
    protected function getErrorHandlerFromSite(ServerRequestInterface $request, int $statusCode): ?PageErrorHandlerInterface
    {
        $site = $request->getAttribute('site');
150
151
152
153
154
155
156
157
        if ($site instanceof Site) {
            try {
                return $site->getErrorHandler($statusCode);
            } catch (PageErrorHandlerNotConfiguredException $e) {
                // No error handler found, so fallback back to the generic TYPO3 error handler.
            }
        }
        return null;
158
    }
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

    /**
     * Ensures that a response object is created as a "fallback" when no error handler is configured.
     *
     * @param ServerRequestInterface $request
     * @param int $statusCode
     * @param string $reason
     * @return ResponseInterface
     */
    protected function handleDefaultError(ServerRequestInterface $request, int $statusCode, string $reason = ''): ResponseInterface
    {
        if (strpos($request->getHeaderLine('Accept'), 'application/json') !== false) {
            return new JsonResponse(['reason' => $reason], $statusCode);
        }
        $content = GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
            'Page Not Found',
            'The page did not exist or was inaccessible.' . ($reason ? ' Reason: ' . $reason : '')
        );
        return new HtmlResponse($content, $statusCode);
    }
179
}