‪TYPO3CMS  10.4
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\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
37 
43 {
44 
48  protected ‪$statusCode;
49 
54 
58  protected ‪$pageUid = 0;
59 
66  public function ‪__construct(int ‪$statusCode, array $configuration)
67  {
68  $this->statusCode = ‪$statusCode;
69  if (empty($configuration['errorContentSource'])) {
70  throw new \InvalidArgumentException('PageContentErrorHandler needs to have a proper link set.', 1522826413);
71  }
72  $this->errorHandlerConfiguration = $configuration;
73  }
74 
83  public function ‪handlePageError(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
84  {
85  try {
86  $resolvedUrl = $this->‪resolveUrl($request, $this->errorHandlerConfiguration['errorContentSource']);
87 
88  $cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('pages');
89  $cacheIdentifier = 'errorPage_' . md5($resolvedUrl);
90  $cacheContent = $cache->get($cacheIdentifier);
91 
92  if (!$cacheContent && $resolvedUrl !== (string)$request->getUri()) {
93  $lockFactory = GeneralUtility::makeInstance(LockFactory::class);
94  $lock = $lockFactory->createLocker(
95  $cacheIdentifier,
97  );
98  try {
99  $locked = $lock->acquire(
101  );
102  if (!$locked) {
103  return $this->‪createGenericErrorResponse();
104  }
105 
106  $subResponse = GeneralUtility::makeInstance(RequestFactory::class)
107  ->request($resolvedUrl, 'GET', $this->‪getSubRequestOptions());
108  $lock->release();
109  } catch (‪LockAcquireWouldBlockException $_) {
110  $lock->release();
111  return $this->‪createGenericErrorResponse();
112  } catch (\‪Exception $e) {
113  throw new \RuntimeException('Error handler could not fetch error page "' . $resolvedUrl . '", reason: ' . $e->getMessage(), 1544172838);
114  }
115  if ($subResponse->getStatusCode() >= 300) {
116  throw new \RuntimeException('Error handler could not fetch error page "' . $resolvedUrl . '", status code: ' . $subResponse->getStatusCode(), 1544172839);
117  }
118 
119  $body = $subResponse->getBody()->getContents();
120  $contentType = $subResponse->getHeader('Content-Type');
121 
122  // Cache body and content-type if sub-response returned a HTTP status 200
123  if ($subResponse->getStatusCode() === 200) {
124  $cacheTags = ['errorPage'];
125  if ($this->pageUid > 0) {
126  // Cache Tag "pageId_" ensures, cache is purged when content of 404 page changes
127  $cacheTags[] = 'pageId_' . ‪$this->pageUid;
128  }
129  $cacheContent = [
130  'body' => $body,
131  'headers' => ['Content-Type' => $contentType],
132  ];
133  $cache->set($cacheIdentifier, $cacheContent, $cacheTags);
134  }
135  }
136  if ($cacheContent && $cacheContent['body'] && $cacheContent['headers']) {
137  // We use a HtmlResponse here, since no Stream is available for cached response content
138  return new ‪HtmlResponse($cacheContent['body'], $this->statusCode, $cacheContent['headers']);
139  }
140  $content = 'The error page could not be resolved, as the error page itself is not accessible';
142  $content = 'Invalid error handler configuration: ' . $this->errorHandlerConfiguration['errorContentSource'];
143  }
144  return new ‪HtmlResponse($content, $this->statusCode);
145  }
146 
147  protected function ‪createGenericErrorResponse(string $message = ''): ResponseInterface
148  {
149  $content = GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
150  'Page Not Found',
151  $message ?: 'The page did not exist or was inaccessible. Error page is being generated'
152  );
153  return new ‪HtmlResponse($content, 503);
154  }
155 
161  protected function ‪getSubRequestOptions(): array
162  {
163  $options = [];
164  if ((int)‪$GLOBALS['TYPO3_CONF_VARS']['HTTP']['timeout'] === 0) {
165  $options = [
166  'timeout' => 10
167  ];
168  }
169  return $options;
170  }
171 
181  protected function ‪resolveUrl(ServerRequestInterface $request, string $typoLinkUrl): string
182  {
183  $linkService = GeneralUtility::makeInstance(LinkService::class);
184  $urlParams = $linkService->resolve($typoLinkUrl);
185  if ($urlParams['type'] !== 'page' && $urlParams['type'] !== 'url') {
186  throw new \InvalidArgumentException('PageContentErrorHandler can only handle TYPO3 urls of types "page" or "url"', 1522826609);
187  }
188  if ($urlParams['type'] === 'url') {
189  return $urlParams['url'];
190  }
191 
192  $this->pageUid = (int)$urlParams['pageuid'];
193 
194  // Get the site related to the configured error page
195  $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($this->pageUid);
196  // Fall back to current request for the site
197  if (!$site instanceof Site) {
198  $site = $request->getAttribute('site', null);
199  }
201  $requestLanguage = $request->getAttribute('language', null);
202  // Try to get the current request language from the site that was found above
203  if ($requestLanguage instanceof SiteLanguage && $requestLanguage->isEnabled()) {
204  try {
205  $language = $site->getLanguageById($requestLanguage->getLanguageId());
206  } catch (\InvalidArgumentException $e) {
207  $language = $site->getDefaultLanguage();
208  }
209  } else {
210  $language = $site->getDefaultLanguage();
211  }
212 
213  // Build Url
214  $uri = $site->getRouter()->generateUri(
215  (int)$urlParams['pageuid'],
216  ['_language' => $language]
217  );
218 
219  // Fallback to the current URL if the site is not having a proper scheme and host
220  $currentUri = $request->getUri();
221  if (empty($uri->getScheme())) {
222  $uri = $uri->withScheme($currentUri->getScheme());
223  }
224  if (empty($uri->getUserInfo())) {
225  $uri = $uri->withUserInfo($currentUri->getUserInfo());
226  }
227  if (empty($uri->getHost())) {
228  $uri = $uri->withHost($currentUri->getHost());
229  }
230  if ($uri->getPort() === null) {
231  $uri = $uri->withPort($currentUri->getPort());
232  }
233 
234  return (string)$uri;
235  }
236 }
‪TYPO3\CMS\Core\Controller\ErrorPageController
Definition: ErrorPageController.php:32
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\createGenericErrorResponse
‪createGenericErrorResponse(string $message='')
Definition: PageContentErrorHandler.php:144
‪TYPO3\CMS\Core\Locking\LockingStrategyInterface\LOCK_CAPABILITY_NOBLOCK
‪const LOCK_CAPABILITY_NOBLOCK
Definition: LockingStrategyInterface.php:40
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:26
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Locking\LockingStrategyInterface
Definition: LockingStrategyInterface.php:26
‪TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
Definition: NoSuchCacheException.php:24
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$pageUid
‪int $pageUid
Definition: PageContentErrorHandler.php:55
‪TYPO3\CMS\Core\Locking\LockingStrategyInterface\LOCK_CAPABILITY_EXCLUSIVE
‪const LOCK_CAPABILITY_EXCLUSIVE
Definition: LockingStrategyInterface.php:30
‪TYPO3\CMS\Core\Locking\Exception\LockAcquireWouldBlockException
Definition: LockAcquireWouldBlockException.php:22
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:40
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage
Definition: SiteLanguage.php:26
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:35
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\getSubRequestOptions
‪array int[] getSubRequestOptions()
Definition: PageContentErrorHandler.php:158
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler
Definition: PageContentErrorHandler.php:43
‪TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException
Definition: InvalidRouteArgumentsException.php:26
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$errorHandlerConfiguration
‪array $errorHandlerConfiguration
Definition: PageContentErrorHandler.php:51
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\__construct
‪__construct(int $statusCode, array $configuration)
Definition: PageContentErrorHandler.php:63
‪TYPO3\CMS\Core\Error\Exception
Definition: Exception.php:22
‪TYPO3\CMS\Core\Http\RequestFactory
Definition: RequestFactory.php:31
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage\isEnabled
‪bool isEnabled()
Definition: SiteLanguage.php:304
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface
Definition: PageErrorHandlerInterface.php:29
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\$statusCode
‪int $statusCode
Definition: PageContentErrorHandler.php:47
‪TYPO3\CMS\Core\Locking\LockFactory
Definition: LockFactory.php:25
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\handlePageError
‪ResponseInterface handlePageError(ServerRequestInterface $request, string $message, array $reasons=[])
Definition: PageContentErrorHandler.php:80
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Error\PageErrorHandler
Definition: FluidPageErrorHandler.php:18
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler\resolveUrl
‪string resolveUrl(ServerRequestInterface $request, string $typoLinkUrl)
Definition: PageContentErrorHandler.php:178
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26