ZF-8419: _isPartialRendering in Zend_Form_Element can break Zend_Form_Element_File::render()

Description

We have just updated to 1.9.6 to find that our file upload forms no longer work.

This is due to a bug that was introduced when ZF-7404 was fixed.

This causes the render() method of Zend_Form_Element_File to be called from renderViewHelper() and renderErrors(). Unfortunately, Zend_Form_Element_File::render() throws an exception, as there are no file decorators.

We have worked around the problem locally by returning early from Zend_Form_Element_File::render() if $this->_isPartialRendering is true, but this doesn't seem ideal. Instead of _isPartialRendering, I would recommend that Zend_Form_Element introduces partialRender(), with an empty implementation by default. Zend_Form_Element_Hash could then implement partialRender to fix ZF-7404.

Comments

The introduction of _isPartialRendering causes Zend_Form_Element_File::render() to be called from Zend_Form_Element_File::renderViewHelper(), which raises an exception when the form element has no decorators. This breaks the file upload form on our site.

A better solution would be to implement Zend_Form_Element::partialRender(), with an empty default implementation, and for Zend_Form_Element_Hash to implement this to fix ZF-7404.

You could add the needed decorator 'File' to the file element. It seems that you are eighter overwriting the default decorators or deleted them.

Actually the exception was added because many people tried to render the file element and wondered why things don't work without this decorator. This change has been made around ZF 1.7.

Yes, we considered adding a decorator to suppress the exception. We've been using the file element in forms without an issue until 1.9.6; it seems that render() wasn't being called until the fix to ZF-7404 was introduced? At least, we never hit the assertion in the past.

We're adding the element with $this->addElement( 'file' ), which doesn't add the File decorator, but we've never hit the exception until now. I'm guessing that we're doing a bunch of non-standard stuff that works fine for us without the decorator being there?

According to documentation there are some ways how this could occur:

  • using setDisableLoadDefaultDecorators(true)
  • extending the file element
  • using removeDecorator() on the file element
  • using setDecorator() without giving the file decorator
  • using clearDecorators()

Note that this exception is raised when no "file" decorator is given. It is raised as soon as the element should be rendered/displayed within your view.

There is no way to have no exception when the decorator is missing from the element.

Note that you also did not state from which release you updated to 1.9.6. This could also be a usefull information for those trying to reproduce this bug.

We upgraded from 1.9.5, and have deployed every Zend upgrade since 1.7, which is the version we started development with.

I understand that there is no way that the exception could not be raised if the decorator is missing, provided that Zend_Form_Element_File::render() gets called. The fact that we've never seen it before therefore seems to be because Zend_Form_Element_File::render() wasn't being called before 1.9.6 with our codebase.

I'm guessing that this is because we're not actually rendering the form in it's entirity; our view just renders the individual decorators that we're interested in. With 1.9.6, rendering ANY decorator will cause the exception to be thrown, whereas I'm guessing in 1.9.5 and previous it was only thrown when rendering the form as a whole? I'm not sure why we're using this pattern. It's along the lines of what is described here: http://weierophinney.net/matthew/archives/…

We have an image upload form with a view that calls $this->form->file->renderViewHelper() and $this->form->file->renderErrors(), where $this->form->file is a Zend_Form_Element_File instance (we don't extend it; we just create it with addElement( 'file' )).

ViewHelper does not support decoration of the file element.

I think you should call renderFile() on the file element rather than renderViewHelper().

Thomas is correct. When you call render*(), __call() intercepts the method call and dispatches the decorator specified. Since the file form elements do not use ViewHelper, but instead use the File decorator, the appropriate method call would be renderFile(), not renderViewHelper().

Thanks - although this really only addresses my misunderstanding of renderViewHelper(). I guess my original report was querying the change that means that calling render*() for any decorator on any form element will call render() on the form element itself. I was worried that this was not acceptable behaviour, and may be unsafe in some circumstances.