How to trace call to output buffer

1k Views Asked by At

So the scenario is that I have an application that uses output buffering, and the application is returning some extra data in addition to the expected results. I can manipulate the point where the expected results are being added to the output buffer to confirm that at this point in the application, the data being sent to the output buffer is correct, thus the unexpected extra data must be coming from another source.

I suspect that the issue is a stray character that is not inside the PHP script tags, but I'm having no luck figuring out which (if any) files is the culprit. For all I know, there could be some file that is being included that is actually doing an explicit echo of the extra data.

So I was hoping to capture the filename and line number of any file that writes to the output buffer, but this is proving much more difficult than I expected. I know where the initial ob_start is, so I've been trying to use a custom output_callback. Here are a few things I've tried already:

ob_start(function($string) { 
    return __FILE__ . ":" . __LINE__ . " : " $string; 
});

This returns the filename and line number where the function is defined, not where it was called from (as expected, I guess, but off to a bad start).

ob_start(function($string) { 
    return print_r(debug_print_backtrace(), true) . " : " $string; 
});

This throws an error about destroying a lambda function.

ob_start(function($string) { 
    return var_dump(debug_backtrace()) . " : " $string; 
});

This had mixed results. I will admit I'm not totally sure why. But in most cases the var_dump resolved to an empty string (nothing), but in one case, it seemed to generate some trace array, but didn't have anything reflecting the origin of the ob_start call (in other words, whatever it output, the source file that actually called the echo wasn't part of the trace).

Keep in mind that with the above, I have been doing some pretty basic testing just to figure out what the custom output_callback function would look like for when I'm ready to drop it into a troubleshooting context. So the issue isn't application-specific, I'm just trying to find a generic way to intercept output functions (echo, print, etc) and get information on where that output call originated.

2

There are 2 best solutions below

9
On

Possible Solutions

1

If you your code is using output buffering to spit out that stray character, the below solution can help. You are also right, that it will only work when the output buffering is flushed/retrieved, not at every print/echo statement. Also, all the native functions do not work in the call back function and print_r is one of them, but we can throw Exception. Please try the below code and see if it helps you in resolving your issue.

ob_start(function($buffer) {
    try {
        $stackTrace = debug_print_backtrace();
        throw new \Exception($stackTrace, 1);
    } catch (\Exception $ex) {
        //This line can be logged to a file instead of appending to the buffer
        $buffer .= PHP_EOL.$ex->getTraceAsString().PHP_EOL.PHP_EOL;
    }
    return $buffer;
});

2

If the first solution does not help resolve your issue, you might want to use advanced debugging and profiling tools like Xdebug

3

If you really don't care about the content that is already being echoed out before the current line or if you want to future proof that the similar issue does not happen in future you can just flush out the buffer using ob_end_clean before your ob_start.

ob_end_clean();
ob_start();

4

Finding all PHP white space after closing tag:

pcregrep -rMl '\?>[\s\n]+\z' *

OR

pcregrep -rM '\?>[\s]+[^\S]*$' *.php
0
On

Use step-through debugging in PhpStorm and enter ob_get_contents() in the PhpStorm console to see if anything is in the output. Use a "binary search" pattern, testing around the halfway point of your "main" function, etc. Then repeat the process inside any function called from "main" that produces that output, until you find it.

Only took me 10-15 minutes to find the offending line this way, after wasting much more time before.