Our blog

 

Flushing Zend Framework

I have recently been messing around trying to figure out the best way of running long processes within a Zend Framework App.

Usually I would code in regular flush(); commands to make sure that the browser didnt time out and also that the user can see that something is happening.

However, the standard MVC structure of the default ZF application system wraps the entire response in an output buffer which is only flushed once everything is done. This has lots of advantages including allowing to add extra headers (FirePHP) on the fly throughout the application process.

For long running scripts though, it is important for me to flush output. My solution to this is twofold.

Firstly set up an 'output' action in my base controller. The output action is looking for an 'act' parameter in its view script. This act param is then used to generate a url string based on the current controller and the action as the act param. This url string is then used to create an iframe which will be used to output the flushing results of the long running script.

For the long running script action itself, I am going to disable layout and also disable view render. Its really important to switch both of these off.

The following needs to be declared at the top of any action that you intend to be viewed via the output iframe.

PHP:
  1. // accessed via output action
  2.     public function longrunningAction(){
  3.         $this->_helper->layout->disableLayout();
  4.         $this->_helper->viewRenderer->setNoRender(true);
  5.         $this->_model->grabImages();
  6.     }

Now I am free to echo out basic HTML and also call flush from within my model. To flush the output you need to call both ob_flush() and then flush(). To make this easier I set up a static method in my static Tools class

PHP:
  1. public static function flush(){
  2.         ob_flush();
  3.         flush();
  4.     }

One issue I have had though is that any time the ZF internals try to add a header, you will get a basically meaningless exception telling you that headers have already been sent. It doesn't tell you what header was attempted to be added or why, so you can be left out in the cold a bit when trying to debug.

If anyone knows a good workaround for this, or can offer a better solution for handling these long running processes I would love to hear about it.

More...

More Reading:

3 Comments

Christoph
October 21st, 2009

Great info! This finally worked for me after hours
of trials and searching the documentation. Thanks!

 

admin
October 21st, 2009

cool :)

FYI, since writing this I have sorted out the headers issue.

Generally the long running actions are calling a particular model method. I do the flushing directly from the model method, but I wrap the whole thing in a try / catch block

If the try fails, I then call this method

PHP:
  1. public function displayException(Exception $e){
  2.         echo'ERROR: ' . $e->getMessage() . '';
  3.         EC_Tools::flush();
  4.         foreach($e->getTrace() as $t){
  5.             EC_Debug::dump($t);
  6.         }
  7.         EC_Debug::dump($e);
  8.         echo $this->_stopscroll;
  9.         die;
  10.     }

EC_Debug is just a subclassed version of Zend_Debug,

EC_Tools is my static tools class and features the flush method described in the article.

Note that by looping through the exception and dumping each step, you get a much better stack trace. Of course you need to make sure that this isn't displayed in production mode.

 

Chris
November 25th, 2011

Do you think this will also work with Magento? Chris.

 

 

Leave a Reply