ZF-5412: Zend_Form_Element_MultiCheckBox::addError() array problem


If you try to add an error to a Zend_Form_Element_MultiCheckBox element an php warning will occur: {quote}Warning: Invalid argument supplied for foreach() in ........\library\Zend\Form\Element.php on line 2101{quote}

You can reproduce the error by executing this little snippet:

$element = new Zend_Form_Element_MultiCheckBox('someVar');
$element->addMultiOptions(array('hello', 'world'))
        ->setLabel("Hello world")


There has been a bug in addError some time ago, which affected Zend_Element::addError (#ZF-3852). Don't know if this is a help for anybody.

The codeblock of the library is the following:

if ($this->isArray() || is_array($value)) {
    $aggregateMessages = array();
    foreach ($value as $val) {
        $aggregateMessages[] = str_replace('%value%', $val, $message);
    $messages[$key] = $aggregateMessages;

The problem seems to be the if-clause. isArray is true, but $value is an empty string, when i run my snippet from above. So the source tries to iterate a string, which does not work and the php-warning for the for-each-loop is thrown. I changed the if-clause to make an AND instead of an OR and it does work:

if ($this->isArray() && is_array($value)) {
    $aggregateMessages = array();
    foreach ($value as $val) {
        $aggregateMessages[] = str_replace('%value%', $val, $message);
    $messages[$key] = $aggregateMessages;

So now, has $value to be an array if we want to loop. It does work, but i don't know if i break something different, but i can't image it, because value HAS TO BE an array for iterating.

This also breaks when using {{addErrorMessages()}} So there is no good way to display errors with this field.

See also: ZF-5603

When using {{addErrorMessages()}} the bug is in {{Zend_Form_Element::_getErrorMessages()}}.

When we add error with {{Zend_Form_Element::addError()}} the following is executed:

* Add an error message and mark element as failed validation
* @param  string $message
* @return Zend_Form_Element
public function addError($message)
   return $this;

Let's take a look at {{markAsError()}} function:

    $messages       = $this->getMessages();
    $customMessages = $this->_getErrorMessages();
    $messages       = $messages + $customMessages;
    if (empty($messages)) {
        $this->_isError = true;
    } else {
        $this->_messages = $messages;
    return $this;

The problem is in {{_getErrorMessages}} function. The resulting {{$message}} var, in case of {{Multi}} field will look like this:

$messages = array(
    0 => array(
        0 => 'My custom errormessage',
        1 => 'My custom errormessage',
        2 => 'My custom errormessage'

It's structure is unknown for {{Zend_View_Helper_FormErrors}}, therefore there are errors.

I strongly suggest to give full control over the method of validating {{Multi}} elements! As for now validators are run on each and every selected value, but there is no way to validate THE WHOLE FIELD, nor give it a GLOBAL SINGLE custom error message. Current architecture does NOT allow i.e. to create a {{Zend_Validate_Count}} which would throw error if less (or more) number of fields have been selected.

This patch resolves the problem.

added new patch with unit tests.

current Zend_Form trunk is defect, test with 1.9.2 final, it's working with no side effects.

fixed with #ZF-4915