File indexing completed on 2024-05-12 06:02:33

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_Filter
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  * @version    $Id$
0020  */
0021 
0022 /**
0023  * @see Zend_Loader
0024  */
0025 // require_once 'Zend/Loader.php';
0026 
0027 /**
0028  * @see Zend_Filter
0029  */
0030 // require_once 'Zend/Filter.php';
0031 
0032 /**
0033  * @see Zend_Validate
0034  */
0035 // require_once 'Zend/Validate.php';
0036 
0037 /**
0038  * @category   Zend
0039  * @package    Zend_Filter
0040  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0041  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0042  */
0043 class Zend_Filter_Input
0044 {
0045 
0046     const ALLOW_EMPTY           = 'allowEmpty';
0047     const BREAK_CHAIN           = 'breakChainOnFailure';
0048     const DEFAULT_VALUE         = 'default';
0049     const MESSAGES              = 'messages';
0050     const ESCAPE_FILTER         = 'escapeFilter';
0051     const FIELDS                = 'fields';
0052     const FILTER                = 'filter';
0053     const FILTER_CHAIN          = 'filterChain';
0054     const MISSING_MESSAGE       = 'missingMessage';
0055     const INPUT_NAMESPACE       = 'inputNamespace';
0056     const VALIDATOR_NAMESPACE   = 'validatorNamespace';
0057     const FILTER_NAMESPACE      = 'filterNamespace';
0058     const NOT_EMPTY_MESSAGE     = 'notEmptyMessage';
0059     const PRESENCE              = 'presence';
0060     const PRESENCE_OPTIONAL     = 'optional';
0061     const PRESENCE_REQUIRED     = 'required';
0062     const RULE                  = 'rule';
0063     const RULE_WILDCARD         = '*';
0064     const VALIDATE              = 'validate';
0065     const VALIDATOR             = 'validator';
0066     const VALIDATOR_CHAIN       = 'validatorChain';
0067     const VALIDATOR_CHAIN_COUNT = 'validatorChainCount';
0068 
0069     /**
0070      * @var array Input data, before processing.
0071      */
0072     protected $_data = array();
0073 
0074     /**
0075      * @var array Association of rules to filters.
0076      */
0077     protected $_filterRules = array();
0078 
0079     /**
0080      * @var array Association of rules to validators.
0081      */
0082     protected $_validatorRules = array();
0083 
0084     /**
0085      * @var array After processing data, this contains mapping of valid fields
0086      * to field values.
0087      */
0088     protected $_validFields = array();
0089 
0090     /**
0091      * @var array After processing data, this contains mapping of validation
0092      * rules that did not pass validation to the array of messages returned
0093      * by the validator chain.
0094      */
0095     protected $_invalidMessages = array();
0096 
0097     /**
0098      * @var array After processing data, this contains mapping of validation
0099      * rules that did not pass validation to the array of error identifiers
0100      * returned by the validator chain.
0101      */
0102     protected $_invalidErrors = array();
0103 
0104     /**
0105      * @var array After processing data, this contains mapping of validation
0106      * rules in which some fields were missing to the array of messages
0107      * indicating which fields were missing.
0108      */
0109     protected $_missingFields = array();
0110 
0111     /**
0112      * @var array After processing, this contains a copy of $_data elements
0113      * that were not mentioned in any validation rule.
0114      */
0115     protected $_unknownFields = array();
0116 
0117     /**
0118      * @var Zend_Filter_Interface The filter object that is run on values
0119      * returned by the getEscaped() method.
0120      */
0121     protected $_defaultEscapeFilter = null;
0122 
0123     /**
0124      * Plugin loaders
0125      * @var array
0126      */
0127     protected $_loaders = array();
0128 
0129     /**
0130      * @var array Default values to use when processing filters and validators.
0131      */
0132     protected $_defaults = array(
0133         self::ALLOW_EMPTY         => false,
0134         self::BREAK_CHAIN         => false,
0135         self::ESCAPE_FILTER       => 'HtmlEntities',
0136         self::MISSING_MESSAGE     => "Field '%field%' is required by rule '%rule%', but the field is missing",
0137         self::NOT_EMPTY_MESSAGE   => "You must give a non-empty value for field '%field%'",
0138         self::PRESENCE            => self::PRESENCE_OPTIONAL
0139     );
0140 
0141     /**
0142      * @var boolean Set to False initially, this is set to True after the
0143      * input data have been processed.  Reset to False in setData() method.
0144      */
0145     protected $_processed = false;
0146 
0147     /**
0148      * Translation object
0149      * @var Zend_Translate
0150      */
0151     protected $_translator;
0152 
0153     /**
0154      * Is translation disabled?
0155      * @var Boolean
0156      */
0157     protected $_translatorDisabled = false;
0158 
0159     /**
0160      * @param array $filterRules
0161      * @param array $validatorRules
0162      * @param array $data       OPTIONAL
0163      * @param array $options    OPTIONAL
0164      */
0165     public function __construct($filterRules, $validatorRules, array $data = null, array $options = null)
0166     {
0167         if ($options) {
0168             $this->setOptions($options);
0169         }
0170 
0171         $this->_filterRules = (array) $filterRules;
0172         $this->_validatorRules = (array) $validatorRules;
0173 
0174         if ($data) {
0175             $this->setData($data);
0176         }
0177     }
0178 
0179     /**
0180      * @param mixed $namespaces
0181      * @return Zend_Filter_Input
0182      * @deprecated since 1.5.0RC1 - use addFilterPrefixPath() or addValidatorPrefixPath instead.
0183      */
0184     public function addNamespace($namespaces)
0185     {
0186         if (!is_array($namespaces)) {
0187             $namespaces = array($namespaces);
0188         }
0189 
0190         foreach ($namespaces as $namespace) {
0191             $prefix = $namespace;
0192             $path = str_replace('_', DIRECTORY_SEPARATOR, $prefix);
0193             $this->addFilterPrefixPath($prefix, $path);
0194             $this->addValidatorPrefixPath($prefix, $path);
0195         }
0196 
0197         return $this;
0198     }
0199 
0200     /**
0201      * Add prefix path for all elements
0202      *
0203      * @param  string $prefix
0204      * @param  string $path
0205      * @return Zend_Filter_Input
0206      */
0207     public function addFilterPrefixPath($prefix, $path)
0208     {
0209         $this->getPluginLoader(self::FILTER)->addPrefixPath($prefix, $path);
0210 
0211         return $this;
0212     }
0213 
0214     /**
0215      * Add prefix path for all elements
0216      *
0217      * @param  string $prefix
0218      * @param  string $path
0219      * @return Zend_Filter_Input
0220      */
0221     public function addValidatorPrefixPath($prefix, $path)
0222     {
0223         $this->getPluginLoader(self::VALIDATE)->addPrefixPath($prefix, $path);
0224 
0225         return $this;
0226     }
0227 
0228     /**
0229      * Set plugin loaders for use with decorators and elements
0230      *
0231      * @param  Zend_Loader_PluginLoader_Interface $loader
0232      * @param  string $type 'filter' or 'validate'
0233      * @return Zend_Filter_Input
0234      * @throws Zend_Filter_Exception on invalid type
0235      */
0236     public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type)
0237     {
0238         $type = strtolower($type);
0239         switch ($type) {
0240             case self::FILTER:
0241             case self::VALIDATE:
0242                 $this->_loaders[$type] = $loader;
0243                 return $this;
0244             default:
0245                 // require_once 'Zend/Filter/Exception.php';
0246                 throw new Zend_Filter_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type));
0247         }
0248 
0249         return $this;
0250     }
0251 
0252     /**
0253      * Retrieve plugin loader for given type
0254      *
0255      * $type may be one of:
0256      * - filter
0257      * - validator
0258      *
0259      * If a plugin loader does not exist for the given type, defaults are
0260      * created.
0261      *
0262      * @param  string $type 'filter' or 'validate'
0263      * @return Zend_Loader_PluginLoader_Interface
0264      * @throws Zend_Filter_Exception on invalid type
0265      */
0266     public function getPluginLoader($type)
0267     {
0268         $type = strtolower($type);
0269         if (!isset($this->_loaders[$type])) {
0270             switch ($type) {
0271                 case self::FILTER:
0272                     $prefixSegment = 'Zend_Filter_';
0273                     $pathSegment   = 'Zend/Filter/';
0274                     break;
0275                 case self::VALIDATE:
0276                     $prefixSegment = 'Zend_Validate_';
0277                     $pathSegment   = 'Zend/Validate/';
0278                     break;
0279                 default:
0280                     // require_once 'Zend/Filter/Exception.php';
0281                     throw new Zend_Filter_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
0282             }
0283 
0284             // require_once 'Zend/Loader/PluginLoader.php';
0285             $this->_loaders[$type] = new Zend_Loader_PluginLoader(
0286                 array($prefixSegment => $pathSegment)
0287             );
0288         }
0289 
0290         return $this->_loaders[$type];
0291     }
0292 
0293     /**
0294      * @return array
0295      */
0296     public function getMessages()
0297     {
0298         $this->_process();
0299         return array_merge($this->_invalidMessages, $this->_missingFields);
0300     }
0301 
0302     /**
0303      * @return array
0304      */
0305     public function getErrors()
0306     {
0307         $this->_process();
0308         return $this->_invalidErrors;
0309     }
0310 
0311     /**
0312      * @return array
0313      */
0314     public function getInvalid()
0315     {
0316         $this->_process();
0317         return $this->_invalidMessages;
0318     }
0319 
0320     /**
0321      * @return array
0322      */
0323     public function getMissing()
0324     {
0325         $this->_process();
0326         return $this->_missingFields;
0327     }
0328 
0329     /**
0330      * @return array
0331      */
0332     public function getUnknown()
0333     {
0334         $this->_process();
0335         return $this->_unknownFields;
0336     }
0337 
0338     /**
0339      * @param string $fieldName OPTIONAL
0340      * @return mixed
0341      */
0342     public function getEscaped($fieldName = null)
0343     {
0344         $this->_process();
0345         $this->_getDefaultEscapeFilter();
0346 
0347         if ($fieldName === null) {
0348             return $this->_escapeRecursive($this->_validFields);
0349         }
0350         if (array_key_exists($fieldName, $this->_validFields)) {
0351             return $this->_escapeRecursive($this->_validFields[$fieldName]);
0352         }
0353         return null;
0354     }
0355 
0356     /**
0357      * @param mixed $value
0358      * @return mixed
0359      */
0360     protected function _escapeRecursive($data)
0361     {
0362         if($data === null) {
0363             return $data;
0364         }
0365 
0366         if (!is_array($data)) {
0367             return $this->_getDefaultEscapeFilter()->filter($data);
0368         }
0369         foreach ($data as &$element) {
0370             $element = $this->_escapeRecursive($element);
0371         }
0372         return $data;
0373     }
0374 
0375     /**
0376      * @param string $fieldName OPTIONAL
0377      * @return mixed
0378      */
0379     public function getUnescaped($fieldName = null)
0380     {
0381         $this->_process();
0382         if ($fieldName === null) {
0383             return $this->_validFields;
0384         }
0385         if (array_key_exists($fieldName, $this->_validFields)) {
0386             return $this->_validFields[$fieldName];
0387         }
0388         return null;
0389     }
0390 
0391     /**
0392      * @param string $fieldName
0393      * @return mixed
0394      */
0395     public function __get($fieldName)
0396     {
0397         return $this->getEscaped($fieldName);
0398     }
0399 
0400     /**
0401      * @return boolean
0402      */
0403     public function hasInvalid()
0404     {
0405         $this->_process();
0406         return !(empty($this->_invalidMessages));
0407     }
0408 
0409     /**
0410      * @return boolean
0411      */
0412     public function hasMissing()
0413     {
0414         $this->_process();
0415         return !(empty($this->_missingFields));
0416     }
0417 
0418     /**
0419      * @return boolean
0420      */
0421     public function hasUnknown()
0422     {
0423         $this->_process();
0424         return !(empty($this->_unknownFields));
0425     }
0426 
0427     /**
0428      * @return boolean
0429      */
0430     public function hasValid()
0431     {
0432         $this->_process();
0433         return !(empty($this->_validFields));
0434     }
0435 
0436     /**
0437      * @param string $fieldName
0438      * @return boolean
0439      */
0440     public function isValid($fieldName = null)
0441     {
0442         $this->_process();
0443         if ($fieldName === null) {
0444             return !($this->hasMissing() || $this->hasInvalid());
0445         }
0446         return array_key_exists($fieldName, $this->_validFields);
0447     }
0448 
0449     /**
0450      * @param string $fieldName
0451      * @return boolean
0452      */
0453     public function __isset($fieldName)
0454     {
0455         $this->_process();
0456         return isset($this->_validFields[$fieldName]);
0457     }
0458 
0459     /**
0460      * @return Zend_Filter_Input
0461      * @throws Zend_Filter_Exception
0462      */
0463     public function process()
0464     {
0465         $this->_process();
0466         if ($this->hasInvalid()) {
0467             // require_once 'Zend/Filter/Exception.php';
0468             throw new Zend_Filter_Exception("Input has invalid fields");
0469         }
0470         if ($this->hasMissing()) {
0471             // require_once 'Zend/Filter/Exception.php';
0472             throw new Zend_Filter_Exception("Input has missing fields");
0473         }
0474 
0475         return $this;
0476     }
0477 
0478     /**
0479      * @param array $data
0480      * @return Zend_Filter_Input
0481      */
0482     public function setData(array $data)
0483     {
0484         $this->_data = $data;
0485 
0486         /**
0487          * Reset to initial state
0488          */
0489         $this->_validFields = array();
0490         $this->_invalidMessages = array();
0491         $this->_invalidErrors = array();
0492         $this->_missingFields = array();
0493         $this->_unknownFields = array();
0494 
0495         $this->_processed = false;
0496 
0497         return $this;
0498     }
0499 
0500     /**
0501      * @param mixed $escapeFilter
0502      * @return Zend_Filter_Interface
0503      */
0504     public function setDefaultEscapeFilter($escapeFilter)
0505     {
0506         if (is_string($escapeFilter) || is_array($escapeFilter)) {
0507             $escapeFilter = $this->_getFilter($escapeFilter);
0508         }
0509         if (!$escapeFilter instanceof Zend_Filter_Interface) {
0510             // require_once 'Zend/Filter/Exception.php';
0511             throw new Zend_Filter_Exception('Escape filter specified does not implement Zend_Filter_Interface');
0512         }
0513         $this->_defaultEscapeFilter = $escapeFilter;
0514         return $escapeFilter;
0515     }
0516 
0517     /**
0518      * @param array $options
0519      * @return Zend_Filter_Input
0520      * @throws Zend_Filter_Exception if an unknown option is given
0521      */
0522     public function setOptions(array $options)
0523     {
0524         foreach ($options as $option => $value) {
0525             switch ($option) {
0526                 case self::ESCAPE_FILTER:
0527                     $this->setDefaultEscapeFilter($value);
0528                     break;
0529                 case self::INPUT_NAMESPACE:
0530                     $this->addNamespace($value);
0531                     break;
0532                 case self::VALIDATOR_NAMESPACE:
0533                     if(is_string($value)) {
0534                         $value = array($value);
0535                     }
0536 
0537                     foreach($value AS $prefix) {
0538                         $this->addValidatorPrefixPath(
0539                                 $prefix,
0540                                 str_replace('_', DIRECTORY_SEPARATOR, $prefix)
0541                         );
0542                     }
0543                     break;
0544                 case self::FILTER_NAMESPACE:
0545                     if(is_string($value)) {
0546                         $value = array($value);
0547                     }
0548 
0549                     foreach($value AS $prefix) {
0550                         $this->addFilterPrefixPath(
0551                                 $prefix,
0552                                 str_replace('_', DIRECTORY_SEPARATOR, $prefix)
0553                         );
0554                     }
0555                     break;
0556                 case self::ALLOW_EMPTY:
0557                 case self::BREAK_CHAIN:
0558                 case self::MISSING_MESSAGE:
0559                 case self::NOT_EMPTY_MESSAGE:
0560                 case self::PRESENCE:
0561                     $this->_defaults[$option] = $value;
0562                     break;
0563                 default:
0564                     // require_once 'Zend/Filter/Exception.php';
0565                     throw new Zend_Filter_Exception("Unknown option '$option'");
0566                     break;
0567             }
0568         }
0569 
0570         return $this;
0571     }
0572 
0573     /**
0574      * Set translation object
0575      *
0576      * @param  Zend_Translate|Zend_Translate_Adapter|null $translator
0577      * @return Zend_Filter_Input
0578      */
0579     public function setTranslator($translator = null)
0580     {
0581         if ((null === $translator) || ($translator instanceof Zend_Translate_Adapter)) {
0582             $this->_translator = $translator;
0583         } elseif ($translator instanceof Zend_Translate) {
0584             $this->_translator = $translator->getAdapter();
0585         } else {
0586             // require_once 'Zend/Validate/Exception.php';
0587             throw new Zend_Validate_Exception('Invalid translator specified');
0588         }
0589 
0590         return $this;
0591     }
0592 
0593     /**
0594      * Return translation object
0595      *
0596      * @return Zend_Translate_Adapter|null
0597      */
0598     public function getTranslator()
0599     {
0600         if ($this->translatorIsDisabled()) {
0601             return null;
0602         }
0603 
0604         if ($this->_translator === null) {
0605             // require_once 'Zend/Registry.php';
0606             if (Zend_Registry::isRegistered('Zend_Translate')) {
0607                 $translator = Zend_Registry::get('Zend_Translate');
0608                 if ($translator instanceof Zend_Translate_Adapter) {
0609                     return $translator;
0610                 } elseif ($translator instanceof Zend_Translate) {
0611                     return $translator->getAdapter();
0612                 }
0613             }
0614         }
0615 
0616         return $this->_translator;
0617     }
0618 
0619     /**
0620      * Indicate whether or not translation should be disabled
0621      *
0622      * @param  bool $flag
0623      * @return Zend_Filter_Input
0624      */
0625     public function setDisableTranslator($flag)
0626     {
0627         $this->_translatorDisabled = (bool) $flag;
0628         return $this;
0629     }
0630 
0631     /**
0632      * Is translation disabled?
0633      *
0634      * @return bool
0635      */
0636     public function translatorIsDisabled()
0637     {
0638         return $this->_translatorDisabled;
0639     }
0640 
0641     /*
0642      * Protected methods
0643      */
0644 
0645     /**
0646      * @return void
0647      */
0648     protected function _filter()
0649     {
0650         foreach ($this->_filterRules as $ruleName => &$filterRule) {
0651             /**
0652              * Make sure we have an array representing this filter chain.
0653              * Don't typecast to (array) because it might be a Zend_Filter object
0654              */
0655             if (!is_array($filterRule)) {
0656                 $filterRule = array($filterRule);
0657             }
0658 
0659             /**
0660              * Filters are indexed by integer, metacommands are indexed by string.
0661              * Pick out the filters.
0662              */
0663             $filterList = array();
0664             foreach ($filterRule as $key => $value) {
0665                 if (is_int($key)) {
0666                     $filterList[] = $value;
0667                 }
0668             }
0669 
0670             /**
0671              * Use defaults for filter metacommands.
0672              */
0673             $filterRule[self::RULE] = $ruleName;
0674             if (!isset($filterRule[self::FIELDS])) {
0675                 $filterRule[self::FIELDS] = $ruleName;
0676             }
0677 
0678             /**
0679              * Load all the filter classes and add them to the chain.
0680              */
0681             if (!isset($filterRule[self::FILTER_CHAIN])) {
0682                 $filterRule[self::FILTER_CHAIN] = new Zend_Filter();
0683                 foreach ($filterList as $filter) {
0684                     if (is_string($filter) || is_array($filter)) {
0685                         $filter = $this->_getFilter($filter);
0686                     }
0687                     $filterRule[self::FILTER_CHAIN]->addFilter($filter);
0688                 }
0689             }
0690 
0691             /**
0692              * If the ruleName is the special wildcard rule,
0693              * then apply the filter chain to all input data.
0694              * Else just process the field named by the rule.
0695              */
0696             if ($ruleName == self::RULE_WILDCARD) {
0697                 foreach (array_keys($this->_data) as $field)  {
0698                     $this->_filterRule(array_merge($filterRule, array(self::FIELDS => $field)));
0699                 }
0700             } else {
0701                 $this->_filterRule($filterRule);
0702             }
0703         }
0704     }
0705 
0706     /**
0707      * @param array $filterRule
0708      * @return void
0709      */
0710     protected function _filterRule(array $filterRule)
0711     {
0712         $field = $filterRule[self::FIELDS];
0713         if (!array_key_exists($field, $this->_data)) {
0714             return;
0715         }
0716         if (is_array($this->_data[$field])) {
0717             foreach ($this->_data[$field] as $key => $value) {
0718                 $this->_data[$field][$key] = $filterRule[self::FILTER_CHAIN]->filter($value);
0719             }
0720         } else {
0721             $this->_data[$field] =
0722                 $filterRule[self::FILTER_CHAIN]->filter($this->_data[$field]);
0723         }
0724     }
0725 
0726     /**
0727      * @return Zend_Filter_Interface
0728      */
0729     protected function _getDefaultEscapeFilter()
0730     {
0731         if ($this->_defaultEscapeFilter !== null) {
0732             return $this->_defaultEscapeFilter;
0733         }
0734         return $this->setDefaultEscapeFilter($this->_defaults[self::ESCAPE_FILTER]);
0735     }
0736 
0737     /**
0738      * @param string $rule
0739      * @param string $field
0740      * @return string
0741      */
0742     protected function _getMissingMessage($rule, $field)
0743     {
0744         $message = $this->_defaults[self::MISSING_MESSAGE];
0745 
0746         if (null !== ($translator = $this->getTranslator())) {
0747             if ($translator->isTranslated(self::MISSING_MESSAGE)) {
0748                 $message = $translator->translate(self::MISSING_MESSAGE);
0749             } else {
0750                 $message = $translator->translate($message);
0751             }
0752         }
0753 
0754         $message = str_replace('%rule%', $rule, $message);
0755         $message = str_replace('%field%', $field, $message);
0756         return $message;
0757     }
0758 
0759     /**
0760      * @return string
0761      */
0762     protected function _getNotEmptyMessage($rule, $field)
0763     {
0764         $message = $this->_defaults[self::NOT_EMPTY_MESSAGE];
0765 
0766         if (null !== ($translator = $this->getTranslator())) {
0767             if ($translator->isTranslated(self::NOT_EMPTY_MESSAGE)) {
0768                 $message = $translator->translate(self::NOT_EMPTY_MESSAGE);
0769             } else {
0770                 $message = $translator->translate($message);
0771             }
0772         }
0773 
0774         $message = str_replace('%rule%', $rule, $message);
0775         $message = str_replace('%field%', $field, $message);
0776         return $message;
0777     }
0778 
0779     /**
0780      * @return void
0781      */
0782     protected function _process()
0783     {
0784         if ($this->_processed === false) {
0785             $this->_filter();
0786             $this->_validate();
0787             $this->_processed = true;
0788         }
0789     }
0790 
0791     /**
0792      * @return void
0793      */
0794     protected function _validate()
0795     {
0796         /**
0797          * Special case: if there are no validators, treat all fields as valid.
0798          */
0799         if (!$this->_validatorRules) {
0800             $this->_validFields = $this->_data;
0801             $this->_data = array();
0802             return;
0803         }
0804         
0805         // remember the default not empty message in case we want to temporarily change it        
0806         $preserveDefaultNotEmptyMessage = $this->_defaults[self::NOT_EMPTY_MESSAGE];
0807 
0808         foreach ($this->_validatorRules as $ruleName => &$validatorRule) {
0809             /**
0810              * Make sure we have an array representing this validator chain.
0811              * Don't typecast to (array) because it might be a Zend_Validate object
0812              */
0813             if (!is_array($validatorRule)) {
0814                 $validatorRule = array($validatorRule);
0815             }
0816 
0817             /**
0818              * Validators are indexed by integer, metacommands are indexed by string.
0819              * Pick out the validators.
0820              */
0821             $validatorList = array();
0822             foreach ($validatorRule as $key => $value) {
0823                 if (is_int($key)) {
0824                     $validatorList[$key] = $value;
0825                 }
0826             }
0827 
0828             /**
0829              * Use defaults for validation metacommands.
0830              */
0831             $validatorRule[self::RULE] = $ruleName;
0832             if (!isset($validatorRule[self::FIELDS])) {
0833                 $validatorRule[self::FIELDS] = $ruleName;
0834             }
0835             if (!isset($validatorRule[self::BREAK_CHAIN])) {
0836                 $validatorRule[self::BREAK_CHAIN] = $this->_defaults[self::BREAK_CHAIN];
0837             }
0838             if (!isset($validatorRule[self::PRESENCE])) {
0839                 $validatorRule[self::PRESENCE] = $this->_defaults[self::PRESENCE];
0840             }
0841             if (!isset($validatorRule[self::ALLOW_EMPTY])) {
0842                 $foundNotEmptyValidator = false;
0843                 
0844                 foreach ($validatorRule as $rule) {
0845                     if ($rule === 'NotEmpty') {
0846                         $foundNotEmptyValidator = true;
0847                         // field may not be empty, we are ready
0848                         break 1;
0849                     }
0850                     
0851                     if (is_array($rule)) {
0852                         $keys      = array_keys($rule);
0853                         $classKey  = array_shift($keys);
0854                         if (isset($rule[$classKey])) {
0855                             $ruleClass = $rule[$classKey];
0856                             if ($ruleClass === 'NotEmpty') {
0857                                 $foundNotEmptyValidator = true;
0858                                 // field may not be empty, we are ready
0859                                 break 1;
0860                             }
0861                         }
0862                     }
0863 
0864                     // we must check if it is an object before using instanceof
0865                     if (!is_object($rule)) {
0866                         // it cannot be a NotEmpty validator, skip this one
0867                         continue;
0868                     }
0869                     
0870                     if($rule instanceof Zend_Validate_NotEmpty) {
0871                         $foundNotEmptyValidator = true;
0872                         // field may not be empty, we are ready
0873                         break 1;
0874                     }
0875                 }
0876                 
0877                 if (!$foundNotEmptyValidator) {
0878                     $validatorRule[self::ALLOW_EMPTY] = $this->_defaults[self::ALLOW_EMPTY];
0879                 } else {
0880                     $validatorRule[self::ALLOW_EMPTY] = false;
0881                 }
0882             }
0883 
0884             if (!isset($validatorRule[self::MESSAGES])) {
0885                 $validatorRule[self::MESSAGES] = array();
0886             } else if (!is_array($validatorRule[self::MESSAGES])) {
0887                 $validatorRule[self::MESSAGES] = array($validatorRule[self::MESSAGES]);
0888             } else if (array_intersect_key($validatorList, $validatorRule[self::MESSAGES])) {
0889                 // this seems pointless... it just re-adds what it already has...
0890                 // I can disable all this and not a single unit test fails...
0891                 // There are now corresponding numeric keys in the validation rule messages array
0892                 // Treat it as a named messages list for all rule validators
0893                 $unifiedMessages = $validatorRule[self::MESSAGES];
0894                 $validatorRule[self::MESSAGES] = array();
0895 
0896                 foreach ($validatorList as $key => $validator) {
0897                     if (array_key_exists($key, $unifiedMessages)) {
0898                         $validatorRule[self::MESSAGES][$key] = $unifiedMessages[$key];
0899                     }
0900                 }
0901             }
0902 
0903             /**
0904              * Load all the validator classes and add them to the chain.
0905              */
0906             if (!isset($validatorRule[self::VALIDATOR_CHAIN])) {
0907                 $validatorRule[self::VALIDATOR_CHAIN] = new Zend_Validate();
0908 
0909                 foreach ($validatorList as $key => $validator) {
0910                     if (is_string($validator) || is_array($validator)) {
0911                         $validator = $this->_getValidator($validator);
0912                     }
0913 
0914                     if (isset($validatorRule[self::MESSAGES][$key])) {
0915                         $value = $validatorRule[self::MESSAGES][$key];
0916                         if (is_array($value)) {
0917                             $validator->setMessages($value);
0918                         } else {
0919                             $validator->setMessage($value);
0920                         }
0921 
0922                         if ($validator instanceof Zend_Validate_NotEmpty) {
0923                             /** we are changing the defaults here, this is alright if all subsequent validators are also a not empty
0924                             * validator, but it goes wrong if one of them is not AND is required!!!
0925                             * that is why we restore the default value at the end of this loop
0926                             */ 
0927                             if (is_array($value)) {
0928                                 $temp = $value; // keep the original value
0929                                 $this->_defaults[self::NOT_EMPTY_MESSAGE] = array_pop($temp);
0930                                 unset($temp);
0931                             } else {
0932                                 $this->_defaults[self::NOT_EMPTY_MESSAGE] = $value;
0933                             }
0934                         }
0935                     }
0936 
0937                     $validatorRule[self::VALIDATOR_CHAIN]->addValidator($validator, $validatorRule[self::BREAK_CHAIN]);
0938                 }
0939                 $validatorRule[self::VALIDATOR_CHAIN_COUNT] = count($validatorList);
0940             }
0941 
0942             /**
0943              * If the ruleName is the special wildcard rule,
0944              * then apply the validator chain to all input data.
0945              * Else just process the field named by the rule.
0946              */
0947             if ($ruleName == self::RULE_WILDCARD) {
0948                 foreach (array_keys($this->_data) as $field)  {
0949                     $this->_validateRule(array_merge($validatorRule, array(self::FIELDS => $field)));
0950                 }
0951             } else {
0952                 $this->_validateRule($validatorRule);
0953             }
0954             
0955             // reset the default not empty message
0956             $this->_defaults[self::NOT_EMPTY_MESSAGE] = $preserveDefaultNotEmptyMessage;
0957         }
0958         
0959 
0960 
0961         /**
0962          * Unset fields in $_data that have been added to other arrays.
0963          * We have to wait until all rules have been processed because
0964          * a given field may be referenced by multiple rules.
0965          */
0966         foreach (array_merge(array_keys($this->_missingFields), array_keys($this->_invalidMessages)) as $rule) {
0967             foreach ((array) $this->_validatorRules[$rule][self::FIELDS] as $field) {
0968                 unset($this->_data[$field]);
0969             }
0970         }
0971         foreach ($this->_validFields as $field => $value) {
0972             unset($this->_data[$field]);
0973         }
0974 
0975         /**
0976          * Anything left over in $_data is an unknown field.
0977          */
0978         $this->_unknownFields = $this->_data;
0979     }
0980 
0981     /**
0982      * @param array $validatorRule
0983      * @return void
0984      */
0985     protected function _validateRule(array $validatorRule)
0986     {
0987         /**
0988          * Get one or more data values from input, and check for missing fields.
0989          * Apply defaults if fields are missing.
0990          */
0991         $data = array();
0992         foreach ((array) $validatorRule[self::FIELDS] as $key => $field) {
0993             if (array_key_exists($field, $this->_data)) {
0994                 $data[$field] = $this->_data[$field];
0995             } else if (isset($validatorRule[self::DEFAULT_VALUE])) {
0996                 /** @todo according to this code default value can't be an array. It has to be reviewed */
0997                 if (!is_array($validatorRule[self::DEFAULT_VALUE])) {
0998                     // Default value is a scalar
0999                     $data[$field] = $validatorRule[self::DEFAULT_VALUE];
1000                 } else {
1001                     // Default value is an array. Search for corresponding key
1002                     if (isset($validatorRule[self::DEFAULT_VALUE][$key])) {
1003                         $data[$field] = $validatorRule[self::DEFAULT_VALUE][$key];
1004                     } else if ($validatorRule[self::PRESENCE] == self::PRESENCE_REQUIRED) {
1005                         // Default value array is provided, but it doesn't have an entry for current field
1006                         // and presence is required
1007                         $this->_missingFields[$validatorRule[self::RULE]][] =
1008                            $this->_getMissingMessage($validatorRule[self::RULE], $field);
1009                     }
1010                 }
1011             } else if ($validatorRule[self::PRESENCE] == self::PRESENCE_REQUIRED) {
1012                 $this->_missingFields[$validatorRule[self::RULE]][] =
1013                     $this->_getMissingMessage($validatorRule[self::RULE], $field);
1014             }
1015         }
1016 
1017         /**
1018          * If any required fields are missing, break the loop.
1019          */
1020         if (isset($this->_missingFields[$validatorRule[self::RULE]]) && count($this->_missingFields[$validatorRule[self::RULE]]) > 0) {
1021             return;
1022         }
1023 
1024         /**
1025          * Evaluate the inputs against the validator chain.
1026          */
1027         if (count((array) $validatorRule[self::FIELDS]) > 1) {
1028             if (!$validatorRule[self::ALLOW_EMPTY]) {
1029                 $emptyFieldsFound = false;
1030                 $errorsList       = array();
1031                 $messages         = array();
1032 
1033                 foreach ($data as $fieldKey => $field) {
1034                     // if there is no Zend_Validate_NotEmpty instance in the rules, we will use the default
1035                     if (!($notEmptyValidator = $this->_getNotEmptyValidatorInstance($validatorRule))) {
1036                         $notEmptyValidator = $this->_getValidator('NotEmpty');
1037                         $notEmptyValidator->setMessage($this->_getNotEmptyMessage($validatorRule[self::RULE], $fieldKey));
1038                     }            
1039                             
1040                     if (!$notEmptyValidator->isValid($field)) {
1041                         foreach ($notEmptyValidator->getMessages() as $messageKey => $message) {
1042                             if (!isset($messages[$messageKey])) {
1043                                 $messages[$messageKey] = $message;
1044                             } else {
1045                                 $messages[] = $message;
1046                             }
1047                         }
1048                         $errorsList[] = $notEmptyValidator->getErrors();
1049                         $emptyFieldsFound = true;
1050                     }
1051                 }
1052 
1053                 if ($emptyFieldsFound) {
1054                     $this->_invalidMessages[$validatorRule[self::RULE]] = $messages;
1055                     $this->_invalidErrors[$validatorRule[self::RULE]]   = array_unique(call_user_func_array('array_merge', $errorsList));
1056                     return;
1057                 }
1058             }
1059 
1060             if (!$validatorRule[self::VALIDATOR_CHAIN]->isValid($data)) {
1061                 $this->_invalidMessages[$validatorRule[self::RULE]] = $validatorRule[self::VALIDATOR_CHAIN]->getMessages();
1062                 $this->_invalidErrors[$validatorRule[self::RULE]] = $validatorRule[self::VALIDATOR_CHAIN]->getErrors();
1063                 return;
1064             }
1065         } else if (count($data) > 0) {
1066             // $data is actually a one element array
1067             $fieldNames = array_keys($data);
1068             $fieldName = reset($fieldNames);
1069             $field     = reset($data);
1070 
1071             $failed = false;
1072             if (!is_array($field)) {
1073                 $field = array($field);
1074             }
1075 
1076             // if there is no Zend_Validate_NotEmpty instance in the rules, we will use the default
1077             if (!($notEmptyValidator = $this->_getNotEmptyValidatorInstance($validatorRule))) {
1078                 $notEmptyValidator = $this->_getValidator('NotEmpty');
1079                 $notEmptyValidator->setMessage($this->_getNotEmptyMessage($validatorRule[self::RULE], $fieldName));
1080             }
1081             
1082             if ($validatorRule[self::ALLOW_EMPTY]) {
1083                 $validatorChain = $validatorRule[self::VALIDATOR_CHAIN];
1084             } else {
1085                 $validatorChain = new Zend_Validate();
1086                 $validatorChain->addValidator($notEmptyValidator, true /* Always break on failure */);
1087                 $validatorChain->addValidator($validatorRule[self::VALIDATOR_CHAIN]);
1088             }
1089 
1090             foreach ($field as $key => $value) {
1091                 if ($validatorRule[self::ALLOW_EMPTY]  &&  !$notEmptyValidator->isValid($value)) {
1092                     // Field is empty AND it's allowed. Do nothing.
1093                     continue;
1094                 }
1095 
1096                 if (!$validatorChain->isValid($value)) {
1097                     if (isset($this->_invalidMessages[$validatorRule[self::RULE]])) {
1098                         $collectedMessages = $this->_invalidMessages[$validatorRule[self::RULE]];
1099                     } else {
1100                         $collectedMessages = array();
1101                     }
1102 
1103                     foreach ($validatorChain->getMessages() as $messageKey => $message) {
1104                         if (!isset($collectedMessages[$messageKey])) {
1105                             $collectedMessages[$messageKey] = $message;
1106                         } else {
1107                             $collectedMessages[] = $message;
1108                         }
1109                     }
1110 
1111                     $this->_invalidMessages[$validatorRule[self::RULE]] = $collectedMessages;
1112                     if (isset($this->_invalidErrors[$validatorRule[self::RULE]])) {
1113                         $this->_invalidErrors[$validatorRule[self::RULE]] = array_merge($this->_invalidErrors[$validatorRule[self::RULE]],
1114                                                                                         $validatorChain->getErrors());
1115                     } else {
1116                         $this->_invalidErrors[$validatorRule[self::RULE]] = $validatorChain->getErrors();
1117                     }
1118                     unset($this->_validFields[$fieldName]);
1119                     $failed = true;
1120                     if ($validatorRule[self::BREAK_CHAIN]) {
1121                         return;
1122                     }
1123                 }
1124             }
1125             if ($failed) {
1126                 return;
1127             }
1128         }
1129 
1130         /**
1131          * If we got this far, the inputs for this rule pass validation.
1132          */
1133         foreach ((array) $validatorRule[self::FIELDS] as $field) {
1134             if (array_key_exists($field, $data)) {
1135                 $this->_validFields[$field] = $data[$field];
1136             }
1137         }
1138     }
1139     
1140     /**
1141      * Check a validatorRule for the presence of a NotEmpty validator instance.
1142      * The purpose is to preserve things like a custom message, that may have been 
1143      * set on the validator outside Zend_Filter_Input.
1144      * @param array $validatorRule
1145      * @return mixed false if none is found, Zend_Validate_NotEmpty instance if found
1146      */
1147     protected function _getNotEmptyValidatorInstance($validatorRule) {
1148         foreach ($validatorRule as $rule => $value) {
1149             if (is_object($value) and $value instanceof Zend_Validate_NotEmpty) {
1150                 return $value;
1151             }
1152         }
1153         
1154         return false;
1155     }
1156 
1157     /**
1158      * @param mixed $classBaseName
1159      * @return Zend_Filter_Interface
1160      */
1161     protected function _getFilter($classBaseName)
1162     {
1163         return $this->_getFilterOrValidator(self::FILTER, $classBaseName);
1164     }
1165 
1166     /**
1167      * @param mixed $classBaseName
1168      * @return Zend_Validate_Interface
1169      */
1170     protected function _getValidator($classBaseName)
1171     {
1172         return $this->_getFilterOrValidator(self::VALIDATE, $classBaseName);
1173     }
1174 
1175     /**
1176      * @param string $type
1177      * @param mixed $classBaseName
1178      * @return Zend_Filter_Interface|Zend_Validate_Interface
1179      * @throws Zend_Filter_Exception
1180      */
1181     protected function _getFilterOrValidator($type, $classBaseName)
1182     {
1183         $args = array();
1184 
1185         if (is_array($classBaseName)) {
1186             $args = $classBaseName;
1187             $classBaseName = array_shift($args);
1188         }
1189 
1190         $interfaceName = 'Zend_' . ucfirst($type) . '_Interface';
1191         $className = $this->getPluginLoader($type)->load(ucfirst($classBaseName));
1192 
1193         $class = new ReflectionClass($className);
1194 
1195         if (!$class->implementsInterface($interfaceName)) {
1196             // require_once 'Zend/Filter/Exception.php';
1197             throw new Zend_Filter_Exception("Class '$className' based on basename '$classBaseName' must implement the '$interfaceName' interface");
1198         }
1199 
1200         if ($class->hasMethod('__construct')) {
1201             $object = $class->newInstanceArgs($args);
1202         } else {
1203             $object = $class->newInstance();
1204         }
1205 
1206         return $object;
1207     }
1208 
1209 }