File indexing completed on 2024-04-28 06:00:02

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_Validate_Interface */
0022 // require_once 'Zend/Validate/Interface.php';
0023 
0024 /**
0025  * Zend_Form
0026  *
0027  * @category   Zend
0028  * @package    Zend_Form
0029  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0030  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0031  * @version    $Id$
0032  */
0033 class Zend_Form implements Iterator, Countable, Zend_Validate_Interface
0034 {
0035     /**#@+
0036      * Plugin loader type constants
0037      */
0038     const DECORATOR = 'DECORATOR';
0039     const ELEMENT = 'ELEMENT';
0040     /**#@-*/
0041 
0042     /**#@+
0043      * Method type constants
0044      */
0045     const METHOD_DELETE = 'delete';
0046     const METHOD_GET    = 'get';
0047     const METHOD_POST   = 'post';
0048     const METHOD_PUT    = 'put';
0049     /**#@-*/
0050 
0051     /**#@+
0052      * Encoding type constants
0053      */
0054     const ENCTYPE_URLENCODED = 'application/x-www-form-urlencoded';
0055     const ENCTYPE_MULTIPART  = 'multipart/form-data';
0056     /**#@-*/
0057 
0058     /**
0059      * Form metadata and attributes
0060      * @var array
0061      */
0062     protected $_attribs = array();
0063 
0064     /**
0065      * Decorators for rendering
0066      * @var array
0067      */
0068     protected $_decorators = array();
0069 
0070     /**
0071      * Default display group class
0072      * @var string
0073      */
0074     protected $_defaultDisplayGroupClass = 'Zend_Form_DisplayGroup';
0075 
0076     /**
0077      * Form description
0078      * @var string
0079      */
0080     protected $_description;
0081 
0082     /**
0083      * Should we disable loading the default decorators?
0084      * @var bool
0085      */
0086     protected $_disableLoadDefaultDecorators = false;
0087 
0088     /**
0089      * Display group prefix paths
0090      * @var array
0091      */
0092     protected $_displayGroupPrefixPaths = array();
0093 
0094     /**
0095      * Groups of elements grouped for display purposes
0096      * @var array
0097      */
0098     protected $_displayGroups = array();
0099 
0100     /**
0101      * Global decorators to apply to all elements
0102      * @var null|array
0103      */
0104     protected $_elementDecorators;
0105 
0106     /**
0107      * Prefix paths to use when creating elements
0108      * @var array
0109      */
0110     protected $_elementPrefixPaths = array();
0111 
0112     /**
0113      * Form elements
0114      * @var array
0115      */
0116     protected $_elements = array();
0117 
0118     /**
0119      * Array to which elements belong (if any)
0120      * @var string
0121      */
0122     protected $_elementsBelongTo;
0123 
0124     /**
0125      * Custom form-level error messages
0126      * @var array
0127      */
0128     protected $_errorMessages = array();
0129 
0130     /**
0131      * Are there errors in the form?
0132      * @var bool
0133      */
0134     protected $_errorsExist = false;
0135 
0136     /**
0137      * Has the form been manually flagged as an error?
0138      * @var bool
0139      */
0140     protected $_errorsForced = false;
0141 
0142     /**
0143      * Form order
0144      * @var int|null
0145      */
0146     protected $_formOrder;
0147 
0148     /**
0149      * Whether or not form elements are members of an array
0150      * @var bool
0151      */
0152     protected $_isArray = false;
0153 
0154     /**
0155      * Form legend
0156      * @var string
0157      */
0158     protected $_legend;
0159 
0160     /**
0161      * Plugin loaders
0162      * @var array
0163      */
0164     protected $_loaders = array();
0165 
0166     /**
0167      * Allowed form methods
0168      * @var array
0169      */
0170     protected $_methods = array('delete', 'get', 'post', 'put');
0171 
0172     /**
0173      * Order in which to display and iterate elements
0174      * @var array
0175      */
0176     protected $_order = array();
0177 
0178     /**
0179      * Whether internal order has been updated or not
0180      * @var bool
0181      */
0182     protected $_orderUpdated = false;
0183 
0184     /**
0185      * Sub form prefix paths
0186      * @var array
0187      */
0188     protected $_subFormPrefixPaths = array();
0189 
0190     /**
0191      * Sub forms
0192      * @var array
0193      */
0194     protected $_subForms = array();
0195 
0196     /**
0197      * @var Zend_Translate
0198      */
0199     protected $_translator;
0200 
0201     /**
0202      * Global default translation adapter
0203      * @var Zend_Translate
0204      */
0205     protected static $_translatorDefault;
0206 
0207     /**
0208      * is the translator disabled?
0209      * @var bool
0210      */
0211     protected $_translatorDisabled = false;
0212 
0213     /**
0214      * @var Zend_View_Interface
0215      */
0216     protected $_view;
0217 
0218     /**
0219      * @var bool
0220      */
0221     protected $_isRendered = false;
0222 
0223     /**
0224      * Constructor
0225      *
0226      * Registers form view helper as decorator
0227      *
0228      * @param mixed $options
0229      */
0230     public function __construct($options = null)
0231     {
0232         if (is_array($options)) {
0233             $this->setOptions($options);
0234         } elseif ($options instanceof Zend_Config) {
0235             $this->setConfig($options);
0236         }
0237 
0238         // Extensions...
0239         $this->init();
0240 
0241         $this->loadDefaultDecorators();
0242     }
0243 
0244     /**
0245      * Clone form object and all children
0246      *
0247      * @return void
0248      */
0249     public function __clone()
0250     {
0251         $elements = array();
0252         foreach ($this->getElements() as $name => $element) {
0253             $elements[] = clone $element;
0254         }
0255         $this->setElements($elements);
0256 
0257         $subForms = array();
0258         foreach ($this->getSubForms() as $name => $subForm) {
0259             $subForms[$name] = clone $subForm;
0260         }
0261         $this->setSubForms($subForms);
0262 
0263         $displayGroups = array();
0264         foreach ($this->_displayGroups as $group)  {
0265             /** @var Zend_Form_DisplayGroup $clone */
0266             $clone    = clone $group;
0267             $elements = array();
0268             foreach ($clone->getElements() as $name => $e) {
0269                 $elements[] = $this->getElement($name);
0270             }
0271             $clone->setElements($elements);
0272             $displayGroups[] = $clone;
0273         }
0274         $this->setDisplayGroups($displayGroups);
0275     }
0276 
0277     /**
0278      * Reset values of form
0279      *
0280      * @return Zend_Form
0281      */
0282     public function reset()
0283     {
0284         /** @var Zend_Form_Element $element */
0285         foreach ($this->getElements() as $element) {
0286             $element->setValue(null);
0287         }
0288         /** @var Zend_Form_SubForm $subForm */
0289         foreach ($this->getSubForms() as $subForm) {
0290             $subForm->reset();
0291         }
0292 
0293         return $this;
0294     }
0295 
0296     /**
0297      * Initialize form (used by extending classes)
0298      *
0299      * @return void
0300      */
0301     public function init()
0302     {
0303     }
0304 
0305     /**
0306      * Set form state from options array
0307      *
0308      * @param  array $options
0309      * @return Zend_Form
0310      */
0311     public function setOptions(array $options)
0312     {
0313         if (isset($options['prefixPath'])) {
0314             $this->addPrefixPaths($options['prefixPath']);
0315             unset($options['prefixPath']);
0316         }
0317 
0318         if (isset($options['elementPrefixPath'])) {
0319             $this->addElementPrefixPaths($options['elementPrefixPath']);
0320             unset($options['elementPrefixPath']);
0321         }
0322 
0323         if (isset($options['displayGroupPrefixPath'])) {
0324             $this->addDisplayGroupPrefixPaths($options['displayGroupPrefixPath']);
0325             unset($options['displayGroupPrefixPath']);
0326         }
0327 
0328         if (isset($options['elementDecorators'])) {
0329             $this->_elementDecorators = $options['elementDecorators'];
0330             unset($options['elementDecorators']);
0331         }
0332 
0333         if (isset($options['elements'])) {
0334             $this->setElements($options['elements']);
0335             unset($options['elements']);
0336         }
0337 
0338         if (isset($options['defaultDisplayGroupClass'])) {
0339             $this->setDefaultDisplayGroupClass($options['defaultDisplayGroupClass']);
0340             unset($options['defaultDisplayGroupClass']);
0341         }
0342 
0343         if (isset($options['displayGroupDecorators'])) {
0344             $displayGroupDecorators = $options['displayGroupDecorators'];
0345             unset($options['displayGroupDecorators']);
0346         }
0347 
0348         if (isset($options['elementsBelongTo'])) {
0349             $elementsBelongTo = $options['elementsBelongTo'];
0350             unset($options['elementsBelongTo']);
0351         }
0352 
0353         if (isset($options['attribs'])) {
0354             $this->addAttribs($options['attribs']);
0355             unset($options['attribs']);
0356         }
0357 
0358         if (isset($options['subForms'])) {
0359             $this->addSubForms($options['subForms']);
0360             unset($options['subForms']);
0361         }
0362 
0363         $forbidden = array(
0364             'Options', 'Config', 'PluginLoader', 'SubForms', 'Translator',
0365             'Attrib', 'Default',
0366         );
0367 
0368         foreach ($options as $key => $value) {
0369             $normalized = ucfirst($key);
0370             if (in_array($normalized, $forbidden)) {
0371                 continue;
0372             }
0373 
0374             $method = 'set' . $normalized;
0375             if (method_exists($this, $method)) {
0376                 if($normalized == 'View' && !($value instanceof Zend_View_Interface)) {
0377                     continue;
0378                 }
0379                 $this->$method($value);
0380             } else {
0381                 $this->setAttrib($key, $value);
0382             }
0383         }
0384 
0385         if (isset($displayGroupDecorators)) {
0386             $this->setDisplayGroupDecorators($displayGroupDecorators);
0387         }
0388 
0389         if (isset($elementsBelongTo)) {
0390             $this->setElementsBelongTo($elementsBelongTo);
0391         }
0392 
0393         return $this;
0394     }
0395 
0396     /**
0397      * Set form state from config object
0398      *
0399      * @param  Zend_Config $config
0400      * @return Zend_Form
0401      */
0402     public function setConfig(Zend_Config $config)
0403     {
0404         return $this->setOptions($config->toArray());
0405     }
0406 
0407 
0408     // Loaders
0409 
0410     /**
0411      * Set plugin loaders for use with decorators and elements
0412      *
0413      * @param  Zend_Loader_PluginLoader_Interface $loader
0414      * @param  string $type 'decorator' or 'element'
0415      * @return Zend_Form
0416      * @throws Zend_Form_Exception on invalid type
0417      */
0418     public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type = null)
0419     {
0420         $type = strtoupper($type);
0421         switch ($type) {
0422             case self::DECORATOR:
0423             case self::ELEMENT:
0424                 $this->_loaders[$type] = $loader;
0425                 return $this;
0426             default:
0427                 // require_once 'Zend/Form/Exception.php';
0428                 throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type));
0429         }
0430     }
0431 
0432     /**
0433      * Retrieve plugin loader for given type
0434      *
0435      * $type may be one of:
0436      * - decorator
0437      * - element
0438      *
0439      * If a plugin loader does not exist for the given type, defaults are
0440      * created.
0441      *
0442      * @param  string $type
0443      * @return Zend_Loader_PluginLoader_Interface
0444      * @throws Zend_Form_Exception
0445      */
0446     public function getPluginLoader($type = null)
0447     {
0448         $type = strtoupper($type);
0449         if (!isset($this->_loaders[$type])) {
0450             switch ($type) {
0451                 case self::DECORATOR:
0452                     $prefixSegment = 'Form_Decorator';
0453                     $pathSegment   = 'Form/Decorator';
0454                     break;
0455                 case self::ELEMENT:
0456                     $prefixSegment = 'Form_Element';
0457                     $pathSegment   = 'Form/Element';
0458                     break;
0459                 default:
0460                     // require_once 'Zend/Form/Exception.php';
0461                     throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
0462             }
0463 
0464             // require_once 'Zend/Loader/PluginLoader.php';
0465             $this->_loaders[$type] = new Zend_Loader_PluginLoader(
0466                 array('Zend_' . $prefixSegment . '_' => 'Zend/' . $pathSegment . '/')
0467             );
0468         }
0469 
0470         return $this->_loaders[$type];
0471     }
0472 
0473     /**
0474      * Add prefix path for plugin loader
0475      *
0476      * If no $type specified, assumes it is a base path for both filters and
0477      * validators, and sets each according to the following rules:
0478      * - decorators: $prefix = $prefix . '_Decorator'
0479      * - elements: $prefix = $prefix . '_Element'
0480      *
0481      * Otherwise, the path prefix is set on the appropriate plugin loader.
0482      *
0483      * If $type is 'decorator', sets the path in the decorator plugin loader
0484      * for all elements. Additionally, if no $type is provided,
0485      * the prefix and path is added to both decorator and element
0486      * plugin loader with following settings:
0487      * $prefix . '_Decorator', $path . '/Decorator/'
0488      * $prefix . '_Element', $path . '/Element/'
0489      *
0490      * @param  string $prefix
0491      * @param  string $path
0492      * @param  string $type
0493      * @return Zend_Form
0494      * @throws Zend_Form_Exception for invalid type
0495      */
0496     public function addPrefixPath($prefix, $path, $type = null)
0497     {
0498         $type = strtoupper($type);
0499         switch ($type) {
0500             case self::DECORATOR:
0501             case self::ELEMENT:
0502                 $loader = $this->getPluginLoader($type);
0503                 $loader->addPrefixPath($prefix, $path);
0504                 return $this;
0505             case null:
0506                 $nsSeparator = (false !== strpos($prefix, '\\'))?'\\':'_';
0507                 $prefix = rtrim($prefix, $nsSeparator);
0508                 $path   = rtrim($path, DIRECTORY_SEPARATOR);
0509                 foreach (array(self::DECORATOR, self::ELEMENT) as $type) {
0510                     $cType        = ucfirst(strtolower($type));
0511                     $pluginPath   = $path . DIRECTORY_SEPARATOR . $cType . DIRECTORY_SEPARATOR;
0512                     $pluginPrefix = $prefix . $nsSeparator . $cType;
0513                     $loader       = $this->getPluginLoader($type);
0514                     $loader->addPrefixPath($pluginPrefix, $pluginPath);
0515                 }
0516                 return $this;
0517             default:
0518                 // require_once 'Zend/Form/Exception.php';
0519                 throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
0520         }
0521     }
0522 
0523     /**
0524      * Add many prefix paths at once
0525      *
0526      * @param  array $spec
0527      * @return Zend_Form
0528      */
0529     public function addPrefixPaths(array $spec)
0530     {
0531         if (isset($spec['prefix']) && isset($spec['path'])) {
0532             return $this->addPrefixPath($spec['prefix'], $spec['path']);
0533         }
0534         foreach ($spec as $type => $paths) {
0535             if (is_numeric($type) && is_array($paths)) {
0536                 $type = null;
0537                 if (isset($paths['prefix']) && isset($paths['path'])) {
0538                     if (isset($paths['type'])) {
0539                         $type = $paths['type'];
0540                     }
0541                     $this->addPrefixPath($paths['prefix'], $paths['path'], $type);
0542                 }
0543             } elseif (!is_numeric($type)) {
0544                 if (!isset($paths['prefix']) || !isset($paths['path'])) {
0545                     continue;
0546                 }
0547                 $this->addPrefixPath($paths['prefix'], $paths['path'], $type);
0548             }
0549         }
0550         return $this;
0551     }
0552 
0553     /**
0554      * Add prefix path for all elements
0555      *
0556      * @param  string $prefix
0557      * @param  string $path
0558      * @param  string $type
0559      * @return Zend_Form
0560      */
0561     public function addElementPrefixPath($prefix, $path, $type = null)
0562     {
0563         $this->_elementPrefixPaths[] = array(
0564             'prefix' => $prefix,
0565             'path'   => $path,
0566             'type'   => $type,
0567         );
0568 
0569         /** @var Zend_Form_Element $element */
0570         foreach ($this->getElements() as $element) {
0571             $element->addPrefixPath($prefix, $path, $type);
0572         }
0573 
0574         /** @var Zend_Form_SubForm $subForm */
0575         foreach ($this->getSubForms() as $subForm) {
0576             $subForm->addElementPrefixPath($prefix, $path, $type);
0577         }
0578 
0579         return $this;
0580     }
0581 
0582     /**
0583      * Add prefix paths for all elements
0584      *
0585      * @param  array $spec
0586      * @return Zend_Form
0587      */
0588     public function addElementPrefixPaths(array $spec)
0589     {
0590         $this->_elementPrefixPaths = $this->_elementPrefixPaths + $spec;
0591 
0592         /** @var Zend_Form_Element $element */
0593         foreach ($this->getElements() as $element) {
0594             $element->addPrefixPaths($spec);
0595         }
0596 
0597         return $this;
0598     }
0599 
0600     /**
0601      * Add prefix path for all display groups
0602      *
0603      * @param  string $prefix
0604      * @param  string $path
0605      * @return Zend_Form
0606      */
0607     public function addDisplayGroupPrefixPath($prefix, $path)
0608     {
0609         $this->_displayGroupPrefixPaths[] = array(
0610             'prefix' => $prefix,
0611             'path'   => $path,
0612         );
0613 
0614         /** @var Zend_Form_DisplayGroup $group */
0615         foreach ($this->getDisplayGroups() as $group) {
0616             $group->addPrefixPath($prefix, $path);
0617         }
0618 
0619         return $this;
0620     }
0621 
0622     /**
0623      * Add multiple display group prefix paths at once
0624      *
0625      * @param  array $spec
0626      * @return Zend_Form
0627      */
0628     public function addDisplayGroupPrefixPaths(array $spec)
0629     {
0630         foreach ($spec as $key => $value) {
0631             if (is_string($value) && !is_numeric($key)) {
0632                 $this->addDisplayGroupPrefixPath($key, $value);
0633                 continue;
0634             }
0635 
0636             if (is_string($value) && is_numeric($key)) {
0637                 continue;
0638             }
0639 
0640             if (is_array($value)) {
0641                 $count = count($value);
0642                 if (array_keys($value) === range(0, $count - 1)) {
0643                     if ($count < 2) {
0644                         continue;
0645                     }
0646                     $prefix = array_shift($value);
0647                     $path   = array_shift($value);
0648                     $this->addDisplayGroupPrefixPath($prefix, $path);
0649                     continue;
0650                 }
0651                 if (array_key_exists('prefix', $value) && array_key_exists('path', $value)) {
0652                     $this->addDisplayGroupPrefixPath($value['prefix'], $value['path']);
0653                 }
0654             }
0655         }
0656         return $this;
0657     }
0658 
0659     // Form metadata:
0660 
0661     /**
0662      * Set form attribute
0663      *
0664      * @param  string $key
0665      * @param  mixed $value
0666      * @return Zend_Form
0667      */
0668     public function setAttrib($key, $value)
0669     {
0670         $key = (string) $key;
0671         $this->_attribs[$key] = $value;
0672         return $this;
0673     }
0674 
0675     /**
0676      * Add multiple form attributes at once
0677      *
0678      * @param  array $attribs
0679      * @return Zend_Form
0680      */
0681     public function addAttribs(array $attribs)
0682     {
0683         foreach ($attribs as $key => $value) {
0684             $this->setAttrib($key, $value);
0685         }
0686         return $this;
0687     }
0688 
0689     /**
0690      * Set multiple form attributes at once
0691      *
0692      * Overwrites any previously set attributes.
0693      *
0694      * @param  array $attribs
0695      * @return Zend_Form
0696      */
0697     public function setAttribs(array $attribs)
0698     {
0699         $this->clearAttribs();
0700         return $this->addAttribs($attribs);
0701     }
0702 
0703     /**
0704      * Retrieve a single form attribute
0705      *
0706      * @param  string $key
0707      * @return mixed
0708      */
0709     public function getAttrib($key)
0710     {
0711         $key = (string) $key;
0712         if (!isset($this->_attribs[$key])) {
0713             return null;
0714         }
0715 
0716         return $this->_attribs[$key];
0717     }
0718 
0719     /**
0720      * Retrieve all form attributes/metadata
0721      *
0722      * @return array
0723      */
0724     public function getAttribs()
0725     {
0726         return $this->_attribs;
0727     }
0728 
0729     /**
0730      * Remove attribute
0731      *
0732      * @param  string $key
0733      * @return bool
0734      */
0735     public function removeAttrib($key)
0736     {
0737         if (isset($this->_attribs[$key])) {
0738             unset($this->_attribs[$key]);
0739             return true;
0740         }
0741 
0742         return false;
0743     }
0744 
0745     /**
0746      * Clear all form attributes
0747      *
0748      * @return Zend_Form
0749      */
0750     public function clearAttribs()
0751     {
0752         $this->_attribs = array();
0753         return $this;
0754     }
0755 
0756     /**
0757      * Set form action
0758      *
0759      * @param  string $action
0760      * @return Zend_Form
0761      */
0762     public function setAction($action)
0763     {
0764         return $this->setAttrib('action', (string) $action);
0765     }
0766 
0767     /**
0768      * Get form action
0769      *
0770      * Sets default to '' if not set.
0771      *
0772      * @return string
0773      */
0774     public function getAction()
0775     {
0776         $action = $this->getAttrib('action');
0777         if (null === $action) {
0778             $action = '';
0779             $this->setAction($action);
0780         }
0781         return $action;
0782     }
0783 
0784     /**
0785      * Set form method
0786      *
0787      * Only values in {@link $_methods()} allowed
0788      *
0789      * @param  string $method
0790      * @return Zend_Form
0791      * @throws Zend_Form_Exception
0792      */
0793     public function setMethod($method)
0794     {
0795         $method = strtolower($method);
0796         if (!in_array($method, $this->_methods)) {
0797             // require_once 'Zend/Form/Exception.php';
0798             throw new Zend_Form_Exception(sprintf('"%s" is an invalid form method', $method));
0799         }
0800         $this->setAttrib('method', $method);
0801         return $this;
0802     }
0803 
0804     /**
0805      * Retrieve form method
0806      *
0807      * @return string
0808      */
0809     public function getMethod()
0810     {
0811         if (null === ($method = $this->getAttrib('method'))) {
0812             $method = self::METHOD_POST;
0813             $this->setAttrib('method', $method);
0814         }
0815         return strtolower($method);
0816     }
0817 
0818     /**
0819      * Set encoding type
0820      *
0821      * @param  string $value
0822      * @return Zend_Form
0823      */
0824     public function setEnctype($value)
0825     {
0826         $this->setAttrib('enctype', $value);
0827         return $this;
0828     }
0829 
0830     /**
0831      * Get encoding type
0832      *
0833      * @return string
0834      */
0835     public function getEnctype()
0836     {
0837         if (null === ($enctype = $this->getAttrib('enctype'))) {
0838             $enctype = self::ENCTYPE_URLENCODED;
0839             $this->setAttrib('enctype', $enctype);
0840         }
0841         return $this->getAttrib('enctype');
0842     }
0843 
0844     /**
0845      * Filter a name to only allow valid variable characters
0846      *
0847      * @param  string $value
0848      * @param  bool $allowBrackets
0849      * @return string
0850      */
0851     public function filterName($value, $allowBrackets = false)
0852     {
0853         $charset = '^a-zA-Z0-9_\x7f-\xff';
0854         if ($allowBrackets) {
0855             $charset .= '\[\]';
0856         }
0857         return preg_replace('/[' . $charset . ']/', '', (string) $value);
0858     }
0859 
0860     /**
0861      * Set form name
0862      *
0863      * @param  string $name
0864      * @return Zend_Form
0865      * @throws Zend_Form_Exception
0866      */
0867     public function setName($name)
0868     {
0869         $name = $this->filterName($name);
0870         if ('' === (string)$name) {
0871             // require_once 'Zend/Form/Exception.php';
0872             throw new Zend_Form_Exception('Invalid name provided; must contain only valid variable characters and be non-empty');
0873         }
0874 
0875         return $this->setAttrib('name', $name);
0876     }
0877 
0878     /**
0879      * Get name attribute
0880      *
0881      * @return null|string
0882      */
0883     public function getName()
0884     {
0885         return $this->getAttrib('name');
0886     }
0887 
0888     /**
0889      * Get fully qualified name
0890      *
0891      * Places name as subitem of array and/or appends brackets.
0892      *
0893      * @return string
0894      */
0895     public function getFullyQualifiedName()
0896     {
0897         return $this->getName();
0898     }
0899 
0900     /**
0901      * Get element id
0902      *
0903      * @return string
0904      */
0905     public function getId()
0906     {
0907         if (null !== ($id = $this->getAttrib('id'))) {
0908             return $id;
0909         }
0910 
0911         $id = $this->getFullyQualifiedName();
0912 
0913         // Bail early if no array notation detected
0914         if (!strstr($id, '[')) {
0915             return $id;
0916         }
0917 
0918         // Strip array notation
0919         if ('[]' == substr($id, -2)) {
0920             $id = substr($id, 0, strlen($id) - 2);
0921         }
0922         $id = str_replace('][', '-', $id);
0923         $id = str_replace(array(']', '['), '-', $id);
0924         $id = trim($id, '-');
0925 
0926         return $id;
0927     }
0928 
0929     /**
0930      * Set form legend
0931      *
0932      * @param  string $value
0933      * @return Zend_Form
0934      */
0935     public function setLegend($value)
0936     {
0937         $this->_legend = (string) $value;
0938         return $this;
0939     }
0940 
0941     /**
0942      * Get form legend
0943      *
0944      * @return string
0945      */
0946     public function getLegend()
0947     {
0948         return $this->_legend;
0949     }
0950 
0951     /**
0952      * Set form description
0953      *
0954      * @param  string $value
0955      * @return Zend_Form
0956      */
0957     public function setDescription($value)
0958     {
0959         $this->_description = (string) $value;
0960         return $this;
0961     }
0962 
0963     /**
0964      * Retrieve form description
0965      *
0966      * @return string
0967      */
0968     public function getDescription()
0969     {
0970         return $this->_description;
0971     }
0972 
0973     /**
0974      * Set form order
0975      *
0976      * @param  int $index
0977      * @return Zend_Form
0978      */
0979     public function setOrder($index)
0980     {
0981         $this->_formOrder = (int) $index;
0982         return $this;
0983     }
0984 
0985     /**
0986      * Get form order
0987      *
0988      * @return int|null
0989      */
0990     public function getOrder()
0991     {
0992         return $this->_formOrder;
0993     }
0994 
0995     /**
0996      * When calling renderFormElements or render this method
0997      * is used to set $_isRendered member to prevent repeatedly
0998      * merging belongsTo setting
0999      */
1000     protected function _setIsRendered()
1001     {
1002         $this->_isRendered = true;
1003         return $this;
1004     }
1005 
1006     /**
1007      * Get the value of $_isRendered member
1008      */
1009     protected function _getIsRendered()
1010     {
1011         return (bool)$this->_isRendered;
1012     }
1013 
1014     // Element interaction:
1015 
1016     /**
1017      * Add a new element
1018      *
1019      * $element may be either a string element type, or an object of type
1020      * Zend_Form_Element. If a string element type is provided, $name must be
1021      * provided, and $options may be optionally provided for configuring the
1022      * element.
1023      *
1024      * If a Zend_Form_Element is provided, $name may be optionally provided,
1025      * and any provided $options will be ignored.
1026      *
1027      * @param  string|Zend_Form_Element $element
1028      * @param  string $name
1029      * @param  array|Zend_Config $options
1030      * @throws Zend_Form_Exception on invalid element
1031      * @return Zend_Form
1032      */
1033     public function addElement($element, $name = null, $options = null)
1034     {
1035         if (is_string($element)) {
1036             if (null === $name) {
1037                 // require_once 'Zend/Form/Exception.php';
1038                 throw new Zend_Form_Exception(
1039                     'Elements specified by string must have an accompanying name'
1040                 );
1041             }
1042 
1043             $this->_elements[$name] = $this->createElement($element, $name, $options);
1044         } elseif ($element instanceof Zend_Form_Element) {
1045             $prefixPaths              = array();
1046             $prefixPaths['decorator'] = $this->getPluginLoader('decorator')->getPaths();
1047             if (!empty($this->_elementPrefixPaths)) {
1048                 $prefixPaths = array_merge($prefixPaths, $this->_elementPrefixPaths);
1049             }
1050 
1051             if (is_array($this->_elementDecorators)
1052                 && 0 == count($element->getDecorators())
1053             ) {
1054                 $element->setDecorators($this->_elementDecorators);
1055             }
1056 
1057             if (null === $name) {
1058                 $name = $element->getName();
1059             }
1060 
1061             $this->_elements[$name] = $element;
1062             $this->_elements[$name]->addPrefixPaths($prefixPaths);
1063         } else {
1064             // require_once 'Zend/Form/Exception.php';
1065             throw new Zend_Form_Exception(
1066                 'Element must be specified by string or Zend_Form_Element instance'
1067             );
1068         }
1069 
1070         $this->_order[$name] = $this->_elements[$name]->getOrder();
1071         $this->_orderUpdated = true;
1072         $this->_setElementsBelongTo($name);
1073 
1074         return $this;
1075     }
1076 
1077     /**
1078      * Create an element
1079      *
1080      * Acts as a factory for creating elements. Elements created with this
1081      * method will not be attached to the form, but will contain element
1082      * settings as specified in the form object (including plugin loader
1083      * prefix paths, default decorators, etc.).
1084      *
1085      * @param  string            $type
1086      * @param  string            $name
1087      * @param  array|Zend_Config $options
1088      * @throws Zend_Form_Exception
1089      * @return Zend_Form_Element
1090      */
1091     public function createElement($type, $name, $options = null)
1092     {
1093         if (!is_string($type)) {
1094             // require_once 'Zend/Form/Exception.php';
1095             throw new Zend_Form_Exception('Element type must be a string indicating type');
1096         }
1097 
1098         if (!is_string($name)) {
1099             // require_once 'Zend/Form/Exception.php';
1100             throw new Zend_Form_Exception('Element name must be a string');
1101         }
1102 
1103         $prefixPaths              = array();
1104         $prefixPaths['decorator'] = $this->getPluginLoader('decorator')->getPaths();
1105         if (!empty($this->_elementPrefixPaths)) {
1106             $prefixPaths = array_merge($prefixPaths, $this->_elementPrefixPaths);
1107         }
1108 
1109         if ($options instanceof Zend_Config) {
1110             $options = $options->toArray();
1111         }
1112 
1113         if ((null === $options) || !is_array($options)) {
1114             $options = array('prefixPath' => $prefixPaths);
1115 
1116             if (is_array($this->_elementDecorators)) {
1117                 $options['decorators'] = $this->_elementDecorators;
1118             }
1119         } elseif (is_array($options)) {
1120             if (array_key_exists('prefixPath', $options)) {
1121                 $options['prefixPath'] = array_merge($prefixPaths, $options['prefixPath']);
1122             } else {
1123                 $options['prefixPath'] = $prefixPaths;
1124             }
1125 
1126             if (is_array($this->_elementDecorators)
1127                 && !array_key_exists('decorators', $options)
1128             ) {
1129                 $options['decorators'] = $this->_elementDecorators;
1130             }
1131         }
1132 
1133         $class = $this->getPluginLoader(self::ELEMENT)->load($type);
1134         $element = new $class($name, $options);
1135 
1136         return $element;
1137     }
1138 
1139     /**
1140      * Add multiple elements at once
1141      *
1142      * @param  array $elements
1143      * @return Zend_Form
1144      */
1145     public function addElements(array $elements)
1146     {
1147         foreach ($elements as $key => $spec) {
1148             $name = null;
1149             if (!is_numeric($key)) {
1150                 $name = $key;
1151             }
1152 
1153             if (is_string($spec) || ($spec instanceof Zend_Form_Element)) {
1154                 $this->addElement($spec, $name);
1155                 continue;
1156             }
1157 
1158             if (is_array($spec)) {
1159                 $argc = count($spec);
1160                 $options = array();
1161                 if (isset($spec['type'])) {
1162                     $type = $spec['type'];
1163                     if (isset($spec['name'])) {
1164                         $name = $spec['name'];
1165                     }
1166                     if (isset($spec['options'])) {
1167                         $options = $spec['options'];
1168                     }
1169                     $this->addElement($type, $name, $options);
1170                 } else {
1171                     switch ($argc) {
1172                         case 0:
1173                             continue;
1174                         case (1 <= $argc):
1175                             $type = array_shift($spec);
1176                         case (2 <= $argc):
1177                             if (null === $name) {
1178                                 $name = array_shift($spec);
1179                             } else {
1180                                 $options = array_shift($spec);
1181                             }
1182                         case (3 <= $argc):
1183                             if (empty($options)) {
1184                                 $options = array_shift($spec);
1185                             }
1186                         default:
1187                             $this->addElement($type, $name, $options);
1188                     }
1189                 }
1190             }
1191         }
1192         return $this;
1193     }
1194 
1195     /**
1196      * Set form elements (overwrites existing elements)
1197      *
1198      * @param  array $elements
1199      * @return Zend_Form
1200      */
1201     public function setElements(array $elements)
1202     {
1203         $this->clearElements();
1204         return $this->addElements($elements);
1205     }
1206 
1207     /**
1208      * Retrieve a single element
1209      *
1210      * @param  string $name
1211      * @return Zend_Form_Element|null
1212      */
1213     public function getElement($name)
1214     {
1215         if (array_key_exists($name, $this->_elements)) {
1216             return $this->_elements[$name];
1217         }
1218         return null;
1219     }
1220 
1221     /**
1222      * Retrieve all elements
1223      *
1224      * @return array
1225      */
1226     public function getElements()
1227     {
1228         return $this->_elements;
1229     }
1230 
1231     /**
1232      * Remove element
1233      *
1234      * @param  string $name
1235      * @return boolean
1236      */
1237     public function removeElement($name)
1238     {
1239         $name = (string) $name;
1240         if (isset($this->_elements[$name])) {
1241             unset($this->_elements[$name]);
1242             if (array_key_exists($name, $this->_order)) {
1243                 unset($this->_order[$name]);
1244                 $this->_orderUpdated = true;
1245             } else {
1246                 /** @var Zend_Form_DisplayGroup $group */
1247                 foreach ($this->_displayGroups as $group) {
1248                     if (null !== $group->getElement($name)) {
1249                         $group->removeElement($name);
1250                     }
1251                 }
1252             }
1253             return true;
1254         }
1255 
1256         return false;
1257     }
1258 
1259     /**
1260      * Remove all form elements
1261      *
1262      * @return Zend_Form
1263      */
1264     public function clearElements()
1265     {
1266         foreach (array_keys($this->_elements) as $key) {
1267             if (array_key_exists($key, $this->_order)) {
1268                 unset($this->_order[$key]);
1269             }
1270         }
1271         $this->_elements     = array();
1272         $this->_orderUpdated = true;
1273         return $this;
1274     }
1275 
1276     /**
1277      * Set default values for elements
1278      *
1279      * Sets values for all elements specified in the array of $defaults.
1280      *
1281      * @param  array $defaults
1282      * @return Zend_Form
1283      */
1284     public function setDefaults(array $defaults)
1285     {
1286         $eBelongTo = null;
1287 
1288         if ($this->isArray()) {
1289             $eBelongTo = $this->getElementsBelongTo();
1290             $defaults = $this->_dissolveArrayValue($defaults, $eBelongTo);
1291         }
1292         /** @var Zend_Form_Element $element */
1293         foreach ($this->getElements() as $name => $element) {
1294             $check = $defaults;
1295             if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) {
1296                 $check = $this->_dissolveArrayValue($defaults, $belongsTo);
1297             }
1298             if (array_key_exists($name, (array)$check)) {
1299                 $this->setDefault($name, $check[$name]);
1300                 $defaults = $this->_dissolveArrayUnsetKey($defaults, $belongsTo, $name);
1301             }
1302         }
1303         /** @var Zend_Form_SubForm $form */
1304         foreach ($this->getSubForms() as $name => $form) {
1305             if (!$form->isArray() && array_key_exists($name, $defaults)) {
1306                 $form->setDefaults($defaults[$name]);
1307             } else {
1308                 $form->setDefaults($defaults);
1309             }
1310         }
1311         return $this;
1312     }
1313 
1314     /**
1315      * Set default value for an element
1316      *
1317      * @param  string $name
1318      * @param  mixed $value
1319      * @return Zend_Form
1320      */
1321     public function setDefault($name, $value)
1322     {
1323         $name = (string) $name;
1324         if ($element = $this->getElement($name)) {
1325             $element->setValue($value);
1326         } else {
1327             if (is_scalar($value)) {
1328                 /** @var Zend_Form_SubForm $subForm */
1329                 foreach ($this->getSubForms() as $subForm) {
1330                     $subForm->setDefault($name, $value);
1331                 }
1332             } elseif (is_array($value) && ($subForm = $this->getSubForm($name))) {
1333                 $subForm->setDefaults($value);
1334             }
1335         }
1336         return $this;
1337     }
1338 
1339     /**
1340      * Retrieve value for single element
1341      *
1342      * @param  string $name
1343      * @return mixed
1344      */
1345     public function getValue($name)
1346     {
1347         if ($element = $this->getElement($name)) {
1348             return $element->getValue();
1349         }
1350 
1351         if ($subForm = $this->getSubForm($name)) {
1352             return $subForm->getValues(true);
1353         }
1354 
1355         /** @var Zend_Form_SubForm $subForm */
1356         foreach ($this->getSubForms() as $subForm) {
1357             if ($name == $subForm->getElementsBelongTo()) {
1358                 return $subForm->getValues(true);
1359             }
1360         }
1361         return null;
1362     }
1363 
1364     /**
1365      * Retrieve all form element values
1366      *
1367      * @param  bool $suppressArrayNotation
1368      * @return array
1369      */
1370     public function getValues($suppressArrayNotation = false)
1371     {
1372         $values = array();
1373         $eBelongTo = null;
1374 
1375         if ($this->isArray()) {
1376             $eBelongTo = $this->getElementsBelongTo();
1377         }
1378         /** @var Zend_Form_Element $element */
1379         foreach ($this->getElements() as $key => $element) {
1380             if (!$element->getIgnore()) {
1381                 $merge = array();
1382                 if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) {
1383                     if ('' !== (string)$belongsTo) {
1384                         $key = $belongsTo . '[' . $key . ']';
1385                     }
1386                 }
1387                 $merge = $this->_attachToArray($element->getValue(), $key);
1388                 $values = $this->_array_replace_recursive($values, $merge);
1389             }
1390         }
1391         /** @var Zend_Form_SubForm $subForm */
1392         foreach ($this->getSubForms() as $key => $subForm) {
1393             $merge = array();
1394             if (!$subForm->isArray()) {
1395                 $merge[$key] = $subForm->getValues();
1396             } else {
1397                 $merge = $this->_attachToArray($subForm->getValues(true),
1398                                                $subForm->getElementsBelongTo());
1399             }
1400             $values = $this->_array_replace_recursive($values, $merge);
1401         }
1402 
1403         if (!$suppressArrayNotation &&
1404             $this->isArray() &&
1405             !$this->_getIsRendered()) {
1406             $values = $this->_attachToArray($values, $this->getElementsBelongTo());
1407         }
1408 
1409         return $values;
1410     }
1411 
1412     /**
1413      * Returns only the valid values from the given form input.
1414      *
1415      * For models that can be saved in a partially valid state, for example when following the builder,
1416      * prototype or state patterns it is particularly interessting to retrieve all the current valid
1417      * values to persist them.
1418      *
1419      * @param  array $data
1420      * @param  bool $suppressArrayNotation
1421      * @return array
1422      */
1423     public function getValidValues($data, $suppressArrayNotation = false)
1424     {
1425         $values = array();
1426         $eBelongTo = null;
1427 
1428         if ($this->isArray()) {
1429             $eBelongTo = $this->getElementsBelongTo();
1430             $data = $this->_dissolveArrayValue($data, $eBelongTo);
1431         }
1432         $context = $data;
1433         /** @var Zend_Form_Element $element */
1434         foreach ($this->getElements() as $key => $element) {
1435             if (!$element->getIgnore()) {
1436                 $check = $data;
1437                 if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) {
1438                     $check = $this->_dissolveArrayValue($data, $belongsTo);
1439                 }
1440                 if (isset($check[$key])) {
1441                     if($element->isValid($check[$key], $context)) {
1442                         $merge = array();
1443                         if ($belongsTo !== $eBelongTo && '' !== (string)$belongsTo) {
1444                             $key = $belongsTo . '[' . $key . ']';
1445                         }
1446                         $merge = $this->_attachToArray($element->getValue(), $key);
1447                         $values = $this->_array_replace_recursive($values, $merge);
1448                     }
1449                     $data = $this->_dissolveArrayUnsetKey($data, $belongsTo, $key);
1450                 }
1451             }
1452         }
1453         /** @var Zend_Form_SubForm $form */
1454         foreach ($this->getSubForms() as $key => $form) {
1455             $merge = array();
1456             if (isset($data[$key]) && !$form->isArray()) {
1457                 $tmp = $form->getValidValues($data[$key]);
1458                 if (!empty($tmp)) {
1459                     $merge[$key] = $tmp;
1460                 }
1461             } else {
1462                 $tmp = $form->getValidValues($data, true);
1463                 if (!empty($tmp)) {
1464                     $merge = $this->_attachToArray($tmp, $form->getElementsBelongTo());
1465                 }
1466             }
1467             $values = $this->_array_replace_recursive($values, $merge);
1468         }
1469         if (!$suppressArrayNotation &&
1470             $this->isArray() &&
1471             !empty($values) &&
1472             !$this->_getIsRendered()) {
1473             $values = $this->_attachToArray($values, $this->getElementsBelongTo());
1474         }
1475 
1476         return $values;
1477     }
1478 
1479     /**
1480      * Get unfiltered element value
1481      *
1482      * @param  string $name
1483      * @return mixed
1484      */
1485     public function getUnfilteredValue($name)
1486     {
1487         if ($element = $this->getElement($name)) {
1488             return $element->getUnfilteredValue();
1489         }
1490         return null;
1491     }
1492 
1493     /**
1494      * Retrieve all unfiltered element values
1495      *
1496      * @return array
1497      */
1498     public function getUnfilteredValues()
1499     {
1500         $values = array();
1501         /** @var Zend_Form_Element $element */
1502         foreach ($this->getElements() as $key => $element) {
1503             $values[$key] = $element->getUnfilteredValue();
1504         }
1505 
1506         return $values;
1507     }
1508 
1509     /**
1510      * Set all elements' filters
1511      *
1512      * @param  array $filters
1513      * @return Zend_Form
1514      */
1515     public function setElementFilters(array $filters)
1516     {
1517         /** @var Zend_Form_Element $element */
1518         foreach ($this->getElements() as $element) {
1519             $element->setFilters($filters);
1520         }
1521         return $this;
1522     }
1523 
1524     /**
1525      * Set name of array elements belong to
1526      *
1527      * @param  string $array
1528      * @return Zend_Form
1529      */
1530     public function setElementsBelongTo($array)
1531     {
1532         $origName = $this->getElementsBelongTo();
1533         $name = $this->filterName($array, true);
1534         if ('' === $name) {
1535             $name = null;
1536         }
1537         $this->_elementsBelongTo = $name;
1538 
1539         if (null === $name) {
1540             $this->setIsArray(false);
1541             if (null !== $origName) {
1542                 $this->_setElementsBelongTo();
1543             }
1544         } else {
1545             $this->setIsArray(true);
1546             $this->_setElementsBelongTo();
1547         }
1548 
1549         return $this;
1550     }
1551 
1552     /**
1553      * Set array to which elements belong
1554      *
1555      * @param  string $name Element name
1556      * @return void
1557      */
1558     protected function _setElementsBelongTo($name = null)
1559     {
1560         $array = $this->getElementsBelongTo();
1561 
1562         if (null === $array) {
1563             return;
1564         }
1565 
1566         if (null === $name) {
1567             /** @var Zend_Form_Element $element */
1568             foreach ($this->getElements() as $element) {
1569                 $element->setBelongsTo($array);
1570             }
1571         } else {
1572             if (null !== ($element = $this->getElement($name))) {
1573                 $element->setBelongsTo($array);
1574             }
1575         }
1576     }
1577 
1578     /**
1579      * Get name of array elements belong to
1580      *
1581      * @return string|null
1582      */
1583     public function getElementsBelongTo()
1584     {
1585         if ((null === $this->_elementsBelongTo) && $this->isArray()) {
1586             $name = $this->getName();
1587             if ('' !== (string)$name) {
1588                 return $name;
1589             }
1590         }
1591         return $this->_elementsBelongTo;
1592     }
1593 
1594     /**
1595      * Set flag indicating elements belong to array
1596      *
1597      * @param  bool $flag Value of flag
1598      * @return Zend_Form
1599      */
1600     public function setIsArray($flag)
1601     {
1602         $this->_isArray = (bool) $flag;
1603         return $this;
1604     }
1605 
1606     /**
1607      * Get flag indicating if elements belong to an array
1608      *
1609      * @return bool
1610      */
1611     public function isArray()
1612     {
1613         return $this->_isArray;
1614     }
1615 
1616     // Element groups:
1617 
1618     /**
1619      * Add a form group/subform
1620      *
1621      * @param  Zend_Form $form
1622      * @param  string $name
1623      * @param  int $order
1624      * @return Zend_Form
1625      */
1626     public function addSubForm(Zend_Form $form, $name, $order = null)
1627     {
1628         $name = (string) $name;
1629         /** @var Zend_Loader_PluginLoader $loader */
1630         foreach ($this->_loaders as $type => $loader) {
1631             $loaderPaths = $loader->getPaths();
1632             foreach ($loaderPaths as $prefix => $paths) {
1633                 foreach ($paths as $path) {
1634                     $form->addPrefixPath($prefix, $path, $type);
1635                 }
1636             }
1637         }
1638 
1639         if (!empty($this->_elementPrefixPaths)) {
1640             foreach ($this->_elementPrefixPaths as $spec) {
1641                 list($prefix, $path, $type) = array_values($spec);
1642                 $form->addElementPrefixPath($prefix, $path, $type);
1643             }
1644         }
1645 
1646         if (!empty($this->_displayGroupPrefixPaths)) {
1647             foreach ($this->_displayGroupPrefixPaths as $spec) {
1648                 list($prefix, $path) = array_values($spec);
1649                 $form->addDisplayGroupPrefixPath($prefix, $path);
1650             }
1651         }
1652 
1653         if (null !== $order) {
1654             $form->setOrder($order);
1655         }
1656 
1657         if (($oldName = $form->getName()) &&
1658             $oldName !== $name &&
1659             $oldName === $form->getElementsBelongTo()) {
1660             $form->setElementsBelongTo($name);
1661         }
1662 
1663         $form->setName($name);
1664         $this->_subForms[$name] = $form;
1665         $this->_order[$name]    = $order;
1666         $this->_orderUpdated    = true;
1667         return $this;
1668     }
1669 
1670     /**
1671      * Add multiple form subForms/subforms at once
1672      *
1673      * @param  array $subForms
1674      * @return Zend_Form
1675      */
1676     public function addSubForms(array $subForms)
1677     {
1678         foreach ($subForms as $key => $spec) {          
1679             $name = (string) $key;
1680             if ($spec instanceof Zend_Form) {
1681                 $this->addSubForm($spec, $name);
1682                 continue;
1683             }
1684 
1685             if (is_array($spec)) {
1686                 $argc  = count($spec);
1687                 $order = null;
1688                 switch ($argc) {
1689                     case 0:
1690                         continue;
1691                     case (1 <= $argc):
1692                         $subForm = array_shift($spec);
1693 
1694                         if (!$subForm instanceof Zend_Form) {
1695                             $subForm = new Zend_Form_SubForm($subForm);
1696                         }
1697                     case (2 <= $argc):
1698                         $name  = array_shift($spec);
1699                     case (3 <= $argc):
1700                         $order = array_shift($spec);
1701                     default:
1702                         $this->addSubForm($subForm, $name, $order);
1703                 }
1704             }
1705         }
1706         return $this;
1707     }
1708 
1709     /**
1710      * Set multiple form subForms/subforms (overwrites)
1711      *
1712      * @param  array $subForms
1713      * @return Zend_Form
1714      */
1715     public function setSubForms(array $subForms)
1716     {
1717         $this->clearSubForms();
1718         return $this->addSubForms($subForms);
1719     }
1720 
1721     /**
1722      * Retrieve a form subForm/subform
1723      *
1724      * @param  string $name
1725      * @return Zend_Form|null
1726      */
1727     public function getSubForm($name)
1728     {
1729         $name = (string) $name;
1730         if (isset($this->_subForms[$name])) {
1731             return $this->_subForms[$name];
1732         }
1733         return null;
1734     }
1735 
1736     /**
1737      * Retrieve all form subForms/subforms
1738      *
1739      * @return array
1740      */
1741     public function getSubForms()
1742     {
1743         return $this->_subForms;
1744     }
1745 
1746     /**
1747      * Remove form subForm/subform
1748      *
1749      * @param  string $name
1750      * @return boolean
1751      */
1752     public function removeSubForm($name)
1753     {
1754         $name = (string) $name;
1755         if (array_key_exists($name, $this->_subForms)) {
1756             unset($this->_subForms[$name]);
1757             if (array_key_exists($name, $this->_order)) {
1758                 unset($this->_order[$name]);
1759                 $this->_orderUpdated = true;
1760             }
1761             return true;
1762         }
1763 
1764         return false;
1765     }
1766 
1767     /**
1768      * Remove all form subForms/subforms
1769      *
1770      * @return Zend_Form
1771      */
1772     public function clearSubForms()
1773     {
1774         foreach (array_keys($this->_subForms) as $key) {
1775             if (array_key_exists($key, $this->_order)) {
1776                 unset($this->_order[$key]);
1777             }
1778         }
1779         $this->_subForms     = array();
1780         $this->_orderUpdated = true;
1781         return $this;
1782     }
1783 
1784 
1785     // Display groups:
1786 
1787     /**
1788      * Set default display group class
1789      *
1790      * @param  string $class
1791      * @return Zend_Form
1792      */
1793     public function setDefaultDisplayGroupClass($class)
1794     {
1795         $this->_defaultDisplayGroupClass = (string) $class;
1796         return $this;
1797     }
1798 
1799     /**
1800      * Retrieve default display group class
1801      *
1802      * @return string
1803      */
1804     public function getDefaultDisplayGroupClass()
1805     {
1806         return $this->_defaultDisplayGroupClass;
1807     }
1808 
1809     /**
1810      * Add a display group
1811      *
1812      * Groups named elements for display purposes.
1813      *
1814      * If a referenced element does not yet exist in the form, it is omitted.
1815      *
1816      * @param  array $elements
1817      * @param  string $name
1818      * @param  array|Zend_Config $options
1819      * @return Zend_Form
1820      * @throws Zend_Form_Exception if no valid elements provided
1821      */
1822     public function addDisplayGroup(array $elements, $name, $options = null)
1823     {
1824         $group = array();
1825         foreach ($elements as $element) {
1826             if($element instanceof Zend_Form_Element) {
1827                 $elementName = $element->getName();
1828                 if (!isset($this->_elements[$elementName])) {
1829                     $this->addElement($element);
1830                 }
1831                 $element = $elementName;
1832             }
1833 
1834             if (isset($this->_elements[$element])) {
1835                 $add = $this->getElement($element);
1836                 if (null !== $add) {
1837                     $group[] = $add;
1838                 }
1839             }
1840         }
1841         if (empty($group)) {
1842             // require_once 'Zend/Form/Exception.php';
1843             throw new Zend_Form_Exception('No valid elements specified for display group');
1844         }
1845 
1846         $name = (string) $name;
1847 
1848         if (is_array($options)) {
1849             $options['form']     = $this;
1850             $options['elements'] = $group;
1851         } elseif ($options instanceof Zend_Config) {
1852             $options = $options->toArray();
1853             $options['form']     = $this;
1854             $options['elements'] = $group;
1855         } else {
1856             $options = array(
1857                 'form'     => $this,
1858                 'elements' => $group,
1859             );
1860         }
1861 
1862         if (isset($options['displayGroupClass'])) {
1863             $class = $options['displayGroupClass'];
1864             unset($options['displayGroupClass']);
1865         } else {
1866             $class = $this->getDefaultDisplayGroupClass();
1867         }
1868 
1869         if (!class_exists($class)) {
1870             // require_once 'Zend/Loader.php';
1871             Zend_Loader::loadClass($class);
1872         }
1873         $this->_displayGroups[$name] = new $class(
1874             $name,
1875             $this->getPluginLoader(self::DECORATOR),
1876             $options
1877         );
1878 
1879         if (!empty($this->_displayGroupPrefixPaths)) {
1880             $this->_displayGroups[$name]->addPrefixPaths($this->_displayGroupPrefixPaths);
1881         }
1882 
1883         $this->_order[$name] = $this->_displayGroups[$name]->getOrder();
1884         $this->_orderUpdated = true;
1885         return $this;
1886     }
1887 
1888     /**
1889      * Add a display group object (used with cloning)
1890      *
1891      * @param  Zend_Form_DisplayGroup $group
1892      * @param  string|null            $name
1893      * @throws Zend_Form_Exception
1894      * @return Zend_Form
1895      */
1896     protected function _addDisplayGroupObject(Zend_Form_DisplayGroup $group, $name = null)
1897     {
1898         if (null === $name) {
1899             $name = $group->getName();
1900             if ('' === (string)$name) {
1901                 // require_once 'Zend/Form/Exception.php';
1902                 throw new Zend_Form_Exception('Invalid display group added; requires name');
1903             }
1904         }
1905 
1906         $this->_displayGroups[$name] = $group;
1907         $group->setForm($this);
1908 
1909         if (!empty($this->_displayGroupPrefixPaths)) {
1910             $this->_displayGroups[$name]->addPrefixPaths($this->_displayGroupPrefixPaths);
1911         }
1912 
1913         $this->_order[$name] = $this->_displayGroups[$name]->getOrder();
1914         $this->_orderUpdated = true;
1915         return $this;
1916     }
1917 
1918     /**
1919      * Add multiple display groups at once
1920      *
1921      * @param  array $groups
1922      * @return Zend_Form
1923      */
1924     public function addDisplayGroups(array $groups)
1925     {
1926         foreach ($groups as $key => $spec) {
1927             $name = null;
1928             if (!is_numeric($key)) {
1929                 $name = $key;
1930             }
1931 
1932             if ($spec instanceof Zend_Form_DisplayGroup) {
1933                 $this->_addDisplayGroupObject($spec);
1934             }
1935 
1936             if (!is_array($spec) || empty($spec)) {
1937                 continue;
1938             }
1939 
1940             $argc    = count($spec);
1941             $options = array();
1942 
1943             if (isset($spec['elements'])) {
1944                 $elements = $spec['elements'];
1945                 if (isset($spec['name'])) {
1946                     $name = $spec['name'];
1947                 }
1948                 if (isset($spec['options'])) {
1949                     $options = $spec['options'];
1950                 }
1951                 $this->addDisplayGroup($elements, $name, $options);
1952             } else {
1953                 switch ($argc) {
1954                     case (1 <= $argc):
1955                         $elements = array_shift($spec);
1956                         if (!is_array($elements) && (null !== $name)) {
1957                             $elements = array_merge((array) $elements, $spec);
1958                             $this->addDisplayGroup($elements, $name);
1959                             break;
1960                         }
1961                     case (2 <= $argc):
1962                         if (null !== $name) {
1963                             $options = array_shift($spec);
1964                             $this->addDisplayGroup($elements, $name, $options);
1965                             break;
1966                         }
1967                         $name = array_shift($spec);
1968                     case (3 <= $argc):
1969                         $options = array_shift($spec);
1970                     default:
1971                         $this->addDisplayGroup($elements, $name, $options);
1972                 }
1973             }
1974         }
1975         return $this;
1976     }
1977 
1978     /**
1979      * Add multiple display groups (overwrites)
1980      *
1981      * @param  array $groups
1982      * @return Zend_Form
1983      */
1984     public function setDisplayGroups(array $groups)
1985     {
1986         return $this->clearDisplayGroups()
1987                     ->addDisplayGroups($groups);
1988     }
1989 
1990     /**
1991      * Return a display group
1992      *
1993      * @param  string $name
1994      * @return Zend_Form_DisplayGroup|null
1995      */
1996     public function getDisplayGroup($name)
1997     {
1998         $name = (string) $name;
1999         if (isset($this->_displayGroups[$name])) {
2000             return $this->_displayGroups[$name];
2001         }
2002 
2003         return null;
2004     }
2005 
2006     /**
2007      * Return all display groups
2008      *
2009      * @return array
2010      */
2011     public function getDisplayGroups()
2012     {
2013         return $this->_displayGroups;
2014     }
2015 
2016     /**
2017      * Remove a display group by name
2018      *
2019      * @param  string $name
2020      * @return boolean
2021      */
2022     public function removeDisplayGroup($name)
2023     {
2024         $name = (string) $name;
2025         if (array_key_exists($name, $this->_displayGroups)) {
2026             /** @var Zend_Form_Element $element */
2027             foreach ($this->_displayGroups[$name] as $key => $element) {
2028                 if (array_key_exists($key, $this->_elements)) {
2029                     $this->_order[$key]  = $element->getOrder();
2030                     $this->_orderUpdated = true;
2031                 }
2032             }
2033             unset($this->_displayGroups[$name]);
2034 
2035             if (array_key_exists($name, $this->_order)) {
2036                 unset($this->_order[$name]);
2037                 $this->_orderUpdated = true;
2038             }
2039             return true;
2040         }
2041 
2042         return false;
2043     }
2044 
2045     /**
2046      * Remove all display groups
2047      *
2048      * @return Zend_Form
2049      */
2050     public function clearDisplayGroups()
2051     {
2052         foreach ($this->_displayGroups as $key => $group) {
2053             if (array_key_exists($key, $this->_order)) {
2054                 unset($this->_order[$key]);
2055             }
2056             /** @var Zend_Form_Element $element */
2057             foreach ($group as $name => $element) {
2058                 if (isset($this->_elements[$name])) {
2059                     $this->_order[$name] = $element->getOrder();
2060                 }
2061                 $this->_order[$name] = $element->getOrder();
2062             }
2063         }
2064         $this->_displayGroups = array();
2065         $this->_orderUpdated  = true;
2066         return $this;
2067     }
2068 
2069 
2070     // Processing
2071 
2072     /**
2073      * Populate form
2074      *
2075      * Proxies to {@link setDefaults()}
2076      *
2077      * @param  array $values
2078      * @return Zend_Form
2079      */
2080     public function populate(array $values)
2081     {
2082         return $this->setDefaults($values);
2083     }
2084 
2085     /**
2086      * Determine array key name from given value
2087      *
2088      * Given a value such as foo[bar][baz], returns the last element (in this case, 'baz').
2089      *
2090      * @param  string $value
2091      * @return string
2092      */
2093     protected function _getArrayName($value)
2094     {
2095         if (!is_string($value) || '' === $value) {
2096             return $value;
2097         }
2098 
2099         if (!strstr($value, '[')) {
2100             return $value;
2101         }
2102 
2103         $endPos = strlen($value) - 1;
2104         if (']' != $value[$endPos]) {
2105             return $value;
2106         }
2107 
2108         $start = strrpos($value, '[') + 1;
2109         $name = substr($value, $start, $endPos - $start);
2110         return $name;
2111     }
2112 
2113     /**
2114      * Extract the value by walking the array using given array path.
2115      *
2116      * Given an array path such as foo[bar][baz], returns the value of the last
2117      * element (in this case, 'baz').
2118      *
2119      * @param  array $value Array to walk
2120      * @param  string $arrayPath Array notation path of the part to extract
2121      * @return string
2122      */
2123     protected function _dissolveArrayValue($value, $arrayPath)
2124     {
2125         // As long as we have more levels
2126         while ($arrayPos = strpos($arrayPath, '[')) {
2127             // Get the next key in the path
2128             $arrayKey = trim(substr($arrayPath, 0, $arrayPos), ']');
2129 
2130             // Set the potentially final value or the next search point in the array
2131             if (isset($value[$arrayKey])) {
2132                 $value = $value[$arrayKey];
2133             }
2134 
2135             // Set the next search point in the path
2136             $arrayPath = trim(substr($arrayPath, $arrayPos + 1), ']');
2137         }
2138 
2139         if (isset($value[$arrayPath])) {
2140             $value = $value[$arrayPath];
2141         }
2142 
2143         return $value;
2144     }
2145 
2146     /**
2147      * Given an array, an optional arrayPath and a key this method
2148      * dissolves the arrayPath and unsets the key within the array
2149      * if it exists.
2150      *
2151      * @param array $array
2152      * @param string|null $arrayPath
2153      * @param string $key
2154      * @return array
2155      */
2156     protected function _dissolveArrayUnsetKey($array, $arrayPath, $key)
2157     {
2158         $unset =& $array;
2159         $path  = trim(strtr((string)$arrayPath, array('[' => '/', ']' => '')), '/');
2160         $segs  = ('' !== $path) ? explode('/', $path) : array();
2161 
2162         foreach ($segs as $seg) {
2163             if (!array_key_exists($seg, (array)$unset)) {
2164                 return $array;
2165             }
2166             $unset =& $unset[$seg];
2167         }
2168         if (array_key_exists($key, (array)$unset)) {
2169             unset($unset[$key]);
2170         }
2171         return $array;
2172     }
2173 
2174     /**
2175      * Converts given arrayPath to an array and attaches given value at the end of it.
2176      *
2177      * @param  mixed $value The value to attach
2178      * @param  string $arrayPath Given array path to convert and attach to.
2179      * @return array
2180      */
2181     protected function _attachToArray($value, $arrayPath)
2182     {
2183         // As long as we have more levels
2184         while ($arrayPos = strrpos($arrayPath, '[')) {
2185             // Get the next key in the path
2186             $arrayKey = trim(substr($arrayPath, $arrayPos + 1), ']');
2187 
2188             // Attach
2189             $value = array($arrayKey => $value);
2190 
2191             // Set the next search point in the path
2192             $arrayPath = trim(substr($arrayPath, 0, $arrayPos), ']');
2193         }
2194 
2195         $value = array($arrayPath => $value);
2196 
2197         return $value;
2198     }
2199 
2200     /**
2201      * Returns a one dimensional numerical indexed array with the
2202      * Elements, SubForms and Elements from DisplayGroups as Values.
2203      *
2204      * Subitems are inserted based on their order Setting if set,
2205      * otherwise they are appended, the resulting numerical index
2206      * may differ from the order value.
2207      *
2208      * @access protected
2209      * @return array
2210      */
2211     public function getElementsAndSubFormsOrdered()
2212     {
2213         $ordered = array();
2214         foreach ($this->_order as $name => $order) {
2215             $order = isset($order) ? $order : count($ordered);
2216             if ($this->$name instanceof Zend_Form_Element ||
2217                 $this->$name instanceof Zend_Form) {
2218                 array_splice($ordered, $order, 0, array($this->$name));
2219             } else if ($this->$name instanceof Zend_Form_DisplayGroup) {
2220                 $subordered = array();
2221                 /** @var Zend_Form_Element $element */
2222                 foreach ($this->$name->getElements() as $element) {
2223                     $suborder = $element->getOrder();
2224                     $suborder = (null !== $suborder) ? $suborder : count($subordered);
2225                     array_splice($subordered, $suborder, 0, array($element));
2226                 }
2227                 if (!empty($subordered)) {
2228                     array_splice($ordered, $order, 0, $subordered);
2229                 }
2230             }
2231         }
2232         return $ordered;
2233     }
2234 
2235     /**
2236      * This is a helper function until php 5.3 is widespreaded
2237      *
2238      * @param array $into
2239      * @return array
2240      */
2241     protected function _array_replace_recursive(array $into)
2242     {
2243         $fromArrays = array_slice(func_get_args(),1);
2244 
2245         foreach ($fromArrays as $from) {
2246             foreach ($from as $key => $value) {
2247                 if (is_array($value)) {
2248                     if (!isset($into[$key])) {
2249                         $into[$key] = array();
2250                     }
2251                     $into[$key] = $this->_array_replace_recursive($into[$key], $from[$key]);
2252                 } else {
2253                     $into[$key] = $value;
2254                 }
2255             }
2256         }
2257         return $into;
2258     }
2259 
2260     /**
2261      * Validate the form
2262      *
2263      * @param  array $data
2264      * @throws Zend_Form_Exception
2265      * @return bool
2266      */
2267     public function isValid($data)
2268     {
2269         if (!is_array($data)) {
2270             // require_once 'Zend/Form/Exception.php';
2271             throw new Zend_Form_Exception(__METHOD__ . ' expects an array');
2272         }
2273         $translator = $this->getTranslator();
2274         $valid      = true;
2275         $eBelongTo  = null;
2276 
2277         if ($this->isArray()) {
2278             $eBelongTo = $this->getElementsBelongTo();
2279             $data = $this->_dissolveArrayValue($data, $eBelongTo);
2280         }
2281         $context = $data;
2282         /** @var Zend_Form_Element $element */
2283         foreach ($this->getElements() as $key => $element) {
2284             if (null !== $translator && $this->hasTranslator()
2285                     && !$element->hasTranslator()) {
2286                 $element->setTranslator($translator);
2287             }
2288             $check = $data;
2289             if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) {
2290                 $check = $this->_dissolveArrayValue($data, $belongsTo);
2291             }
2292             if (!isset($check[$key])) {
2293                 $valid = $element->isValid(null, $context) && $valid;
2294             } else {
2295                 $valid = $element->isValid($check[$key], $context) && $valid;
2296                 $data = $this->_dissolveArrayUnsetKey($data, $belongsTo, $key);
2297             }
2298         }
2299         /** @var Zend_Form_SubForm $form */
2300         foreach ($this->getSubForms() as $key => $form) {
2301             if (null !== $translator && $this->hasTranslator()
2302                     && !$form->hasTranslator()) {
2303                 $form->setTranslator($translator);
2304             }
2305             if (isset($data[$key]) && !$form->isArray()) {
2306                 $valid = $form->isValid($data[$key]) && $valid;
2307             } else {
2308                 $valid = $form->isValid($data) && $valid;
2309             }
2310         }
2311 
2312         $this->_errorsExist = !$valid;
2313 
2314         // If manually flagged as an error, return invalid status
2315         if ($this->_errorsForced) {
2316             return false;
2317         }
2318 
2319         return $valid;
2320     }
2321 
2322     /**
2323      * Validate a partial form
2324      *
2325      * Does not check for required flags.
2326      *
2327      * @param  array $data
2328      * @return boolean
2329      */
2330     public function isValidPartial(array $data)
2331     {
2332         $eBelongTo  = null;
2333 
2334         if ($this->isArray()) {
2335             $eBelongTo = $this->getElementsBelongTo();
2336             $data = $this->_dissolveArrayValue($data, $eBelongTo);
2337         }
2338 
2339         $translator = $this->getTranslator();
2340         $valid      = true;
2341         $context    = $data;
2342 
2343         /** @var Zend_Form_Element $element */
2344         foreach ($this->getElements() as $key => $element) {
2345             $check = $data;
2346             if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) {
2347                 $check = $this->_dissolveArrayValue($data, $belongsTo);
2348             }
2349             if (isset($check[$key])) {
2350                 if (null !== $translator && !$element->hasTranslator()) {
2351                     $element->setTranslator($translator);
2352                 }
2353                 $valid = $element->isValid($check[$key], $context) && $valid;
2354                 $data = $this->_dissolveArrayUnsetKey($data, $belongsTo, $key);
2355             }
2356         }
2357         /** @var Zend_Form_SubForm $form */
2358         foreach ($this->getSubForms() as $key => $form) {
2359             if (null !== $translator && !$form->hasTranslator()) {
2360                 $form->setTranslator($translator);
2361             }
2362             if (isset($data[$key]) && !$form->isArray()) {
2363                 $valid = $form->isValidPartial($data[$key]) && $valid;
2364             } else {
2365                 $valid = $form->isValidPartial($data) && $valid;
2366             }
2367         }
2368 
2369         $this->_errorsExist = !$valid;
2370         return $valid;
2371     }
2372 
2373     /**
2374      * Process submitted AJAX data
2375      *
2376      * Checks if provided $data is valid, via {@link isValidPartial()}. If so,
2377      * it returns JSON-encoded boolean true. If not, it returns JSON-encoded
2378      * error messages (as returned by {@link getMessages()}).
2379      *
2380      * @param  array $data
2381      * @return string JSON-encoded boolean true or error messages
2382      */
2383     public function processAjax(array $data)
2384     {
2385         // require_once 'Zend/Json.php';
2386         if ($this->isValidPartial($data)) {
2387             return Zend_Json::encode(true);
2388         }
2389         $messages = $this->getMessages();
2390         return Zend_Json::encode($messages);
2391     }
2392 
2393     /**
2394      * Add a custom error message to return in the event of failed validation
2395      *
2396      * @param  string $message
2397      * @return Zend_Form
2398      */
2399     public function addErrorMessage($message)
2400     {
2401         $this->_errorMessages[] = (string) $message;
2402         return $this;
2403     }
2404 
2405     /**
2406      * Add multiple custom error messages to return in the event of failed validation
2407      *
2408      * @param  array $messages
2409      * @return Zend_Form
2410      */
2411     public function addErrorMessages(array $messages)
2412     {
2413         foreach ($messages as $message) {
2414             $this->addErrorMessage($message);
2415         }
2416         return $this;
2417     }
2418 
2419     /**
2420      * Same as addErrorMessages(), but clears custom error message stack first
2421      *
2422      * @param  array $messages
2423      * @return Zend_Form
2424      */
2425     public function setErrorMessages(array $messages)
2426     {
2427         $this->clearErrorMessages();
2428         return $this->addErrorMessages($messages);
2429     }
2430 
2431     /**
2432      * Retrieve custom error messages
2433      *
2434      * @return array
2435      */
2436     public function getErrorMessages()
2437     {
2438         return $this->_errorMessages;
2439     }
2440 
2441     /**
2442      * Clear custom error messages stack
2443      *
2444      * @return Zend_Form
2445      */
2446     public function clearErrorMessages()
2447     {
2448         $this->_errorMessages = array();
2449         return $this;
2450     }
2451 
2452     /**
2453      * Mark the element as being in a failed validation state
2454      *
2455      * @return Zend_Form
2456      */
2457     public function markAsError()
2458     {
2459         $this->_errorsExist  = true;
2460         $this->_errorsForced = true;
2461         return $this;
2462     }
2463 
2464     /**
2465      * Add an error message and mark element as failed validation
2466      *
2467      * @param  string $message
2468      * @return Zend_Form
2469      */
2470     public function addError($message)
2471     {
2472         $this->addErrorMessage($message);
2473         $this->markAsError();
2474         return $this;
2475     }
2476 
2477     /**
2478      * Add multiple error messages and flag element as failed validation
2479      *
2480      * @param  array $messages
2481      * @return Zend_Form
2482      */
2483     public function addErrors(array $messages)
2484     {
2485         foreach ($messages as $message) {
2486             $this->addError($message);
2487         }
2488         return $this;
2489     }
2490 
2491     /**
2492      * Overwrite any previously set error messages and flag as failed validation
2493      *
2494      * @param  array $messages
2495      * @return Zend_Form
2496      */
2497     public function setErrors(array $messages)
2498     {
2499         $this->clearErrorMessages();
2500         return $this->addErrors($messages);
2501     }
2502 
2503 
2504     public function persistData()
2505     {
2506     }
2507 
2508     /**
2509      * Are there errors in the form?
2510      *
2511      * @deprecated since 1.11.1 - use hasErrors() instead
2512      * @return bool
2513      */
2514     public function isErrors()
2515     {
2516         return $this->hasErrors();
2517     }
2518 
2519     /**
2520      * Are there errors in the form?
2521      *
2522      * @return bool
2523      */
2524     public function hasErrors()
2525     {
2526         $errors = $this->_errorsExist;
2527 
2528         if (!$errors) {
2529             /** @var Zend_Form_Element $element */
2530             foreach ($this->getElements() as $element) {
2531                 if ($element->hasErrors()) {
2532                     $errors = true;
2533                     break;
2534                 }
2535             }
2536 
2537             /** @var Zend_Form_SubForm $subForm */
2538             foreach ($this->getSubForms() as $subForm) {
2539                 if ($subForm->hasErrors()) {
2540                     $errors = true;
2541                     break;
2542                 }
2543             }
2544         }
2545 
2546         return $errors;
2547     }
2548 
2549     /**
2550      * Get error codes for all elements failing validation
2551      *
2552      * @param  string $name
2553      * @param  bool   $suppressArrayNotation
2554      * @return array
2555      */
2556     public function getErrors($name = null, $suppressArrayNotation = false)
2557     {
2558         $errors = array();
2559         if (null !== $name) {
2560             if (isset($this->_elements[$name])) {
2561                 return $this->getElement($name)->getErrors();
2562             } else if (isset($this->_subForms[$name])) {
2563                 return $this->getSubForm($name)->getErrors(null, true);
2564             }
2565         }
2566 
2567         /** @var Zend_Form_Element $element */
2568         foreach ($this->_elements as $key => $element) {
2569             $errors[$key] = $element->getErrors();
2570         }
2571         /** @var Zend_Form_SubForm $subForm */
2572         foreach ($this->getSubForms() as $key => $subForm) {
2573             $merge = array();
2574             if (!$subForm->isArray()) {
2575                 $merge[$key] = $subForm->getErrors();
2576             } else {
2577                 $merge = $this->_attachToArray($subForm->getErrors(null, true),
2578                                                $subForm->getElementsBelongTo());
2579             }
2580             $errors = $this->_array_replace_recursive($errors, $merge);
2581         }
2582 
2583         if (!$suppressArrayNotation &&
2584             $this->isArray() &&
2585             !$this->_getIsRendered()) {
2586             $errors = $this->_attachToArray($errors, $this->getElementsBelongTo());
2587         }
2588 
2589         return $errors;
2590     }
2591 
2592     /**
2593      * Retrieve error messages from elements failing validations
2594      *
2595      * @param  string $name
2596      * @param  bool $suppressArrayNotation
2597      * @return array
2598      */
2599     public function getMessages($name = null, $suppressArrayNotation = false)
2600     {
2601         if (null !== $name) {
2602             if (isset($this->_elements[$name])) {
2603                 return $this->getElement($name)->getMessages();
2604             } else if (isset($this->_subForms[$name])) {
2605                 return $this->getSubForm($name)->getMessages(null, true);
2606             }
2607             /** @var Zend_Form_SubForm $subForm */
2608             foreach ($this->getSubForms() as $key => $subForm) {
2609                 if ($subForm->isArray()) {
2610                     $belongTo = $subForm->getElementsBelongTo();
2611                     if ($name == $this->_getArrayName($belongTo)) {
2612                         return $subForm->getMessages(null, true);
2613                     }
2614                 }
2615             }
2616         }
2617 
2618         $customMessages = $this->_getErrorMessages();
2619         if ($this->isErrors() && !empty($customMessages)) {
2620             return $customMessages;
2621         }
2622 
2623         $messages = array();
2624 
2625         /** @var Zend_Form_Element $element */
2626         foreach ($this->getElements() as $name => $element) {
2627             $eMessages = $element->getMessages();
2628             if (!empty($eMessages)) {
2629                 $messages[$name] = $eMessages;
2630             }
2631         }
2632 
2633         /** @var Zend_Form_SubForm $subForm */
2634         foreach ($this->getSubForms() as $key => $subForm) {
2635             $merge = $subForm->getMessages(null, true);
2636             if (!empty($merge)) {
2637                 if (!$subForm->isArray()) {
2638                     $merge = array($key => $merge);
2639                 } else {
2640                     $merge = $this->_attachToArray($merge,
2641                                                    $subForm->getElementsBelongTo());
2642                 }
2643                 $messages = $this->_array_replace_recursive($messages, $merge);
2644             }
2645         }
2646 
2647         if (!$suppressArrayNotation &&
2648             $this->isArray() &&
2649             !$this->_getIsRendered()) {
2650             $messages = $this->_attachToArray($messages, $this->getElementsBelongTo());
2651         }
2652 
2653         return $messages;
2654     }
2655 
2656     /**
2657      * Retrieve translated custom error messages
2658      * Proxies to {@link _getErrorMessages()}.
2659      *
2660      * @return array
2661      */
2662     public function getCustomMessages()
2663     {
2664         return $this->_getErrorMessages();
2665     }
2666 
2667 
2668     // Rendering
2669 
2670     /**
2671      * Set view object
2672      *
2673      * @param  Zend_View_Interface $view
2674      * @return Zend_Form
2675      */
2676     public function setView(Zend_View_Interface $view = null)
2677     {
2678         $this->_view = $view;
2679         return $this;
2680     }
2681 
2682     /**
2683      * Retrieve view object
2684      *
2685      * If none registered, attempts to pull from ViewRenderer.
2686      *
2687      * @return Zend_View_Interface|null
2688      */
2689     public function getView()
2690     {
2691         if (null === $this->_view) {
2692             // require_once 'Zend/Controller/Action/HelperBroker.php';
2693             $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
2694             $this->setView($viewRenderer->view);
2695         }
2696 
2697         return $this->_view;
2698     }
2699 
2700     /**
2701      * Instantiate a decorator based on class name or class name fragment
2702      *
2703      * @param  string $name
2704      * @param  null|array $options
2705      * @return Zend_Form_Decorator_Interface
2706      */
2707     protected function _getDecorator($name, $options)
2708     {
2709         $class = $this->getPluginLoader(self::DECORATOR)->load($name);
2710         if (null === $options) {
2711             $decorator = new $class;
2712         } else {
2713             $decorator = new $class($options);
2714         }
2715 
2716         return $decorator;
2717     }
2718 
2719     /**
2720      * Add a decorator for rendering the element
2721      *
2722      * @param  string|Zend_Form_Decorator_Interface $decorator
2723      * @param  array|Zend_Config                    $options Options with which to initialize decorator
2724      * @throws Zend_Form_Exception
2725      * @return Zend_Form
2726      */
2727     public function addDecorator($decorator, $options = null)
2728     {
2729         if ($decorator instanceof Zend_Form_Decorator_Interface) {
2730             $name = get_class($decorator);
2731         } elseif (is_string($decorator)) {
2732             $name      = $decorator;
2733             $decorator = array(
2734                 'decorator' => $name,
2735                 'options'   => $options,
2736             );
2737         } elseif (is_array($decorator)) {
2738             foreach ($decorator as $name => $spec) {
2739                 break;
2740             }
2741             if (is_numeric($name)) {
2742                 // require_once 'Zend/Form/Exception.php';
2743                 throw new Zend_Form_Exception('Invalid alias provided to addDecorator; must be alphanumeric string');
2744             }
2745             if (is_string($spec)) {
2746                 $decorator = array(
2747                     'decorator' => $spec,
2748                     'options'   => $options,
2749                 );
2750             } elseif ($spec instanceof Zend_Form_Decorator_Interface) {
2751                 $decorator = $spec;
2752             }
2753         } else {
2754             // require_once 'Zend/Form/Exception.php';
2755             throw new Zend_Form_Exception('Invalid decorator provided to addDecorator; must be string or Zend_Form_Decorator_Interface');
2756         }
2757 
2758         $this->_decorators[$name] = $decorator;
2759 
2760         return $this;
2761     }
2762 
2763     /**
2764      * Add many decorators at once
2765      *
2766      * @param  array $decorators
2767      * @throws Zend_Form_Exception
2768      * @return Zend_Form
2769      */
2770     public function addDecorators(array $decorators)
2771     {
2772         foreach ($decorators as $decoratorName => $decoratorInfo) {
2773             if (is_string($decoratorInfo) ||
2774                 $decoratorInfo instanceof Zend_Form_Decorator_Interface) {
2775                 if (!is_numeric($decoratorName)) {
2776                     $this->addDecorator(array($decoratorName => $decoratorInfo));
2777                 } else {
2778                     $this->addDecorator($decoratorInfo);
2779                 }
2780             } elseif (is_array($decoratorInfo)) {
2781                 $argc    = count($decoratorInfo);
2782                 $options = array();
2783                 if (isset($decoratorInfo['decorator'])) {
2784                     $decorator = $decoratorInfo['decorator'];
2785                     if (isset($decoratorInfo['options'])) {
2786                         $options = $decoratorInfo['options'];
2787                     }
2788                     $this->addDecorator($decorator, $options);
2789                 } else {
2790                     switch (true) {
2791                         case (0 == $argc):
2792                             break;
2793                         case (1 <= $argc):
2794                             $decorator  = array_shift($decoratorInfo);
2795                         case (2 <= $argc):
2796                             $options = array_shift($decoratorInfo);
2797                         default:
2798                             $this->addDecorator($decorator, $options);
2799                             break;
2800                     }
2801                 }
2802             } else {
2803                 // require_once 'Zend/Form/Exception.php';
2804                 throw new Zend_Form_Exception('Invalid decorator passed to addDecorators()');
2805             }
2806         }
2807 
2808         return $this;
2809     }
2810 
2811     /**
2812      * Overwrite all decorators
2813      *
2814      * @param  array $decorators
2815      * @return Zend_Form
2816      */
2817     public function setDecorators(array $decorators)
2818     {
2819         $this->clearDecorators();
2820         return $this->addDecorators($decorators);
2821     }
2822 
2823     /**
2824      * Retrieve a registered decorator
2825      *
2826      * @param  string $name
2827      * @return false|Zend_Form_Decorator_Abstract
2828      */
2829     public function getDecorator($name)
2830     {
2831         if (!isset($this->_decorators[$name])) {
2832             $len = strlen($name);
2833             foreach ($this->_decorators as $localName => $decorator) {
2834                 if ($len > strlen($localName)) {
2835                     continue;
2836                 }
2837 
2838                 if (0 === substr_compare($localName, $name, -$len, $len, true)) {
2839                     if (is_array($decorator)) {
2840                         return $this->_loadDecorator($decorator, $localName);
2841                     }
2842                     return $decorator;
2843                 }
2844             }
2845             return false;
2846         }
2847 
2848         if (is_array($this->_decorators[$name])) {
2849             return $this->_loadDecorator($this->_decorators[$name], $name);
2850         }
2851 
2852         return $this->_decorators[$name];
2853     }
2854 
2855     /**
2856      * Retrieve all decorators
2857      *
2858      * @return array
2859      */
2860     public function getDecorators()
2861     {
2862         foreach ($this->_decorators as $key => $value) {
2863             if (is_array($value)) {
2864                 $this->_loadDecorator($value, $key);
2865             }
2866         }
2867         return $this->_decorators;
2868     }
2869 
2870     /**
2871      * Remove a single decorator
2872      *
2873      * @param  string $name
2874      * @return bool
2875      */
2876     public function removeDecorator($name)
2877     {
2878         $decorator = $this->getDecorator($name);
2879         if ($decorator) {
2880             if (array_key_exists($name, $this->_decorators)) {
2881                 unset($this->_decorators[$name]);
2882             } else {
2883                 $class = get_class($decorator);
2884                 if (!array_key_exists($class, $this->_decorators)) {
2885                     return false;
2886                 }
2887                 unset($this->_decorators[$class]);
2888             }
2889             return true;
2890         }
2891 
2892         return false;
2893     }
2894 
2895     /**
2896      * Clear all decorators
2897      *
2898      * @return Zend_Form
2899      */
2900     public function clearDecorators()
2901     {
2902         $this->_decorators = array();
2903         return $this;
2904     }
2905 
2906     /**
2907      * Set all element decorators as specified
2908      *
2909      * @param  array $decorators
2910      * @param  array|null $elements Specific elements to decorate or exclude from decoration
2911      * @param  bool $include Whether $elements is an inclusion or exclusion list
2912      * @return Zend_Form
2913      */
2914     public function setElementDecorators(array $decorators, array $elements = null, $include = true)
2915     {
2916         if (is_array($elements)) {
2917             if ($include) {
2918                 $elementObjs = array();
2919                 foreach ($elements as $name) {
2920                     if (null !== ($element = $this->getElement($name))) {
2921                         $elementObjs[] = $element;
2922                     }
2923                 }
2924             } else {
2925                 $elementObjs = $this->getElements();
2926                 foreach ($elements as $name) {
2927                     if (array_key_exists($name, $elementObjs)) {
2928                         unset($elementObjs[$name]);
2929                     }
2930                 }
2931             }
2932         } else {
2933             $elementObjs = $this->getElements();
2934         }
2935 
2936         /** @var Zend_Form_Element $element */
2937         foreach ($elementObjs as $element) {
2938             $element->setDecorators($decorators);
2939         }
2940 
2941         $this->_elementDecorators = $decorators;
2942 
2943         return $this;
2944     }
2945 
2946     /**
2947      * Set all display group decorators as specified
2948      *
2949      * @param  array $decorators
2950      * @return Zend_Form
2951      */
2952     public function setDisplayGroupDecorators(array $decorators)
2953     {
2954         /** @var Zend_Form_DisplayGroup $group */
2955         foreach ($this->getDisplayGroups() as $group) {
2956             $group->setDecorators($decorators);
2957         }
2958 
2959         return $this;
2960     }
2961 
2962     /**
2963      * Set all subform decorators as specified
2964      *
2965      * @param  array $decorators
2966      * @return Zend_Form
2967      */
2968     public function setSubFormDecorators(array $decorators)
2969     {
2970         /** @var Zend_Form_SubForm $form */
2971         foreach ($this->getSubForms() as $form) {
2972             $form->setDecorators($decorators);
2973         }
2974 
2975         return $this;
2976     }
2977 
2978     /**
2979      * Render form
2980      *
2981      * @param  Zend_View_Interface $view
2982      * @return string
2983      */
2984     public function render(Zend_View_Interface $view = null)
2985     {
2986         if (null !== $view) {
2987             $this->setView($view);
2988         }
2989 
2990         $content = '';
2991         /** @var Zend_Form_Decorator_Abstract $decorator */
2992         foreach ($this->getDecorators() as $decorator) {
2993             $decorator->setElement($this);
2994             $content = $decorator->render($content);
2995         }
2996         $this->_setIsRendered();
2997         return $content;
2998     }
2999 
3000     /**
3001      * Serialize as string
3002      *
3003      * Proxies to {@link render()}.
3004      *
3005      * @return string
3006      */
3007     public function __toString()
3008     {
3009         try {
3010             $return = $this->render();
3011             return $return;
3012         } catch (Exception $e) {
3013             $message = "Exception caught by form: " . $e->getMessage()
3014                      . "\nStack Trace:\n" . $e->getTraceAsString();
3015             trigger_error($message, E_USER_WARNING);
3016             return '';
3017         }
3018     }
3019 
3020 
3021     // Localization:
3022 
3023     /**
3024      * Set translator object
3025      *
3026      * @param  Zend_Translate|Zend_Translate_Adapter|null $translator
3027      * @throws Zend_Form_Exception
3028      * @return Zend_Form
3029      */
3030     public function setTranslator($translator = null)
3031     {
3032         if (null === $translator) {
3033             $this->_translator = null;
3034         } elseif ($translator instanceof Zend_Translate_Adapter) {
3035             $this->_translator = $translator;
3036         } elseif ($translator instanceof Zend_Translate) {
3037             $this->_translator = $translator->getAdapter();
3038         } else {
3039             // require_once 'Zend/Form/Exception.php';
3040             throw new Zend_Form_Exception('Invalid translator specified');
3041         }
3042 
3043         return $this;
3044     }
3045 
3046     /**
3047      * Set global default translator object
3048      *
3049      * @param  Zend_Translate|Zend_Translate_Adapter|null $translator
3050      * @throws Zend_Form_Exception
3051      * @return void
3052      */
3053     public static function setDefaultTranslator($translator = null)
3054     {
3055         if (null === $translator) {
3056             self::$_translatorDefault = null;
3057         } elseif ($translator instanceof Zend_Translate_Adapter) {
3058             self::$_translatorDefault = $translator;
3059         } elseif ($translator instanceof Zend_Translate) {
3060             self::$_translatorDefault = $translator->getAdapter();
3061         } else {
3062             // require_once 'Zend/Form/Exception.php';
3063             throw new Zend_Form_Exception('Invalid translator specified');
3064         }
3065     }
3066 
3067     /**
3068      * Retrieve translator object
3069      *
3070      * @return Zend_Translate|null
3071      */
3072     public function getTranslator()
3073     {
3074         if ($this->translatorIsDisabled()) {
3075             return null;
3076         }
3077 
3078         if (null === $this->_translator) {
3079             return self::getDefaultTranslator();
3080         }
3081 
3082         return $this->_translator;
3083     }
3084 
3085     /**
3086      * Does this form have its own specific translator?
3087      *
3088      * @return bool
3089      */
3090     public function hasTranslator()
3091     {
3092         return (bool)$this->_translator;
3093     }
3094 
3095     /**
3096      * Get global default translator object
3097      *
3098      * @return null|Zend_Translate
3099      */
3100     public static function getDefaultTranslator()
3101     {
3102         if (null === self::$_translatorDefault) {
3103             // require_once 'Zend/Registry.php';
3104             if (Zend_Registry::isRegistered('Zend_Translate')) {
3105                 $translator = Zend_Registry::get('Zend_Translate');
3106                 if ($translator instanceof Zend_Translate_Adapter) {
3107                     return $translator;
3108                 } elseif ($translator instanceof Zend_Translate) {
3109                     return $translator->getAdapter();
3110                 }
3111             }
3112         }
3113         return self::$_translatorDefault;
3114     }
3115 
3116     /**
3117      * Is there a default translation object set?
3118      *
3119      * @return boolean
3120      */
3121     public static function hasDefaultTranslator()
3122     {
3123         return (bool)self::$_translatorDefault;
3124     }
3125 
3126     /**
3127      * Indicate whether or not translation should be disabled
3128      *
3129      * @param  bool $flag
3130      * @return Zend_Form
3131      */
3132     public function setDisableTranslator($flag)
3133     {
3134         $this->_translatorDisabled = (bool) $flag;
3135         return $this;
3136     }
3137 
3138     /**
3139      * Is translation disabled?
3140      *
3141      * @return bool
3142      */
3143     public function translatorIsDisabled()
3144     {
3145         return $this->_translatorDisabled;
3146     }
3147 
3148     /**
3149      * Overloading: access to elements, form groups, and display groups
3150      *
3151      * @param  string $name
3152      * @return Zend_Form_Element|Zend_Form|null
3153      */
3154     public function __get($name)
3155     {
3156         if (isset($this->_elements[$name])) {
3157             return $this->_elements[$name];
3158         } elseif (isset($this->_subForms[$name])) {
3159             return $this->_subForms[$name];
3160         } elseif (isset($this->_displayGroups[$name])) {
3161             return $this->_displayGroups[$name];
3162         }
3163 
3164         return null;
3165     }
3166 
3167     /**
3168      * Overloading: access to elements, form groups, and display groups
3169      *
3170      * @param  string $name
3171      * @param  Zend_Form_Element|Zend_Form $value
3172      * @return void
3173      * @throws Zend_Form_Exception for invalid $value
3174      */
3175     public function __set($name, $value)
3176     {
3177         if ($value instanceof Zend_Form_Element) {
3178             $this->addElement($value, $name);
3179             return;
3180         } elseif ($value instanceof Zend_Form) {
3181             $this->addSubForm($value, $name);
3182             return;
3183         } elseif (is_array($value)) {
3184             $this->addDisplayGroup($value, $name);
3185             return;
3186         }
3187 
3188         // require_once 'Zend/Form/Exception.php';
3189         if (is_object($value)) {
3190             $type = get_class($value);
3191         } else {
3192             $type = gettype($value);
3193         }
3194         throw new Zend_Form_Exception('Only form elements and groups may be overloaded; variable of type "' . $type . '" provided');
3195     }
3196 
3197     /**
3198      * Overloading: access to elements, form groups, and display groups
3199      *
3200      * @param  string $name
3201      * @return boolean
3202      */
3203     public function __isset($name)
3204     {
3205         if (isset($this->_elements[$name])
3206             || isset($this->_subForms[$name])
3207             || isset($this->_displayGroups[$name]))
3208         {
3209             return true;
3210         }
3211 
3212         return false;
3213     }
3214 
3215     /**
3216      * Overloading: access to elements, form groups, and display groups
3217      *
3218      * @param  string $name
3219      * @return void
3220      */
3221     public function __unset($name)
3222     {
3223         if (isset($this->_elements[$name])) {
3224             unset($this->_elements[$name]);
3225         } elseif (isset($this->_subForms[$name])) {
3226             unset($this->_subForms[$name]);
3227         } elseif (isset($this->_displayGroups[$name])) {
3228             unset($this->_displayGroups[$name]);
3229         }
3230     }
3231 
3232     /**
3233      * Overloading: allow rendering specific decorators
3234      *
3235      * Call renderDecoratorName() to render a specific decorator.
3236      *
3237      * @param  string $method
3238      * @param  array $args
3239      * @return string
3240      * @throws Zend_Form_Exception for invalid decorator or invalid method call
3241      */
3242     public function __call($method, $args)
3243     {
3244         if ('render' == substr($method, 0, 6)) {
3245             $decoratorName = substr($method, 6);
3246             if (false !== ($decorator = $this->getDecorator($decoratorName))) {
3247                 $decorator->setElement($this);
3248                 $seed = '';
3249                 if (0 < count($args)) {
3250                     $seed = array_shift($args);
3251                 }
3252                 if ($decoratorName === 'FormElements' ||
3253                     $decoratorName === 'PrepareElements') {
3254                         $this->_setIsRendered();
3255                 }
3256                 return $decorator->render($seed);
3257             }
3258 
3259             // require_once 'Zend/Form/Exception.php';
3260             throw new Zend_Form_Exception(sprintf('Decorator by name %s does not exist', $decoratorName));
3261         }
3262 
3263         // require_once 'Zend/Form/Exception.php';
3264         throw new Zend_Form_Exception(sprintf('Method %s does not exist', $method));
3265     }
3266 
3267     // Interfaces: Iterator, Countable
3268 
3269     /**
3270      * Current element/subform/display group
3271      *
3272      * @throws Zend_Form_Exception
3273      * @return Zend_Form_Element|Zend_Form_DisplayGroup|Zend_Form
3274      */
3275     public function current()
3276     {
3277         $this->_sort();
3278         current($this->_order);
3279         $key = key($this->_order);
3280 
3281         if (isset($this->_elements[$key])) {
3282             return $this->getElement($key);
3283         } elseif (isset($this->_subForms[$key])) {
3284             return $this->getSubForm($key);
3285         } elseif (isset($this->_displayGroups[$key])) {
3286             return $this->getDisplayGroup($key);
3287         } else {
3288             // require_once 'Zend/Form/Exception.php';
3289             throw new Zend_Form_Exception(sprintf('Corruption detected in form; invalid key ("%s") found in internal iterator', (string) $key));
3290         }
3291     }
3292 
3293     /**
3294      * Current element/subform/display group name
3295      *
3296      * @return string
3297      */
3298     public function key()
3299     {
3300         $this->_sort();
3301         return key($this->_order);
3302     }
3303 
3304     /**
3305      * Move pointer to next element/subform/display group
3306      *
3307      * @return void
3308      */
3309     public function next()
3310     {
3311         $this->_sort();
3312         next($this->_order);
3313     }
3314 
3315     /**
3316      * Move pointer to beginning of element/subform/display group loop
3317      *
3318      * @return void
3319      */
3320     public function rewind()
3321     {
3322         $this->_sort();
3323         reset($this->_order);
3324     }
3325 
3326     /**
3327      * Determine if current element/subform/display group is valid
3328      *
3329      * @return bool
3330      */
3331     public function valid()
3332     {
3333         $this->_sort();
3334         return (current($this->_order) !== false);
3335     }
3336 
3337     /**
3338      * Count of elements/subforms that are iterable
3339      *
3340      * @return int
3341      */
3342     public function count()
3343     {
3344         return count($this->_order);
3345     }
3346 
3347     /**
3348      * Set flag to disable loading default decorators
3349      *
3350      * @param  bool $flag
3351      * @return Zend_Form
3352      */
3353     public function setDisableLoadDefaultDecorators($flag)
3354     {
3355         $this->_disableLoadDefaultDecorators = (bool) $flag;
3356         return $this;
3357     }
3358 
3359     /**
3360      * Should we load the default decorators?
3361      *
3362      * @return bool
3363      */
3364     public function loadDefaultDecoratorsIsDisabled()
3365     {
3366         return $this->_disableLoadDefaultDecorators;
3367     }
3368 
3369     /**
3370      * Load the default decorators
3371      *
3372      * @return Zend_Form
3373      */
3374     public function loadDefaultDecorators()
3375     {
3376         if ($this->loadDefaultDecoratorsIsDisabled()) {
3377             return $this;
3378         }
3379 
3380         $decorators = $this->getDecorators();
3381         if (empty($decorators)) {
3382             $this->addDecorator('FormElements')
3383                  ->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form'))
3384                  ->addDecorator('Form');
3385         }
3386         return $this;
3387     }
3388 
3389     /**
3390      * Remove an element from iteration
3391      *
3392      * @param  string $name Element/group/form name
3393      * @return void
3394      */
3395     public function removeFromIteration($name)
3396     {
3397         if (array_key_exists($name, $this->_order)) {
3398             unset($this->_order[$name]);
3399             $this->_orderUpdated = true;
3400         }
3401     }
3402 
3403     /**
3404      * Sort items according to their order
3405      *
3406      * @throws Zend_Form_Exception
3407      * @return void
3408      */
3409     protected function _sort()
3410     {
3411         if ($this->_orderUpdated) {
3412             $items = array();
3413             $index = 0;
3414             foreach ($this->_order as $key => $order) {
3415                 if (null === $order) {
3416                     if (null === ($order = $this->{$key}->getOrder())) {
3417                         while (array_search($index, $this->_order, true)) {
3418                             ++$index;
3419                         }
3420                         $items[$index] = $key;
3421                         ++$index;
3422                     } else {
3423                         $items[$order] = $key;
3424                     }
3425                 } elseif (isset($items[$order]) && $items[$order] !== $key) {
3426                     throw new Zend_Form_Exception('Form elements ' .
3427                         $items[$order] . ' and ' . $key .
3428                         ' have the same order (' .
3429                         $order . ') - ' .
3430                         'this would result in only the last added element to be rendered'
3431                     );
3432                 } else {
3433                     $items[$order] = $key;
3434                 }
3435             }
3436 
3437             $items = array_flip($items);
3438             asort($items);
3439             $this->_order = $items;
3440             $this->_orderUpdated = false;
3441         }
3442     }
3443 
3444     /**
3445      * Lazy-load a decorator
3446      *
3447      * @param  array $decorator Decorator type and options
3448      * @param  mixed $name Decorator name or alias
3449      * @return Zend_Form_Decorator_Interface
3450      */
3451     protected function _loadDecorator(array $decorator, $name)
3452     {
3453         $sameName = false;
3454         if ($name == $decorator['decorator']) {
3455             $sameName = true;
3456         }
3457 
3458         $instance = $this->_getDecorator($decorator['decorator'], $decorator['options']);
3459         if ($sameName) {
3460             $newName            = get_class($instance);
3461             $decoratorNames     = array_keys($this->_decorators);
3462             $order              = array_flip($decoratorNames);
3463             $order[$newName]    = $order[$name];
3464             $decoratorsExchange = array();
3465             unset($order[$name]);
3466             asort($order);
3467             foreach ($order as $key => $index) {
3468                 if ($key == $newName) {
3469                     $decoratorsExchange[$key] = $instance;
3470                     continue;
3471                 }
3472                 $decoratorsExchange[$key] = $this->_decorators[$key];
3473             }
3474             $this->_decorators = $decoratorsExchange;
3475         } else {
3476             $this->_decorators[$name] = $instance;
3477         }
3478 
3479         return $instance;
3480     }
3481 
3482     /**
3483      * Retrieve optionally translated custom error messages
3484      *
3485      * @return array
3486      */
3487     protected function _getErrorMessages()
3488     {
3489         $messages   = $this->getErrorMessages();
3490         $translator = $this->getTranslator();
3491         if (null !== $translator) {
3492             foreach ($messages as $key => $message) {
3493                 $messages[$key] = $translator->translate($message);
3494             }
3495         }
3496         return $messages;
3497     }
3498 }