ob_get_clean returns empty string, ob_get_flush outputs string

I am utilizing a template-type system, to filter the_content. My filter contains this partial code:

include_once ( self::$dir . 'views/templates/' . $post_type . '/' . $display . '.php' );
$contents = ob_get_clean();
return $contents;

For some reason, this works locally but not a test server (WP Engine – nginx). I have verified that the file is indeed being included.

On the test server, $contents is actually an empty string (I var_dump’ed it to be sure, it’s string(0)"").

What is bizarre is that I replaced the above code with:

include_once ( self::$dir . 'views/templates/' . $post_type . '/' . $display . '.php' );

just for fun. This code outputs the object buffered template page to the screen. I cannot use ob_flush because I need to return the object buffer. Outputting the buffer causes it to display in the wrong place, obviously because I need to return the string from the filter.

Why does ob_get_clean() return an empty string while ob_end_flush() outputs the correct buffer content to the page?

After some thinking, I switched the include_once to plain ‘ol include.
This had the effect of causing the server to generate a 502 error.

So…I’m now thinking that for some reason my filter is causing some type of loop or something be being called multiple times? I’m honestly not sure. I researched what others in my situation have done, and my code looks practically identical to other methods of using a custom template system.

An important note: When the callback is called via a shortcode, it behaves as expected.

The whole point of this is using custom meta data/page content layout for single & archive displays of CPT’s in a theme-independent way. The shortcode is used for the archive-type display (I say archive-type because it’s a manually induced archive, not a typical archive-{cpt}.php). I went with a shortcode for the ‘archive’ pages so that the site owner could have control over the permalink, title, and overall content of the archive page. I went with a filter on the_content so it can be theme-independent as possible.

Full filter code:

add_filter( 'the_content', array( $this, 'filter_content' ) );
function filter_content($content) {

    global $post;

    if( !$this->we_belong_here() ) {
    //does post type check

        return $content;


    $display = 'archive';

    if( is_single( $post ) ) {

        $display = 'single';


    return $this->shortcode( array( 'post_type' => $post->post_type, 'display' => $display, 'content' => $content, 'method' => 'class' ) );


function shortcode($atts) {

    extract( shortcode_atts( array(

        'post_type' => '',

        'display' => 'archive',

        'method' => 'shortcode',

        'content' => ''

    ), $atts ) );


    include ( self::$dir . 'views/templates/' . $post_type . '/' . $display .'.php' );

    $contents = ob_get_clean();

    if($contents) return $contents; else return $content;


Solutions Collecting From Web of "ob_get_clean returns empty string, ob_get_flush outputs string"

The reason my code was failing was due to it reaching a memory limit.

I was adding a filter to the_content, and was calling another plugin’s API (Custom Field Suite) to get some meta data that was of the WYSIWYG type. This type of field runs through the_content filter, so I was hitting an infinite loop.

I couldn’t find this out on WP Engine because they have very limited error logging.

I ended up testing the site on a MediaTemple server, which resulted in the correct error message being displayed.

To fix this, I removed the filter from the_content BEFORE I called the meta data getter, then added it back after.