Skip to content

Commit 5a4dbcb

Browse files
authored
Merge pull request #5 from activecollab/feature/silence-exceptions
Feature/silence exceptions
2 parents 94b52a4 + ad2e26b commit 5a4dbcb

File tree

3 files changed

+155
-6
lines changed

3 files changed

+155
-6
lines changed

README.md

+14
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,17 @@ All of these methods accept three parameters:
1818
1. `$request` (`\Psr\Http\Message\ServerRequestInterface` instance)
1919
2. `$param_name` (string)
2020
3. `$default` (mixed, `NULL` by default)
21+
22+
## Exception Handling
23+
24+
When action fails due to an exception, system will return 500 HTTP error, with a message that does not expose any of the system details.
25+
26+
This is done in such a way that new `RuntimeException` is constructed, with generic error message, and real exception is passed as `$previous` constructor argument of the new exception. If you have your system configured so exceptions are fully described when 500 errors are rendered (in debug mode for example), you'll be able to access original exception detials like that.
27+
28+
To change default exception message, call `setLogExceptionMessage()` controller method:
29+
30+
```php
31+
$controller->setLogExceptionMessage('Something weird happened: {exception}');
32+
```
33+
34+
If `$logger` is added to the controller (during construction or later on), all exceptions that actions throw will be logged with error level.

src/Controller.php

+91-6
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
use ActiveCollab\Controller\ResultEncoder\ResultEncoderInterface;
1717
use Exception;
1818
use Interop\Container\ContainerInterface;
19+
use InvalidArgumentException;
1920
use Psr\Http\Message\ResponseInterface;
2021
use Psr\Http\Message\ServerRequestInterface;
2122
use Psr\Log\LoggerInterface;
23+
use RuntimeException;
2224

2325
/**
2426
* @package ActiveCollab\Controller
@@ -32,14 +34,31 @@ abstract class Controller implements ContainerAccessInterface, ControllerInterfa
3234
*/
3335
private $result_encoder;
3436

37+
/**
38+
* @var LoggerInterface
39+
*/
40+
private $logger;
41+
42+
/**
43+
* @var string
44+
*/
45+
private $action_exception_message = 'Whoops, something went wrong...';
46+
47+
/**
48+
* @var string
49+
*/
50+
private $log_exception_message = 'Controller action aborted with an exception';
51+
3552
/**
3653
* @param ContainerInterface $container
3754
* @param ResultEncoderInterface $result_encoder
55+
* @param LoggerInterface|null $logger
3856
*/
39-
public function __construct(ContainerInterface &$container, ResultEncoderInterface &$result_encoder)
57+
public function __construct(ContainerInterface &$container, ResultEncoderInterface &$result_encoder, LoggerInterface $logger = null)
4058
{
4159
$this->setContainer($container);
4260
$this->setResultEncoder($result_encoder);
61+
$this->setLogger($logger);
4362
}
4463

4564
/**
@@ -60,6 +79,68 @@ public function &setResultEncoder(ResultEncoderInterface $result_encoder)
6079
return $this;
6180
}
6281

82+
/**
83+
* {@inheritdoc}
84+
*/
85+
public function getLogger()
86+
{
87+
return $this->logger;
88+
}
89+
90+
/**
91+
* {@inheritdoc}
92+
*/
93+
public function &setLogger(LoggerInterface $logger = null)
94+
{
95+
$this->logger = $logger;
96+
97+
return $this;
98+
}
99+
100+
/**
101+
* {@inheritdoc}
102+
*/
103+
public function getClientFacingExceptionMessage()
104+
{
105+
return $this->action_exception_message;
106+
}
107+
108+
/**
109+
* {@inheritdoc}
110+
*/
111+
public function &setClientFacingExceptionMessage($message)
112+
{
113+
if (empty($message)) {
114+
throw new InvalidArgumentException("Client facing exception message can't be empty");
115+
}
116+
117+
$this->action_exception_message = $message;
118+
119+
return $this;
120+
}
121+
122+
/**
123+
* {@inheritdoc}
124+
*/
125+
public function getLogExceptionMessage()
126+
{
127+
return $this->log_exception_message;
128+
}
129+
130+
/**
131+
* {@inheritdoc}
132+
*/
133+
public function &setLogExceptionMessage($message)
134+
{
135+
if (empty($message)) {
136+
throw new InvalidArgumentException("Log exception message can't be empty");
137+
}
138+
139+
$this->log_exception_message = $message;
140+
141+
return $this;
142+
}
143+
63144
/**
64145
* Run before every action.
65146
*
@@ -114,13 +195,17 @@ public function __invoke(ServerRequestInterface $request, ResponseInterface $res
114195
try {
115196
return $this->getResultEncoder()->encode(call_user_func([&$this, $action], $request, $arguments), $request, $response);
116197
} catch (Exception $e) {
117-
if ($this->logger instanceof LoggerInterface) {
118-
$this->logger->warning("Exception is caught with message {$e->getMessage()}", [
119-
'exception' => $e,
120-
]);
198+
if ($this->logger) {
199+
$this->logger->error($this->getLogExceptionMessage(), ['exception' => $e]);
200+
}
201+
202+
$exception_message = $this->action_exception_message;
203+
204+
if (strpos($exception_message, '{message}') !== false) {
205+
$exception_message = str_replace('{message}', $e->getMessage(), $exception_message);
121206
}
122207

123-
return $this->getResultEncoder()->encode($e, $request, $response);
208+
return $this->getResultEncoder()->encode(new RuntimeException($exception_message, 0, $e), $request, $response);
124209
}
125210
}
126211
} else {

src/ControllerInterface.php

+50
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace ActiveCollab\Controller;
1010

1111
use ActiveCollab\Controller\ResultEncoder\ResultEncoderInterface;
12+
use Psr\Log\LoggerInterface;
1213

1314
/**
1415
* @package ActiveCollab\Controller
@@ -30,6 +31,55 @@ public function getResultEncoder();
3031
*/
3132
public function &setResultEncoder(ResultEncoderInterface $result_encoder);
3233

34+
/**
35+
* Return logger.
36+
*
37+
* @return LoggerInterface|null
38+
*/
39+
public function getLogger();
40+
41+
/**
42+
* Set logger.
43+
*
44+
* @param LoggerInterface|null $logger
45+
* @return $this
46+
*/
47+
public function &setLogger(LoggerInterface $logger = null);
48+
49+
/**
50+
* Return message that is returned as 500 error when action breaks due to an exception.
51+
*
52+
* @return string
53+
*/
54+
public function getClientFacingExceptionMessage();
55+
56+
/**
57+
* Set message that is returned as 500 error when action breaks due to exception.
58+
*
59+
* If you wish to include actual exception's message, add {message} to the test. Example:
60+
*
61+
* $controller->setActionExceptionMessage('Failed because {message}');
62+
*
63+
* @param string $message
64+
* @return $this
65+
*/
66+
public function &setClientFacingExceptionMessage($message);
67+
68+
/**
69+
* Return message that will be logged if contraoller action fails due to an exception.
70+
*
71+
* @return string
72+
*/
73+
public function getLogExceptionMessage();
74+
75+
/**
76+
* Set message that will be logged if contraoller action fails due to an exception.
77+
*
78+
* @param string $message
79+
* @return $this
80+
*/
81+
public function &setLogExceptionMessage($message);
82+
3383
/**
3484
* Return controller name, without namespace.
3585
*

0 commit comments

Comments
 (0)