PageRenderTime 47ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Zend/Filter/Input.php

https://bitbucket.org/andrewjleavitt/magestudy
PHP | 1126 lines | 646 code | 122 blank | 358 comment | 115 complexity | 2bcbe0296f1ee09fac8add7d29203cc8 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Filter
  17. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: Input.php 22472 2010-06-20 07:36:16Z thomas $
  20. */
  21. /**
  22. * @see Zend_Loader
  23. */
  24. #require_once 'Zend/Loader.php';
  25. /**
  26. * @see Zend_Filter
  27. */
  28. #require_once 'Zend/Filter.php';
  29. /**
  30. * @see Zend_Validate
  31. */
  32. #require_once 'Zend/Validate.php';
  33. /**
  34. * @category Zend
  35. * @package Zend_Filter
  36. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  37. * @license http://framework.zend.com/license/new-bsd New BSD License
  38. */
  39. class Zend_Filter_Input
  40. {
  41. const ALLOW_EMPTY = 'allowEmpty';
  42. const BREAK_CHAIN = 'breakChainOnFailure';
  43. const DEFAULT_VALUE = 'default';
  44. const MESSAGES = 'messages';
  45. const ESCAPE_FILTER = 'escapeFilter';
  46. const FIELDS = 'fields';
  47. const FILTER = 'filter';
  48. const FILTER_CHAIN = 'filterChain';
  49. const MISSING_MESSAGE = 'missingMessage';
  50. const INPUT_NAMESPACE = 'inputNamespace';
  51. const VALIDATOR_NAMESPACE = 'validatorNamespace';
  52. const FILTER_NAMESPACE = 'filterNamespace';
  53. const NOT_EMPTY_MESSAGE = 'notEmptyMessage';
  54. const PRESENCE = 'presence';
  55. const PRESENCE_OPTIONAL = 'optional';
  56. const PRESENCE_REQUIRED = 'required';
  57. const RULE = 'rule';
  58. const RULE_WILDCARD = '*';
  59. const VALIDATE = 'validate';
  60. const VALIDATOR = 'validator';
  61. const VALIDATOR_CHAIN = 'validatorChain';
  62. const VALIDATOR_CHAIN_COUNT = 'validatorChainCount';
  63. /**
  64. * @var array Input data, before processing.
  65. */
  66. protected $_data = array();
  67. /**
  68. * @var array Association of rules to filters.
  69. */
  70. protected $_filterRules = array();
  71. /**
  72. * @var array Association of rules to validators.
  73. */
  74. protected $_validatorRules = array();
  75. /**
  76. * @var array After processing data, this contains mapping of valid fields
  77. * to field values.
  78. */
  79. protected $_validFields = array();
  80. /**
  81. * @var array After processing data, this contains mapping of validation
  82. * rules that did not pass validation to the array of messages returned
  83. * by the validator chain.
  84. */
  85. protected $_invalidMessages = array();
  86. /**
  87. * @var array After processing data, this contains mapping of validation
  88. * rules that did not pass validation to the array of error identifiers
  89. * returned by the validator chain.
  90. */
  91. protected $_invalidErrors = array();
  92. /**
  93. * @var array After processing data, this contains mapping of validation
  94. * rules in which some fields were missing to the array of messages
  95. * indicating which fields were missing.
  96. */
  97. protected $_missingFields = array();
  98. /**
  99. * @var array After processing, this contains a copy of $_data elements
  100. * that were not mentioned in any validation rule.
  101. */
  102. protected $_unknownFields = array();
  103. /**
  104. * @var Zend_Filter_Interface The filter object that is run on values
  105. * returned by the getEscaped() method.
  106. */
  107. protected $_defaultEscapeFilter = null;
  108. /**
  109. * Plugin loaders
  110. * @var array
  111. */
  112. protected $_loaders = array();
  113. /**
  114. * @var array Default values to use when processing filters and validators.
  115. */
  116. protected $_defaults = array(
  117. self::ALLOW_EMPTY => false,
  118. self::BREAK_CHAIN => false,
  119. self::ESCAPE_FILTER => 'HtmlEntities',
  120. self::MISSING_MESSAGE => "Field '%field%' is required by rule '%rule%', but the field is missing",
  121. self::NOT_EMPTY_MESSAGE => "You must give a non-empty value for field '%field%'",
  122. self::PRESENCE => self::PRESENCE_OPTIONAL
  123. );
  124. /**
  125. * @var boolean Set to False initially, this is set to True after the
  126. * input data have been processed. Reset to False in setData() method.
  127. */
  128. protected $_processed = false;
  129. /**
  130. * Translation object
  131. * @var Zend_Translate
  132. */
  133. protected $_translator;
  134. /**
  135. * Is translation disabled?
  136. * @var Boolean
  137. */
  138. protected $_translatorDisabled = false;
  139. /**
  140. * @param array $filterRules
  141. * @param array $validatorRules
  142. * @param array $data OPTIONAL
  143. * @param array $options OPTIONAL
  144. */
  145. public function __construct($filterRules, $validatorRules, array $data = null, array $options = null)
  146. {
  147. if ($options) {
  148. $this->setOptions($options);
  149. }
  150. $this->_filterRules = (array) $filterRules;
  151. $this->_validatorRules = (array) $validatorRules;
  152. if ($data) {
  153. $this->setData($data);
  154. }
  155. }
  156. /**
  157. * @param mixed $namespaces
  158. * @return Zend_Filter_Input
  159. * @deprecated since 1.5.0RC1 - use addFilterPrefixPath() or addValidatorPrefixPath instead.
  160. */
  161. public function addNamespace($namespaces)
  162. {
  163. if (!is_array($namespaces)) {
  164. $namespaces = array($namespaces);
  165. }
  166. foreach ($namespaces as $namespace) {
  167. $prefix = $namespace;
  168. $path = str_replace('_', DIRECTORY_SEPARATOR, $prefix);
  169. $this->addFilterPrefixPath($prefix, $path);
  170. $this->addValidatorPrefixPath($prefix, $path);
  171. }
  172. return $this;
  173. }
  174. /**
  175. * Add prefix path for all elements
  176. *
  177. * @param string $prefix
  178. * @param string $path
  179. * @return Zend_Filter_Input
  180. */
  181. public function addFilterPrefixPath($prefix, $path)
  182. {
  183. $this->getPluginLoader(self::FILTER)->addPrefixPath($prefix, $path);
  184. return $this;
  185. }
  186. /**
  187. * Add prefix path for all elements
  188. *
  189. * @param string $prefix
  190. * @param string $path
  191. * @return Zend_Filter_Input
  192. */
  193. public function addValidatorPrefixPath($prefix, $path)
  194. {
  195. $this->getPluginLoader(self::VALIDATE)->addPrefixPath($prefix, $path);
  196. return $this;
  197. }
  198. /**
  199. * Set plugin loaders for use with decorators and elements
  200. *
  201. * @param Zend_Loader_PluginLoader_Interface $loader
  202. * @param string $type 'filter' or 'validate'
  203. * @return Zend_Filter_Input
  204. * @throws Zend_Filter_Exception on invalid type
  205. */
  206. public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type)
  207. {
  208. $type = strtolower($type);
  209. switch ($type) {
  210. case self::FILTER:
  211. case self::VALIDATE:
  212. $this->_loaders[$type] = $loader;
  213. return $this;
  214. default:
  215. #require_once 'Zend/Filter/Exception.php';
  216. throw new Zend_Filter_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type));
  217. }
  218. return $this;
  219. }
  220. /**
  221. * Retrieve plugin loader for given type
  222. *
  223. * $type may be one of:
  224. * - filter
  225. * - validator
  226. *
  227. * If a plugin loader does not exist for the given type, defaults are
  228. * created.
  229. *
  230. * @param string $type 'filter' or 'validate'
  231. * @return Zend_Loader_PluginLoader_Interface
  232. * @throws Zend_Filter_Exception on invalid type
  233. */
  234. public function getPluginLoader($type)
  235. {
  236. $type = strtolower($type);
  237. if (!isset($this->_loaders[$type])) {
  238. switch ($type) {
  239. case self::FILTER:
  240. $prefixSegment = 'Zend_Filter_';
  241. $pathSegment = 'Zend/Filter/';
  242. break;
  243. case self::VALIDATE:
  244. $prefixSegment = 'Zend_Validate_';
  245. $pathSegment = 'Zend/Validate/';
  246. break;
  247. default:
  248. #require_once 'Zend/Filter/Exception.php';
  249. throw new Zend_Filter_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
  250. }
  251. #require_once 'Zend/Loader/PluginLoader.php';
  252. $this->_loaders[$type] = new Zend_Loader_PluginLoader(
  253. array($prefixSegment => $pathSegment)
  254. );
  255. }
  256. return $this->_loaders[$type];
  257. }
  258. /**
  259. * @return array
  260. */
  261. public function getMessages()
  262. {
  263. $this->_process();
  264. return array_merge($this->_invalidMessages, $this->_missingFields);
  265. }
  266. /**
  267. * @return array
  268. */
  269. public function getErrors()
  270. {
  271. $this->_process();
  272. return $this->_invalidErrors;
  273. }
  274. /**
  275. * @return array
  276. */
  277. public function getInvalid()
  278. {
  279. $this->_process();
  280. return $this->_invalidMessages;
  281. }
  282. /**
  283. * @return array
  284. */
  285. public function getMissing()
  286. {
  287. $this->_process();
  288. return $this->_missingFields;
  289. }
  290. /**
  291. * @return array
  292. */
  293. public function getUnknown()
  294. {
  295. $this->_process();
  296. return $this->_unknownFields;
  297. }
  298. /**
  299. * @param string $fieldName OPTIONAL
  300. * @return mixed
  301. */
  302. public function getEscaped($fieldName = null)
  303. {
  304. $this->_process();
  305. $this->_getDefaultEscapeFilter();
  306. if ($fieldName === null) {
  307. return $this->_escapeRecursive($this->_validFields);
  308. }
  309. if (array_key_exists($fieldName, $this->_validFields)) {
  310. return $this->_escapeRecursive($this->_validFields[$fieldName]);
  311. }
  312. return null;
  313. }
  314. /**
  315. * @param mixed $value
  316. * @return mixed
  317. */
  318. protected function _escapeRecursive($data)
  319. {
  320. if($data === null) {
  321. return $data;
  322. }
  323. if (!is_array($data)) {
  324. return $this->_getDefaultEscapeFilter()->filter($data);
  325. }
  326. foreach ($data as &$element) {
  327. $element = $this->_escapeRecursive($element);
  328. }
  329. return $data;
  330. }
  331. /**
  332. * @param string $fieldName OPTIONAL
  333. * @return mixed
  334. */
  335. public function getUnescaped($fieldName = null)
  336. {
  337. $this->_process();
  338. if ($fieldName === null) {
  339. return $this->_validFields;
  340. }
  341. if (array_key_exists($fieldName, $this->_validFields)) {
  342. return $this->_validFields[$fieldName];
  343. }
  344. return null;
  345. }
  346. /**
  347. * @param string $fieldName
  348. * @return mixed
  349. */
  350. public function __get($fieldName)
  351. {
  352. return $this->getEscaped($fieldName);
  353. }
  354. /**
  355. * @return boolean
  356. */
  357. public function hasInvalid()
  358. {
  359. $this->_process();
  360. return !(empty($this->_invalidMessages));
  361. }
  362. /**
  363. * @return boolean
  364. */
  365. public function hasMissing()
  366. {
  367. $this->_process();
  368. return !(empty($this->_missingFields));
  369. }
  370. /**
  371. * @return boolean
  372. */
  373. public function hasUnknown()
  374. {
  375. $this->_process();
  376. return !(empty($this->_unknownFields));
  377. }
  378. /**
  379. * @return boolean
  380. */
  381. public function hasValid()
  382. {
  383. $this->_process();
  384. return !(empty($this->_validFields));
  385. }
  386. /**
  387. * @param string $fieldName
  388. * @return boolean
  389. */
  390. public function isValid($fieldName = null)
  391. {
  392. $this->_process();
  393. if ($fieldName === null) {
  394. return !($this->hasMissing() || $this->hasInvalid());
  395. }
  396. return array_key_exists($fieldName, $this->_validFields);
  397. }
  398. /**
  399. * @param string $fieldName
  400. * @return boolean
  401. */
  402. public function __isset($fieldName)
  403. {
  404. $this->_process();
  405. return isset($this->_validFields[$fieldName]);
  406. }
  407. /**
  408. * @return Zend_Filter_Input
  409. * @throws Zend_Filter_Exception
  410. */
  411. public function process()
  412. {
  413. $this->_process();
  414. if ($this->hasInvalid()) {
  415. #require_once 'Zend/Filter/Exception.php';
  416. throw new Zend_Filter_Exception("Input has invalid fields");
  417. }
  418. if ($this->hasMissing()) {
  419. #require_once 'Zend/Filter/Exception.php';
  420. throw new Zend_Filter_Exception("Input has missing fields");
  421. }
  422. return $this;
  423. }
  424. /**
  425. * @param array $data
  426. * @return Zend_Filter_Input
  427. */
  428. public function setData(array $data)
  429. {
  430. $this->_data = $data;
  431. /**
  432. * Reset to initial state
  433. */
  434. $this->_validFields = array();
  435. $this->_invalidMessages = array();
  436. $this->_invalidErrors = array();
  437. $this->_missingFields = array();
  438. $this->_unknownFields = array();
  439. $this->_processed = false;
  440. return $this;
  441. }
  442. /**
  443. * @param mixed $escapeFilter
  444. * @return Zend_Filter_Interface
  445. */
  446. public function setDefaultEscapeFilter($escapeFilter)
  447. {
  448. if (is_string($escapeFilter) || is_array($escapeFilter)) {
  449. $escapeFilter = $this->_getFilter($escapeFilter);
  450. }
  451. if (!$escapeFilter instanceof Zend_Filter_Interface) {
  452. #require_once 'Zend/Filter/Exception.php';
  453. throw new Zend_Filter_Exception('Escape filter specified does not implement Zend_Filter_Interface');
  454. }
  455. $this->_defaultEscapeFilter = $escapeFilter;
  456. return $escapeFilter;
  457. }
  458. /**
  459. * @param array $options
  460. * @return Zend_Filter_Input
  461. * @throws Zend_Filter_Exception if an unknown option is given
  462. */
  463. public function setOptions(array $options)
  464. {
  465. foreach ($options as $option => $value) {
  466. switch ($option) {
  467. case self::ESCAPE_FILTER:
  468. $this->setDefaultEscapeFilter($value);
  469. break;
  470. case self::INPUT_NAMESPACE:
  471. $this->addNamespace($value);
  472. break;
  473. case self::VALIDATOR_NAMESPACE:
  474. if(is_string($value)) {
  475. $value = array($value);
  476. }
  477. foreach($value AS $prefix) {
  478. $this->addValidatorPrefixPath(
  479. $prefix,
  480. str_replace('_', DIRECTORY_SEPARATOR, $prefix)
  481. );
  482. }
  483. break;
  484. case self::FILTER_NAMESPACE:
  485. if(is_string($value)) {
  486. $value = array($value);
  487. }
  488. foreach($value AS $prefix) {
  489. $this->addFilterPrefixPath(
  490. $prefix,
  491. str_replace('_', DIRECTORY_SEPARATOR, $prefix)
  492. );
  493. }
  494. break;
  495. case self::ALLOW_EMPTY:
  496. case self::BREAK_CHAIN:
  497. case self::MISSING_MESSAGE:
  498. case self::NOT_EMPTY_MESSAGE:
  499. case self::PRESENCE:
  500. $this->_defaults[$option] = $value;
  501. break;
  502. default:
  503. #require_once 'Zend/Filter/Exception.php';
  504. throw new Zend_Filter_Exception("Unknown option '$option'");
  505. break;
  506. }
  507. }
  508. return $this;
  509. }
  510. /**
  511. * Set translation object
  512. *
  513. * @param Zend_Translate|Zend_Translate_Adapter|null $translator
  514. * @return Zend_Filter_Input
  515. */
  516. public function setTranslator($translator = null)
  517. {
  518. if ((null === $translator) || ($translator instanceof Zend_Translate_Adapter)) {
  519. $this->_translator = $translator;
  520. } elseif ($translator instanceof Zend_Translate) {
  521. $this->_translator = $translator->getAdapter();
  522. } else {
  523. #require_once 'Zend/Validate/Exception.php';
  524. throw new Zend_Validate_Exception('Invalid translator specified');
  525. }
  526. return $this;
  527. }
  528. /**
  529. * Return translation object
  530. *
  531. * @return Zend_Translate_Adapter|null
  532. */
  533. public function getTranslator()
  534. {
  535. if ($this->translatorIsDisabled()) {
  536. return null;
  537. }
  538. if ($this->_translator === null) {
  539. #require_once 'Zend/Registry.php';
  540. if (Zend_Registry::isRegistered('Zend_Translate')) {
  541. $translator = Zend_Registry::get('Zend_Translate');
  542. if ($translator instanceof Zend_Translate_Adapter) {
  543. return $translator;
  544. } elseif ($translator instanceof Zend_Translate) {
  545. return $translator->getAdapter();
  546. }
  547. }
  548. }
  549. return $this->_translator;
  550. }
  551. /**
  552. * Indicate whether or not translation should be disabled
  553. *
  554. * @param bool $flag
  555. * @return Zend_Filter_Input
  556. */
  557. public function setDisableTranslator($flag)
  558. {
  559. $this->_translatorDisabled = (bool) $flag;
  560. return $this;
  561. }
  562. /**
  563. * Is translation disabled?
  564. *
  565. * @return bool
  566. */
  567. public function translatorIsDisabled()
  568. {
  569. return $this->_translatorDisabled;
  570. }
  571. /*
  572. * Protected methods
  573. */
  574. /**
  575. * @return void
  576. */
  577. protected function _filter()
  578. {
  579. foreach ($this->_filterRules as $ruleName => &$filterRule) {
  580. /**
  581. * Make sure we have an array representing this filter chain.
  582. * Don't typecast to (array) because it might be a Zend_Filter object
  583. */
  584. if (!is_array($filterRule)) {
  585. $filterRule = array($filterRule);
  586. }
  587. /**
  588. * Filters are indexed by integer, metacommands are indexed by string.
  589. * Pick out the filters.
  590. */
  591. $filterList = array();
  592. foreach ($filterRule as $key => $value) {
  593. if (is_int($key)) {
  594. $filterList[] = $value;
  595. }
  596. }
  597. /**
  598. * Use defaults for filter metacommands.
  599. */
  600. $filterRule[self::RULE] = $ruleName;
  601. if (!isset($filterRule[self::FIELDS])) {
  602. $filterRule[self::FIELDS] = $ruleName;
  603. }
  604. /**
  605. * Load all the filter classes and add them to the chain.
  606. */
  607. if (!isset($filterRule[self::FILTER_CHAIN])) {
  608. $filterRule[self::FILTER_CHAIN] = new Zend_Filter();
  609. foreach ($filterList as $filter) {
  610. if (is_string($filter) || is_array($filter)) {
  611. $filter = $this->_getFilter($filter);
  612. }
  613. $filterRule[self::FILTER_CHAIN]->addFilter($filter);
  614. }
  615. }
  616. /**
  617. * If the ruleName is the special wildcard rule,
  618. * then apply the filter chain to all input data.
  619. * Else just process the field named by the rule.
  620. */
  621. if ($ruleName == self::RULE_WILDCARD) {
  622. foreach (array_keys($this->_data) as $field) {
  623. $this->_filterRule(array_merge($filterRule, array(self::FIELDS => $field)));
  624. }
  625. } else {
  626. $this->_filterRule($filterRule);
  627. }
  628. }
  629. }
  630. /**
  631. * @param array $filterRule
  632. * @return void
  633. */
  634. protected function _filterRule(array $filterRule)
  635. {
  636. $field = $filterRule[self::FIELDS];
  637. if (!array_key_exists($field, $this->_data)) {
  638. return;
  639. }
  640. if (is_array($this->_data[$field])) {
  641. foreach ($this->_data[$field] as $key => $value) {
  642. $this->_data[$field][$key] = $filterRule[self::FILTER_CHAIN]->filter($value);
  643. }
  644. } else {
  645. $this->_data[$field] =
  646. $filterRule[self::FILTER_CHAIN]->filter($this->_data[$field]);
  647. }
  648. }
  649. /**
  650. * @return Zend_Filter_Interface
  651. */
  652. protected function _getDefaultEscapeFilter()
  653. {
  654. if ($this->_defaultEscapeFilter !== null) {
  655. return $this->_defaultEscapeFilter;
  656. }
  657. return $this->setDefaultEscapeFilter($this->_defaults[self::ESCAPE_FILTER]);
  658. }
  659. /**
  660. * @param string $rule
  661. * @param string $field
  662. * @return string
  663. */
  664. protected function _getMissingMessage($rule, $field)
  665. {
  666. $message = $this->_defaults[self::MISSING_MESSAGE];
  667. if (null !== ($translator = $this->getTranslator())) {
  668. if ($translator->isTranslated(self::MISSING_MESSAGE)) {
  669. $message = $translator->translate(self::MISSING_MESSAGE);
  670. } else {
  671. $message = $translator->translate($message);
  672. }
  673. }
  674. $message = str_replace('%rule%', $rule, $message);
  675. $message = str_replace('%field%', $field, $message);
  676. return $message;
  677. }
  678. /**
  679. * @return string
  680. */
  681. protected function _getNotEmptyMessage($rule, $field)
  682. {
  683. $message = $this->_defaults[self::NOT_EMPTY_MESSAGE];
  684. if (null !== ($translator = $this->getTranslator())) {
  685. if ($translator->isTranslated(self::NOT_EMPTY_MESSAGE)) {
  686. $message = $translator->translate(self::NOT_EMPTY_MESSAGE);
  687. } else {
  688. $message = $translator->translate($message);
  689. }
  690. }
  691. $message = str_replace('%rule%', $rule, $message);
  692. $message = str_replace('%field%', $field, $message);
  693. return $message;
  694. }
  695. /**
  696. * @return void
  697. */
  698. protected function _process()
  699. {
  700. if ($this->_processed === false) {
  701. $this->_filter();
  702. $this->_validate();
  703. $this->_processed = true;
  704. }
  705. }
  706. /**
  707. * @return void
  708. */
  709. protected function _validate()
  710. {
  711. /**
  712. * Special case: if there are no validators, treat all fields as valid.
  713. */
  714. if (!$this->_validatorRules) {
  715. $this->_validFields = $this->_data;
  716. $this->_data = array();
  717. return;
  718. }
  719. foreach ($this->_validatorRules as $ruleName => &$validatorRule) {
  720. /**
  721. * Make sure we have an array representing this validator chain.
  722. * Don't typecast to (array) because it might be a Zend_Validate object
  723. */
  724. if (!is_array($validatorRule)) {
  725. $validatorRule = array($validatorRule);
  726. }
  727. /**
  728. * Validators are indexed by integer, metacommands are indexed by string.
  729. * Pick out the validators.
  730. */
  731. $validatorList = array();
  732. foreach ($validatorRule as $key => $value) {
  733. if (is_int($key)) {
  734. $validatorList[$key] = $value;
  735. }
  736. }
  737. /**
  738. * Use defaults for validation metacommands.
  739. */
  740. $validatorRule[self::RULE] = $ruleName;
  741. if (!isset($validatorRule[self::FIELDS])) {
  742. $validatorRule[self::FIELDS] = $ruleName;
  743. }
  744. if (!isset($validatorRule[self::BREAK_CHAIN])) {
  745. $validatorRule[self::BREAK_CHAIN] = $this->_defaults[self::BREAK_CHAIN];
  746. }
  747. if (!isset($validatorRule[self::PRESENCE])) {
  748. $validatorRule[self::PRESENCE] = $this->_defaults[self::PRESENCE];
  749. }
  750. if (!isset($validatorRule[self::ALLOW_EMPTY])) {
  751. $validatorRule[self::ALLOW_EMPTY] = $this->_defaults[self::ALLOW_EMPTY];
  752. }
  753. if (!isset($validatorRule[self::MESSAGES])) {
  754. $validatorRule[self::MESSAGES] = array();
  755. } else if (!is_array($validatorRule[self::MESSAGES])) {
  756. $validatorRule[self::MESSAGES] = array($validatorRule[self::MESSAGES]);
  757. } else if (array_intersect_key($validatorList, $validatorRule[self::MESSAGES])) {
  758. // There are now corresponding numeric keys in the validation rule messages array
  759. // Treat it as a named messages list for all rule validators
  760. $unifiedMessages = $validatorRule[self::MESSAGES];
  761. $validatorRule[self::MESSAGES] = array();
  762. foreach ($validatorList as $key => $validator) {
  763. if (array_key_exists($key, $unifiedMessages)) {
  764. $validatorRule[self::MESSAGES][$key] = $unifiedMessages[$key];
  765. }
  766. }
  767. }
  768. /**
  769. * Load all the validator classes and add them to the chain.
  770. */
  771. if (!isset($validatorRule[self::VALIDATOR_CHAIN])) {
  772. $validatorRule[self::VALIDATOR_CHAIN] = new Zend_Validate();
  773. foreach ($validatorList as $key => $validator) {
  774. if (is_string($validator) || is_array($validator)) {
  775. $validator = $this->_getValidator($validator);
  776. }
  777. if (isset($validatorRule[self::MESSAGES][$key])) {
  778. $value = $validatorRule[self::MESSAGES][$key];
  779. if (is_array($value)) {
  780. $validator->setMessages($value);
  781. } else {
  782. $validator->setMessage($value);
  783. }
  784. if ($validator instanceof Zend_Validate_NotEmpty) {
  785. $this->_defaults[self::NOT_EMPTY_MESSAGE] = $value;
  786. }
  787. }
  788. $validatorRule[self::VALIDATOR_CHAIN]->addValidator($validator, $validatorRule[self::BREAK_CHAIN]);
  789. }
  790. $validatorRule[self::VALIDATOR_CHAIN_COUNT] = count($validatorList);
  791. }
  792. /**
  793. * If the ruleName is the special wildcard rule,
  794. * then apply the validator chain to all input data.
  795. * Else just process the field named by the rule.
  796. */
  797. if ($ruleName == self::RULE_WILDCARD) {
  798. foreach (array_keys($this->_data) as $field) {
  799. $this->_validateRule(array_merge($validatorRule, array(self::FIELDS => $field)));
  800. }
  801. } else {
  802. $this->_validateRule($validatorRule);
  803. }
  804. }
  805. /**
  806. * Unset fields in $_data that have been added to other arrays.
  807. * We have to wait until all rules have been processed because
  808. * a given field may be referenced by multiple rules.
  809. */
  810. foreach (array_merge(array_keys($this->_missingFields), array_keys($this->_invalidMessages)) as $rule) {
  811. foreach ((array) $this->_validatorRules[$rule][self::FIELDS] as $field) {
  812. unset($this->_data[$field]);
  813. }
  814. }
  815. foreach ($this->_validFields as $field => $value) {
  816. unset($this->_data[$field]);
  817. }
  818. /**
  819. * Anything left over in $_data is an unknown field.
  820. */
  821. $this->_unknownFields = $this->_data;
  822. }
  823. /**
  824. * @param array $validatorRule
  825. * @return void
  826. */
  827. protected function _validateRule(array $validatorRule)
  828. {
  829. /**
  830. * Get one or more data values from input, and check for missing fields.
  831. * Apply defaults if fields are missing.
  832. */
  833. $data = array();
  834. foreach ((array) $validatorRule[self::FIELDS] as $key => $field) {
  835. if (array_key_exists($field, $this->_data)) {
  836. $data[$field] = $this->_data[$field];
  837. } else if (isset($validatorRule[self::DEFAULT_VALUE])) {
  838. /** @todo according to this code default value can't be an array. It has to be reviewed */
  839. if (!is_array($validatorRule[self::DEFAULT_VALUE])) {
  840. // Default value is a scalar
  841. $data[$field] = $validatorRule[self::DEFAULT_VALUE];
  842. } else {
  843. // Default value is an array. Search for corresponding key
  844. if (isset($validatorRule[self::DEFAULT_VALUE][$key])) {
  845. $data[$field] = $validatorRule[self::DEFAULT_VALUE][$key];
  846. } else if ($validatorRule[self::PRESENCE] == self::PRESENCE_REQUIRED) {
  847. // Default value array is provided, but it doesn't have an entry for current field
  848. // and presence is required
  849. $this->_missingFields[$validatorRule[self::RULE]][] =
  850. $this->_getMissingMessage($validatorRule[self::RULE], $field);
  851. }
  852. }
  853. } else if ($validatorRule[self::PRESENCE] == self::PRESENCE_REQUIRED) {
  854. $this->_missingFields[$validatorRule[self::RULE]][] =
  855. $this->_getMissingMessage($validatorRule[self::RULE], $field);
  856. }
  857. }
  858. /**
  859. * If any required fields are missing, break the loop.
  860. */
  861. if (isset($this->_missingFields[$validatorRule[self::RULE]]) && count($this->_missingFields[$validatorRule[self::RULE]]) > 0) {
  862. return;
  863. }
  864. /**
  865. * Evaluate the inputs against the validator chain.
  866. */
  867. if (count((array) $validatorRule[self::FIELDS]) > 1) {
  868. if (!$validatorRule[self::ALLOW_EMPTY]) {
  869. $emptyFieldsFound = false;
  870. $errorsList = array();
  871. $messages = array();
  872. foreach ($data as $fieldKey => $field) {
  873. $notEmptyValidator = $this->_getValidator('NotEmpty');
  874. $notEmptyValidator->setMessage($this->_getNotEmptyMessage($validatorRule[self::RULE], $fieldKey));
  875. if (!$notEmptyValidator->isValid($field)) {
  876. foreach ($notEmptyValidator->getMessages() as $messageKey => $message) {
  877. if (!isset($messages[$messageKey])) {
  878. $messages[$messageKey] = $message;
  879. } else {
  880. $messages[] = $message;
  881. }
  882. }
  883. $errorsList[] = $notEmptyValidator->getErrors();
  884. $emptyFieldsFound = true;
  885. }
  886. }
  887. if ($emptyFieldsFound) {
  888. $this->_invalidMessages[$validatorRule[self::RULE]] = $messages;
  889. $this->_invalidErrors[$validatorRule[self::RULE]] = array_unique(call_user_func_array('array_merge', $errorsList));
  890. return;
  891. }
  892. }
  893. if (!$validatorRule[self::VALIDATOR_CHAIN]->isValid($data)) {
  894. $this->_invalidMessages[$validatorRule[self::RULE]] = $validatorRule[self::VALIDATOR_CHAIN]->getMessages();
  895. $this->_invalidErrors[$validatorRule[self::RULE]] = $validatorRule[self::VALIDATOR_CHAIN]->getErrors();
  896. return;
  897. }
  898. } else if (count($data) > 0) {
  899. // $data is actually a one element array
  900. $fieldNames = array_keys($data);
  901. $fieldName = reset($fieldNames);
  902. $field = reset($data);
  903. $failed = false;
  904. if (!is_array($field)) {
  905. $field = array($field);
  906. }
  907. $notEmptyValidator = $this->_getValidator('NotEmpty');
  908. $notEmptyValidator->setMessage($this->_getNotEmptyMessage($validatorRule[self::RULE], $fieldName));
  909. if ($validatorRule[self::ALLOW_EMPTY]) {
  910. $validatorChain = $validatorRule[self::VALIDATOR_CHAIN];
  911. } else {
  912. $validatorChain = new Zend_Validate();
  913. $validatorChain->addValidator($notEmptyValidator, true /* Always break on failure */);
  914. $validatorChain->addValidator($validatorRule[self::VALIDATOR_CHAIN]);
  915. }
  916. foreach ($field as $value) {
  917. if ($validatorRule[self::ALLOW_EMPTY] && !$notEmptyValidator->isValid($value)) {
  918. // Field is empty AND it's allowed. Do nothing.
  919. continue;
  920. }
  921. if (!$validatorChain->isValid($value)) {
  922. if (isset($this->_invalidMessages[$validatorRule[self::RULE]])) {
  923. $collectedMessages = $this->_invalidMessages[$validatorRule[self::RULE]];
  924. } else {
  925. $collectedMessages = array();
  926. }
  927. foreach ($validatorChain->getMessages() as $messageKey => $message) {
  928. if (!isset($collectedMessages[$messageKey])) {
  929. $collectedMessages[$messageKey] = $message;
  930. } else {
  931. $collectedMessages[] = $message;
  932. }
  933. }
  934. $this->_invalidMessages[$validatorRule[self::RULE]] = $collectedMessages;
  935. if (isset($this->_invalidErrors[$validatorRule[self::RULE]])) {
  936. $this->_invalidErrors[$validatorRule[self::RULE]] = array_merge($this->_invalidErrors[$validatorRule[self::RULE]],
  937. $validatorChain->getErrors());
  938. } else {
  939. $this->_invalidErrors[$validatorRule[self::RULE]] = $validatorChain->getErrors();
  940. }
  941. unset($this->_validFields[$fieldName]);
  942. $failed = true;
  943. if ($validatorRule[self::BREAK_CHAIN]) {
  944. return;
  945. }
  946. }
  947. }
  948. if ($failed) {
  949. return;
  950. }
  951. }
  952. /**
  953. * If we got this far, the inputs for this rule pass validation.
  954. */
  955. foreach ((array) $validatorRule[self::FIELDS] as $field) {
  956. if (array_key_exists($field, $data)) {
  957. $this->_validFields[$field] = $data[$field];
  958. }
  959. }
  960. }
  961. /**
  962. * @param mixed $classBaseName
  963. * @return Zend_Filter_Interface
  964. */
  965. protected function _getFilter($classBaseName)
  966. {
  967. return $this->_getFilterOrValidator(self::FILTER, $classBaseName);
  968. }
  969. /**
  970. * @param mixed $classBaseName
  971. * @return Zend_Validate_Interface
  972. */
  973. protected function _getValidator($classBaseName)
  974. {
  975. return $this->_getFilterOrValidator(self::VALIDATE, $classBaseName);
  976. }
  977. /**
  978. * @param string $type
  979. * @param mixed $classBaseName
  980. * @return Zend_Filter_Interface|Zend_Validate_Interface
  981. * @throws Zend_Filter_Exception
  982. */
  983. protected function _getFilterOrValidator($type, $classBaseName)
  984. {
  985. $args = array();
  986. if (is_array($classBaseName)) {
  987. $args = $classBaseName;
  988. $classBaseName = array_shift($args);
  989. }
  990. $interfaceName = 'Zend_' . ucfirst($type) . '_Interface';
  991. $className = $this->getPluginLoader($type)->load(ucfirst($classBaseName));
  992. $class = new ReflectionClass($className);
  993. if (!$class->implementsInterface($interfaceName)) {
  994. #require_once 'Zend/Filter/Exception.php';
  995. throw new Zend_Filter_Exception("Class '$className' based on basename '$classBaseName' must implement the '$interfaceName' interface");
  996. }
  997. if ($class->hasMethod('__construct')) {
  998. $object = $class->newInstanceArgs($args);
  999. } else {
  1000. $object = $class->newInstance();
  1001. }
  1002. return $object;
  1003. }
  1004. }