/library/Zend/InputFilter/BaseInputFilter.php

https://github.com/zucchi/zf2 · PHP · 440 lines · 251 code · 40 blank · 149 comment · 38 complexity · fb76a7180cb2ec675aec1b91661c64e9 MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. * @package Zend_InputFilter
  9. */
  10. namespace Zend\InputFilter;
  11. use ArrayAccess;
  12. use Traversable;
  13. use Zend\Stdlib\ArrayUtils;
  14. /**
  15. * @todo How should we deal with required input when data is missing?
  16. * should a message be returned? if so, what message?
  17. * @category Zend
  18. * @package Zend_InputFilter
  19. */
  20. class BaseInputFilter implements InputFilterInterface
  21. {
  22. protected $data;
  23. protected $inputs = array();
  24. protected $invalidInputs;
  25. protected $validationGroup;
  26. protected $validInputs;
  27. /**
  28. * Countable: number of inputs in this input filter
  29. *
  30. * Only details the number of direct children.
  31. *
  32. * @return int
  33. */
  34. public function count()
  35. {
  36. return count($this->inputs);
  37. }
  38. /**
  39. * Add an input to the input filter
  40. *
  41. * @param InputInterface|InputFilterInterface $input
  42. * @param null|string $name Name used to retrieve this input
  43. * @throws Exception\InvalidArgumentException
  44. * @return InputFilterInterface
  45. */
  46. public function add($input, $name = null)
  47. {
  48. if (!$input instanceof InputInterface && !$input instanceof InputFilterInterface) {
  49. throw new Exception\InvalidArgumentException(sprintf(
  50. '%s expects an instance of %s or %s as its first argument; received "%s"',
  51. __METHOD__,
  52. 'Zend\InputFilter\InputInterface',
  53. 'Zend\InputFilter\InputFilterInterface',
  54. (is_object($input) ? get_class($input) : gettype($input))
  55. ));
  56. }
  57. if ($input instanceof InputInterface && (empty($name) || is_int($name))) {
  58. $name = $input->getName();
  59. }
  60. if (isset($this->inputs[$name]) && $this->inputs[$name] instanceof InputInterface) {
  61. // The element already exists, so merge the config. Please note that the order is important (already existing
  62. // input is merged with the parameter given)
  63. $input->merge($this->inputs[$name]);
  64. }
  65. $this->inputs[$name] = $input;
  66. return $this;
  67. }
  68. /**
  69. * Retrieve a named input
  70. *
  71. * @param string $name
  72. * @throws Exception\InvalidArgumentException
  73. * @return InputInterface|InputFilterInterface
  74. */
  75. public function get($name)
  76. {
  77. if (!array_key_exists($name, $this->inputs)) {
  78. throw new Exception\InvalidArgumentException(sprintf(
  79. '%s: no input found matching "%s"',
  80. __METHOD__,
  81. $name
  82. ));
  83. }
  84. return $this->inputs[$name];
  85. }
  86. /**
  87. * Test if an input or input filter by the given name is attached
  88. *
  89. * @param string $name
  90. * @return bool
  91. */
  92. public function has($name)
  93. {
  94. return (array_key_exists($name, $this->inputs));
  95. }
  96. /**
  97. * Remove a named input
  98. *
  99. * @param string $name
  100. * @return InputFilterInterface
  101. */
  102. public function remove($name)
  103. {
  104. unset($this->inputs[$name]);
  105. return $this;
  106. }
  107. /**
  108. * Set data to use when validating and filtering
  109. *
  110. * @param array|Traversable $data
  111. * @throws Exception\InvalidArgumentException
  112. * @return InputFilterInterface
  113. */
  114. public function setData($data)
  115. {
  116. if (!is_array($data) && !$data instanceof Traversable) {
  117. throw new Exception\InvalidArgumentException(sprintf(
  118. '%s expects an array or Traversable argument; received %s',
  119. __METHOD__,
  120. (is_object($data) ? get_class($data) : gettype($data))
  121. ));
  122. }
  123. if (is_object($data) && !$data instanceof ArrayAccess) {
  124. $data = ArrayUtils::iteratorToArray($data);
  125. }
  126. $this->data = $data;
  127. $this->populate();
  128. return $this;
  129. }
  130. /**
  131. * Is the data set valid?
  132. *
  133. * @throws Exception\RuntimeException
  134. * @return bool
  135. */
  136. public function isValid()
  137. {
  138. if (null === $this->data) {
  139. throw new Exception\RuntimeException(sprintf(
  140. '%s: no data present to validate!',
  141. __METHOD__
  142. ));
  143. }
  144. $this->validInputs = array();
  145. $this->invalidInputs = array();
  146. $valid = true;
  147. $inputs = $this->validationGroup ?: array_keys($this->inputs);
  148. foreach ($inputs as $name) {
  149. $input = $this->inputs[$name];
  150. if (!array_key_exists($name, $this->data)
  151. || (null === $this->data[$name])
  152. || (is_string($this->data[$name]) && strlen($this->data[$name]) === 0)
  153. ) {
  154. if ($input instanceof InputInterface) {
  155. // - test if input is required
  156. if (!$input->isRequired()) {
  157. $this->validInputs[$name] = $input;
  158. continue;
  159. }
  160. // - test if input allows empty
  161. if ($input->allowEmpty()) {
  162. $this->validInputs[$name] = $input;
  163. continue;
  164. }
  165. }
  166. // make sure we have a value (empty) for validation
  167. $this->data[$name] = '';
  168. }
  169. if ($input instanceof InputFilterInterface) {
  170. if (!$input->isValid()) {
  171. $this->invalidInputs[$name] = $input;
  172. $valid = false;
  173. continue;
  174. }
  175. $this->validInputs[$name] = $input;
  176. continue;
  177. }
  178. if ($input instanceof InputInterface) {
  179. if (!$input->isValid($this->data)) {
  180. // Validation failure
  181. $this->invalidInputs[$name] = $input;
  182. $valid = false;
  183. if ($input->breakOnFailure()) {
  184. return false;
  185. }
  186. continue;
  187. }
  188. $this->validInputs[$name] = $input;
  189. continue;
  190. }
  191. }
  192. return $valid;
  193. }
  194. /**
  195. * Provide a list of one or more elements indicating the complete set to validate
  196. *
  197. * When provided, calls to {@link isValid()} will only validate the provided set.
  198. *
  199. * If the initial value is {@link VALIDATE_ALL}, the current validation group, if
  200. * any, should be cleared.
  201. *
  202. * Implementations should allow passing a single array value, or multiple arguments,
  203. * each specifying a single input.
  204. *
  205. * @param mixed $name
  206. * @return InputFilterInterface
  207. */
  208. public function setValidationGroup($name)
  209. {
  210. if ($name === self::VALIDATE_ALL) {
  211. $this->validationGroup = null;
  212. return $this;
  213. }
  214. if (is_array($name)) {
  215. $inputs = array();
  216. foreach ($name as $key => $value) {
  217. if (!$this->has($key)) {
  218. $inputs[] = $value;
  219. } else {
  220. $inputs[] = $key;
  221. // Recursively populate validation groups for sub input filters
  222. $this->inputs[$key]->setValidationGroup($value);
  223. }
  224. }
  225. if (!empty($inputs)) {
  226. $this->validateValidationGroup($inputs);
  227. $this->validationGroup = $inputs;
  228. }
  229. return $this;
  230. }
  231. $inputs = func_get_args();
  232. $this->validateValidationGroup($inputs);
  233. $this->validationGroup = $inputs;
  234. return $this;
  235. }
  236. /**
  237. * Return a list of inputs that were invalid.
  238. *
  239. * Implementations should return an associative array of name/input pairs
  240. * that failed validation.
  241. *
  242. * @return InputInterface[]
  243. */
  244. public function getInvalidInput()
  245. {
  246. return (is_array($this->invalidInputs) ? $this->invalidInputs : array());
  247. }
  248. /**
  249. * Return a list of inputs that were valid.
  250. *
  251. * Implementations should return an associative array of name/input pairs
  252. * that passed validation.
  253. *
  254. * @return InputInterface[]
  255. */
  256. public function getValidInput()
  257. {
  258. return (is_array($this->validInputs) ? $this->validInputs : array());
  259. }
  260. /**
  261. * Retrieve a value from a named input
  262. *
  263. * @param string $name
  264. * @throws Exception\InvalidArgumentException
  265. * @return mixed
  266. */
  267. public function getValue($name)
  268. {
  269. if (!array_key_exists($name, $this->inputs)) {
  270. throw new Exception\InvalidArgumentException(sprintf(
  271. '%s expects a valid input name; "%s" was not found in the filter',
  272. __METHOD__,
  273. $name
  274. ));
  275. }
  276. $input = $this->inputs[$name];
  277. return $input->getValue();
  278. }
  279. /**
  280. * Return a list of filtered values
  281. *
  282. * List should be an associative array, with the values filtered. If
  283. * validation failed, this should raise an exception.
  284. *
  285. * @return array
  286. */
  287. public function getValues()
  288. {
  289. $inputs = $this->validationGroup ?: array_keys($this->inputs);
  290. $values = array();
  291. foreach ($inputs as $name) {
  292. $input = $this->inputs[$name];
  293. if ($input instanceof InputFilterInterface) {
  294. $values[$name] = $input->getValues();
  295. continue;
  296. }
  297. $values[$name] = $input->getValue();
  298. }
  299. return $values;
  300. }
  301. /**
  302. * Retrieve a raw (unfiltered) value from a named input
  303. *
  304. * @param string $name
  305. * @throws Exception\InvalidArgumentException
  306. * @return mixed
  307. */
  308. public function getRawValue($name)
  309. {
  310. if (!array_key_exists($name, $this->inputs)) {
  311. throw new Exception\InvalidArgumentException(sprintf(
  312. '%s expects a valid input name; "%s" was not found in the filter',
  313. __METHOD__,
  314. $name
  315. ));
  316. }
  317. $input = $this->inputs[$name];
  318. return $input->getRawValue();
  319. }
  320. /**
  321. * Return a list of unfiltered values
  322. *
  323. * List should be an associative array of named input/value pairs,
  324. * with the values unfiltered.
  325. *
  326. * @return array
  327. */
  328. public function getRawValues()
  329. {
  330. $values = array();
  331. foreach ($this->inputs as $name => $input) {
  332. if ($input instanceof InputFilterInterface) {
  333. $values[$name] = $input->getRawValues();
  334. continue;
  335. }
  336. $values[$name] = $input->getRawValue();
  337. }
  338. return $values;
  339. }
  340. /**
  341. * Return a list of validation failure messages
  342. *
  343. * Should return an associative array of named input/message list pairs.
  344. * Pairs should only be returned for inputs that failed validation.
  345. *
  346. * @return array
  347. */
  348. public function getMessages()
  349. {
  350. $messages = array();
  351. foreach ($this->getInvalidInput() as $name => $input) {
  352. $messages[$name] = $input->getMessages();
  353. }
  354. return $messages;
  355. }
  356. /**
  357. * Ensure all names of a validation group exist as input in the filter
  358. *
  359. * @param array $inputs
  360. * @return void
  361. * @throws Exception\InvalidArgumentException
  362. */
  363. protected function validateValidationGroup(array $inputs)
  364. {
  365. foreach ($inputs as $name) {
  366. if (!array_key_exists($name, $this->inputs)) {
  367. throw new Exception\InvalidArgumentException(sprintf(
  368. 'setValidationGroup() expects a list of valid input names; "%s" was not found',
  369. $name
  370. ));
  371. }
  372. }
  373. }
  374. /**
  375. * Populate the values of all attached inputs
  376. *
  377. * @return void
  378. */
  379. protected function populate()
  380. {
  381. foreach (array_keys($this->inputs) as $name) {
  382. $input = $this->inputs[$name];
  383. if (!isset($this->data[$name])) {
  384. // No value; clear value in this input
  385. if ($input instanceof InputFilterInterface) {
  386. $input->setData(array());
  387. continue;
  388. }
  389. $input->setValue(null);
  390. continue;
  391. }
  392. $value = $this->data[$name];
  393. if ($input instanceof InputFilterInterface) {
  394. $input->setData($value);
  395. continue;
  396. }
  397. $input->setValue($value);
  398. }
  399. }
  400. }