Zend Framework: Validate Identical Field

This little object is a canned and generic validator for Zend Framework. It is intended for use with Form Elements, but it should work fairly broadly. The basic need it addresses is to validate that one field value matches another (e.g. password/email an associated validation field, a captcha challenge, etc.). I looked hard for a ready made answer to this issue, but after much searching and failed experimentation with other canned solutions I decided to write my OWN generic answer to the problem.

An example call to this function might look something like this:

<?php
class PasswordForm extends Zend_Form {
  public function
__construct($options = null) {
   
parent::__construct($options);
   
$this->setName('login');
   
   
$this->addElement(new Zend_Form_Element_Password('password'))->getElement('password')
      ->
setLabel('Password')
      ->
setRequired(true)
      ->
addFilter('StripTags')
      ->
addFilter('StringTrim')
      ->
addValidator('NotEmpty')
      ->
addValidator('IdenticalField', false, array('password_confirm', 'Confirm Password'))
    ;
   
   
$this->addElement(new Zend_Form_Element_Password('password_confirm'))->getElement('password_confirm')
      ->
setLabel('Confirm Password')
      ->
setRequired(true)
      ->
addFilter('StripTags')
      ->
addFilter('StringTrim')
      ->
addValidator('NotEmpty')
    ;
   
   
$submitName = Zend_Registry::get('config')->const->form->button->submit->name;
   
$this->addElement(new Zend_Form_Element_Submit($submitName))->getElement($submitName)
      ->
setLabel('Save Password')
      ->
setAttrib('id', Zend_Registry::get('config')->const->form->button->submit->id)
    ;
  }
}
?>

Aaaaaand the valididator itself:

<?php
   
require_once 'Zend/Validate/Abstract.php';    
    
class
Zend_Validate_IdenticalField extends Zend_Validate_Abstract {
  const
NOT_MATCH = 'notMatch';
  const
MISSING_FIELD_NAME = 'missingFieldName';
  const
INVALID_FIELD_NAME = 'invalidFieldName';
   
 
/**
   * @var array
  */
 
protected $_messageTemplates = array(
   
self::MISSING_FIELD_NAME  =>
     
'DEVELOPMENT ERROR: Field name to match against was not provided.',
   
self::INVALID_FIELD_NAME  =>
     
'DEVELOPMENT ERROR: The field "%fieldName%" was not provided to match against.',
   
self::NOT_MATCH =>
     
'Does not match %fieldTitle%.'
 
);
   
 
/**
   * @var array
  */
 
protected $_messageVariables = array(
   
'fieldName' => '_fieldName',
   
'fieldTitle' => '_fieldTitle'
 
);
   
 
/**
   * Name of the field as it appear in the $context array.
   *
   * @var string
   */
 
protected $_fieldName;
   
 
/**
   * Title of the field to display in an error message.
   *
   * If evaluates to false then will be set to $this->_fieldName.
   *
   * @var string
  */
 
protected $_fieldTitle;
   
 
/**
   * Sets validator options
   *
   * @param  string $fieldName
   * @param  string $fieldTitle
   * @return void
  */
 
public function __construct($fieldName, $fieldTitle = null) {
   
$this->setFieldName($fieldName);
   
$this->setFieldTitle($fieldTitle);
  }
   
 
/**
   * Returns the field name.
   *
   * @return string
  */
 
public function getFieldName() {
    return
$this->_fieldName;
  }
   
 
/**
   * Sets the field name.
   *
   * @param  string $fieldName
   * @return Zend_Validate_IdenticalField Provides a fluent interface
  */
 
public function setFieldName($fieldName) {
   
$this->_fieldName = $fieldName;
    return
$this;
  }
   
 
/**
   * Returns the field title.
   *
   * @return integer
  */
 
public function getFieldTitle() {
    return
$this->_fieldTitle;
  }
   
 
/**
   * Sets the field title.
   *
   * @param  string:null $fieldTitle
   * @return Zend_Validate_IdenticalField Provides a fluent interface
  */
 
public function setFieldTitle($fieldTitle = null) {
   
$this->_fieldTitle = $fieldTitle ? $fieldTitle : $this->_fieldName;
    return
$this;
  }
   
 
/**
   * Defined by Zend_Validate_Interface
   *
   * Returns true if and only if a field name has been set, the field name is available in the
   * context, and the value of that field name matches the provided value.
   *
   * @param  string $value
   *
   * @return boolean 
  */ 
 
public function isValid($value, $context = null) {
   
$this->_setValue($value);
   
$field = $this->getFieldName();
   
    if (empty(
$field)) {
     
$this->_error(self::MISSING_FIELD_NAME);
      return
false;
    } elseif (!isset(
$context[$field])) {
     
$this->_error(self::INVALID_FIELD_NAME);
      return
false;
    } elseif (
is_array($context)) {
      if (
$value == $context[$field]) {
        return
true;
      }
    } elseif (
is_string($context) && ($value == $context)) {
      return
true;
    }
   
$this->_error(self::NOT_MATCH);
    return
false;
  }
}
?>

For the sake of simplicity I have renamed the objects here to use the Zend namespace as explaining the eccentricities of extending objects and registering paths in Zend goes well beyond the scope of what I wish to cover in this article. In practice, one should not plop this object into the Zend/Validate directory but should create a different path in the library and register that path with the appropriate Zend objects.