-
-
Save bmack/06bb00bfee09b7c5b79fd981c64fa9c9 to your computer and use it in GitHub Desktop.
<?php | |
namespace B13\AnyProject\PageErrorHandler; | |
/* | |
* This file is part of a b13 extension. | |
* | |
* 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. | |
*/ | |
use Psr\Http\Message\ResponseInterface; | |
use Psr\Http\Message\ServerRequestInterface; | |
use TYPO3\CMS\Core\Cache\CacheManager; | |
use TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler; | |
use TYPO3\CMS\Core\Exception\SiteNotFoundException; | |
use TYPO3\CMS\Core\Http\HtmlResponse; | |
use TYPO3\CMS\Core\Http\MiddlewareDispatcher; | |
use TYPO3\CMS\Core\Http\MiddlewareStackResolver; | |
use TYPO3\CMS\Core\LinkHandling\LinkService; | |
use TYPO3\CMS\Core\Package\PackageManager; | |
use TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException; | |
use TYPO3\CMS\Core\Service\DependencyOrderingService; | |
use TYPO3\CMS\Core\Site\Entity\Site; | |
use TYPO3\CMS\Core\Site\SiteFinder; | |
use TYPO3\CMS\Core\Utility\GeneralUtility; | |
use TYPO3\CMS\Frontend\Http\RequestHandler; | |
/** | |
* Does not execute a CURL request for internal pages. Just set it up in an extension | |
* and refer to it in your Site Configuration (PHP Error Handler) | |
*/ | |
class LocalPageErrorHandler extends PageContentErrorHandler | |
{ | |
/** | |
* @param ServerRequestInterface $request | |
* @param string $message | |
* @param array $reasons | |
* @return ResponseInterface | |
* @throws \RuntimeException | |
*/ | |
public function handlePageError(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface | |
{ | |
$targetDetails = $this->resolveDetails($this->errorHandlerConfiguration['errorContentSource']); | |
if ($targetDetails['type'] !== 'page' && $targetDetails['type'] !== 'url') { | |
throw new \InvalidArgumentException('PageContentErrorHandler can only handle TYPO3 urls of types "page" or "url"', 1522826609); | |
} | |
if ($targetDetails['type'] === 'page') { | |
$response = $this->buildSubRequest($request, (int)$targetDetails['pageuid']); | |
return $response->withStatusCode($this->statusCode); | |
} | |
if ($targetDetails['type'] !== 'url') { | |
return new HtmlResponse('Big fail', $this->statusCode); | |
} | |
$resolvedUrl = $targetDetails['url']; | |
try { | |
$content = null; | |
$report = []; | |
if ($resolvedUrl !== (string)$request->getUri()) { | |
$content = GeneralUtility::getUrl($resolvedUrl, 0, null, $report); | |
if ($content === false && ((int)$report['error'] === -1 || (int)$report['error'] > 200)) { | |
throw new \RuntimeException('Error handler could not fetch error page "' . $resolvedUrl . '", reason: ' . $report['message'], 1544172838); | |
} | |
} | |
} catch (InvalidRouteArgumentsException | SiteNotFoundException $e) { | |
$content = 'Invalid error handler configuration: ' . $this->errorHandlerConfiguration['errorContentSource']; | |
} | |
return new HtmlResponse($content, $this->statusCode); | |
} | |
/** | |
* @param ServerRequestInterface $request | |
* @param int $pageId | |
* @return ResponseInterface | |
* @throws SiteNotFoundException | |
* @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException | |
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException | |
* @throws \TYPO3\CMS\Core\Exception | |
*/ | |
protected function buildSubRequest(ServerRequestInterface $request, int $pageId): ResponseInterface | |
{ | |
$site = $request->getAttribute('site', null); | |
if (!$site instanceof Site) { | |
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($pageId); | |
$request = $request->withAttribute('site', $site); | |
} | |
$request = $request->withQueryParams(['id' => $pageId]); | |
$dispatcher = $this->buildDispatcher(); | |
return $dispatcher->handle($request); | |
} | |
/** | |
* @param string $typoLinkUrl | |
* @return array | |
*/ | |
protected function resolveDetails(string $typoLinkUrl): array | |
{ | |
$linkService = GeneralUtility::makeInstance(LinkService::class); | |
return $linkService->resolve($typoLinkUrl); | |
} | |
/** | |
* @return MiddlewareDispatcher | |
* @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException | |
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException | |
* @throws \TYPO3\CMS\Core\Exception | |
*/ | |
protected function buildDispatcher() | |
{ | |
$requestHandler = GeneralUtility::makeInstance(RequestHandler::class); | |
$resolver = new MiddlewareStackResolver( | |
GeneralUtility::makeInstance(PackageManager::class), | |
GeneralUtility::makeInstance(DependencyOrderingService::class), | |
GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_core') | |
); | |
$middlewares = $resolver->resolve('frontend'); | |
return new MiddlewareDispatcher($requestHandler, $middlewares); | |
} | |
} |
So you are saying the core PageContentErrorHandler should be fixed to not use GeneralUtility::getUrl() too - as in the code above? Looks like a good idea.
Yes, GeneralUtility::getUrl() should not be used to fetch a local page.
I get an endless loop with this error handler in the following configuration:
- I have a default language "English" and a second language "English (US)" with a fallbackMode: fallback
- I have a page which is disabled in the default language and has no translation for the US-Language.
Expected behavior:
When I open the en-us Version of the page I get a 404 error and the page shows the content of my 404-Page.
Actual behavior:
The page takes ages to load and I get a "Tried to allocate xxx bytes of memory ..."-Error.
I couldn't fully break down the problem or fix it. But I found out, that the Error Handler gets called over and over again. I guess the Request, that is dispatched by the error handler calls the same page again and again.
I'm using TYPO3 10.4.16
@bmack Otherwise thanks a lot for this error handler. It's way more elegant, than the core solution IMHO 🥇
@josefglatz I'm not sure I get correctly your question, but I will try to answer.
GeneralUtility::getUrl makes a curl (or file_get_content) request, which will open a new child process on the website's php-fpm pool.
If the request takes time to fulfill (because of many reasons), the php-fpm pool can be quickly saturated.
In the case of the PageContentErrorHandler, fetching the 404 page should be done in the same php process.