leftclickben
leftclickben

Reputation: 4614

What is the benefit of passing a callback to ob_start compared to just processing the result of ob_get_clean()?

I am wondering if there is any real benefit to using this...

function getSomeContent() {
    ob_start(function($content) {
        // ... modify content ...
        return $content;
    }
    // ... output stuff ...
    return ob_get_clean();
}

...as opposed to this...

function getSomeContent() {
    ob_start();
    // ... output stuff ...
    $result = ob_get_clean();
    // ... modify content ...
    return $result;
}

...?

Assume the "output stuff" and "modify content" parts are the same in each case. The key point is that the "modify content" has changed its location, being in a callback in the first case, and being "inline" in the second case.

Is there a performance benefit of one over the other? For example, does the second form make two copies of the buffer contents when the first uses only one? Or is it purely a coding style decision? Why would you choose one form over the other?

I can see there are differences in scope access, because any variables in the enclosing scope will be available in the "modify content" part of the second example, where they would have to be "passed in" with a use clause in the first example. In fact this is exactly why I would normally choose the second form.

Upvotes: 2

Views: 2963

Answers (2)

leftclickben
leftclickben

Reputation: 4614

Just to clarify Ninsuo's correct answer a bit.

My two code samples actually do not produce the same result. In fact, using the callback in combination with ob_get_clean() is completely useless.

This is because the callback is applied when cleaning or flushing the buffer.

However ob_get_clean() retrieves the contents first, and then cleans the buffer. Which means that the contents returned are not the result returned by the callback, but the input passed to the callback.

I wrote these two simple (and hacky) scripts to demonstrate.

This one uses ob_get_clean() and does not produce the correct result:

// Tests the use of callbacks with ob_get_clean().
class TestWithGetClean {
    // This variable is set when obcallback is called.
    public $foo = null;

    // The output buffer callback.
    public function obcallback($value) { 
        $this->foo = 'set';
        return $value . '(modified)'; 
    }

    // Main method.
    public function run() {
        ob_start(array($this, 'obcallback'));
        echo 'this is the output', PHP_EOL;
        return ob_get_clean();
    }
}

// Run the test with ob_get_clean().
$t = new TestWithGetClean();
echo $t->run(); // This method returns a value in this test. (But not the correct value!)
echo $t->foo, PHP_EOL;

The output from running this is:

this is the output

set

The text '(modified)' does not appear anywhere. Note however that the instance variable $foo is set, so the callback is definitely called, however the output is not as I originally expected.

Compare to this one which uses ob_end_flush():

// Tests the use of callbacks with ob_end_flush().
class TestWithEndFlush {
    // This variable is set when obcallback is called.
    public $foo = null;
    
    // The output buffer callback.
    public function obcallback($value) { 
        $this->foo = 'set';
        return $value . '(modified)' . PHP_EOL; 
    }

    // Main method.
    public function run() {
        ob_start(array($this, 'obcallback'));
        echo 'this is the output', PHP_EOL;
        ob_end_flush();
    }
}

// Run the test with ob_end_flush().
$t2 = new TestWithEndFlush();
$t2->run(); // This method produces output in this test.
echo $t2->foo, PHP_EOL;

This one produces the following output:

this is the output

(modified)

set

However, this is of course not as useful because the output goes directly to the client, so we cannot further manipulate the result. (For example, wrapping the text in a Symfony HttpFoundation Component Request object).

Upvotes: 1

Alain
Alain

Reputation: 36954

Now your code is clear yes, in your first samples you given a case where you used $result twice (that wasn't a good idea).

My main idea is : you call ob_start with a callback only if you do not need to use your $result in your current scope. Your first example becomes :

ob_start(function($content) {
    // ... modify content ...
    return $content;
}
// ... output stuff ...
ob_end_clean();

In this case, the job with $result is made in a new scope and this can make your code cleaner (example: you call ob_start(array($this, 'method'));), and you don't need to unset your $result to free it from your main scope at the end of your job (I assume you're doing something else of course).

Upvotes: 2

Related Questions