Custom Exception Handling
31. März 2023
31. März 2023
If you want to extend the core exception handling, you can override the handler with a custom handler.
See also How to extend the error and exception handling.
In this example we will describe how to check user access in a controller action and throw a 403 if the user is not logged in. This is just one example of many use cases.
Let's start with the controller action. In your action, you want to check if a user is logged in, if not, throw an error.
if ($this->feUser === null) {
throw new LoginDeniedException('Access denied');
}
We defined an own Execption class for this.
<?php
namespace Vendor\CstmExtension\Exception;
class LoginDeniedException extends \Exception
{
}
There is an exception handler at content element level. This Typoscript config allows you to override this handler.
config.contentObjectExceptionHandler = Vendor\CstmExtension\Error\Frontend\ProductionExceptionHandler
In your handler, you can catch the LoginDeniedException and do whatever you want, such as redirecting to the login page as shown here.
<?php
class ProductionExceptionHandler extends \TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler
{
use ExceptionHandlerTrait;
public function handle(
\Exception $exception,
AbstractContentObject $contentObject = null,
$contentObjectConfiguration = []): string
{
if ($exception instanceof LoginDeniedException) {
$request = $GLOBALS['TYPO3_REQUEST'];
header('Location: ' . $this->getLoginPage((string)$request->getUri()), true, 302);
exit;
}
return parent::handle($exception, $contentObject, $contentObjectConfiguration);
}
}
It's a good habit to define the login page in the errorHandling section of your site config.
errorHandling:
-
errorCode: 403
errorHandler: Page
errorContentSource: 't3://page?uid=3427'
The getLoginPage function finally reads the config and builds a valid link to the target page.
<?php
trait ExceptionHandlerTrait
{
protected function getLoginPage(string $url): string
{
// default is to root page
$link = '/';
// loads the site config as array
$request = $GLOBALS['TYPO3_REQUEST'];
$site = $request->getAttribute('site')->getConfiguration();
// check and loop if there is a 403 handler
if (!empty($config['errorHandling'])) {
foreach ($config['errorHandling'] as $errorHandler) {
if (!empty($errorHandler['errorCode']) && !empty($errorHandler['errorContentSource'])
&& $errorHandler['errorCode'] == 403) {
// add the calling url as return attribute
$signed = $this->signUrl($url);
// use the typolink renderer to get a valid url
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$link = $contentObject->typoLink_URL([
'parameter' => $errorHandler['errorContentSource'],
'additionalParams' => '&return_url=' . $signed,
]);
}
}
}
return $link;
}
protected function signUrl(string $url): string
{
return sprintf(
'%s@%s',
$url,
GeneralUtility::hmac($url, FrontendUserAuthentication::class)
);
}
}