Page 1 of 1
The downside of Output Buffers
Posted: Mon Mar 13, 2006 7:50 pm
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.
Posted: Mon Mar 13, 2006 8:00 pm
by feyd
Eating up memory, often needlessly.
Posted: Mon Mar 13, 2006 9:04 pm
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.
Posted: Mon Mar 13, 2006 9:31 pm
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.
Posted: Tue Mar 14, 2006 1:25 am
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();
Posted: Tue Mar 14, 2006 3:36 am
by matthijs
That's a pretty cool solution Christopher.
Posted: Tue Mar 14, 2006 5:14 am
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.
Posted: Tue Mar 14, 2006 6:20 am
by Chris Corbyn
Interesting... might have to expand on something I'm doing to include this kind of thing.
Posted: Tue Mar 14, 2006 6:31 am
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.
Posted: Tue Mar 14, 2006 6:50 am
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();
?>
Posted: Tue Mar 14, 2006 8:04 pm
by Ambush Commander
MediaWiki, I know, uses a $wgOut object.
Posted: Tue Mar 14, 2006 8:35 pm
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!
Posted: Wed Mar 15, 2006 12:49 am
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.
Posted: Wed Mar 15, 2006 2:52 am
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.
Posted: Wed Mar 15, 2006 11:48 am
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.