use Shaarli\Config\ConfigManager;
use Shaarli\Feed\FeedBuilder;
use Shaarli\Formatter\FormatterFactory;
+use Shaarli\Front\Controller\Visitor\ErrorController;
use Shaarli\History;
use Shaarli\Http\HttpAccess;
use Shaarli\Netscape\NetscapeBookmarkUtils;
);
};
+ $container['errorHandler'] = function (ShaarliContainer $container): ErrorController {
+ return new ErrorController($container);
+ };
+
return $container;
}
}
namespace Shaarli\Front;
use Shaarli\Container\ShaarliContainer;
-use Shaarli\Front\Exception\ShaarliFrontException;
use Shaarli\Front\Exception\UnauthorizedException;
use Slim\Http\Request;
use Slim\Http\Response;
$this->checkOpenShaarli($request, $response, $next);
return $next($request, $response);
- } catch (ShaarliFrontException $e) {
- // Possible functional error
- $this->container->pageBuilder->reset();
- $this->container->pageBuilder->assign('message', nl2br($e->getMessage()));
-
- $response = $response->withStatus($e->getCode());
-
- return $response->write($this->container->pageBuilder->render('error', $this->container->basePath));
} catch (UnauthorizedException $e) {
$returnUrl = urlencode($this->container->environment['REQUEST_URI']);
return $response->withRedirect($this->container->basePath . '/login?returnurl=' . $returnUrl);
- } catch (\Throwable $e) {
- // Unknown error encountered
- $this->container->pageBuilder->reset();
- if ($this->container->conf->get('dev.debug', false)) {
- $this->container->pageBuilder->assign('message', $e->getMessage());
- $this->container->pageBuilder->assign(
- 'stacktrace',
- nl2br(get_class($e) .': '. PHP_EOL . $e->getTraceAsString())
- );
- } else {
- $this->container->pageBuilder->assign('message', t('An unexpected error occurred.'));
- }
-
- $response = $response->withStatus(500);
-
- return $response->write($this->container->pageBuilder->render('error', $this->container->basePath));
}
+ // Other exceptions are handled by ErrorController
}
/**
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller\Visitor;
+
+use Shaarli\Front\Exception\ShaarliFrontException;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+/**
+ * Controller used to render the error page, with a provided exception.
+ * It is actually used as a Slim error handler.
+ */
+class ErrorController extends ShaarliVisitorController
+{
+ public function __invoke(Request $request, Response $response, \Throwable $throwable): Response
+ {
+ // Unknown error encountered
+ $this->container->pageBuilder->reset();
+
+ if ($throwable instanceof ShaarliFrontException) {
+ // Functional error
+ $this->assignView('message', nl2br($throwable->getMessage()));
+
+ $response = $response->withStatus($throwable->getCode());
+ } else {
+ // Internal error (any other Throwable)
+ if ($this->container->conf->get('dev.debug', false)) {
+ $this->assignView('message', $throwable->getMessage());
+ $this->assignView(
+ 'stacktrace',
+ nl2br(get_class($throwable) .': '. PHP_EOL . $throwable->getTraceAsString())
+ );
+ } else {
+ $this->assignView('message', t('An unexpected error occurred.'));
+ }
+
+ $response = $response->withStatus(500);
+ }
+
+
+ return $response->write($this->render('error'));
+ }
+}
}
/**
- * Test middleware execution with controller throwing a known front exception
+ * Test middleware execution with controller throwing a known front exception.
+ * The exception should be thrown to be later handled by the error handler.
*/
public function testMiddlewareExecutionWithFrontException(): void
{
});
$this->container->pageBuilder = $pageBuilder;
- /** @var Response $result */
- $result = $this->middleware->__invoke($request, $response, $controller);
+ $this->expectException(LoginBannedException::class);
- static::assertInstanceOf(Response::class, $result);
- static::assertSame(401, $result->getStatusCode());
- static::assertContains('error', (string) $result->getBody());
+ $this->middleware->__invoke($request, $response, $controller);
}
/**
* Test middleware execution with controller throwing a not authorized exception
+ * The middle should send a redirection response to the login page.
*/
public function testMiddlewareExecutionWithUnauthorizedException(): void
{
}
/**
- * Test middleware execution with controller throwing a not authorized exception
+ * Test middleware execution with controller throwing a not authorized exception.
+ * The exception should be thrown to be later handled by the error handler.
*/
- public function testMiddlewareExecutionWithServerExceptionWith(): void
+ public function testMiddlewareExecutionWithServerException(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
return $uri;
});
+ $dummyException = new class() extends \Exception {};
+
$response = new Response();
- $controller = function (): void {
- throw new \Exception();
+ $controller = function () use ($dummyException): void {
+ throw $dummyException;
};
$parameters = [];
})
;
- /** @var Response $result */
- $result = $this->middleware->__invoke($request, $response, $controller);
+ $this->expectException(get_class($dummyException));
- static::assertSame(500, $result->getStatusCode());
- static::assertContains('error', (string) $result->getBody());
- static::assertSame('An unexpected error occurred.', $parameters['message']);
+ $this->middleware->__invoke($request, $response, $controller);
}
public function testMiddlewareExecutionWithUpdates(): void
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller\Visitor;
+
+use PHPUnit\Framework\TestCase;
+use Shaarli\Front\Exception\ShaarliFrontException;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+class ErrorControllerTest extends TestCase
+{
+ use FrontControllerMockHelper;
+
+ /** @var ErrorController */
+ protected $controller;
+
+ public function setUp(): void
+ {
+ $this->createContainer();
+
+ $this->controller = new ErrorController($this->container);
+ }
+
+ /**
+ * Test displaying error with a ShaarliFrontException: display exception message and use its code for HTTTP code
+ */
+ public function testDisplayFrontExceptionError(): void
+ {
+ $request = $this->createMock(Request::class);
+ $response = new Response();
+
+ $message = 'error message';
+ $errorCode = 418;
+
+ // Save RainTPL assigned variables
+ $assignedVariables = [];
+ $this->assignTemplateVars($assignedVariables);
+
+ $result = ($this->controller)(
+ $request,
+ $response,
+ new class($message, $errorCode) extends ShaarliFrontException {}
+ );
+
+ static::assertSame($errorCode, $result->getStatusCode());
+ static::assertSame($message, $assignedVariables['message']);
+ static::assertArrayNotHasKey('stacktrace', $assignedVariables);
+ }
+
+ /**
+ * Test displaying error with any exception (no debug): only display an error occurred with HTTP 500.
+ */
+ public function testDisplayAnyExceptionErrorNoDebug(): void
+ {
+ $request = $this->createMock(Request::class);
+ $response = new Response();
+
+ // Save RainTPL assigned variables
+ $assignedVariables = [];
+ $this->assignTemplateVars($assignedVariables);
+
+ $result = ($this->controller)($request, $response, new \Exception('abc'));
+
+ static::assertSame(500, $result->getStatusCode());
+ static::assertSame('An unexpected error occurred.', $assignedVariables['message']);
+ static::assertArrayNotHasKey('stacktrace', $assignedVariables);
+ }
+}