File indexing completed on 2025-01-19 05:21:08
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 }