Skip to content

Commit a239fa5

Browse files
committed
Debugger: removes output buffer for Bar, Bluescreen and production error. It decides whether clean or flush output buffers.
- ob_start() without callback & chunk-size is usually used to capture output to string -> clean it - ob_start() with callback and/or chunk-size is usually used to preprocess (gzip) or branch off (bluescreen) output -> flush it zlib.output_compression and ob_gzhandler are unremovable in PHP >= 5.4 only after first chunk was sent
1 parent 5d5b6d7 commit a239fa5

9 files changed

Lines changed: 30 additions & 12 deletions

src/Tracy/Debugger.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class Debugger
3838
/** @var string reserved memory; also prevents double rendering */
3939
private static $reserved;
4040

41+
/** @var int initial output buffer level */
42+
private static $obLevel;
43+
4144
/********************* errors and exceptions reporting ****************d*g**/
4245

4346
/** @var bool|int determines whether any error will cause immediate death; if integer that it's matched against error severity */
@@ -129,6 +132,7 @@ public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL)
129132
{
130133
self::$reserved = str_repeat('t', 3e5);
131134
self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(TRUE);
135+
self::$obLevel = ob_get_level();
132136
error_reporting(E_ALL);
133137

134138
if ($mode !== NULL || self::$productionMode === NULL) {
@@ -203,6 +207,7 @@ public static function shutdownHandler()
203207

204208
} elseif (self::$showBar && !connection_aborted() && !self::$productionMode && self::isHtmlMode()) {
205209
self::$reserved = NULL;
210+
self::removeOutputBuffers();
206211
self::getBar()->render();
207212
}
208213
}
@@ -231,6 +236,7 @@ public static function exceptionHandler($exception, $exit = TRUE)
231236
}
232237

233238
Helpers::improveException($exception);
239+
self::removeOutputBuffers();
234240

235241
if (self::$productionMode) {
236242
try {
@@ -339,10 +345,6 @@ public static function errorHandler($severity, $message, $file, $line, $context)
339345
$e = new ErrorException($message, 0, $severity, $file, $line);
340346
$e->context = $context;
341347
$e->skippable = TRUE;
342-
do {
343-
$level = ob_get_level();
344-
@ob_end_clean(); // @ may not exist or is not removable
345-
} while ($level !== ob_get_level());
346348
self::exceptionHandler($e);
347349
}
348350

@@ -375,6 +377,21 @@ private static function isHtmlMode()
375377
}
376378

377379

380+
private static function removeOutputBuffers()
381+
{
382+
while (ob_get_level() > self::$obLevel) {
383+
$status = ob_get_status();
384+
if (in_array($status['name'], ['ob_gzhandler', 'zlib output compression'])) {
385+
break;
386+
}
387+
$fnc = $status['chunk_size'] ? 'ob_end_flush' : 'ob_end_clean';
388+
if (!@$fnc()) { // @ may be not removable
389+
break;
390+
}
391+
}
392+
}
393+
394+
378395
/********************* services ****************d*g**/
379396

380397

tests/Tracy/Debugger.E_COMPILE_ERROR.console.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ require __DIR__ . '/../bootstrap.php';
1717
Debugger::$productionMode = FALSE;
1818
header('Content-Type: text/plain');
1919

20+
ob_start();
2021
Debugger::enable();
2122

2223
$onFatalErrorCalled = FALSE;
@@ -47,7 +48,6 @@ Unable to log error: Directory is not specified.
4748
Debugger::$onFatalError[] = function () use (& $onFatalErrorCalled) {
4849
$onFatalErrorCalled = TRUE;
4950
};
50-
ob_start();
5151

5252

5353
function first($arg1, $arg2)

tests/Tracy/Debugger.E_ERROR.console.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ require __DIR__ . '/../bootstrap.php';
1717
Debugger::$productionMode = FALSE;
1818
header('Content-Type: text/plain');
1919

20+
ob_start();
2021
Debugger::enable();
2122

2223
$onFatalErrorCalled = FALSE;
@@ -55,7 +56,6 @@ Unable to log error: Directory is not specified.
5556
Debugger::$onFatalError[] = function () use (& $onFatalErrorCalled) {
5657
$onFatalErrorCalled = TRUE;
5758
};
58-
ob_start();
5959

6060

6161
function first($arg1, $arg2)

tests/Tracy/Debugger.E_ERROR.html.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ if (PHP_SAPI === 'cli') {
2121
Debugger::$productionMode = FALSE;
2222
header('Content-Type: text/html');
2323

24+
ob_start();
2425
Debugger::enable();
2526

2627

@@ -38,7 +39,7 @@ register_shutdown_function(function () use (& $onFatalErrorCalled) {
3839
Debugger::$onFatalError[] = function () use (& $onFatalErrorCalled) {
3940
$onFatalErrorCalled = TRUE;
4041
};
41-
ob_start();
42+
4243

4344

4445
function first($arg1, $arg2)

tests/Tracy/Debugger.barDump().phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ if (PHP_SAPI === 'cli') {
1919
Debugger::$productionMode = FALSE;
2020
header('Content-Type: text/html');
2121

22+
ob_start();
2223
Debugger::enable();
2324

2425
register_shutdown_function(function () {
2526
preg_match('#debug.innerHTML = (".*");#', ob_get_clean(), $m);
2627
Assert::matchFile(__DIR__ . '/Debugger.barDump().expect', json_decode($m[1]));
2728
echo 'OK!'; // prevents PHP bug #62725
2829
});
29-
ob_start();
3030

3131

3232
$arr = [10, 20.2, TRUE, FALSE, NULL, 'hello', ['key1' => 'val1', 'key2' => TRUE], (object) ['key1' => 'val1', 'key2' => TRUE]];

tests/Tracy/Debugger.barDump().showLocation.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Debugger::$productionMode = FALSE;
2020
Debugger::$showLocation = TRUE;
2121
header('Content-Type: text/html');
2222

23+
ob_start();
2324
Debugger::enable();
2425

2526
register_shutdown_function(function () {
@@ -38,7 +39,6 @@ EOD
3839
, json_decode($m[1]));
3940
echo 'OK!'; // prevents PHP bug #62725
4041
});
41-
ob_start();
4242

4343

4444
Debugger::barDump('value');

tests/Tracy/Debugger.logging.error.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Debugger::$logDirectory = TEMP_DIR;
2121

2222
Debugger::getLogger()->mailer = function () {};
2323

24+
ob_start();
2425
Debugger::enable(Debugger::PRODUCTION, NULL, 'admin@example.com');
2526

2627

@@ -29,7 +30,6 @@ register_shutdown_function(function () {
2930
Assert::true(is_file(Debugger::$logDirectory . '/email-sent'));
3031
echo 'OK!'; // prevents PHP bug #62725
3132
});
32-
ob_start();
3333

3434

3535
missing_function();

tests/Tracy/Debugger.warnings.console.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require __DIR__ . '/../bootstrap.php';
1414
Debugger::$productionMode = FALSE;
1515
header('Content-Type: text/plain');
1616

17+
ob_start();
1718
Debugger::enable();
1819

1920

@@ -38,7 +39,6 @@ function third($arg1)
3839
require 'E_COMPILE_WARNING.php'; // E_COMPILE_WARNING
3940
}
4041

41-
ob_start();
4242
first(10, 'any string');
4343
Assert::match("
4444
%a%: mktime(): You should be using the time() function instead in %a% on line %d%

tests/Tracy/Debugger.warnings.html.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ if (PHP_SAPI === 'cli') {
1919
Debugger::$productionMode = FALSE;
2020
header('Content-Type: text/html');
2121

22+
ob_start();
2223
Debugger::enable();
2324

2425
register_shutdown_function(function () {
@@ -47,7 +48,6 @@ Warning: Unsupported declare \'foo\' in %a% on line %d%%A%', $output);
4748
</div>%A%', json_decode($m[1]));
4849
echo 'OK!'; // prevents PHP bug #62725
4950
});
50-
ob_start();
5151

5252

5353
function first($arg1, $arg2)

0 commit comments

Comments
 (0)