‪TYPO3CMS  ‪main
PageContentErrorHandler.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ResponseFactoryInterface;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
33 
39 {
40  protected int ‪$statusCode;
42  protected int ‪$pageUid = 0;
44  protected ResponseFactoryInterface ‪$responseFactory;
47 
52  public function ‪__construct(int ‪$statusCode, array $configuration)
53  {
54  $this->statusCode = ‪$statusCode;
55  if (empty($configuration['errorContentSource'])) {
56  throw new \InvalidArgumentException('PageContentErrorHandler needs to have a proper link set.', 1522826413);
57  }
58  $this->errorHandlerConfiguration = $configuration;
59 
60  // @todo Convert this to DI once this class can be injected properly.
61  $container = GeneralUtility::getContainer();
62  $this->application = $container->get(Application::class);
63  $this->responseFactory = $container->get(ResponseFactoryInterface::class);
64  $this->siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
65  $this->link = $container->get(LinkService::class);
66  }
67 
68  public function ‪handlePageError(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
69  {
70  try {
71  $urlParams = $this->link->resolve($this->errorHandlerConfiguration['errorContentSource']);
72  $urlParams['pageuid'] = (int)($urlParams['pageuid'] ?? 0);
73  $resolvedUrl = $this->‪resolveUrl($request, $urlParams);
74 
75  // avoid denial-of-service amplification scenario
76  if ($resolvedUrl === (string)$request->getUri()) {
77  return new ‪HtmlResponse(
78  'The error page could not be resolved, as the error page itself is not accessible',
79  $this->statusCode
80  );
81  }
82  // Create a sub-request and do not take any special query parameters into account
83  $subRequest = $request->withQueryParams([])->withUri(new ‪Uri($resolvedUrl))->withMethod('GET');
84  $subResponse = $this->‪stashEnvironment(fn(): ResponseInterface => $this->‪sendSubRequest($subRequest, $urlParams['pageuid'], $request));
85 
86  if ($subResponse->getStatusCode() >= 300) {
87  throw new \RuntimeException(sprintf('Error handler could not fetch error page "%s", status code: %s', $resolvedUrl, $subResponse->getStatusCode()), 1544172839);
88  }
89 
90  return $this->responseFactory->createResponse($this->statusCode)
91  ->withHeader('content-type', $subResponse->getHeader('content-type'))
92  ->withBody($subResponse->getBody());
94  return new ‪HtmlResponse('Invalid error handler configuration: ' . $this->errorHandlerConfiguration['errorContentSource']);
95  }
96  }
97 
101  protected function ‪stashEnvironment(callable $fetcher): ResponseInterface
102  {
103  $parkedTsfe = ‪$GLOBALS['TSFE'] ?? null;
104  ‪$GLOBALS['TSFE'] = null;
105 
106  $result = $fetcher();
107 
108  ‪$GLOBALS['TSFE'] = $parkedTsfe;
109 
110  return $result;
111  }
112 
118  protected function ‪sendSubRequest(ServerRequestInterface $request, int $pageId, ServerRequestInterface $originalRequest): ResponseInterface
119  {
120  $site = $request->getAttribute('site');
121  if (!$site instanceof ‪Site) {
122  $site = $this->siteFinder->getSiteByPageId($pageId);
123  $request = $request->withAttribute('site', $site);
124  }
125 
126  $request = $request->withAttribute('originalRequest', $originalRequest);
127 
128  return $this->application->handle($request);
129  }
130 
134  protected function ‪resolveUrl(ServerRequestInterface $request, array $urlParams): string
135  {
136  if (!in_array($urlParams['type'], ['page', 'url'])) {
137  throw new \InvalidArgumentException('PageContentErrorHandler can only handle TYPO3 urls of types "page" or "url"', 1522826609);
138  }
139  if ($urlParams['type'] === 'url') {
140  return $urlParams['url'];
141  }
142 
143  // Get the site related to the configured error page
144  $site = $this->siteFinder->getSiteByPageId($urlParams['pageuid']);
145  // Fall back to current request for the site
146  if (!$site instanceof ‪Site) {
147  $site = $request->getAttribute('site', null);
148  }
150  $requestLanguage = $request->getAttribute('language', null);
151  // Try to get the current request language from the site that was found above
152  if ($requestLanguage instanceof ‪SiteLanguage && $requestLanguage->‪isEnabled()) {
153  try {
154  $language = $site->getLanguageById($requestLanguage->getLanguageId());
155  } catch (\InvalidArgumentException $e) {
156  $language = $site->getDefaultLanguage();
157  }
158  } else {
159  $language = $site->getDefaultLanguage();
160  }
161 
162  // Requested language or default language is disabled in current site => Fetch first "enabled" language
163  if (!$language->isEnabled()) {
164  $enabledLanguages = $site->getLanguages();
165  if ($enabledLanguages === []) {
166  throw new \RuntimeException(
167  'Site ' . $site->getIdentifier() . ' does not define any enabled language.',
168  1674487171
169  );
170  }
171  $language = reset($enabledLanguages);
172  }
173 
174  // Build Url
175  $uri = $site->getRouter()->generateUri(
176  (int)$urlParams['pageuid'],
177  ['_language' => $language]
178  );
179 
180  // Fallback to the current URL if the site is not having a proper scheme and host
181  $currentUri = $request->getUri();
182  if (empty($uri->getScheme())) {
183  $uri = $uri->withScheme($currentUri->getScheme());
184  }
185  if (empty($uri->getUserInfo())) {
186  $uri = $uri->withUserInfo($currentUri->getUserInfo());
187  }
188  if (empty($uri->getHost())) {
189  $uri = $uri->withHost($currentUri->getHost());
190  }
191  if ($uri->getPort() === null) {
192  $uri = $uri->withPort($currentUri->getPort());
193  }
194 
195  return (string)$uri;
196  }
197 }
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage\isEnabled
‪isEnabled()
Definition: SiteLanguage.php:257
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$link
‪LinkService $link
Definition: PageContentErrorHandler.php:46
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$pageUid
‪int $pageUid
Definition: PageContentErrorHandler.php:42
‪TYPO3\CMS\Core\Http\Uri
Definition: Uri.php:30
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage
Definition: SiteLanguage.php:27
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\sendSubRequest
‪sendSubRequest(ServerRequestInterface $request, int $pageId, ServerRequestInterface $originalRequest)
Definition: PageContentErrorHandler.php:118
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\handlePageError
‪handlePageError(ServerRequestInterface $request, string $message, array $reasons=[])
Definition: PageContentErrorHandler.php:68
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler
Definition: PageContentErrorHandler.php:39
‪TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException
Definition: InvalidRouteArgumentsException.php:25
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$errorHandlerConfiguration
‪array $errorHandlerConfiguration
Definition: PageContentErrorHandler.php:41
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\stashEnvironment
‪stashEnvironment(callable $fetcher)
Definition: PageContentErrorHandler.php:101
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$responseFactory
‪ResponseFactoryInterface $responseFactory
Definition: PageContentErrorHandler.php:44
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\__construct
‪__construct(int $statusCode, array $configuration)
Definition: PageContentErrorHandler.php:52
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface
Definition: PageErrorHandlerInterface.php:29
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$siteFinder
‪SiteFinder $siteFinder
Definition: PageContentErrorHandler.php:45
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$statusCode
‪int $statusCode
Definition: PageContentErrorHandler.php:40
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$application
‪Application $application
Definition: PageContentErrorHandler.php:43
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Frontend\Http\Application
Definition: Application.php:35
‪TYPO3\CMS\Core\Error\PageErrorHandler
Definition: FluidPageErrorHandler.php:18
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:28
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\resolveUrl
‪resolveUrl(ServerRequestInterface $request, array $urlParams)
Definition: PageContentErrorHandler.php:134