Skip to content

Commit 2f7dc12

Browse files
authored
Merge pull request #31 from RyanNerd/errorHandler-outputBuffering
Default config passes in the outputBuffering setting to the `errorHandler` constructor
2 parents 36cbda8 + b199a92 commit 2f7dc12

File tree

2 files changed

+122
-2
lines changed

2 files changed

+122
-2
lines changed

src/config.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939
->method('setCacheFile', DI\get('settings.routerCacheFile')),
4040
Slim\Router::class => DI\get('router'),
4141
'errorHandler' => DI\object(Slim\Handlers\Error::class)
42-
->constructor(DI\get('settings.displayErrorDetails')),
42+
->constructor(DI\get('settings.displayErrorDetails'), DI\get('settings.outputBuffering')),
4343
'phpErrorHandler' => DI\object(Slim\Handlers\PhpError::class)
44-
->constructor(DI\get('settings.displayErrorDetails')),
44+
->constructor(DI\get('settings.displayErrorDetails'), DI\get('settings.outputBuffering')),
4545
'notFoundHandler' => DI\object(Slim\Handlers\NotFound::class),
4646
'notAllowedHandler' => DI\object(Slim\Handlers\NotAllowed::class),
4747
'environment' => function () {

tests/ErrorTest.php

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
namespace DI\Bridge\Slim\Test;
4+
5+
use DI\Bridge\Slim\App;
6+
use DI\ContainerBuilder;
7+
use Slim\Handlers\Error;
8+
use PHPUnit\Framework\TestCase;
9+
use DI\Bridge\Slim\Test\Mock\RequestFactory;
10+
use Slim\Http\Response;
11+
use Throwable;
12+
13+
class ErrorTest extends TestCase
14+
{
15+
/**
16+
* Test default errorHandler
17+
*
18+
* @test
19+
*/
20+
public function default_exception_handling()
21+
{
22+
// Send error_log output to a temp file.
23+
$logFile = tempnam(sys_get_temp_dir(), 'slim-bridge');
24+
ini_set('error_log', $logFile);
25+
26+
$app = new App;
27+
$c = $app->getContainer();
28+
29+
// Sanity check - default for displayErrorDetails should be false
30+
$displayErrorDetails = $c->get('settings.displayErrorDetails');
31+
$this->assertFalse($displayErrorDetails);
32+
33+
/** @var Error $error */
34+
$error = $c->get('errorHandler');
35+
$response = $error(RequestFactory::create('/'), new Response(), new TestException());
36+
$reasonPhrase = $response->getReasonPhrase();
37+
$this->assertEquals('Internal Server Error', $reasonPhrase);
38+
39+
$log = file_get_contents($logFile);
40+
$this->assertNotEmpty($log);
41+
}
42+
43+
/**
44+
* Test custom errorHandler
45+
*
46+
* @test
47+
*/
48+
public function custom_exception_handling()
49+
{
50+
// Send error_log output to a temp file.
51+
$logFile = tempnam(sys_get_temp_dir(), 'slim-bridge');
52+
ini_set('error_log', $logFile);
53+
54+
$app = new BridgeApp(
55+
[
56+
'settings.displayErrorDetails' => true,
57+
'settings.outputBuffering' => 'append'
58+
]);
59+
$c = $app->getContainer();
60+
61+
// Sanity checks
62+
$displayErrorDetails = $c->get('settings.displayErrorDetails');
63+
$this->assertTrue($displayErrorDetails);
64+
$outputBuffering = $c->get('settings.outputBuffering');
65+
$this->assertEquals('append', $outputBuffering);
66+
67+
/** @var Error $error */
68+
$error = $c->get('errorHandler');
69+
70+
$response = $error(RequestFactory::create('/'), new Response(), new TestException());
71+
$reasonPhrase = $response->getReasonPhrase();
72+
$this->assertEquals('Internal Server Error', $reasonPhrase);
73+
74+
$log = file_get_contents($logFile);
75+
$this->assertEmpty($log);
76+
}
77+
}
78+
79+
/**
80+
* Class TestException
81+
*
82+
* Test Exception that starts its own output buffer
83+
*/
84+
class TestException extends \Exception
85+
{
86+
public function __construct($message = "", $code = 0, Throwable $previous = null)
87+
{
88+
parent::__construct($message, $code, $previous);
89+
90+
// the Slim Error handler calls `ob_get_clean()` regardless of any outputBuffering setting:
91+
// 'preappend' => $body->write(ob_get_clean() . $output);
92+
// 'append' => $body->write($output . ob_get_clean());
93+
// false || anything else => ob_get_clean(); $body->write($output);
94+
//
95+
// We start our own OB for testing to keep the OB stack clean
96+
ob_start();
97+
}
98+
}
99+
100+
/**
101+
* Class BridgeApp
102+
*
103+
* Override the configuration via the configureContainer() hook.
104+
*/
105+
class BridgeApp extends App
106+
{
107+
protected $config = [];
108+
109+
public function __construct(array $config)
110+
{
111+
$this->config = $config;
112+
113+
parent::__construct();
114+
}
115+
116+
public function configureContainer(ContainerBuilder $builder)
117+
{
118+
$builder->addDefinitions($this->config);
119+
}
120+
}

0 commit comments

Comments
 (0)