File indexing completed on 2024-12-22 05:36:43

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Form
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  */
0020 
0021 /** @see Zend_Filter */
0022 // require_once 'Zend/Filter.php';
0023 
0024 /** @see Zend_Form */
0025 // require_once 'Zend/Form.php';
0026 
0027 /** @see Zend_Validate_Interface */
0028 // require_once 'Zend/Validate/Interface.php';
0029 
0030 /** @see Zend_Validate_Abstract */
0031 // require_once 'Zend/Validate/Abstract.php';
0032 
0033 /**
0034  * Zend_Form_Element
0035  *
0036  * @category   Zend
0037  * @package    Zend_Form
0038  * @subpackage Element
0039  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0040  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0041  * @version    $Id$
0042  */
0043 class Zend_Form_Element implements Zend_Validate_Interface
0044 {
0045     /**
0046      * Element Constants
0047      */
0048     const DECORATOR = 'DECORATOR';
0049     const FILTER    = 'FILTER';
0050     const VALIDATE  = 'VALIDATE';
0051 
0052     /**
0053      * Default view helper to use
0054      * @var string
0055      */
0056     public $helper = 'formText';
0057 
0058     /**
0059      * 'Allow empty' flag
0060      * @var bool
0061      */
0062     protected $_allowEmpty = true;
0063 
0064     /**
0065      * Flag indicating whether or not to insert NotEmpty validator when element is required
0066      * @var bool
0067      */
0068     protected $_autoInsertNotEmptyValidator = true;
0069 
0070     /**
0071      * Array to which element belongs
0072      * @var string
0073      */
0074     protected $_belongsTo;
0075 
0076     /**
0077      * Element decorators
0078      * @var array
0079      */
0080     protected $_decorators = array();
0081 
0082     /**
0083      * Element description
0084      * @var string
0085      */
0086     protected $_description;
0087 
0088     /**
0089      * Should we disable loading the default decorators?
0090      * @var bool
0091      */
0092     protected $_disableLoadDefaultDecorators = false;
0093 
0094     /**
0095      * Custom error messages
0096      * @var array
0097      */
0098     protected $_errorMessages = array();
0099 
0100     /**
0101      * Validation errors
0102      * @var array
0103      */
0104     protected $_errors = array();
0105 
0106     /**
0107      * Separator to use when concatenating aggregate error messages (for
0108      * elements having array values)
0109      * @var string
0110      */
0111     protected $_errorMessageSeparator = '; ';
0112 
0113     /**
0114      * Element filters
0115      * @var array
0116      */
0117     protected $_filters = array();
0118 
0119     /**
0120      * Ignore flag (used when retrieving values at form level)
0121      * @var bool
0122      */
0123     protected $_ignore = false;
0124 
0125     /**
0126      * Does the element represent an array?
0127      * @var bool
0128      */
0129     protected $_isArray = false;
0130 
0131     /**
0132      * Is the error marked as in an invalid state?
0133      * @var bool
0134      */
0135     protected $_isError = false;
0136 
0137     /**
0138      * Has the element been manually marked as invalid?
0139      * @var bool
0140      */
0141     protected $_isErrorForced = false;
0142 
0143     /**
0144      * Element label
0145      * @var string
0146      */
0147     protected $_label;
0148 
0149     /**
0150      * Plugin loaders for filter and validator chains
0151      * @var array
0152      */
0153     protected $_loaders = array();
0154 
0155     /**
0156      * Formatted validation error messages
0157      * @var array
0158      */
0159     protected $_messages = array();
0160 
0161     /**
0162      * Element name
0163      * @var string
0164      */
0165     protected $_name;
0166 
0167     /**
0168      * Order of element
0169      * @var int
0170      */
0171     protected $_order;
0172 
0173     /**
0174      * Required flag
0175      * @var bool
0176      */
0177     protected $_required = false;
0178 
0179     /**
0180      * @var Zend_Translate
0181      */
0182     protected $_translator;
0183 
0184     /**
0185      * Is translation disabled?
0186      * @var bool
0187      */
0188     protected $_translatorDisabled = false;
0189 
0190     /**
0191      * Element type
0192      * @var string
0193      */
0194     protected $_type;
0195 
0196     /**
0197      * Array of initialized validators
0198      * @var array Validators
0199      */
0200     protected $_validators = array();
0201 
0202     /**
0203      * Array of un-initialized validators
0204      * @var array
0205      */
0206     protected $_validatorRules = array();
0207 
0208     /**
0209      * Element value
0210      * @var mixed
0211      */
0212     protected $_value;
0213 
0214     /**
0215      * @var Zend_View_Interface
0216      */
0217     protected $_view;
0218 
0219     /**
0220      * Is a specific decorator being rendered via the magic renderDecorator()?
0221      *
0222      * This is to allow execution of logic inside the render() methods of child
0223      * elements during the magic call while skipping the parent render() method.
0224      *
0225      * @var bool
0226      */
0227     protected $_isPartialRendering = false;
0228 
0229     /**
0230      * Use one error message for array elements with concatenated values
0231      *
0232      * @var bool
0233      */
0234     protected $_concatJustValuesInErrorMessage = false;
0235 
0236     /**
0237      * Constructor
0238      *
0239      * $spec may be:
0240      * - string: name of element
0241      * - array: options with which to configure element
0242      * - Zend_Config: Zend_Config with options for configuring element
0243      *
0244      * @param  string|array|Zend_Config $spec
0245      * @param  array|Zend_Config $options
0246      * @return void
0247      * @throws Zend_Form_Exception if no element name after initialization
0248      */
0249     public function __construct($spec, $options = null)
0250     {
0251         if (is_string($spec)) {
0252             $this->setName($spec);
0253         } elseif (is_array($spec)) {
0254             $this->setOptions($spec);
0255         } elseif ($spec instanceof Zend_Config) {
0256             $this->setConfig($spec);
0257         }
0258 
0259         if (is_string($spec) && is_array($options)) {
0260             $this->setOptions($options);
0261         } elseif (is_string($spec) && ($options instanceof Zend_Config)) {
0262             $this->setConfig($options);
0263         }
0264 
0265         if (null === $this->getName()) {
0266             // require_once 'Zend/Form/Exception.php';
0267             throw new Zend_Form_Exception('Zend_Form_Element requires each element to have a name');
0268         }
0269 
0270         /**
0271          * Extensions
0272          */
0273         $this->init();
0274 
0275         /**
0276          * Register ViewHelper decorator by default
0277          */
0278         $this->loadDefaultDecorators();
0279     }
0280 
0281     /**
0282      * Initialize object; used by extending classes
0283      *
0284      * @return void
0285      */
0286     public function init()
0287     {
0288     }
0289 
0290     /**
0291      * Set flag to disable loading default decorators
0292      *
0293      * @param  bool $flag
0294      * @return Zend_Form_Element
0295      */
0296     public function setDisableLoadDefaultDecorators($flag)
0297     {
0298         $this->_disableLoadDefaultDecorators = (bool) $flag;
0299         return $this;
0300     }
0301 
0302     /**
0303      * Should we load the default decorators?
0304      *
0305      * @return bool
0306      */
0307     public function loadDefaultDecoratorsIsDisabled()
0308     {
0309         return $this->_disableLoadDefaultDecorators;
0310     }
0311 
0312     /**
0313      * Load default decorators
0314      *
0315      * @return Zend_Form_Element
0316      */
0317     public function loadDefaultDecorators()
0318     {
0319         if ($this->loadDefaultDecoratorsIsDisabled()) {
0320             return $this;
0321         }
0322 
0323         $decorators = $this->getDecorators();
0324         if (empty($decorators)) {
0325             $this->addDecorator('ViewHelper')
0326                  ->addDecorator('Errors')
0327                  ->addDecorator('Description', array('tag' => 'p', 'class' => 'description'))
0328                  ->addDecorator('HtmlTag', array(
0329                      'tag' => 'dd',
0330                      'id'  => array('callback' => array(get_class($this), 'resolveElementId'))
0331                  ))
0332                  ->addDecorator('Label', array('tag' => 'dt'));
0333         }
0334         return $this;
0335     }
0336 
0337     /**
0338      * Used to resolve and return an element ID
0339      *
0340      * Passed to the HtmlTag decorator as a callback in order to provide an ID.
0341      * 
0342      * @param  Zend_Form_Decorator_Interface $decorator 
0343      * @return string
0344      */
0345     public static function resolveElementId(Zend_Form_Decorator_Interface $decorator)
0346     {
0347         return $decorator->getElement()->getId() . '-element';
0348     }
0349 
0350     /**
0351      * Set object state from options array
0352      *
0353      * @param  array $options
0354      * @return Zend_Form_Element
0355      */
0356     public function setOptions(array $options)
0357     {
0358         if (isset($options['prefixPath'])) {
0359             $this->addPrefixPaths($options['prefixPath']);
0360             unset($options['prefixPath']);
0361         }
0362 
0363         if (isset($options['disableTranslator'])) {
0364             $this->setDisableTranslator($options['disableTranslator']);
0365             unset($options['disableTranslator']);
0366         }
0367 
0368         unset($options['options']);
0369         unset($options['config']);
0370 
0371         foreach ($options as $key => $value) {
0372             $method = 'set' . ucfirst($key);
0373 
0374             if (in_array($method, array('setTranslator', 'setPluginLoader', 'setView'))) {
0375                 if (!is_object($value)) {
0376                     continue;
0377                 }
0378             }
0379 
0380             if (method_exists($this, $method)) {
0381                 // Setter exists; use it
0382                 $this->$method($value);
0383             } else {
0384                 // Assume it's metadata
0385                 $this->setAttrib($key, $value);
0386             }
0387         }
0388         return $this;
0389     }
0390 
0391     /**
0392      * Set object state from Zend_Config object
0393      *
0394      * @param  Zend_Config $config
0395      * @return Zend_Form_Element
0396      */
0397     public function setConfig(Zend_Config $config)
0398     {
0399         return $this->setOptions($config->toArray());
0400     }
0401 
0402 
0403     // Localization:
0404 
0405     /**
0406      * Set translator object for localization
0407      *
0408      * @param  Zend_Translate|null $translator
0409      * @return Zend_Form_Element
0410      */
0411     public function setTranslator($translator = null)
0412     {
0413         if (null === $translator) {
0414             $this->_translator = null;
0415         } elseif ($translator instanceof Zend_Translate_Adapter) {
0416             $this->_translator = $translator;
0417         } elseif ($translator instanceof Zend_Translate) {
0418             $this->_translator = $translator->getAdapter();
0419         } else {
0420             // require_once 'Zend/Form/Exception.php';
0421             throw new Zend_Form_Exception('Invalid translator specified');
0422         }
0423         return $this;
0424     }
0425 
0426     /**
0427      * Retrieve localization translator object
0428      *
0429      * @return Zend_Translate_Adapter|null
0430      */
0431     public function getTranslator()
0432     {
0433         if ($this->translatorIsDisabled()) {
0434             return null;
0435         }
0436 
0437         if (null === $this->_translator) {
0438             return Zend_Form::getDefaultTranslator();
0439         }
0440         return $this->_translator;
0441     }
0442 
0443     /**
0444      * Does this element have its own specific translator?
0445      *
0446      * @return bool
0447      */
0448     public function hasTranslator()
0449     {
0450         return (bool)$this->_translator;
0451     }
0452 
0453     /**
0454      * Indicate whether or not translation should be disabled
0455      *
0456      * @param  bool $flag
0457      * @return Zend_Form_Element
0458      */
0459     public function setDisableTranslator($flag)
0460     {
0461         $this->_translatorDisabled = (bool) $flag;
0462         return $this;
0463     }
0464 
0465     /**
0466      * Is translation disabled?
0467      *
0468      * @return bool
0469      */
0470     public function translatorIsDisabled()
0471     {
0472         return $this->_translatorDisabled;
0473     }
0474 
0475     // Metadata
0476 
0477     /**
0478      * Filter a name to only allow valid variable characters
0479      *
0480      * @param  string $value
0481      * @param  bool $allowBrackets
0482      * @return string
0483      */
0484     public function filterName($value, $allowBrackets = false)
0485     {
0486         $charset = '^a-zA-Z0-9_\x7f-\xff';
0487         if ($allowBrackets) {
0488             $charset .= '\[\]';
0489         }
0490         return preg_replace('/[' . $charset . ']/', '', (string) $value);
0491     }
0492 
0493     /**
0494      * Set element name
0495      *
0496      * @param  string $name
0497      * @return Zend_Form_Element
0498      */
0499     public function setName($name)
0500     {
0501         $name = $this->filterName($name);
0502         if ('' === $name) {
0503             // require_once 'Zend/Form/Exception.php';
0504             throw new Zend_Form_Exception('Invalid name provided; must contain only valid variable characters and be non-empty');
0505         }
0506 
0507         $this->_name = $name;
0508         return $this;
0509     }
0510 
0511     /**
0512      * Return element name
0513      *
0514      * @return string
0515      */
0516     public function getName()
0517     {
0518         return $this->_name;
0519     }
0520 
0521     /**
0522      * Get fully qualified name
0523      *
0524      * Places name as subitem of array and/or appends brackets.
0525      *
0526      * @return string
0527      */
0528     public function getFullyQualifiedName()
0529     {
0530         $name = $this->getName();
0531 
0532         if (null !== ($belongsTo = $this->getBelongsTo())) {
0533             $name = $belongsTo . '[' . $name . ']';
0534         }
0535 
0536         if ($this->isArray()) {
0537             $name .= '[]';
0538         }
0539 
0540         return $name;
0541     }
0542 
0543     /**
0544      * Get element id
0545      *
0546      * @return string
0547      */
0548     public function getId()
0549     {
0550         if (isset($this->id)) {
0551             return $this->id;
0552         }
0553 
0554         $id = $this->getFullyQualifiedName();
0555 
0556         // Bail early if no array notation detected
0557         if (!strstr($id, '[')) {
0558             return $id;
0559         }
0560 
0561         // Strip array notation
0562         if ('[]' == substr($id, -2)) {
0563             $id = substr($id, 0, strlen($id) - 2);
0564         }
0565         $id = str_replace('][', '-', $id);
0566         $id = str_replace(array(']', '['), '-', $id);
0567         $id = trim($id, '-');
0568 
0569         return $id;
0570     }
0571 
0572     /**
0573      * Set element value
0574      *
0575      * @param  mixed $value
0576      * @return Zend_Form_Element
0577      */
0578     public function setValue($value)
0579     {
0580         $this->_value = $value;
0581         return $this;
0582     }
0583 
0584     /**
0585      * Filter a value
0586      *
0587      * @param  string $value
0588      * @param  string $key
0589      * @return void
0590      */
0591     protected function _filterValue(&$value, &$key)
0592     {
0593         foreach ($this->getFilters() as $filter) {
0594             $value = $filter->filter($value);
0595         }
0596     }
0597 
0598     /**
0599      * Retrieve filtered element value
0600      *
0601      * @return mixed
0602      */
0603     public function getValue()
0604     {
0605         $valueFiltered = $this->_value;
0606 
0607         if ($this->isArray() && is_array($valueFiltered)) {
0608             array_walk_recursive($valueFiltered, array($this, '_filterValue'));
0609         } else {
0610             $this->_filterValue($valueFiltered, $valueFiltered);
0611         }
0612 
0613         return $valueFiltered;
0614     }
0615 
0616     /**
0617      * Retrieve unfiltered element value
0618      *
0619      * @return mixed
0620      */
0621     public function getUnfilteredValue()
0622     {
0623         return $this->_value;
0624     }
0625 
0626     /**
0627      * Set element label
0628      *
0629      * @param  string $label
0630      * @return Zend_Form_Element
0631      */
0632     public function setLabel($label)
0633     {
0634         $this->_label = (string) $label;
0635         return $this;
0636     }
0637 
0638     /**
0639      * Retrieve element label
0640      *
0641      * @return string
0642      */
0643     public function getLabel()
0644     {
0645         $translator = $this->getTranslator();
0646         if (null !== $translator) {
0647             return $translator->translate($this->_label);
0648         }
0649 
0650         return $this->_label;
0651     }
0652 
0653     /**
0654      * Set element order
0655      *
0656      * @param  int $order
0657      * @return Zend_Form_Element
0658      */
0659     public function setOrder($order)
0660     {
0661         $this->_order = (int) $order;
0662         return $this;
0663     }
0664 
0665     /**
0666      * Retrieve element order
0667      *
0668      * @return int
0669      */
0670     public function getOrder()
0671     {
0672         return $this->_order;
0673     }
0674 
0675     /**
0676      * Set required flag
0677      *
0678      * @param  bool $flag Default value is true
0679      * @return Zend_Form_Element
0680      */
0681     public function setRequired($flag = true)
0682     {
0683         $this->_required = (bool) $flag;
0684         return $this;
0685     }
0686 
0687     /**
0688      * Is the element required?
0689      *
0690      * @return bool
0691      */
0692     public function isRequired()
0693     {
0694         return $this->_required;
0695     }
0696 
0697     /**
0698      * Set flag indicating whether a NotEmpty validator should be inserted when element is required
0699      *
0700      * @param  bool $flag
0701      * @return Zend_Form_Element
0702      */
0703     public function setAutoInsertNotEmptyValidator($flag)
0704     {
0705         $this->_autoInsertNotEmptyValidator = (bool) $flag;
0706         return $this;
0707     }
0708 
0709     /**
0710      * Get flag indicating whether a NotEmpty validator should be inserted when element is required
0711      *
0712      * @return bool
0713      */
0714     public function autoInsertNotEmptyValidator()
0715     {
0716         return $this->_autoInsertNotEmptyValidator;
0717     }
0718 
0719     /**
0720      * Set element description
0721      *
0722      * @param  string $description
0723      * @return Zend_Form_Element
0724      */
0725     public function setDescription($description)
0726     {
0727         $this->_description = (string) $description;
0728         return $this;
0729     }
0730 
0731     /**
0732      * Retrieve element description
0733      *
0734      * @return string
0735      */
0736     public function getDescription()
0737     {
0738         return $this->_description;
0739     }
0740 
0741     /**
0742      * Set 'allow empty' flag
0743      *
0744      * When the allow empty flag is enabled and the required flag is false, the
0745      * element will validate with empty values.
0746      *
0747      * @param  bool $flag
0748      * @return Zend_Form_Element
0749      */
0750     public function setAllowEmpty($flag)
0751     {
0752         $this->_allowEmpty = (bool) $flag;
0753         return $this;
0754     }
0755 
0756     /**
0757      * Get 'allow empty' flag
0758      *
0759      * @return bool
0760      */
0761     public function getAllowEmpty()
0762     {
0763         return $this->_allowEmpty;
0764     }
0765 
0766     /**
0767      * Set ignore flag (used when retrieving values at form level)
0768      *
0769      * @param  bool $flag
0770      * @return Zend_Form_Element
0771      */
0772     public function setIgnore($flag)
0773     {
0774         $this->_ignore = (bool) $flag;
0775         return $this;
0776     }
0777 
0778     /**
0779      * Get ignore flag (used when retrieving values at form level)
0780      *
0781      * @return bool
0782      */
0783     public function getIgnore()
0784     {
0785         return $this->_ignore;
0786     }
0787 
0788     /**
0789      * Set flag indicating if element represents an array
0790      *
0791      * @param  bool $flag
0792      * @return Zend_Form_Element
0793      */
0794     public function setIsArray($flag)
0795     {
0796         $this->_isArray = (bool) $flag;
0797         return $this;
0798     }
0799 
0800     /**
0801      * Is the element representing an array?
0802      *
0803      * @return bool
0804      */
0805     public function isArray()
0806     {
0807         return $this->_isArray;
0808     }
0809 
0810     /**
0811      * Set array to which element belongs
0812      *
0813      * @param  string $array
0814      * @return Zend_Form_Element
0815      */
0816     public function setBelongsTo($array)
0817     {
0818         $array = $this->filterName($array, true);
0819         if (!empty($array)) {
0820             $this->_belongsTo = $array;
0821         }
0822 
0823         return $this;
0824     }
0825 
0826     /**
0827      * Return array name to which element belongs
0828      *
0829      * @return string
0830      */
0831     public function getBelongsTo()
0832     {
0833         return $this->_belongsTo;
0834     }
0835 
0836     /**
0837      * Return element type
0838      *
0839      * @return string
0840      */
0841     public function getType()
0842     {
0843         if (null === $this->_type) {
0844             $this->_type = get_class($this);
0845         }
0846 
0847         return $this->_type;
0848     }
0849 
0850     /**
0851      * Set element attribute
0852      *
0853      * @param  string $name
0854      * @param  mixed $value
0855      * @return Zend_Form_Element
0856      * @throws Zend_Form_Exception for invalid $name values
0857      */
0858     public function setAttrib($name, $value)
0859     {
0860         $name = (string) $name;
0861         if ('_' == $name[0]) {
0862             // require_once 'Zend/Form/Exception.php';
0863             throw new Zend_Form_Exception(sprintf('Invalid attribute "%s"; must not contain a leading underscore', $name));
0864         }
0865 
0866         if (null === $value) {
0867             unset($this->$name);
0868         } else {
0869             $this->$name = $value;
0870         }
0871 
0872         return $this;
0873     }
0874 
0875     /**
0876      * Set multiple attributes at once
0877      *
0878      * @param  array $attribs
0879      * @return Zend_Form_Element
0880      */
0881     public function setAttribs(array $attribs)
0882     {
0883         foreach ($attribs as $key => $value) {
0884             $this->setAttrib($key, $value);
0885         }
0886 
0887         return $this;
0888     }
0889 
0890     /**
0891      * Retrieve element attribute
0892      *
0893      * @param  string $name
0894      * @return string
0895      */
0896     public function getAttrib($name)
0897     {
0898         $name = (string) $name;
0899         if (isset($this->$name)) {
0900             return $this->$name;
0901         }
0902 
0903         return null;
0904     }
0905 
0906     /**
0907      * Return all attributes
0908      *
0909      * @return array
0910      */
0911     public function getAttribs()
0912     {
0913         $attribs = get_object_vars($this);
0914         unset($attribs['helper']);
0915         foreach ($attribs as $key => $value) {
0916             if ('_' == substr($key, 0, 1)) {
0917                 unset($attribs[$key]);
0918             }
0919         }
0920 
0921         return $attribs;
0922     }
0923 
0924     /**
0925      * Use one error message for array elements with concatenated values
0926      *
0927      * @param boolean $concatJustValuesInErrorMessage
0928      * @return Zend_Form_Element
0929      */
0930     public function setConcatJustValuesInErrorMessage($concatJustValuesInErrorMessage)
0931     {
0932         $this->_concatJustValuesInErrorMessage = $concatJustValuesInErrorMessage;
0933         return $this;
0934     }
0935 
0936     /**
0937      * Use one error message for array elements with concatenated values
0938      *
0939      * @return boolean
0940      */
0941     public function getConcatJustValuesInErrorMessage()
0942     {
0943         return $this->_concatJustValuesInErrorMessage;
0944     }
0945 
0946     /**
0947      * Overloading: retrieve object property
0948      *
0949      * Prevents access to properties beginning with '_'.
0950      *
0951      * @param  string $key
0952      * @return mixed
0953      */
0954     public function __get($key)
0955     {
0956         if ('_' == $key[0]) {
0957             // require_once 'Zend/Form/Exception.php';
0958             throw new Zend_Form_Exception(sprintf('Cannot retrieve value for protected/private property "%s"', $key));
0959         }
0960 
0961         if (!isset($this->$key)) {
0962             return null;
0963         }
0964 
0965         return $this->$key;
0966     }
0967 
0968     /**
0969      * Overloading: set object property
0970      *
0971      * @param  string $key
0972      * @param  mixed $value
0973      * @return voide
0974      */
0975     public function __set($key, $value)
0976     {
0977         $this->setAttrib($key, $value);
0978     }
0979 
0980     /**
0981      * Overloading: allow rendering specific decorators
0982      *
0983      * Call renderDecoratorName() to render a specific decorator.
0984      *
0985      * @param  string $method
0986      * @param  array $args
0987      * @return string
0988      * @throws Zend_Form_Exception for invalid decorator or invalid method call
0989      */
0990     public function __call($method, $args)
0991     {
0992         if ('render' == substr($method, 0, 6)) {
0993             $this->_isPartialRendering = true;
0994             $this->render();
0995             $this->_isPartialRendering = false;
0996             $decoratorName = substr($method, 6);
0997             if (false !== ($decorator = $this->getDecorator($decoratorName))) {
0998                 $decorator->setElement($this);
0999                 $seed = '';
1000                 if (0 < count($args)) {
1001                     $seed = array_shift($args);
1002                 }
1003                 return $decorator->render($seed);
1004             }
1005 
1006             // require_once 'Zend/Form/Element/Exception.php';
1007             throw new Zend_Form_Element_Exception(sprintf('Decorator by name %s does not exist', $decoratorName));
1008         }
1009 
1010         // require_once 'Zend/Form/Element/Exception.php';
1011         throw new Zend_Form_Element_Exception(sprintf('Method %s does not exist', $method));
1012     }
1013 
1014     // Loaders
1015 
1016     /**
1017      * Set plugin loader to use for validator or filter chain
1018      *
1019      * @param  Zend_Loader_PluginLoader_Interface $loader
1020      * @param  string $type 'decorator', 'filter', or 'validate'
1021      * @return Zend_Form_Element
1022      * @throws Zend_Form_Exception on invalid type
1023      */
1024     public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type)
1025     {
1026         $type = strtoupper($type);
1027         switch ($type) {
1028             case self::DECORATOR:
1029             case self::FILTER:
1030             case self::VALIDATE:
1031                 $this->_loaders[$type] = $loader;
1032                 return $this;
1033             default:
1034                 // require_once 'Zend/Form/Exception.php';
1035                 throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type));
1036         }
1037     }
1038 
1039     /**
1040      * Retrieve plugin loader for validator or filter chain
1041      *
1042      * Instantiates with default rules if none available for that type. Use
1043      * 'decorator', 'filter', or 'validate' for $type.
1044      *
1045      * @param  string $type
1046      * @return Zend_Loader_PluginLoader
1047      * @throws Zend_Loader_Exception on invalid type.
1048      */
1049     public function getPluginLoader($type)
1050     {
1051         $type = strtoupper($type);
1052         switch ($type) {
1053             case self::FILTER:
1054             case self::VALIDATE:
1055                 $prefixSegment = ucfirst(strtolower($type));
1056                 $pathSegment   = $prefixSegment;
1057             case self::DECORATOR:
1058                 if (!isset($prefixSegment)) {
1059                     $prefixSegment = 'Form_Decorator';
1060                     $pathSegment   = 'Form/Decorator';
1061                 }
1062                 if (!isset($this->_loaders[$type])) {
1063                     // require_once 'Zend/Loader/PluginLoader.php';
1064                     $this->_loaders[$type] = new Zend_Loader_PluginLoader(
1065                         array('Zend_' . $prefixSegment . '_' => 'Zend/' . $pathSegment . '/')
1066                     );
1067                 }
1068                 return $this->_loaders[$type];
1069             default:
1070                 // require_once 'Zend/Form/Exception.php';
1071                 throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
1072         }
1073     }
1074 
1075     /**
1076      * Add prefix path for plugin loader
1077      *
1078      * If no $type specified, assumes it is a base path for both filters and
1079      * validators, and sets each according to the following rules:
1080      * - decorators: $prefix = $prefix . '_Decorator'
1081      * - filters: $prefix = $prefix . '_Filter'
1082      * - validators: $prefix = $prefix . '_Validate'
1083      *
1084      * Otherwise, the path prefix is set on the appropriate plugin loader.
1085      *
1086      * @param  string $prefix
1087      * @param  string $path
1088      * @param  string $type
1089      * @return Zend_Form_Element
1090      * @throws Zend_Form_Exception for invalid type
1091      */
1092     public function addPrefixPath($prefix, $path, $type = null)
1093     {
1094         $type = strtoupper($type);
1095         switch ($type) {
1096             case self::DECORATOR:
1097             case self::FILTER:
1098             case self::VALIDATE:
1099                 $loader = $this->getPluginLoader($type);
1100                 $loader->addPrefixPath($prefix, $path);
1101                 return $this;
1102             case null:
1103                 $nsSeparator = (false !== strpos($prefix, '\\'))?'\\':'_';
1104                 $prefix = rtrim($prefix, $nsSeparator) . $nsSeparator;
1105                 $path   = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
1106                 foreach (array(self::DECORATOR, self::FILTER, self::VALIDATE) as $type) {
1107                     $cType        = ucfirst(strtolower($type));
1108                     $loader       = $this->getPluginLoader($type);
1109                     $loader->addPrefixPath($prefix . $cType, $path . $cType . DIRECTORY_SEPARATOR);
1110                 }
1111                 return $this;
1112             default:
1113                 // require_once 'Zend/Form/Exception.php';
1114                 throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
1115         }
1116     }
1117 
1118     /**
1119      * Add many prefix paths at once
1120      *
1121      * @param  array $spec
1122      * @return Zend_Form_Element
1123      */
1124     public function addPrefixPaths(array $spec)
1125     {
1126         if (isset($spec['prefix']) && isset($spec['path'])) {
1127             return $this->addPrefixPath($spec['prefix'], $spec['path']);
1128         }
1129         foreach ($spec as $type => $paths) {
1130             if (is_numeric($type) && is_array($paths)) {
1131                 $type = null;
1132                 if (isset($paths['prefix']) && isset($paths['path'])) {
1133                     if (isset($paths['type'])) {
1134                         $type = $paths['type'];
1135                     }
1136                     $this->addPrefixPath($paths['prefix'], $paths['path'], $type);
1137                 }
1138             } elseif (!is_numeric($type)) {
1139                 if (!isset($paths['prefix']) || !isset($paths['path'])) {
1140                     foreach ($paths as $prefix => $spec) {
1141                         if (is_array($spec)) {
1142                             foreach ($spec as $path) {
1143                                 if (!is_string($path)) {
1144                                     continue;
1145                                 }
1146                                 $this->addPrefixPath($prefix, $path, $type);
1147                             }
1148                         } elseif (is_string($spec)) {
1149                             $this->addPrefixPath($prefix, $spec, $type);
1150                         }
1151                     }
1152                 } else {
1153                     $this->addPrefixPath($paths['prefix'], $paths['path'], $type);
1154                 }
1155             }
1156         }
1157         return $this;
1158     }
1159 
1160     // Validation
1161 
1162     /**
1163      * Add validator to validation chain
1164      *
1165      * Note: will overwrite existing validators if they are of the same class.
1166      *
1167      * @param  string|Zend_Validate_Interface $validator
1168      * @param  bool $breakChainOnFailure
1169      * @param  array $options
1170      * @return Zend_Form_Element
1171      * @throws Zend_Form_Exception if invalid validator type
1172      */
1173     public function addValidator($validator, $breakChainOnFailure = false, $options = array())
1174     {
1175         if ($validator instanceof Zend_Validate_Interface) {
1176             $name = get_class($validator);
1177 
1178             if (!isset($validator->zfBreakChainOnFailure)) {
1179                 $validator->zfBreakChainOnFailure = $breakChainOnFailure;
1180             }
1181         } elseif (is_string($validator)) {
1182             $name      = $validator;
1183             $validator = array(
1184                 'validator' => $validator,
1185                 'breakChainOnFailure' => $breakChainOnFailure,
1186                 'options'             => $options,
1187             );
1188         } else {
1189             // require_once 'Zend/Form/Exception.php';
1190             throw new Zend_Form_Exception('Invalid validator provided to addValidator; must be string or Zend_Validate_Interface');
1191         }
1192 
1193 
1194         $this->_validators[$name] = $validator;
1195 
1196         return $this;
1197     }
1198 
1199     /**
1200      * Add multiple validators
1201      *
1202      * @param  array $validators
1203      * @return Zend_Form_Element
1204      */
1205     public function addValidators(array $validators)
1206     {
1207         foreach ($validators as $validatorInfo) {
1208             if (is_string($validatorInfo)) {
1209                 $this->addValidator($validatorInfo);
1210             } elseif ($validatorInfo instanceof Zend_Validate_Interface) {
1211                 $this->addValidator($validatorInfo);
1212             } elseif (is_array($validatorInfo)) {
1213                 $argc                = count($validatorInfo);
1214                 $breakChainOnFailure = false;
1215                 $options             = array();
1216                 if (isset($validatorInfo['validator'])) {
1217                     $validator = $validatorInfo['validator'];
1218                     if (isset($validatorInfo['breakChainOnFailure'])) {
1219                         $breakChainOnFailure = $validatorInfo['breakChainOnFailure'];
1220                     }
1221                     if (isset($validatorInfo['options'])) {
1222                         $options = $validatorInfo['options'];
1223                     }
1224                     $this->addValidator($validator, $breakChainOnFailure, $options);
1225                 } else {
1226                     switch (true) {
1227                         case (0 == $argc):
1228                             break;
1229                         case (1 <= $argc):
1230                             $validator  = array_shift($validatorInfo);
1231                         case (2 <= $argc):
1232                             $breakChainOnFailure = array_shift($validatorInfo);
1233                         case (3 <= $argc):
1234                             $options = array_shift($validatorInfo);
1235                         default:
1236                             $this->addValidator($validator, $breakChainOnFailure, $options);
1237                             break;
1238                     }
1239                 }
1240             } else {
1241                 // require_once 'Zend/Form/Exception.php';
1242                 throw new Zend_Form_Exception('Invalid validator passed to addValidators()');
1243             }
1244         }
1245 
1246         return $this;
1247     }
1248 
1249     /**
1250      * Set multiple validators, overwriting previous validators
1251      *
1252      * @param  array $validators
1253      * @return Zend_Form_Element
1254      */
1255     public function setValidators(array $validators)
1256     {
1257         $this->clearValidators();
1258         return $this->addValidators($validators);
1259     }
1260 
1261     /**
1262      * Retrieve a single validator by name
1263      *
1264      * @param  string $name
1265      * @return Zend_Validate_Interface|false False if not found, validator otherwise
1266      */
1267     public function getValidator($name)
1268     {
1269         if (!isset($this->_validators[$name])) {
1270             $len = strlen($name);
1271             foreach ($this->_validators as $localName => $validator) {
1272                 if ($len > strlen($localName)) {
1273                     continue;
1274                 }
1275                 if (0 === substr_compare($localName, $name, -$len, $len, true)) {
1276                     if (is_array($validator)) {
1277                         return $this->_loadValidator($validator);
1278                     }
1279                     return $validator;
1280                 }
1281             }
1282             return false;
1283         }
1284 
1285         if (is_array($this->_validators[$name])) {
1286             return $this->_loadValidator($this->_validators[$name]);
1287         }
1288 
1289         return $this->_validators[$name];
1290     }
1291 
1292     /**
1293      * Retrieve all validators
1294      *
1295      * @return array
1296      */
1297     public function getValidators()
1298     {
1299         $validators = array();
1300         foreach ($this->_validators as $key => $value) {
1301             if ($value instanceof Zend_Validate_Interface) {
1302                 $validators[$key] = $value;
1303                 continue;
1304             }
1305             $validator = $this->_loadValidator($value);
1306             $validators[get_class($validator)] = $validator;
1307         }
1308         return $validators;
1309     }
1310 
1311     /**
1312      * Remove a single validator by name
1313      *
1314      * @param  string $name
1315      * @return bool
1316      */
1317     public function removeValidator($name)
1318     {
1319         if (isset($this->_validators[$name])) {
1320             unset($this->_validators[$name]);
1321         } else {
1322             $len = strlen($name);
1323             foreach (array_keys($this->_validators) as $validator) {
1324                 if ($len > strlen($validator)) {
1325                     continue;
1326                 }
1327                 if (0 === substr_compare($validator, $name, -$len, $len, true)) {
1328                     unset($this->_validators[$validator]);
1329                     break;
1330                 }
1331             }
1332         }
1333 
1334         return $this;
1335     }
1336 
1337     /**
1338      * Clear all validators
1339      *
1340      * @return Zend_Form_Element
1341      */
1342     public function clearValidators()
1343     {
1344         $this->_validators = array();
1345         return $this;
1346     }
1347 
1348     /**
1349      * Validate element value
1350      *
1351      * If a translation adapter is registered, any error messages will be
1352      * translated according to the current locale, using the given error code;
1353      * if no matching translation is found, the original message will be
1354      * utilized.
1355      *
1356      * Note: The *filtered* value is validated.
1357      *
1358      * @param  mixed $value
1359      * @param  mixed $context
1360      * @return boolean
1361      */
1362     public function isValid($value, $context = null)
1363     {
1364         $this->setValue($value);
1365         $value = $this->getValue();
1366 
1367         if ((('' === $value) || (null === $value))
1368             && !$this->isRequired()
1369             && $this->getAllowEmpty()
1370         ) {
1371             return true;
1372         }
1373 
1374         if ($this->isRequired()
1375             && $this->autoInsertNotEmptyValidator()
1376             && !$this->getValidator('NotEmpty'))
1377         {
1378             $validators = $this->getValidators();
1379             $notEmpty   = array('validator' => 'NotEmpty', 'breakChainOnFailure' => true);
1380             array_unshift($validators, $notEmpty);
1381             $this->setValidators($validators);
1382         }
1383 
1384         // Find the correct translator. Zend_Validate_Abstract::getDefaultTranslator()
1385         // will get either the static translator attached to Zend_Validate_Abstract
1386         // or the 'Zend_Translate' from Zend_Registry.
1387         if (Zend_Validate_Abstract::hasDefaultTranslator() &&
1388             !Zend_Form::hasDefaultTranslator())
1389         {
1390             $translator = Zend_Validate_Abstract::getDefaultTranslator();
1391             if ($this->hasTranslator()) {
1392                 // only pick up this element's translator if it was attached directly.
1393                 $translator = $this->getTranslator();
1394             }
1395         } else {
1396             $translator = $this->getTranslator();
1397         }
1398 
1399         $this->_messages = array();
1400         $this->_errors   = array();
1401         $result          = true;
1402         $isArray         = $this->isArray();
1403         foreach ($this->getValidators() as $key => $validator) {
1404             if (method_exists($validator, 'setTranslator')) {
1405                 if (method_exists($validator, 'hasTranslator')) {
1406                     if (!$validator->hasTranslator()) {
1407                         $validator->setTranslator($translator);
1408                     }
1409                 } else {
1410                     $validator->setTranslator($translator);
1411                 }
1412             }
1413 
1414             if (method_exists($validator, 'setDisableTranslator')) {
1415                 $validator->setDisableTranslator($this->translatorIsDisabled());
1416             }
1417 
1418             if ($isArray && is_array($value)) {
1419                 $messages = array();
1420                 $errors   = array();
1421                 if (empty($value)) {
1422                     if ($this->isRequired()
1423                         || (!$this->isRequired() && !$this->getAllowEmpty())
1424                     ) {
1425                         $value = '';
1426                     }
1427                 }
1428                 foreach ((array)$value as $val) {
1429                     if (!$validator->isValid($val, $context)) {
1430                         $result = false;
1431                         if ($this->_hasErrorMessages()) {
1432                             $messages = $this->_getErrorMessages();
1433                             $errors   = $messages;
1434                         } else {
1435                             $messages = array_merge($messages, $validator->getMessages());
1436                             $errors   = array_merge($errors,   $validator->getErrors());
1437                         }
1438                     }
1439                 }
1440                 if ($result) {
1441                     continue;
1442                 }
1443             } elseif ($validator->isValid($value, $context)) {
1444                 continue;
1445             } else {
1446                 $result = false;
1447                 if ($this->_hasErrorMessages()) {
1448                     $messages = $this->_getErrorMessages();
1449                     $errors   = $messages;
1450                 } else {
1451                     $messages = $validator->getMessages();
1452                     $errors   = array_keys($messages);
1453                 }
1454             }
1455 
1456             $result          = false;
1457             $this->_messages = array_merge($this->_messages, $messages);
1458             $this->_errors   = array_merge($this->_errors,   $errors);
1459 
1460             if ($validator->zfBreakChainOnFailure) {
1461                 break;
1462             }
1463         }
1464 
1465         // If element manually flagged as invalid, return false
1466         if ($this->_isErrorForced) {
1467             return false;
1468         }
1469 
1470         return $result;
1471     }
1472 
1473     /**
1474      * Add a custom error message to return in the event of failed validation
1475      *
1476      * @param  string $message
1477      * @return Zend_Form_Element
1478      */
1479     public function addErrorMessage($message)
1480     {
1481         $this->_errorMessages[] = (string) $message;
1482         return $this;
1483     }
1484 
1485     /**
1486      * Add multiple custom error messages to return in the event of failed validation
1487      *
1488      * @param  array $messages
1489      * @return Zend_Form_Element
1490      */
1491     public function addErrorMessages(array $messages)
1492     {
1493         foreach ($messages as $message) {
1494             $this->addErrorMessage($message);
1495         }
1496         return $this;
1497     }
1498 
1499     /**
1500      * Same as addErrorMessages(), but clears custom error message stack first
1501      *
1502      * @param  array $messages
1503      * @return Zend_Form_Element
1504      */
1505     public function setErrorMessages(array $messages)
1506     {
1507         $this->clearErrorMessages();
1508         return $this->addErrorMessages($messages);
1509     }
1510 
1511     /**
1512      * Retrieve custom error messages
1513      *
1514      * @return array
1515      */
1516     public function getErrorMessages()
1517     {
1518         return $this->_errorMessages;
1519     }
1520 
1521     /**
1522      * Clear custom error messages stack
1523      *
1524      * @return Zend_Form_Element
1525      */
1526     public function clearErrorMessages()
1527     {
1528         $this->_errorMessages = array();
1529         return $this;
1530     }
1531 
1532     /**
1533      * Get errorMessageSeparator
1534      *
1535      * @return string
1536      */
1537     public function getErrorMessageSeparator()
1538     {
1539         return $this->_errorMessageSeparator;
1540     }
1541 
1542     /**
1543      * Set errorMessageSeparator
1544      *
1545      * @param  string $separator
1546      * @return Zend_Form_Element
1547      */
1548     public function setErrorMessageSeparator($separator)
1549     {
1550         $this->_errorMessageSeparator = $separator;
1551         return $this;
1552     }
1553 
1554     /**
1555      * Mark the element as being in a failed validation state
1556      *
1557      * @return Zend_Form_Element
1558      */
1559     public function markAsError()
1560     {
1561         $messages       = $this->getMessages();
1562         $customMessages = $this->_getErrorMessages();
1563         $messages       = $messages + $customMessages;
1564         if (empty($messages)) {
1565             $this->_isError = true;
1566         } else {
1567             $this->_messages = $messages;
1568         }
1569         $this->_isErrorForced = true;
1570         return $this;
1571     }
1572 
1573     /**
1574      * Add an error message and mark element as failed validation
1575      *
1576      * @param  string $message
1577      * @return Zend_Form_Element
1578      */
1579     public function addError($message)
1580     {
1581         $this->addErrorMessage($message);
1582         $this->markAsError();
1583         return $this;
1584     }
1585 
1586     /**
1587      * Add multiple error messages and flag element as failed validation
1588      *
1589      * @param  array $messages
1590      * @return Zend_Form_Element
1591      */
1592     public function addErrors(array $messages)
1593     {
1594         foreach ($messages as $message) {
1595             $this->addError($message);
1596         }
1597         return $this;
1598     }
1599 
1600     /**
1601      * Overwrite any previously set error messages and flag as failed validation
1602      *
1603      * @param  array $messages
1604      * @return Zend_Form_Element
1605      */
1606     public function setErrors(array $messages)
1607     {
1608         $this->clearErrorMessages();
1609         return $this->addErrors($messages);
1610     }
1611 
1612     /**
1613      * Are there errors registered?
1614      *
1615      * @return bool
1616      */
1617     public function hasErrors()
1618     {
1619         return (!empty($this->_messages) || $this->_isError);
1620     }
1621 
1622     /**
1623      * Retrieve validator chain errors
1624      *
1625      * @return array
1626      */
1627     public function getErrors()
1628     {
1629         return $this->_errors;
1630     }
1631 
1632     /**
1633      * Retrieve error messages
1634      *
1635      * @return array
1636      */
1637     public function getMessages()
1638     {
1639         return $this->_messages;
1640     }
1641 
1642 
1643     // Filtering
1644 
1645     /**
1646      * Add a filter to the element
1647      *
1648      * @param  string|Zend_Filter_Interface $filter
1649      * @return Zend_Form_Element
1650      */
1651     public function addFilter($filter, $options = array())
1652     {
1653         if ($filter instanceof Zend_Filter_Interface) {
1654             $name = get_class($filter);
1655         } elseif (is_string($filter)) {
1656             $name = $filter;
1657             $filter = array(
1658                 'filter'  => $filter,
1659                 'options' => $options,
1660             );
1661             $this->_filters[$name] = $filter;
1662         } else {
1663             // require_once 'Zend/Form/Exception.php';
1664             throw new Zend_Form_Exception('Invalid filter provided to addFilter; must be string or Zend_Filter_Interface');
1665         }
1666 
1667         $this->_filters[$name] = $filter;
1668 
1669         return $this;
1670     }
1671 
1672     /**
1673      * Add filters to element
1674      *
1675      * @param  array $filters
1676      * @return Zend_Form_Element
1677      */
1678     public function addFilters(array $filters)
1679     {
1680         foreach ($filters as $filterInfo) {
1681             if (is_string($filterInfo)) {
1682                 $this->addFilter($filterInfo);
1683             } elseif ($filterInfo instanceof Zend_Filter_Interface) {
1684                 $this->addFilter($filterInfo);
1685             } elseif (is_array($filterInfo)) {
1686                 $argc                = count($filterInfo);
1687                 $options             = array();
1688                 if (isset($filterInfo['filter'])) {
1689                     $filter = $filterInfo['filter'];
1690                     if (isset($filterInfo['options'])) {
1691                         $options = $filterInfo['options'];
1692                     }
1693                     $this->addFilter($filter, $options);
1694                 } else {
1695                     switch (true) {
1696                         case (0 == $argc):
1697                             break;
1698                         case (1 <= $argc):
1699                             $filter  = array_shift($filterInfo);
1700                         case (2 <= $argc):
1701                             $options = array_shift($filterInfo);
1702                         default:
1703                             $this->addFilter($filter, $options);
1704                             break;
1705                     }
1706                 }
1707             } else {
1708                 // require_once 'Zend/Form/Exception.php';
1709                 throw new Zend_Form_Exception('Invalid filter passed to addFilters()');
1710             }
1711         }
1712 
1713         return $this;
1714     }
1715 
1716     /**
1717      * Add filters to element, overwriting any already existing
1718      *
1719      * @param  array $filters
1720      * @return Zend_Form_Element
1721      */
1722     public function setFilters(array $filters)
1723     {
1724         $this->clearFilters();
1725         return $this->addFilters($filters);
1726     }
1727 
1728     /**
1729      * Retrieve a single filter by name
1730      *
1731      * @param  string $name
1732      * @return Zend_Filter_Interface
1733      */
1734     public function getFilter($name)
1735     {
1736         if (!isset($this->_filters[$name])) {
1737             $len = strlen($name);
1738             foreach ($this->_filters as $localName => $filter) {
1739                 if ($len > strlen($localName)) {
1740                     continue;
1741                 }
1742 
1743                 if (0 === substr_compare($localName, $name, -$len, $len, true)) {
1744                     if (is_array($filter)) {
1745                         return $this->_loadFilter($filter);
1746                     }
1747                     return $filter;
1748                 }
1749             }
1750             return false;
1751         }
1752 
1753         if (is_array($this->_filters[$name])) {
1754             return $this->_loadFilter($this->_filters[$name]);
1755         }
1756 
1757         return $this->_filters[$name];
1758     }
1759 
1760     /**
1761      * Get all filters
1762      *
1763      * @return array
1764      */
1765     public function getFilters()
1766     {
1767         $filters = array();
1768         foreach ($this->_filters as $key => $value) {
1769             if ($value instanceof Zend_Filter_Interface) {
1770                 $filters[$key] = $value;
1771                 continue;
1772             }
1773             $filter = $this->_loadFilter($value);
1774             $filters[get_class($filter)] = $filter;
1775         }
1776         return $filters;
1777     }
1778 
1779     /**
1780      * Remove a filter by name
1781      *
1782      * @param  string $name
1783      * @return Zend_Form_Element
1784      */
1785     public function removeFilter($name)
1786     {
1787         if (isset($this->_filters[$name])) {
1788             unset($this->_filters[$name]);
1789         } else {
1790             $len = strlen($name);
1791             foreach (array_keys($this->_filters) as $filter) {
1792                 if ($len > strlen($filter)) {
1793                     continue;
1794                 }
1795                 if (0 === substr_compare($filter, $name, -$len, $len, true)) {
1796                     unset($this->_filters[$filter]);
1797                     break;
1798                 }
1799             }
1800         }
1801 
1802         return $this;
1803     }
1804 
1805     /**
1806      * Clear all filters
1807      *
1808      * @return Zend_Form_Element
1809      */
1810     public function clearFilters()
1811     {
1812         $this->_filters = array();
1813         return $this;
1814     }
1815 
1816     // Rendering
1817 
1818     /**
1819      * Set view object
1820      *
1821      * @param  Zend_View_Interface $view
1822      * @return Zend_Form_Element
1823      */
1824     public function setView(Zend_View_Interface $view = null)
1825     {
1826         $this->_view = $view;
1827         return $this;
1828     }
1829 
1830     /**
1831      * Retrieve view object
1832      *
1833      * Retrieves from ViewRenderer if none previously set.
1834      *
1835      * @return null|Zend_View_Interface
1836      */
1837     public function getView()
1838     {
1839         if (null === $this->_view) {
1840             // require_once 'Zend/Controller/Action/HelperBroker.php';
1841             $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
1842             $this->setView($viewRenderer->view);
1843         }
1844         return $this->_view;
1845     }
1846 
1847     /**
1848      * Instantiate a decorator based on class name or class name fragment
1849      *
1850      * @param  string $name
1851      * @param  null|array $options
1852      * @return Zend_Form_Decorator_Interface
1853      */
1854     protected function _getDecorator($name, $options)
1855     {
1856         $class = $this->getPluginLoader(self::DECORATOR)->load($name);
1857         if (null === $options) {
1858             $decorator = new $class;
1859         } else {
1860             $decorator = new $class($options);
1861         }
1862 
1863         return $decorator;
1864     }
1865 
1866     /**
1867      * Add a decorator for rendering the element
1868      *
1869      * @param  string|Zend_Form_Decorator_Interface $decorator
1870      * @param  array|Zend_Config $options Options with which to initialize decorator
1871      * @return Zend_Form_Element
1872      */
1873     public function addDecorator($decorator, $options = null)
1874     {
1875         if ($decorator instanceof Zend_Form_Decorator_Interface) {
1876             $name = get_class($decorator);
1877         } elseif (is_string($decorator)) {
1878             $name      = $decorator;
1879             $decorator = array(
1880                 'decorator' => $name,
1881                 'options'   => $options,
1882             );
1883         } elseif (is_array($decorator)) {
1884             foreach ($decorator as $name => $spec) {
1885                 break;
1886             }
1887             if (is_numeric($name)) {
1888                 // require_once 'Zend/Form/Exception.php';
1889                 throw new Zend_Form_Exception('Invalid alias provided to addDecorator; must be alphanumeric string');
1890             }
1891             if (is_string($spec)) {
1892                 $decorator = array(
1893                     'decorator' => $spec,
1894                     'options'   => $options,
1895                 );
1896             } elseif ($spec instanceof Zend_Form_Decorator_Interface) {
1897                 $decorator = $spec;
1898             }
1899         } else {
1900             // require_once 'Zend/Form/Exception.php';
1901             throw new Zend_Form_Exception('Invalid decorator provided to addDecorator; must be string or Zend_Form_Decorator_Interface');
1902         }
1903 
1904         $this->_decorators[$name] = $decorator;
1905 
1906         return $this;
1907     }
1908 
1909     /**
1910      * Add many decorators at once
1911      *
1912      * @param  array $decorators
1913      * @return Zend_Form_Element
1914      */
1915     public function addDecorators(array $decorators)
1916     {
1917         foreach ($decorators as $decoratorName => $decoratorInfo) {
1918             if (is_string($decoratorInfo) ||
1919                 $decoratorInfo instanceof Zend_Form_Decorator_Interface) {
1920                 if (!is_numeric($decoratorName)) {
1921                     $this->addDecorator(array($decoratorName => $decoratorInfo));
1922                 } else {
1923                     $this->addDecorator($decoratorInfo);
1924                 }
1925             } elseif (is_array($decoratorInfo)) {
1926                 $argc    = count($decoratorInfo);
1927                 $options = array();
1928                 if (isset($decoratorInfo['decorator'])) {
1929                     $decorator = $decoratorInfo['decorator'];
1930                     if (isset($decoratorInfo['options'])) {
1931                         $options = $decoratorInfo['options'];
1932                     }
1933                     $this->addDecorator($decorator, $options);
1934                 } else {
1935                     switch (true) {
1936                         case (0 == $argc):
1937                             break;
1938                         case (1 <= $argc):
1939                             $decorator  = array_shift($decoratorInfo);
1940                         case (2 <= $argc):
1941                             $options = array_shift($decoratorInfo);
1942                         default:
1943                             $this->addDecorator($decorator, $options);
1944                             break;
1945                     }
1946                 }
1947             } else {
1948                 // require_once 'Zend/Form/Exception.php';
1949                 throw new Zend_Form_Exception('Invalid decorator passed to addDecorators()');
1950             }
1951         }
1952 
1953         return $this;
1954     }
1955 
1956     /**
1957      * Overwrite all decorators
1958      *
1959      * @param  array $decorators
1960      * @return Zend_Form_Element
1961      */
1962     public function setDecorators(array $decorators)
1963     {
1964         $this->clearDecorators();
1965         return $this->addDecorators($decorators);
1966     }
1967 
1968     /**
1969      * Retrieve a registered decorator
1970      *
1971      * @param  string $name
1972      * @return false|Zend_Form_Decorator_Abstract
1973      */
1974     public function getDecorator($name)
1975     {
1976         if (!isset($this->_decorators[$name])) {
1977             $len = strlen($name);
1978             foreach ($this->_decorators as $localName => $decorator) {
1979                 if ($len > strlen($localName)) {
1980                     continue;
1981                 }
1982 
1983                 if (0 === substr_compare($localName, $name, -$len, $len, true)) {
1984                     if (is_array($decorator)) {
1985                         return $this->_loadDecorator($decorator, $localName);
1986                     }
1987                     return $decorator;
1988                 }
1989             }
1990             return false;
1991         }
1992 
1993         if (is_array($this->_decorators[$name])) {
1994             return $this->_loadDecorator($this->_decorators[$name], $name);
1995         }
1996 
1997         return $this->_decorators[$name];
1998     }
1999 
2000     /**
2001      * Retrieve all decorators
2002      *
2003      * @return array
2004      */
2005     public function getDecorators()
2006     {
2007         foreach ($this->_decorators as $key => $value) {
2008             if (is_array($value)) {
2009                 $this->_loadDecorator($value, $key);
2010             }
2011         }
2012         return $this->_decorators;
2013     }
2014 
2015     /**
2016      * Remove a single decorator
2017      *
2018      * @param  string $name
2019      * @return Zend_Form_Element
2020      */
2021     public function removeDecorator($name)
2022     {
2023         if (isset($this->_decorators[$name])) {
2024             unset($this->_decorators[$name]);
2025         } else {
2026             $len = strlen($name);
2027             foreach (array_keys($this->_decorators) as $decorator) {
2028                 if ($len > strlen($decorator)) {
2029                     continue;
2030                 }
2031                 if (0 === substr_compare($decorator, $name, -$len, $len, true)) {
2032                     unset($this->_decorators[$decorator]);
2033                     break;
2034                 }
2035             }
2036         }
2037 
2038         return $this;
2039     }
2040 
2041     /**
2042      * Clear all decorators
2043      *
2044      * @return Zend_Form_Element
2045      */
2046     public function clearDecorators()
2047     {
2048         $this->_decorators = array();
2049         return $this;
2050     }
2051 
2052     /**
2053      * Render form element
2054      *
2055      * @param  Zend_View_Interface $view
2056      * @return string
2057      */
2058     public function render(Zend_View_Interface $view = null)
2059     {
2060         if ($this->_isPartialRendering) {
2061             return '';
2062         }
2063 
2064         if (null !== $view) {
2065             $this->setView($view);
2066         }
2067 
2068         $content = '';
2069         foreach ($this->getDecorators() as $decorator) {
2070             $decorator->setElement($this);
2071             $content = $decorator->render($content);
2072         }
2073         return $content;
2074     }
2075 
2076     /**
2077      * String representation of form element
2078      *
2079      * Proxies to {@link render()}.
2080      *
2081      * @return string
2082      */
2083     public function __toString()
2084     {
2085         try {
2086             $return = $this->render();
2087             return $return;
2088         } catch (Exception $e) {
2089             trigger_error($e->getMessage(), E_USER_WARNING);
2090             return '';
2091         }
2092     }
2093 
2094     /**
2095      * Lazy-load a filter
2096      *
2097      * @param  array $filter
2098      * @return Zend_Filter_Interface
2099      */
2100     protected function _loadFilter(array $filter)
2101     {
2102         $origName = $filter['filter'];
2103         $name     = $this->getPluginLoader(self::FILTER)->load($filter['filter']);
2104 
2105         if (array_key_exists($name, $this->_filters)) {
2106             // require_once 'Zend/Form/Exception.php';
2107             throw new Zend_Form_Exception(sprintf('Filter instance already exists for filter "%s"', $origName));
2108         }
2109 
2110         if (empty($filter['options'])) {
2111             $instance = new $name;
2112         } else {
2113             $r = new ReflectionClass($name);
2114             if ($r->hasMethod('__construct')) {
2115                 $instance = $r->newInstanceArgs((array) $filter['options']);
2116             } else {
2117                 $instance = $r->newInstance();
2118             }
2119         }
2120 
2121         if ($origName != $name) {
2122             $filterNames  = array_keys($this->_filters);
2123             $order        = array_flip($filterNames);
2124             $order[$name] = $order[$origName];
2125             $filtersExchange = array();
2126             unset($order[$origName]);
2127             asort($order);
2128             foreach ($order as $key => $index) {
2129                 if ($key == $name) {
2130                     $filtersExchange[$key] = $instance;
2131                     continue;
2132                 }
2133                 $filtersExchange[$key] = $this->_filters[$key];
2134             }
2135             $this->_filters = $filtersExchange;
2136         } else {
2137             $this->_filters[$name] = $instance;
2138         }
2139 
2140         return $instance;
2141     }
2142 
2143     /**
2144      * Lazy-load a validator
2145      *
2146      * @param  array $validator Validator definition
2147      * @return Zend_Validate_Interface
2148      */
2149     protected function _loadValidator(array $validator)
2150     {
2151         $origName = $validator['validator'];
2152         $name     = $this->getPluginLoader(self::VALIDATE)->load($validator['validator']);
2153 
2154         if (array_key_exists($name, $this->_validators)) {
2155             // require_once 'Zend/Form/Exception.php';
2156             throw new Zend_Form_Exception(sprintf('Validator instance already exists for validator "%s"', $origName));
2157         }
2158 
2159         $messages = false;
2160         if (isset($validator['options']) && array_key_exists('messages', (array)$validator['options'])) {
2161             $messages = $validator['options']['messages'];
2162             unset($validator['options']['messages']);
2163         }
2164 
2165         if (empty($validator['options'])) {
2166             $instance = new $name;
2167         } else {
2168             $r = new ReflectionClass($name);
2169             if ($r->hasMethod('__construct')) {
2170                 $numeric = false;
2171                 if (is_array($validator['options'])) {
2172                     $keys    = array_keys($validator['options']);
2173                     foreach($keys as $key) {
2174                         if (is_numeric($key)) {
2175                             $numeric = true;
2176                             break;
2177                         }
2178                     }
2179                 }
2180 
2181                 if ($numeric) {
2182                     $instance = $r->newInstanceArgs((array) $validator['options']);
2183                 } else {
2184                     $instance = $r->newInstance($validator['options']);
2185                 }
2186             } else {
2187                 $instance = $r->newInstance();
2188             }
2189         }
2190 
2191         if ($messages) {
2192             if (is_array($messages)) {
2193                 $instance->setMessages($messages);
2194             } elseif (is_string($messages)) {
2195                 $instance->setMessage($messages);
2196             }
2197         }
2198         $instance->zfBreakChainOnFailure = $validator['breakChainOnFailure'];
2199 
2200         if ($origName != $name) {
2201             $validatorNames     = array_keys($this->_validators);
2202             $order              = array_flip($validatorNames);
2203             $order[$name]       = $order[$origName];
2204             $validatorsExchange = array();
2205             unset($order[$origName]);
2206             asort($order);
2207             foreach ($order as $key => $index) {
2208                 if ($key == $name) {
2209                     $validatorsExchange[$key] = $instance;
2210                     continue;
2211                 }
2212                 $validatorsExchange[$key] = $this->_validators[$key];
2213             }
2214             $this->_validators = $validatorsExchange;
2215         } else {
2216             $this->_validators[$name] = $instance;
2217         }
2218 
2219         return $instance;
2220     }
2221 
2222     /**
2223      * Lazy-load a decorator
2224      *
2225      * @param  array $decorator Decorator type and options
2226      * @param  mixed $name Decorator name or alias
2227      * @return Zend_Form_Decorator_Interface
2228      */
2229     protected function _loadDecorator(array $decorator, $name)
2230     {
2231         $sameName = false;
2232         if ($name == $decorator['decorator']) {
2233             $sameName = true;
2234         }
2235 
2236         $instance = $this->_getDecorator($decorator['decorator'], $decorator['options']);
2237         if ($sameName) {
2238             $newName            = get_class($instance);
2239             $decoratorNames     = array_keys($this->_decorators);
2240             $order              = array_flip($decoratorNames);
2241             $order[$newName]    = $order[$name];
2242             $decoratorsExchange = array();
2243             unset($order[$name]);
2244             asort($order);
2245             foreach ($order as $key => $index) {
2246                 if ($key == $newName) {
2247                     $decoratorsExchange[$key] = $instance;
2248                     continue;
2249                 }
2250                 $decoratorsExchange[$key] = $this->_decorators[$key];
2251             }
2252             $this->_decorators = $decoratorsExchange;
2253         } else {
2254             $this->_decorators[$name] = $instance;
2255         }
2256 
2257         return $instance;
2258     }
2259 
2260     /**
2261      * Retrieve error messages and perform translation and value substitution
2262      *
2263      * @return array
2264      */
2265     protected function _getErrorMessages()
2266     {
2267         $translator = $this->getTranslator();
2268         $messages   = $this->getErrorMessages();
2269         $value      = $this->getValue();
2270         foreach ($messages as $key => $message) {
2271             if (null !== $translator) {
2272                 $message = $translator->translate($message);
2273             }
2274             if ($this->isArray() || is_array($value)) {
2275                 $aggregateMessages = array();
2276                 foreach ($value as $val) {
2277                     $aggregateMessages[] = str_replace('%value%', $val, $message);
2278                 }
2279                 if (count($aggregateMessages)) {
2280                     if ($this->_concatJustValuesInErrorMessage) {
2281                         $values = implode($this->getErrorMessageSeparator(), $value);
2282                         $messages[$key] = str_replace('%value%', $values, $message);
2283                     } else {
2284                         $messages[$key] = implode($this->getErrorMessageSeparator(), $aggregateMessages);
2285                     }
2286                 }
2287             } else {
2288                 $messages[$key] = str_replace('%value%', $value, $message);
2289             }
2290         }
2291         return $messages;
2292     }
2293 
2294     /**
2295      * Are there custom error messages registered?
2296      *
2297      * @return bool
2298      */
2299     protected function _hasErrorMessages()
2300     {
2301         return !empty($this->_errorMessages);
2302     }
2303 }