ZF-2808: Using $this->action to chain view content together causes unexpected content in body of response

Description

When using $this->action to call another controller to generate content from a view script and that controller action also has a $this->action call in its view script the output will not be what is expected as the second $this->action call' output will be found twice in the body of the response object. Here is example code to reproduce the problem.

============================

--- IndexController.php --- <?php require_once 'Zend/Controller/Action.php'; class IndexController extends Zend_Controller_Action { public function testAction() { } public function anotherAction() { } } ?>

--- TestController.php --- <?php require_once 'Zend/Controller/Action.php'; class TestController extends Zend_Controller_Action { public function indexAction() { } } ?>

--- views/scripts/index/test.phtml ---

Here is the start of our test

<?php echo $this->action("another", "index", "default"); ?>

--- views/scripts/index/another.phtml ---

Here is another file. We should only have on line above and one line below this one.

<?php echo $this->action("index", "test", "default"); ?>

--- views/scripts/test/index.phtml ---

This should only be displayed once.

=== WILL OUTPUT ===

Here is the start of our test

This should only be displayed once.

Here is another file. We should only have on line above and one line below this one.

This should only be displayed once.

=== BUT SHOULD OUTPUT ===

Here is the start of our test

Here is another file. We should only have on line above and one line below this one.

This should only be displayed once.

==========================

Comments

What action are you calling from the URL when you get the output? Without that crucial piece of information, it's hard to judge whether the expected output is what should really be expected.

I'm calling the IndexController / TestAction. URL - http://localhost/index/test

I performed some debugging today since I needed to get around/past this issue (and now that I know the Zend Framework a lot better). The problem is in the View Action Helper. In the action method the results are retrieved from the response object "$return = $this->response->getBody()". After this the response object is left alone. To test, I cleared the response object body and now the code example works as expected (as does the rest of my development site).

Hopefully, I'm not breaking something else but it seems to make logical sense that since we are looking to return the output of the "action" method to the caller and let them deal with how the output is displayed, we should clear our buffering response object.

Thanks,

Mike

Assigning to Ralph.

It seems that the logic for the Action Helper is a little screwy... I see how this differs from render(), but should we really ever need to echo the output from an action() call? Shouldn't the result of that action get appended to the Response body, if auto-rendering is enabled...? Could this be the culprit instead, that ViewRenderer is appending the results of the action to the body AND echo is appending to the output buffer... I may be misunderstanding the changes from 1.0 to 1.5, though. :D

For the record the issues was present in 1.0 and 1.5 (and now 1.5.1).

In response to the output being appended to the output buffer and then also echo'ed causing the issue; if you don't echo the output of the action call you don't get any output. To test further I modified the constructor of the Action View Helper to use the FrontController's Request, Response and Dispatch instances (not clones) and in this case (again without echo'ing the action response) you do get output but again not the expected output. Still using the same code given above with the same URL (http://localhost/index/test) the output I got was:

=================

This should only be displayed once.

Here is another file. We should only have on line above and one line below this one.

Here is the start of our test

=================

Not what you would want or expect. So, this would mean one of two things, (a) due to the nature of the "logic" an echo is required and the fix that I've stated above (i.e. clearing the output buffer) or (b) there is a bug somewhere in the output buffering logic that needs to be fixed to make this work different and therefore output the proper ordering of content without the echo.

In further review of the code, the underlying issue is the fact that we are dealing with a single instance of the Action View Helper (at least in my code because in my bootstrap I give a single View instance to the View Rendered helper). Because of this during the "action" method there is no code to contend with the fact that it "could" be called recursively. There is the resetObjects method that is called at the beginning of the method. This will clear the instances of the Response, Request and Dispatch objects that are being used. What isn't being taking into account is that during the dispatch call within the action method, the Action View Helper can be called again and while the resetObjects method of the 2nd (or 3rd... Nth) call will clear the required objects for the call present, these objects (i.e. Request, Response, Dispatch) will be left in a previous state when the dispatch from the 2nd (or 3rd ... Nth) call returns in the up stream call (i.e. 1st call). So upon return the objects should be put back into a proper state.

Given what I've stated above, while clearing the response output buffer fixes my issue that I'm currently having it doesn't fix the underlying issue of recursive calls clobbering the state of the Response, Request, Dispatch objects internal the Action View Helper instance. The better fix would be to either clone new instances for each entry into the action method at which point it wouldn't matter what was done to the objects OR saving the state of the objects at the start of the action method and returning the object state at the end of the method. I'm not sure what the penalty of cloning an object is compared to saving its state but cloning would seem easier.

I guess, that's my 2 cents. :-)

Scheduling for next mini release.

I seem to be having a similar issue with chaining and the ViewRenderer helper. I'm still digging into it but it appears that recursive calls can loose state and therefore drop "forward" calls.

My current issue is I have a content part of a layout that calls the view->action helper to instantiate a controller/action, this controller action performs a controller->_forward to another module/controller/action. When the controller that was called from the view->action returns (after processing through dispatch) the dispatch process that owns the original view->action call doesn't know about the controller->_forward and it is lost, therefore not output any content that was supposed to be there.

(If you are wondering why I'm going through such hoops, it is because I'm building reusable modules on top of the Zend Framework that an application can then call from within its own module infrastructure to process their own workflow.)

I believe I have a handle on the cause of the issue which appears to be very similar to the recursive issues I've found in the view->action helper, which I'm still working on the best way to fix. Once I have both I will comment back here.

Thanks,

Mike

After a lot of debugging it simply appears to be a variable scoping issue within the Action View Helper. Hopefully here is a better description of the call stack when I was having the issue (last described):

When rendering a view, the views script would call the Action view helper. During the Action call the controller that was called would forward to another controller. This would cause the original Action view helper call to complete at which point the forwarded controller should have run. But it didn't. When I reviewed the call stack and the variable contents of the objects involved I can see the proper controller information in the request object up until the return of the Action view helper.

It appears to me that because the Action view helper makes clones of the Request, Response and Dispatch objects of the Front controller the forward call (in my example) is getting lost in the call stack. By making the object non-clone it works as expected.

In thinking through it, it seems probable that this will cause other issues in regards to recursive calls to Action that act on other properties of the Request, Response and Dispatch objects. I haven't ran into this in my code yet but it does seem likely considering where this ticket started and my comments on April 11th.

Fixed by issue ZF-3456

Fixed in trunk at r10438 Fixed in 1.5.3 at r10440 Fixed in 1.6.0 at r10442