ZF-12053: The 'Identical' validator on a Form's text element appears to compare filtered to unfiltered value.

Description

We use lowercased email addresses for account names. Creating a new account involves a form with two text fields for email address, the second one to confirm spelling. Since we lowercase the names anyway for storage (we don't want to allow different accounts to differ only in alphabetic case), we use the StringToLower filter on both fields. But when we use the Identical validator on the confirmation field, specifying the token to compare to as the email field, I've verified with Xdebug that the validator compares the filtered value of the token (email) field to the unfiltered value of the confirmation field. The end result for a user that tends to spell their email address in mixed case is that the validation always fails, but when the form re-displays with the error message, in fact the original field and the confirmation field show as being identical because by that time both have been filtered. If this isn't a bug in the code, it surely deserves some mention in the documentation...

Here is the code we were using:

    $email = new Zend_Form_Element_Text('email');
    $email->setLabel('Email ');
    $email->setOptions(array('class' => 'field-input-signup'))
          ->setRequired(true)
          ->addFilter('StringToLower')
          ->addFilter('StringTrim')
          ->addValidator('NotEmpty', true,array(
            'messages' => array(
              Zend_Validate_NotEmpty::IS_EMPTY
                => $this->getView()->translate("Please enter email address"))))
          ->addValidator('EmailAddress', true);

    $cemail = new Zend_Form_Element_Text('cemail');
    $cemail->setLabel('Confirm Email ');
    $cemail->setOptions(array('class' => 'field-input-signup'))
           ->setRequired(true)
           ->addFilter('StringToLower')
           ->addFilter('StringTrim')
           ->addValidator('NotEmpty', true,array(
             'messages' => array(
               Zend_Validate_NotEmpty::IS_EMPTY
                 => $this->getView()->translate("Please enter confirm email address"))))
           ->addValidator('Identical', false, array(
             'token' => 'email',
             'messages' => array(
                Zend_Validate_Identical::NOT_SAME
                  => $this->getView()->translate(
                     "'Email' and 'Confirm Email' do not match. Please try again."))));

IMHO, given the name 'Identical', I would expect it always to compare the unfiltered values.

Comments

I don't have much experience with Zend_Form, so I may be doing it wrong, but I am seeing a different problem against trunk: even identical lowercased value comparisons fail. When I run this unit test, which is very similar to your reproduction case:


Index: tests/Zend/Form/ElementTest.php
===================================================================
--- tests/Zend/Form/ElementTest.php     (revision 24624)
+++ tests/Zend/Form/ElementTest.php     (working copy)
@@ -2189,6 +2189,27 @@
         $validator = $username->getValidator('regex');
         $this->assertTrue($validator->zfBreakChainOnFailure);
     }
+
+    /**
+     * @group ZF-12053
+     */
+    public function testIdenticalValidatorComparesFilteredFormElementValuesCorrectly()
+    {
+        $f1 = new Zend_Form_Element('f1');
+        $f1->addFilter('StringToLower');
+
+        $f2 = new Zend_Form_Element('f2');
+        $f2->addFilter('StringToLower')
+           ->addValidator('Identical', false, array(
+               'token' => 'f1',
+           ));
+
+        $form = new Zend_Form(array('elements' => array($f1,$f2)));
+        // Identically-cased values matching filtered result entered in each field
+        $this->assertTrue($form->isValid(array('f1'=>'foobar', 'f2'=>'foobar')));
+        // Differently-cased values entered in each field
+        $this->assertTrue($form->isValid(array('f1'=>'FooBar', 'f2'=>'FOOBAR')));
+    }
 }

 class Zend_Form_ElementTest_Decorator extends Zend_Form_Decorator_Abstract

I get a failure at the first assert. Even when I follow the exact example in the manual (here) I get the same result.

bq. IMHO, given the name 'Identical', I would expect it always to compare the unfiltered values.

I don't see any mention of it in the manual, but the docblock for Zend_Form_Element::isValid has this:


Note: The *filtered* value is validated.

I just noticed this on my own site and I too found it weird that the identical field is validated against the unfiltered value. Using StringToLower as filter and identical validator on field2 I'll add some examples below.

Fails as field2 will be validated as "test" against "TEST" from field1. Field1: TEST Field2: TEST

Fails as field2 will be validated as "test" against "Test" from field1. Field1: Test Field2: test

Success as both fields will be "test" during validation. Field1: test Field2: Test