ZF-5642: Zend_Form_Decorator_Errors passes invalid $errors array to formErrors when form has _elementsBelongTo set and is more than one level deep

Description

When a sub-form has _elementsBelongTo set and utilizes the "Errors" decorator, it will throw warnings when attempting to render the errors. This is based on the following occurring:

Zend_Form_Decorator_Errors::render() calls the getMessages() on form.

When there are no messages for Zend_Form::getMessages() to process, it falls through to the last case which calls the Zend_Form::_attachToArray() function attempts to put the $messages value on to the end of an array based on Zend_Form::getElementsBelongTo() function. As we have explicitly set that value, Zend_Form::getMessages() returns an array representation of the Zend_Form::getElementsBelongTo() value.

Zend_Form_Decorator_Errors::render() then passes the array returned by Zend_Form::getMessages() to Zend_View::__call() by calling formErrors() on the view object.

Zend_View::__call() then calls Zend_View_Helper_FormErrors::formErrors() passing the array returned by Zend_Form::getMessages().

If the "escape" parameter for the helper is not set to false and the array passed to Zend_View_Helper_FormErrors::formErrors() is more than one level deep, a warning is generated when it attempts to escape the array returned in the foreach statement.

When attempting to render the array passed in as the message, the implode statement will also generate a warning as the second level of the $errors array is an array that it implode is attempting to concatenate.

I have verified that this issue occurs in all release tags from 1.6.1 and newer.

Comments

Does this cause an error on line 78 of the FormErrors View Helper (foreach loop expects array)?

Actually it occurs from line 75 of FormErrors. I have attached the stack return by Xdebug for the warning:

Call Stack

Time Memory Function Location

1 0.0002 73396 {main}( ) ../index.php:0 2 0.0942 5435712 Zend_Controller_Front->dispatch( ) ../index.php:42 3 0.1014 5914980 Zend_Controller_Dispatcher_Standard->dispatch( ) ../Front.php:946 4 0.1122 6659440 Zend_Controller_Action->dispatch( ) ../Standard.php:293 5 0.1574 7067144 Zend_Controller_Action_HelperBroker->notifyPostDispatch( ) ../Action.php:512 6 0.1575 7067144 Zend_Controller_Action_Helper_ViewRenderer->postDispatch( ) ../HelperBroker.php:171 7 0.1575 7067144 Zend_Controller_Action_Helper_ViewRenderer->render( ) ../ViewRenderer.php:981 8 0.1582 7067620 Zend_Controller_Action_Helper_ViewRenderer->renderScript( ) ../ViewRenderer.php:942 9 0.1582 7067620 Zend_View_Abstract->render( ) ../ViewRenderer.php:921 10 0.1582 7108888 Zend_View->_run( ) ../Abstract.php:787 11 0.1583 7110196 include( '/virtualhosts/bbxadmin/application/default/views/scripts/rule/form.ajax.phtml' ) ../View.php:107 12 0.1583 7110196 Zend_Form->__toString( ) ../Form.php:0 13 0.1583 7110196 Zend_Form->render( ) ../Form.php:2611 14 0.1930 7259720 Zend_Form_Decorator_Errors->render( ) ../Form.php:2596 15 0.1932 7259720 Zend_View->formErrors( ) ../Errors.php:60 16 0.1932 7259720 Zend_View_Abstract->__call( ) ../Abstract.php:0 17 0.1940 7277072 call_user_func_array ( ) ../Abstract.php:318 18 0.1940 7277072 Zend_View_Helper_FormErrors->formErrors( ) ../Abstract.php:0 19 0.1940 7277388 Zend_View_Abstract->escape( ) ../FormErrors.php:75 20 0.1940 7277388 call_user_func ( ) ../Abstract.php:804

It's still here in 1.9.2 !

Code to reproduce:


$form = new Zend_Form():
$form->addElement($form->createElement('text','foo'));
$form->setIsArray(true);
$form->setElementsBelongTo('arraykey');
$form->addDecorator('Errors');
$form->render();

This reproduce in Zend_Form_SubForm too. repro:


$subForm = new Zend_Form_SubForm;
$subForm->setView(new Zend_View);
$subForm->clearDecorators();
$subForm->addDecorator('Errors');
echo $subForm;

It causes


Warning: htmlspecialchars() expects parameter 1 to be string,
 array given in Zend\View\Abstract.php on line 850

This can be solved with $errors to be flattened. Or View_Helper_FormErrors should be accept hierarchical errors. This is sample of flat.


Index: Form/Decorator/Errors.php
===================================================================
--- Form/Decorator/Errors.php   (revision 18722)
+++ Form/Decorator/Errors.php   (working copy)
@@ -57,6 +57,19 @@
 
         $separator = $this->getSeparator();
         $placement = $this->getPlacement();
+
+        // ZF-5642
+        $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($errors));
+        foreach ($iterator as $key => $error) {
+            $flat[$key] = $error;
+        }
+        if (!isset($flat)) {
+            return $content;
+        }
+        else {
+            $errors = $flat;
+        }
+
         $errors    = $view->formErrors($errors, $this->getOptions());
 
         switch ($placement) {

Bulk change of all issues last updated before 1st January 2010 as "Won't Fix".

Feel free to re-open and provide a patch if you want to fix this issue.

And this is why we don;t use Zend Framework anymore. It took you nearly 4 years to tell me go F myself!