-
-
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.