<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@zend.com so we can send you a copy immediately. * * @category Zend * @package Zend_Form * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ /** @see Zend_Filter */ require_once 'Zend/Filter.php'; /** @see Zend_Form */ require_once 'Zend/Form.php'; /** @see Zend_Validate_Interface */ require_once 'Zend/Validate/Interface.php'; /** @see Zend_Validate_Abstract */ require_once 'Zend/Validate/Abstract.php'; /** * Zend_Form_Element * * @category Zend * @package Zend_Form * @subpackage Element * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @version $Id$ */ class Zend_Form_Element implements Zend_Validate_Interface { /** * Element Constants */ const DECORATOR = 'DECORATOR'; const FILTER = 'FILTER'; const VALIDATE = 'VALIDATE'; /** * Default view helper to use * @var string */ public $helper = 'formText'; /** * 'Allow empty' flag * @var bool */ protected $_allowEmpty = true; /** * Flag indicating whether or not to insert NotEmpty validator when element is required * @var bool */ protected $_autoInsertNotEmptyValidator = true; /** * Array to which element belongs * @var string */ protected $_belongsTo; /** * Element decorators * @var array */ protected $_decorators = array(); /** * Element description * @var string */ protected $_description; /** * Should we disable loading the default decorators? * @var bool */ protected $_disableLoadDefaultDecorators = false; /** * Custom error messages * @var array */ protected $_errorMessages = array(); /** * Validation errors * @var array */ protected $_errors = array(); /** * Separator to use when concatenating aggregate error messages (for * elements having array values) * @var string */ protected $_errorMessageSeparator = '; '; /** * Element filters * @var array */ protected $_filters = array(); /** * Ignore flag (used when retrieving values at form level) * @var bool */ protected $_ignore = false; /** * Does the element represent an array? * @var bool */ protected $_isArray = false; /** * Is the error marked as in an invalid state? * @var bool */ protected $_isError = false; /** * Has the element been manually marked as invalid? * @var bool */ protected $_isErrorForced = false; /** * Element label * @var string */ protected $_label; /** * Plugin loaders for filter and validator chains * @var array */ protected $_loaders = array(); /** * Formatted validation error messages * @var array */ protected $_messages = array(); /** * Element name * @var string */ protected $_name; /** * Order of element * @var int */ protected $_order; /** * Required flag * @var bool */ protected $_required = false; /** * @var Zend_Translate */ protected $_translator; /** * Is translation disabled? * @var bool */ protected $_translatorDisabled = false; /** * Element type * @var string */ protected $_type; /** * Array of initialized validators * @var array Validators */ protected $_validators = array(); /** * Array of un-initialized validators * @var array */ protected $_validatorRules = array(); /** * Element value * @var mixed */ protected $_value; /** * @var Zend_View_Interface */ protected $_view; /** * Is a specific decorator being rendered via the magic renderDecorator()? * * This is to allow execution of logic inside the render() methods of child * elements during the magic call while skipping the parent render() method. * * @var bool */ protected $_isPartialRendering = false; /** * Use one error message for array elements with concatenated values * * @var bool */ protected $_concatJustValuesInErrorMessage = false; /** * Constructor * * $spec may be: * - string: name of element * - array: options with which to configure element * - Zend_Config: Zend_Config with options for configuring element * * @param string|array|Zend_Config $spec * @param array|Zend_Config $options * @return void * @throws Zend_Form_Exception if no element name after initialization */ public function __construct($spec, $options = null) { if (is_string($spec)) { $this->setName($spec); } elseif (is_array($spec)) { $this->setOptions($spec); } elseif ($spec instanceof Zend_Config) { $this->setConfig($spec); } if (is_string($spec) && is_array($options)) { $this->setOptions($options); } elseif (is_string($spec) && ($options instanceof Zend_Config)) { $this->setConfig($options); } if (null === $this->getName()) { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Zend_Form_Element requires each element to have a name'); } /** * Extensions */ $this->init(); /** * Register ViewHelper decorator by default */ $this->loadDefaultDecorators(); } /** * Initialize object; used by extending classes * * @return void */ public function init() { } /** * Set flag to disable loading default decorators * * @param bool $flag * @return Zend_Form_Element */ public function setDisableLoadDefaultDecorators($flag) { $this->_disableLoadDefaultDecorators = (bool) $flag; return $this; } /** * Should we load the default decorators? * * @return bool */ public function loadDefaultDecoratorsIsDisabled() { return $this->_disableLoadDefaultDecorators; } /** * Load default decorators * * @return Zend_Form_Element */ public function loadDefaultDecorators() { if ($this->loadDefaultDecoratorsIsDisabled()) { return $this; } $decorators = $this->getDecorators(); if (empty($decorators)) { $this->addDecorator('ViewHelper') ->addDecorator('Errors') ->addDecorator('Description', array('tag' => 'p', 'class' => 'description')) ->addDecorator('HtmlTag', array( 'tag' => 'dd', 'id' => array('callback' => array(get_class($this), 'resolveElementId')) )) ->addDecorator('Label', array('tag' => 'dt')); } return $this; } /** * Used to resolve and return an element ID * * Passed to the HtmlTag decorator as a callback in order to provide an ID. * * @param Zend_Form_Decorator_Interface $decorator * @return string */ public static function resolveElementId(Zend_Form_Decorator_Interface $decorator) { return $decorator->getElement()->getId() . '-element'; } /** * Set object state from options array * * @param array $options * @return Zend_Form_Element */ public function setOptions(array $options) { if (isset($options['prefixPath'])) { $this->addPrefixPaths($options['prefixPath']); unset($options['prefixPath']); } if (isset($options['disableTranslator'])) { $this->setDisableTranslator($options['disableTranslator']); unset($options['disableTranslator']); } unset($options['options']); unset($options['config']); foreach ($options as $key => $value) { $method = 'set' . ucfirst($key); if (in_array($method, array('setTranslator', 'setPluginLoader', 'setView'))) { if (!is_object($value)) { continue; } } if (method_exists($this, $method)) { // Setter exists; use it $this->$method($value); } else { // Assume it's metadata $this->setAttrib($key, $value); } } return $this; } /** * Set object state from Zend_Config object * * @param Zend_Config $config * @return Zend_Form_Element */ public function setConfig(Zend_Config $config) { return $this->setOptions($config->toArray()); } // Localization: /** * Set translator object for localization * * @param Zend_Translate|null $translator * @return Zend_Form_Element */ public function setTranslator($translator = null) { if (null === $translator) { $this->_translator = null; } elseif ($translator instanceof Zend_Translate_Adapter) { $this->_translator = $translator; } elseif ($translator instanceof Zend_Translate) { $this->_translator = $translator->getAdapter(); } else { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid translator specified'); } return $this; } /** * Retrieve localization translator object * * @return Zend_Translate_Adapter|null */ public function getTranslator() { if ($this->translatorIsDisabled()) { return null; } if (null === $this->_translator) { return Zend_Form::getDefaultTranslator(); } return $this->_translator; } /** * Does this element have its own specific translator? * * @return bool */ public function hasTranslator() { return (bool)$this->_translator; } /** * Indicate whether or not translation should be disabled * * @param bool $flag * @return Zend_Form_Element */ public function setDisableTranslator($flag) { $this->_translatorDisabled = (bool) $flag; return $this; } /** * Is translation disabled? * * @return bool */ public function translatorIsDisabled() { return $this->_translatorDisabled; } // Metadata /** * Filter a name to only allow valid variable characters * * @param string $value * @param bool $allowBrackets * @return string */ public function filterName($value, $allowBrackets = false) { $charset = '^a-zA-Z0-9_\x7f-\xff'; if ($allowBrackets) { $charset .= '\[\]'; } return preg_replace('/[' . $charset . ']/', '', (string) $value); } /** * Set element name * * @param string $name * @return Zend_Form_Element */ public function setName($name) { $name = $this->filterName($name); if ('' === $name) { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid name provided; must contain only valid variable characters and be non-empty'); } $this->_name = $name; return $this; } /** * Return element name * * @return string */ public function getName() { return $this->_name; } /** * Get fully qualified name * * Places name as subitem of array and/or appends brackets. * * @return string */ public function getFullyQualifiedName() { $name = $this->getName(); if (null !== ($belongsTo = $this->getBelongsTo())) { $name = $belongsTo . '[' . $name . ']'; } if ($this->isArray()) { $name .= '[]'; } return $name; } /** * Get element id * * @return string */ public function getId() { if (isset($this->id)) { return $this->id; } $id = $this->getFullyQualifiedName(); // Bail early if no array notation detected if (!strstr($id, '[')) { return $id; } // Strip array notation if ('[]' == substr($id, -2)) { $id = substr($id, 0, strlen($id) - 2); } $id = str_replace('][', '-', $id); $id = str_replace(array(']', '['), '-', $id); $id = trim($id, '-'); return $id; } /** * Set element value * * @param mixed $value * @return Zend_Form_Element */ public function setValue($value) { $this->_value = $value; return $this; } /** * Filter a value * * @param string $value * @param string $key * @return void */ protected function _filterValue(&$value, &$key) { foreach ($this->getFilters() as $filter) { $value = $filter->filter($value); } } /** * Retrieve filtered element value * * @return mixed */ public function getValue() { $valueFiltered = $this->_value; if ($this->isArray() && is_array($valueFiltered)) { array_walk_recursive($valueFiltered, array($this, '_filterValue')); } else { $this->_filterValue($valueFiltered, $valueFiltered); } return $valueFiltered; } /** * Retrieve unfiltered element value * * @return mixed */ public function getUnfilteredValue() { return $this->_value; } /** * Set element label * * @param string $label * @return Zend_Form_Element */ public function setLabel($label) { $this->_label = (string) $label; return $this; } /** * Retrieve element label * * @return string */ public function getLabel() { $translator = $this->getTranslator(); if (null !== $translator) { return $translator->translate($this->_label); } return $this->_label; } /** * Set element order * * @param int $order * @return Zend_Form_Element */ public function setOrder($order) { $this->_order = (int) $order; return $this; } /** * Retrieve element order * * @return int */ public function getOrder() { return $this->_order; } /** * Set required flag * * @param bool $flag Default value is true * @return Zend_Form_Element */ public function setRequired($flag = true) { $this->_required = (bool) $flag; return $this; } /** * Is the element required? * * @return bool */ public function isRequired() { return $this->_required; } /** * Set flag indicating whether a NotEmpty validator should be inserted when element is required * * @param bool $flag * @return Zend_Form_Element */ public function setAutoInsertNotEmptyValidator($flag) { $this->_autoInsertNotEmptyValidator = (bool) $flag; return $this; } /** * Get flag indicating whether a NotEmpty validator should be inserted when element is required * * @return bool */ public function autoInsertNotEmptyValidator() { return $this->_autoInsertNotEmptyValidator; } /** * Set element description * * @param string $description * @return Zend_Form_Element */ public function setDescription($description) { $this->_description = (string) $description; return $this; } /** * Retrieve element description * * @return string */ public function getDescription() { return $this->_description; } /** * Set 'allow empty' flag * * When the allow empty flag is enabled and the required flag is false, the * element will validate with empty values. * * @param bool $flag * @return Zend_Form_Element */ public function setAllowEmpty($flag) { $this->_allowEmpty = (bool) $flag; return $this; } /** * Get 'allow empty' flag * * @return bool */ public function getAllowEmpty() { return $this->_allowEmpty; } /** * Set ignore flag (used when retrieving values at form level) * * @param bool $flag * @return Zend_Form_Element */ public function setIgnore($flag) { $this->_ignore = (bool) $flag; return $this; } /** * Get ignore flag (used when retrieving values at form level) * * @return bool */ public function getIgnore() { return $this->_ignore; } /** * Set flag indicating if element represents an array * * @param bool $flag * @return Zend_Form_Element */ public function setIsArray($flag) { $this->_isArray = (bool) $flag; return $this; } /** * Is the element representing an array? * * @return bool */ public function isArray() { return $this->_isArray; } /** * Set array to which element belongs * * @param string $array * @return Zend_Form_Element */ public function setBelongsTo($array) { $array = $this->filterName($array, true); if (!empty($array)) { $this->_belongsTo = $array; } return $this; } /** * Return array name to which element belongs * * @return string */ public function getBelongsTo() { return $this->_belongsTo; } /** * Return element type * * @return string */ public function getType() { if (null === $this->_type) { $this->_type = get_class($this); } return $this->_type; } /** * Set element attribute * * @param string $name * @param mixed $value * @return Zend_Form_Element * @throws Zend_Form_Exception for invalid $name values */ public function setAttrib($name, $value) { $name = (string) $name; if ('_' == $name[0]) { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception(sprintf('Invalid attribute "%s"; must not contain a leading underscore', $name)); } if (null === $value) { unset($this->$name); } else { $this->$name = $value; } return $this; } /** * Set multiple attributes at once * * @param array $attribs * @return Zend_Form_Element */ public function setAttribs(array $attribs) { foreach ($attribs as $key => $value) { $this->setAttrib($key, $value); } return $this; } /** * Retrieve element attribute * * @param string $name * @return string */ public function getAttrib($name) { $name = (string) $name; if (isset($this->$name)) { return $this->$name; } return null; } /** * Return all attributes * * @return array */ public function getAttribs() { $attribs = get_object_vars($this); unset($attribs['helper']); foreach ($attribs as $key => $value) { if ('_' == substr($key, 0, 1)) { unset($attribs[$key]); } } return $attribs; } /** * Use one error message for array elements with concatenated values * * @param boolean $concatJustValuesInErrorMessage * @return Zend_Form_Element */ public function setConcatJustValuesInErrorMessage($concatJustValuesInErrorMessage) { $this->_concatJustValuesInErrorMessage = $concatJustValuesInErrorMessage; return $this; } /** * Use one error message for array elements with concatenated values * * @return boolean */ public function getConcatJustValuesInErrorMessage() { return $this->_concatJustValuesInErrorMessage; } /** * Overloading: retrieve object property * * Prevents access to properties beginning with '_'. * * @param string $key * @return mixed */ public function __get($key) { if ('_' == $key[0]) { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception(sprintf('Cannot retrieve value for protected/private property "%s"', $key)); } if (!isset($this->$key)) { return null; } return $this->$key; } /** * Overloading: set object property * * @param string $key * @param mixed $value * @return voide */ public function __set($key, $value) { $this->setAttrib($key, $value); } /** * Overloading: allow rendering specific decorators * * Call renderDecoratorName() to render a specific decorator. * * @param string $method * @param array $args * @return string * @throws Zend_Form_Exception for invalid decorator or invalid method call */ public function __call($method, $args) { if ('render' == substr($method, 0, 6)) { $this->_isPartialRendering = true; $this->render(); $this->_isPartialRendering = false; $decoratorName = substr($method, 6); if (false !== ($decorator = $this->getDecorator($decoratorName))) { $decorator->setElement($this); $seed = ''; if (0 < count($args)) { $seed = array_shift($args); } return $decorator->render($seed); } require_once 'Zend/Form/Element/Exception.php'; throw new Zend_Form_Element_Exception(sprintf('Decorator by name %s does not exist', $decoratorName)); } require_once 'Zend/Form/Element/Exception.php'; throw new Zend_Form_Element_Exception(sprintf('Method %s does not exist', $method)); } // Loaders /** * Set plugin loader to use for validator or filter chain * * @param Zend_Loader_PluginLoader_Interface $loader * @param string $type 'decorator', 'filter', or 'validate' * @return Zend_Form_Element * @throws Zend_Form_Exception on invalid type */ public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type) { $type = strtoupper($type); switch ($type) { case self::DECORATOR: case self::FILTER: case self::VALIDATE: $this->_loaders[$type] = $loader; return $this; default: require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type)); } } /** * Retrieve plugin loader for validator or filter chain * * Instantiates with default rules if none available for that type. Use * 'decorator', 'filter', or 'validate' for $type. * * @param string $type * @return Zend_Loader_PluginLoader * @throws Zend_Loader_Exception on invalid type. */ public function getPluginLoader($type) { $type = strtoupper($type); switch ($type) { case self::FILTER: case self::VALIDATE: $prefixSegment = ucfirst(strtolower($type)); $pathSegment = $prefixSegment; case self::DECORATOR: if (!isset($prefixSegment)) { $prefixSegment = 'Form_Decorator'; $pathSegment = 'Form/Decorator'; } if (!isset($this->_loaders[$type])) { require_once 'Zend/Loader/PluginLoader.php'; $this->_loaders[$type] = new Zend_Loader_PluginLoader( array('Zend_' . $prefixSegment . '_' => 'Zend/' . $pathSegment . '/') ); } return $this->_loaders[$type]; default: require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type)); } } /** * Add prefix path for plugin loader * * If no $type specified, assumes it is a base path for both filters and * validators, and sets each according to the following rules: * - decorators: $prefix = $prefix . '_Decorator' * - filters: $prefix = $prefix . '_Filter' * - validators: $prefix = $prefix . '_Validate' * * Otherwise, the path prefix is set on the appropriate plugin loader. * * @param string $prefix * @param string $path * @param string $type * @return Zend_Form_Element * @throws Zend_Form_Exception for invalid type */ public function addPrefixPath($prefix, $path, $type = null) { $type = strtoupper($type); switch ($type) { case self::DECORATOR: case self::FILTER: case self::VALIDATE: $loader = $this->getPluginLoader($type); $loader->addPrefixPath($prefix, $path); return $this; case null: $nsSeparator = (false !== strpos($prefix, '\\'))?'\\':'_'; $prefix = rtrim($prefix, $nsSeparator) . $nsSeparator; $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; foreach (array(self::DECORATOR, self::FILTER, self::VALIDATE) as $type) { $cType = ucfirst(strtolower($type)); $loader = $this->getPluginLoader($type); $loader->addPrefixPath($prefix . $cType, $path . $cType . DIRECTORY_SEPARATOR); } return $this; default: require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type)); } } /** * Add many prefix paths at once * * @param array $spec * @return Zend_Form_Element */ public function addPrefixPaths(array $spec) { if (isset($spec['prefix']) && isset($spec['path'])) { return $this->addPrefixPath($spec['prefix'], $spec['path']); } foreach ($spec as $type => $paths) { if (is_numeric($type) && is_array($paths)) { $type = null; if (isset($paths['prefix']) && isset($paths['path'])) { if (isset($paths['type'])) { $type = $paths['type']; } $this->addPrefixPath($paths['prefix'], $paths['path'], $type); } } elseif (!is_numeric($type)) { if (!isset($paths['prefix']) || !isset($paths['path'])) { foreach ($paths as $prefix => $spec) { if (is_array($spec)) { foreach ($spec as $path) { if (!is_string($path)) { continue; } $this->addPrefixPath($prefix, $path, $type); } } elseif (is_string($spec)) { $this->addPrefixPath($prefix, $spec, $type); } } } else { $this->addPrefixPath($paths['prefix'], $paths['path'], $type); } } } return $this; } // Validation /** * Add validator to validation chain * * Note: will overwrite existing validators if they are of the same class. * * @param string|Zend_Validate_Interface $validator * @param bool $breakChainOnFailure * @param array $options * @return Zend_Form_Element * @throws Zend_Form_Exception if invalid validator type */ public function addValidator($validator, $breakChainOnFailure = false, $options = array()) { if ($validator instanceof Zend_Validate_Interface) { $name = get_class($validator); if (!isset($validator->zfBreakChainOnFailure)) { $validator->zfBreakChainOnFailure = $breakChainOnFailure; } } elseif (is_string($validator)) { $name = $validator; $validator = array( 'validator' => $validator, 'breakChainOnFailure' => $breakChainOnFailure, 'options' => $options, ); } else { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid validator provided to addValidator; must be string or Zend_Validate_Interface'); } $this->_validators[$name] = $validator; return $this; } /** * Add multiple validators * * @param array $validators * @return Zend_Form_Element */ public function addValidators(array $validators) { foreach ($validators as $validatorInfo) { if (is_string($validatorInfo)) { $this->addValidator($validatorInfo); } elseif ($validatorInfo instanceof Zend_Validate_Interface) { $this->addValidator($validatorInfo); } elseif (is_array($validatorInfo)) { $argc = count($validatorInfo); $breakChainOnFailure = false; $options = array(); if (isset($validatorInfo['validator'])) { $validator = $validatorInfo['validator']; if (isset($validatorInfo['breakChainOnFailure'])) { $breakChainOnFailure = $validatorInfo['breakChainOnFailure']; } if (isset($validatorInfo['options'])) { $options = $validatorInfo['options']; } $this->addValidator($validator, $breakChainOnFailure, $options); } else { switch (true) { case (0 == $argc): break; case (1 <= $argc): $validator = array_shift($validatorInfo); case (2 <= $argc): $breakChainOnFailure = array_shift($validatorInfo); case (3 <= $argc): $options = array_shift($validatorInfo); default: $this->addValidator($validator, $breakChainOnFailure, $options); break; } } } else { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid validator passed to addValidators()'); } } return $this; } /** * Set multiple validators, overwriting previous validators * * @param array $validators * @return Zend_Form_Element */ public function setValidators(array $validators) { $this->clearValidators(); return $this->addValidators($validators); } /** * Retrieve a single validator by name * * @param string $name * @return Zend_Validate_Interface|false False if not found, validator otherwise */ public function getValidator($name) { if (!isset($this->_validators[$name])) { $len = strlen($name); foreach ($this->_validators as $localName => $validator) { if ($len > strlen($localName)) { continue; } if (0 === substr_compare($localName, $name, -$len, $len, true)) { if (is_array($validator)) { return $this->_loadValidator($validator); } return $validator; } } return false; } if (is_array($this->_validators[$name])) { return $this->_loadValidator($this->_validators[$name]); } return $this->_validators[$name]; } /** * Retrieve all validators * * @return array */ public function getValidators() { $validators = array(); foreach ($this->_validators as $key => $value) { if ($value instanceof Zend_Validate_Interface) { $validators[$key] = $value; continue; } $validator = $this->_loadValidator($value); $validators[get_class($validator)] = $validator; } return $validators; } /** * Remove a single validator by name * * @param string $name * @return bool */ public function removeValidator($name) { if (isset($this->_validators[$name])) { unset($this->_validators[$name]); } else { $len = strlen($name); foreach (array_keys($this->_validators) as $validator) { if ($len > strlen($validator)) { continue; } if (0 === substr_compare($validator, $name, -$len, $len, true)) { unset($this->_validators[$validator]); break; } } } return $this; } /** * Clear all validators * * @return Zend_Form_Element */ public function clearValidators() { $this->_validators = array(); return $this; } /** * Validate element value * * If a translation adapter is registered, any error messages will be * translated according to the current locale, using the given error code; * if no matching translation is found, the original message will be * utilized. * * Note: The *filtered* value is validated. * * @param mixed $value * @param mixed $context * @return boolean */ public function isValid($value, $context = null) { $this->setValue($value); $value = $this->getValue(); if ((('' === $value) || (null === $value)) && !$this->isRequired() && $this->getAllowEmpty() ) { return true; } if ($this->isRequired() && $this->autoInsertNotEmptyValidator() && !$this->getValidator('NotEmpty')) { $validators = $this->getValidators(); $notEmpty = array('validator' => 'NotEmpty', 'breakChainOnFailure' => true); array_unshift($validators, $notEmpty); $this->setValidators($validators); } // Find the correct translator. Zend_Validate_Abstract::getDefaultTranslator() // will get either the static translator attached to Zend_Validate_Abstract // or the 'Zend_Translate' from Zend_Registry. if (Zend_Validate_Abstract::hasDefaultTranslator() && !Zend_Form::hasDefaultTranslator()) { $translator = Zend_Validate_Abstract::getDefaultTranslator(); if ($this->hasTranslator()) { // only pick up this element's translator if it was attached directly. $translator = $this->getTranslator(); } } else { $translator = $this->getTranslator(); } $this->_messages = array(); $this->_errors = array(); $result = true; $isArray = $this->isArray(); foreach ($this->getValidators() as $key => $validator) { if (method_exists($validator, 'setTranslator')) { if (method_exists($validator, 'hasTranslator')) { if (!$validator->hasTranslator()) { $validator->setTranslator($translator); } } else { $validator->setTranslator($translator); } } if (method_exists($validator, 'setDisableTranslator')) { $validator->setDisableTranslator($this->translatorIsDisabled()); } if ($isArray && is_array($value)) { $messages = array(); $errors = array(); if (empty($value)) { if ($this->isRequired() || (!$this->isRequired() && !$this->getAllowEmpty()) ) { $value = ''; } } foreach ((array)$value as $val) { if (!$validator->isValid($val, $context)) { $result = false; if ($this->_hasErrorMessages()) { $messages = $this->_getErrorMessages(); $errors = $messages; } else { $messages = array_merge($messages, $validator->getMessages()); $errors = array_merge($errors, $validator->getErrors()); } } } if ($result) { continue; } } elseif ($validator->isValid($value, $context)) { continue; } else { $result = false; if ($this->_hasErrorMessages()) { $messages = $this->_getErrorMessages(); $errors = $messages; } else { $messages = $validator->getMessages(); $errors = array_keys($messages); } } $result = false; $this->_messages = array_merge($this->_messages, $messages); $this->_errors = array_merge($this->_errors, $errors); if ($validator->zfBreakChainOnFailure) { break; } } // If element manually flagged as invalid, return false if ($this->_isErrorForced) { return false; } return $result; } /** * Add a custom error message to return in the event of failed validation * * @param string $message * @return Zend_Form_Element */ public function addErrorMessage($message) { $this->_errorMessages[] = (string) $message; return $this; } /** * Add multiple custom error messages to return in the event of failed validation * * @param array $messages * @return Zend_Form_Element */ public function addErrorMessages(array $messages) { foreach ($messages as $message) { $this->addErrorMessage($message); } return $this; } /** * Same as addErrorMessages(), but clears custom error message stack first * * @param array $messages * @return Zend_Form_Element */ public function setErrorMessages(array $messages) { $this->clearErrorMessages(); return $this->addErrorMessages($messages); } /** * Retrieve custom error messages * * @return array */ public function getErrorMessages() { return $this->_errorMessages; } /** * Clear custom error messages stack * * @return Zend_Form_Element */ public function clearErrorMessages() { $this->_errorMessages = array(); return $this; } /** * Get errorMessageSeparator * * @return string */ public function getErrorMessageSeparator() { return $this->_errorMessageSeparator; } /** * Set errorMessageSeparator * * @param string $separator * @return Zend_Form_Element */ public function setErrorMessageSeparator($separator) { $this->_errorMessageSeparator = $separator; return $this; } /** * Mark the element as being in a failed validation state * * @return Zend_Form_Element */ public function markAsError() { $messages = $this->getMessages(); $customMessages = $this->_getErrorMessages(); $messages = $messages + $customMessages; if (empty($messages)) { $this->_isError = true; } else { $this->_messages = $messages; } $this->_isErrorForced = true; return $this; } /** * Add an error message and mark element as failed validation * * @param string $message * @return Zend_Form_Element */ public function addError($message) { $this->addErrorMessage($message); $this->markAsError(); return $this; } /** * Add multiple error messages and flag element as failed validation * * @param array $messages * @return Zend_Form_Element */ public function addErrors(array $messages) { foreach ($messages as $message) { $this->addError($message); } return $this; } /** * Overwrite any previously set error messages and flag as failed validation * * @param array $messages * @return Zend_Form_Element */ public function setErrors(array $messages) { $this->clearErrorMessages(); return $this->addErrors($messages); } /** * Are there errors registered? * * @return bool */ public function hasErrors() { return (!empty($this->_messages) || $this->_isError); } /** * Retrieve validator chain errors * * @return array */ public function getErrors() { return $this->_errors; } /** * Retrieve error messages * * @return array */ public function getMessages() { return $this->_messages; } // Filtering /** * Add a filter to the element * * @param string|Zend_Filter_Interface $filter * @return Zend_Form_Element */ public function addFilter($filter, $options = array()) { if ($filter instanceof Zend_Filter_Interface) { $name = get_class($filter); } elseif (is_string($filter)) { $name = $filter; $filter = array( 'filter' => $filter, 'options' => $options, ); $this->_filters[$name] = $filter; } else { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid filter provided to addFilter; must be string or Zend_Filter_Interface'); } $this->_filters[$name] = $filter; return $this; } /** * Add filters to element * * @param array $filters * @return Zend_Form_Element */ public function addFilters(array $filters) { foreach ($filters as $filterInfo) { if (is_string($filterInfo)) { $this->addFilter($filterInfo); } elseif ($filterInfo instanceof Zend_Filter_Interface) { $this->addFilter($filterInfo); } elseif (is_array($filterInfo)) { $argc = count($filterInfo); $options = array(); if (isset($filterInfo['filter'])) { $filter = $filterInfo['filter']; if (isset($filterInfo['options'])) { $options = $filterInfo['options']; } $this->addFilter($filter, $options); } else { switch (true) { case (0 == $argc): break; case (1 <= $argc): $filter = array_shift($filterInfo); case (2 <= $argc): $options = array_shift($filterInfo); default: $this->addFilter($filter, $options); break; } } } else { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid filter passed to addFilters()'); } } return $this; } /** * Add filters to element, overwriting any already existing * * @param array $filters * @return Zend_Form_Element */ public function setFilters(array $filters) { $this->clearFilters(); return $this->addFilters($filters); } /** * Retrieve a single filter by name * * @param string $name * @return Zend_Filter_Interface */ public function getFilter($name) { if (!isset($this->_filters[$name])) { $len = strlen($name); foreach ($this->_filters as $localName => $filter) { if ($len > strlen($localName)) { continue; } if (0 === substr_compare($localName, $name, -$len, $len, true)) { if (is_array($filter)) { return $this->_loadFilter($filter); } return $filter; } } return false; } if (is_array($this->_filters[$name])) { return $this->_loadFilter($this->_filters[$name]); } return $this->_filters[$name]; } /** * Get all filters * * @return array */ public function getFilters() { $filters = array(); foreach ($this->_filters as $key => $value) { if ($value instanceof Zend_Filter_Interface) { $filters[$key] = $value; continue; } $filter = $this->_loadFilter($value); $filters[get_class($filter)] = $filter; } return $filters; } /** * Remove a filter by name * * @param string $name * @return Zend_Form_Element */ public function removeFilter($name) { if (isset($this->_filters[$name])) { unset($this->_filters[$name]); } else { $len = strlen($name); foreach (array_keys($this->_filters) as $filter) { if ($len > strlen($filter)) { continue; } if (0 === substr_compare($filter, $name, -$len, $len, true)) { unset($this->_filters[$filter]); break; } } } return $this; } /** * Clear all filters * * @return Zend_Form_Element */ public function clearFilters() { $this->_filters = array(); return $this; } // Rendering /** * Set view object * * @param Zend_View_Interface $view * @return Zend_Form_Element */ public function setView(Zend_View_Interface $view = null) { $this->_view = $view; return $this; } /** * Retrieve view object * * Retrieves from ViewRenderer if none previously set. * * @return null|Zend_View_Interface */ public function getView() { if (null === $this->_view) { require_once 'Zend/Controller/Action/HelperBroker.php'; $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); $this->setView($viewRenderer->view); } return $this->_view; } /** * Instantiate a decorator based on class name or class name fragment * * @param string $name * @param null|array $options * @return Zend_Form_Decorator_Interface */ protected function _getDecorator($name, $options) { $class = $this->getPluginLoader(self::DECORATOR)->load($name); if (null === $options) { $decorator = new $class; } else { $decorator = new $class($options); } return $decorator; } /** * Add a decorator for rendering the element * * @param string|Zend_Form_Decorator_Interface $decorator * @param array|Zend_Config $options Options with which to initialize decorator * @return Zend_Form_Element */ public function addDecorator($decorator, $options = null) { if ($decorator instanceof Zend_Form_Decorator_Interface) { $name = get_class($decorator); } elseif (is_string($decorator)) { $name = $decorator; $decorator = array( 'decorator' => $name, 'options' => $options, ); } elseif (is_array($decorator)) { foreach ($decorator as $name => $spec) { break; } if (is_numeric($name)) { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid alias provided to addDecorator; must be alphanumeric string'); } if (is_string($spec)) { $decorator = array( 'decorator' => $spec, 'options' => $options, ); } elseif ($spec instanceof Zend_Form_Decorator_Interface) { $decorator = $spec; } } else { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid decorator provided to addDecorator; must be string or Zend_Form_Decorator_Interface'); } $this->_decorators[$name] = $decorator; return $this; } /** * Add many decorators at once * * @param array $decorators * @return Zend_Form_Element */ public function addDecorators(array $decorators) { foreach ($decorators as $decoratorName => $decoratorInfo) { if (is_string($decoratorInfo) || $decoratorInfo instanceof Zend_Form_Decorator_Interface) { if (!is_numeric($decoratorName)) { $this->addDecorator(array($decoratorName => $decoratorInfo)); } else { $this->addDecorator($decoratorInfo); } } elseif (is_array($decoratorInfo)) { $argc = count($decoratorInfo); $options = array(); if (isset($decoratorInfo['decorator'])) { $decorator = $decoratorInfo['decorator']; if (isset($decoratorInfo['options'])) { $options = $decoratorInfo['options']; } $this->addDecorator($decorator, $options); } else { switch (true) { case (0 == $argc): break; case (1 <= $argc): $decorator = array_shift($decoratorInfo); case (2 <= $argc): $options = array_shift($decoratorInfo); default: $this->addDecorator($decorator, $options); break; } } } else { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception('Invalid decorator passed to addDecorators()'); } } return $this; } /** * Overwrite all decorators * * @param array $decorators * @return Zend_Form_Element */ public function setDecorators(array $decorators) { $this->clearDecorators(); return $this->addDecorators($decorators); } /** * Retrieve a registered decorator * * @param string $name * @return false|Zend_Form_Decorator_Abstract */ public function getDecorator($name) { if (!isset($this->_decorators[$name])) { $len = strlen($name); foreach ($this->_decorators as $localName => $decorator) { if ($len > strlen($localName)) { continue; } if (0 === substr_compare($localName, $name, -$len, $len, true)) { if (is_array($decorator)) { return $this->_loadDecorator($decorator, $localName); } return $decorator; } } return false; } if (is_array($this->_decorators[$name])) { return $this->_loadDecorator($this->_decorators[$name], $name); } return $this->_decorators[$name]; } /** * Retrieve all decorators * * @return array */ public function getDecorators() { foreach ($this->_decorators as $key => $value) { if (is_array($value)) { $this->_loadDecorator($value, $key); } } return $this->_decorators; } /** * Remove a single decorator * * @param string $name * @return Zend_Form_Element */ public function removeDecorator($name) { if (isset($this->_decorators[$name])) { unset($this->_decorators[$name]); } else { $len = strlen($name); foreach (array_keys($this->_decorators) as $decorator) { if ($len > strlen($decorator)) { continue; } if (0 === substr_compare($decorator, $name, -$len, $len, true)) { unset($this->_decorators[$decorator]); break; } } } return $this; } /** * Clear all decorators * * @return Zend_Form_Element */ public function clearDecorators() { $this->_decorators = array(); return $this; } /** * Render form element * * @param Zend_View_Interface $view * @return string */ public function render(Zend_View_Interface $view = null) { if ($this->_isPartialRendering) { return ''; } if (null !== $view) { $this->setView($view); } $content = ''; foreach ($this->getDecorators() as $decorator) { $decorator->setElement($this); $content = $decorator->render($content); } return $content; } /** * String representation of form element * * Proxies to {@link render()}. * * @return string */ public function __toString() { try { $return = $this->render(); return $return; } catch (Exception $e) { trigger_error($e->getMessage(), E_USER_WARNING); return ''; } } /** * Lazy-load a filter * * @param array $filter * @return Zend_Filter_Interface */ protected function _loadFilter(array $filter) { $origName = $filter['filter']; $name = $this->getPluginLoader(self::FILTER)->load($filter['filter']); if (array_key_exists($name, $this->_filters)) { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception(sprintf('Filter instance already exists for filter "%s"', $origName)); } if (empty($filter['options'])) { $instance = new $name; } else { $r = new ReflectionClass($name); if ($r->hasMethod('__construct')) { $instance = $r->newInstanceArgs((array) $filter['options']); } else { $instance = $r->newInstance(); } } if ($origName != $name) { $filterNames = array_keys($this->_filters); $order = array_flip($filterNames); $order[$name] = $order[$origName]; $filtersExchange = array(); unset($order[$origName]); asort($order); foreach ($order as $key => $index) { if ($key == $name) { $filtersExchange[$key] = $instance; continue; } $filtersExchange[$key] = $this->_filters[$key]; } $this->_filters = $filtersExchange; } else { $this->_filters[$name] = $instance; } return $instance; } /** * Lazy-load a validator * * @param array $validator Validator definition * @return Zend_Validate_Interface */ protected function _loadValidator(array $validator) { $origName = $validator['validator']; $name = $this->getPluginLoader(self::VALIDATE)->load($validator['validator']); if (array_key_exists($name, $this->_validators)) { require_once 'Zend/Form/Exception.php'; throw new Zend_Form_Exception(sprintf('Validator instance already exists for validator "%s"', $origName)); } $messages = false; if (isset($validator['options']) && array_key_exists('messages', (array)$validator['options'])) { $messages = $validator['options']['messages']; unset($validator['options']['messages']); } if (empty($validator['options'])) { $instance = new $name; } else { $r = new ReflectionClass($name); if ($r->hasMethod('__construct')) { $numeric = false; if (is_array($validator['options'])) { $keys = array_keys($validator['options']); foreach($keys as $key) { if (is_numeric($key)) { $numeric = true; break; } } } if ($numeric) { $instance = $r->newInstanceArgs((array) $validator['options']); } else { $instance = $r->newInstance($validator['options']); } } else { $instance = $r->newInstance(); } } if ($messages) { if (is_array($messages)) { $instance->setMessages($messages); } elseif (is_string($messages)) { $instance->setMessage($messages); } } $instance->zfBreakChainOnFailure = $validator['breakChainOnFailure']; if ($origName != $name) { $validatorNames = array_keys($this->_validators); $order = array_flip($validatorNames); $order[$name] = $order[$origName]; $validatorsExchange = array(); unset($order[$origName]); asort($order); foreach ($order as $key => $index) { if ($key == $name) { $validatorsExchange[$key] = $instance; continue; } $validatorsExchange[$key] = $this->_validators[$key]; } $this->_validators = $validatorsExchange; } else { $this->_validators[$name] = $instance; } return $instance; } /** * Lazy-load a decorator * * @param array $decorator Decorator type and options * @param mixed $name Decorator name or alias * @return Zend_Form_Decorator_Interface */ protected function _loadDecorator(array $decorator, $name) { $sameName = false; if ($name == $decorator['decorator']) { $sameName = true; } $instance = $this->_getDecorator($decorator['decorator'], $decorator['options']); if ($sameName) { $newName = get_class($instance); $decoratorNames = array_keys($this->_decorators); $order = array_flip($decoratorNames); $order[$newName] = $order[$name]; $decoratorsExchange = array(); unset($order[$name]); asort($order); foreach ($order as $key => $index) { if ($key == $newName) { $decoratorsExchange[$key] = $instance; continue; } $decoratorsExchange[$key] = $this->_decorators[$key]; } $this->_decorators = $decoratorsExchange; } else { $this->_decorators[$name] = $instance; } return $instance; } /** * Retrieve error messages and perform translation and value substitution * * @return array */ protected function _getErrorMessages() { $translator = $this->getTranslator(); $messages = $this->getErrorMessages(); $value = $this->getValue(); foreach ($messages as $key => $message) { if (null !== $translator) { $message = $translator->translate($message); } if ($this->isArray() || is_array($value)) { $aggregateMessages = array(); foreach ($value as $val) { $aggregateMessages[] = str_replace('%value%', $val, $message); } if (count($aggregateMessages)) { if ($this->_concatJustValuesInErrorMessage) { $values = implode($this->getErrorMessageSeparator(), $value); $messages[$key] = str_replace('%value%', $values, $message); } else { $messages[$key] = implode($this->getErrorMessageSeparator(), $aggregateMessages); } } } else { $messages[$key] = str_replace('%value%', $value, $message); } } return $messages; } /** * Are there custom error messages registered? * * @return bool */ protected function _hasErrorMessages() { return !empty($this->_errorMessages); } }