The downside of Output Buffers

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
User avatar
evilmonkey
Forum Regular
Posts: 823
Joined: Sun Oct 06, 2002 1:24 pm
Location: Toronto, Canada

The downside of Output Buffers

Post by evilmonkey »

Hello,

I use output buffers (ob_start/ob_end_flush) to do various things on my website. For one thing, I am sometimes forced to use the header() function to redirect users to other pages (error pages and the like). I have headers, footers, (I use include_once), and I also use the smarty templating system. I use output buffers to suppress the "headers already sent" errors when I need to redirect. My question is, is there a downside to doing this? (unexpected behaviour, slow-downs, etc.)

Thanks.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Eating up memory, often needlessly.
User avatar
Buddha443556
Forum Regular
Posts: 873
Joined: Fri Mar 19, 2004 1:51 pm

Post by Buddha443556 »

On a redirect, that would be needlessly as output buffer is allocated 40KB. Including headers, footers and smarty is probably more costly though for just a redirect.
User avatar
evilmonkey
Forum Regular
Posts: 823
Joined: Sun Oct 06, 2002 1:24 pm
Location: Toronto, Canada

Post by evilmonkey »

Buddha443556 wrote:On a redirect, that would be needlessly as output buffer is allocated 40KB. Including headers, footers and smarty is probably more costly though for just a redirect.
No, but the user is only redirected if (for example) he is not logged in...or if there's an input error. If everything is fine, then a page is displayed.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

A better way to deal with this is to have a Response object that handles the final HTTP output and frees you code to focus on building and setting things. In it simplest form it is:

Code: Select all

class Response {
    var $_headers = array();
    var $_content = '';

    function setRedirect($location) {
        $this->addHeader("Location: $location");
    }

    function addHeader($string, $replace=true) {
        $this->_headers[$string] = $replace;
    }

    function addContent($content) {
        $this->_content .= $content;
    }

    function out() {
        if ($this->_headers) {
            foreach ($this->_headers as $string => $replace) {
                header($string, $replace);
            }
        } else {
            echo $this->_content;
        }
    }

}
I usually make this object available, along with a Request object, available via a Registry/Service Locator of some sort. The program adds content and/or sets headers as needed. At the very end of the main script you do a $response->out() and it sorts out what needs to be sent. It allows you code to be kept very modular, because the rendering parts can just do their job, but if there is some sort of error or access problem then just set the redirect and bail out.

Example usage:

Code: Select all

$response = new Response();

$response->addContent('Hello World!');
$response->setRedirect('other_page.php');

$response->out();
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

That's a pretty cool solution Christopher.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Came across this idea before and have been using it ever since in my own mini framework. Works very well - a lot better than having echo's and header() calls all over the place... Since its all in one object, it's easy to add in other possible manipulations like gzipping the output, or doing some post-processing.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Interesting... might have to expand on something I'm doing to include this kind of thing.
User avatar
Buddha443556
Forum Regular
Posts: 873
Joined: Fri Mar 19, 2004 1:51 pm

Post by Buddha443556 »

That Response object is pretty neat.
No, but the user is only redirected if (for example) he is not logged in...or if there's an input error. If everything is fine, then a page is displayed.
Obviously we do things differently. If my user isn't logged in then the page controller simple includes a different handler - no redirect. Input error, still no redirect, just an error message along with <FORM> in question. I actually try to avoid redirects because I don't want to load DB, sessions, templates and whatever unless they're needed.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

I use Output buffering for templating.... I do redirects etc in the controller before I load the template if I need to:

(Maybe my idea of what a "controller" should do is wrong)

Code: Select all

<?php

require_once('config.class.php');
require_once('includes/core_components.inc.php');

//The controller is part of
// a larger collection of objects including the
// core, database and config parts of the app

class controller extends myTpl
{
	var $moduleName = 'module_name'; /* Can be boolean false */
	
	function controller($tpl='some.tpl.php')
	{
		//Initilializes the objects below us
		$this->start();

		//You can leave the module name empty if the user is
		// only required to be logged in
		// This is where any redirect logic would be.  In fact, the authorize()
		// call sometimes forces a redirect or a "denied" template to be used
		$this->login->authorize($this->moduleName); /* Optional */
		
		//Load the template for this module
		parent::myTpl($this->conf->rootPath.'/templates/'.$this->conf->template.'/'.$tpl);
	}	

	function show()
	{
		/* 
		 Modify $this->tplVars here.
		 Example:
		 
		 $this->tplVars['FOO'] = 'Bar';
		 */
		
		$source = $this->render();
		$source = $this->addHeaders($source, array(
			'PAGE1' => $this->moduleName,
			'PAGE2' => $this->moduleName,
			'WINDOW TITLE' => $this->conf->appTitle
			/* 'NAV' => '' //This will remove the navigation bar from overall_header.tpl.php */
		));
		$source = $this->addFooters($source);
		return $source;
	}
}

$con =& new controller;
echo $con->show();

?>
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

MediaWiki, I know, uses a $wgOut object.
User avatar
evilmonkey
Forum Regular
Posts: 823
Joined: Sun Oct 06, 2002 1:24 pm
Location: Toronto, Canada

Post by evilmonkey »

Wow, so much OOP. I'm still stuck between straigh Procedural and OOP, I normally write functions and use sessions to move data around. I have a user class that holds all the data about a given user, but that's about it. Thanks for the input guys!
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Buddha443556 wrote:Obviously we do things differently. If my user isn't logged in then the page controller simple includes a different handler - no redirect. Input error, still no redirect, just an error message along with <FORM> in question. I actually try to avoid redirects because I don't want to load DB, sessions, templates and whatever unless they're needed.
Actually you almost exactly describe what I do as well. I really only redirect after forms are accepted to deal with back button issues. I forward (dispatch) errors rather than redirecting. So errors are really not that good of an example.

I really use a Response object to ease page building. The one I use also allows any child to add Scripts, Stylesheets, Meta tags, etc. That's probably the main reason for me -- any widget can add a stylesheet or some Javascript without needing to know anything about the detail of page building -- and it just works.
(#10850)
User avatar
onion2k
Jedi Mod
Posts: 5263
Joined: Tue Dec 21, 2004 5:03 pm
Location: usrlab.com

Post by onion2k »

What's the difference between using PHP's output buffer and that response object? I can't see any. Seems to just be reinventing something thats already built in.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

onion2k wrote:What's the difference between using PHP's output buffer and that response object? I can't see any. Seems to just be reinventing something thats already built in.
They really do two different things. Output buffering captures the output that would normally be sent to the screen so it can be dealt with like an string. The Response object uncouples the specifics of page building from your code. So you may capture an include() in an outbuffer that contains part of the content area of a page -- give it to the Respose object along with some Javascript that that content needs. The Response object puts it all together with the JS in the header and the content in the body.

I should note also that having heirarchical Views further simplfies this process -- the Response object renders a tree of View objects. It may seem like overkill for simple pages, but it really adds flexiblity to not have page-building spaghetti code on every page in larger projects. I find that hierarchical page building saves time in the long run over the traditional header/content/footer sequential page building.
(#10850)
Post Reply