3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\InputFilter
;
14 use Zend\Stdlib\ArrayUtils
;
15 use Zend\Stdlib\InitializableInterface
;
18 * @todo How should we deal with required input when data is missing?
19 * should a message be returned? if so, what message?
21 class BaseInputFilter
implements InputFilterInterface
, UnknownInputsCapableInterface
, InitializableInterface
24 protected $inputs = array();
25 protected $invalidInputs;
26 protected $validationGroup;
27 protected $validInputs;
30 * This function is automatically called when creating element with factory. It
31 * allows to perform various operations (add elements...)
35 public function init()
40 * Countable: number of inputs in this input filter
42 * Only details the number of direct children.
46 public function count()
48 return count($this->inputs
);
52 * Add an input to the input filter
54 * @param InputInterface|InputFilterInterface $input
55 * @param null|string $name Name used to retrieve this input
56 * @throws Exception\InvalidArgumentException
57 * @return InputFilterInterface
59 public function add($input, $name = null)
61 if (!$input instanceof InputInterface
&& !$input instanceof InputFilterInterface
) {
62 throw new Exception\
InvalidArgumentException(sprintf(
63 '%s expects an instance of %s or %s as its first argument; received "%s"',
65 'Zend\InputFilter\InputInterface',
66 'Zend\InputFilter\InputFilterInterface',
67 (is_object($input) ?
get_class($input) : gettype($input))
71 if ($input instanceof InputInterface
&& (empty($name) ||
is_int($name))) {
72 $name = $input->getName();
75 if (isset($this->inputs
[$name]) && $this->inputs
[$name] instanceof InputInterface
) {
76 // The element already exists, so merge the config. Please note
77 // that this merges the new input into the original.
78 $original = $this->inputs
[$name];
79 $original->merge($input);
83 $this->inputs
[$name] = $input;
88 * Retrieve a named input
91 * @throws Exception\InvalidArgumentException
92 * @return InputInterface|InputFilterInterface
94 public function get($name)
96 if (!array_key_exists($name, $this->inputs
)) {
97 throw new Exception\
InvalidArgumentException(sprintf(
98 '%s: no input found matching "%s"',
103 return $this->inputs
[$name];
107 * Test if an input or input filter by the given name is attached
109 * @param string $name
112 public function has($name)
114 return (array_key_exists($name, $this->inputs
));
118 * Remove a named input
120 * @param string $name
121 * @return InputFilterInterface
123 public function remove($name)
125 unset($this->inputs
[$name]);
130 * Set data to use when validating and filtering
132 * @param array|Traversable $data
133 * @throws Exception\InvalidArgumentException
134 * @return InputFilterInterface
136 public function setData($data)
138 if (!is_array($data) && !$data instanceof Traversable
) {
139 throw new Exception\
InvalidArgumentException(sprintf(
140 '%s expects an array or Traversable argument; received %s',
142 (is_object($data) ?
get_class($data) : gettype($data))
145 if (is_object($data) && !$data instanceof ArrayAccess
) {
146 $data = ArrayUtils
::iteratorToArray($data);
154 * Is the data set valid?
156 * @throws Exception\RuntimeException
159 public function isValid()
161 $data = $this->getRawValues();
162 if (null === $data) {
163 throw new Exception\
RuntimeException(sprintf(
164 '%s: no data present to validate!',
169 $inputs = $this->validationGroup ?
: array_keys($this->inputs
);
170 return $this->validateInputs($inputs, $data);
174 * Validate a set of inputs against the current data
176 * @param array $inputs
180 protected function validateInputs(array $inputs, array $data = array())
182 // backwards compatibility
184 $data = $this->getRawValues();
187 $this->validInputs
= array();
188 $this->invalidInputs
= array();
191 foreach ($inputs as $name) {
192 $input = $this->inputs
[$name];
193 $dataExists = array_key_exists($name, $data);
195 // key doesn't exist, but input is not required; valid
197 && $input instanceof InputInterface
198 && !$input->isRequired()
200 $this->validInputs
[$name] = $input;
204 // key doesn't exist, input is required, allows empty; valid if
205 // continueIfEmpty is false or input doesn't implement
206 // that interface; otherwise validation chain continues
208 && $input instanceof InputInterface
209 && $input->isRequired()
210 && $input->allowEmpty()
212 if(!($input instanceOf EmptyContextInterface
&& $input->continueIfEmpty())) {
213 $this->validInputs
[$name] = $input;
218 // key exists, is null, input is not required; valid
220 && null === $data[$name]
221 && $input instanceof InputInterface
222 && !$input->isRequired()
224 $this->validInputs
[$name] = $input;
228 // key exists, is null, input is required, allows empty; valid if
229 // continueIfEmpty is false or input doesn't implement
230 // that interface; otherwise validation chain continues
232 && null === $data[$name]
233 && $input instanceof InputInterface
234 && $input->isRequired()
235 && $input->allowEmpty()
237 if (!($input instanceof EmptyContextInterface
&& $input->continueIfEmpty())) {
238 $this->validInputs
[$name] = $input;
243 // key exists, empty string, input is not required, allows empty; valid
245 && '' === $data[$name]
246 && $input instanceof InputInterface
247 && !$input->isRequired()
248 && $input->allowEmpty()
250 $this->validInputs
[$name] = $input;
254 // key exists, empty string, input is required, allows empty; valid
255 // if continueIfEmpty is false, otherwise validation continues
257 && '' === $data[$name]
258 && $input instanceof InputInterface
259 && $input->isRequired()
260 && $input->allowEmpty()
262 if (!($input instanceof EmptyContextInterface
&& $input->continueIfEmpty())) {
263 $this->validInputs
[$name] = $input;
268 // key exists, is array representing file, no file present, input not
269 // required or allows empty; valid
271 && is_array($data[$name])
273 (isset($data[$name]['error'])
274 && $data[$name]['error'] === UPLOAD_ERR_NO_FILE
)
275 ||
(count($data[$name]) === 1
276 && isset($data[$name][0])
277 && is_array($data[$name][0])
278 && isset($data[$name][0]['error'])
279 && $data[$name][0]['error'] === UPLOAD_ERR_NO_FILE
)
281 && $input instanceof InputInterface
282 && (!$input->isRequired() ||
$input->allowEmpty())
284 $this->validInputs
[$name] = $input;
288 // make sure we have a value (empty) for validation
293 // Validate an input filter
294 if ($input instanceof InputFilterInterface
) {
295 if (!$input->isValid()) {
296 $this->invalidInputs
[$name] = $input;
300 $this->validInputs
[$name] = $input;
305 if ($input instanceof InputInterface
) {
306 if (!$input->isValid($data)) {
307 // Validation failure
308 $this->invalidInputs
[$name] = $input;
311 if ($input->breakOnFailure()) {
316 $this->validInputs
[$name] = $input;
325 * Provide a list of one or more elements indicating the complete set to validate
327 * When provided, calls to {@link isValid()} will only validate the provided set.
329 * If the initial value is {@link VALIDATE_ALL}, the current validation group, if
330 * any, should be cleared.
332 * Implementations should allow passing a single array value, or multiple arguments,
333 * each specifying a single input.
336 * @return InputFilterInterface
338 public function setValidationGroup($name)
340 if ($name === self
::VALIDATE_ALL
) {
341 $this->validationGroup
= null;
345 if (is_array($name)) {
347 foreach ($name as $key => $value) {
348 if (!$this->has($key)) {
353 // Recursively populate validation groups for sub input filters
354 $this->inputs
[$key]->setValidationGroup($value);
358 if (!empty($inputs)) {
359 $this->validateValidationGroup($inputs);
360 $this->validationGroup
= $inputs;
366 $inputs = func_get_args();
367 $this->validateValidationGroup($inputs);
368 $this->validationGroup
= $inputs;
374 * Return a list of inputs that were invalid.
376 * Implementations should return an associative array of name/input pairs
377 * that failed validation.
379 * @return InputInterface[]
381 public function getInvalidInput()
383 return (is_array($this->invalidInputs
) ?
$this->invalidInputs
: array());
387 * Return a list of inputs that were valid.
389 * Implementations should return an associative array of name/input pairs
390 * that passed validation.
392 * @return InputInterface[]
394 public function getValidInput()
396 return (is_array($this->validInputs
) ?
$this->validInputs
: array());
400 * Retrieve a value from a named input
402 * @param string $name
403 * @throws Exception\InvalidArgumentException
406 public function getValue($name)
408 if (!array_key_exists($name, $this->inputs
)) {
409 throw new Exception\
InvalidArgumentException(sprintf(
410 '%s expects a valid input name; "%s" was not found in the filter',
415 $input = $this->inputs
[$name];
416 return $input->getValue();
420 * Return a list of filtered values
422 * List should be an associative array, with the values filtered. If
423 * validation failed, this should raise an exception.
427 public function getValues()
429 $inputs = $this->validationGroup ?
: array_keys($this->inputs
);
431 foreach ($inputs as $name) {
432 $input = $this->inputs
[$name];
434 if ($input instanceof InputFilterInterface
) {
435 $values[$name] = $input->getValues();
438 $values[$name] = $input->getValue();
444 * Retrieve a raw (unfiltered) value from a named input
446 * @param string $name
447 * @throws Exception\InvalidArgumentException
450 public function getRawValue($name)
452 if (!array_key_exists($name, $this->inputs
)) {
453 throw new Exception\
InvalidArgumentException(sprintf(
454 '%s expects a valid input name; "%s" was not found in the filter',
459 $input = $this->inputs
[$name];
460 return $input->getRawValue();
464 * Return a list of unfiltered values
466 * List should be an associative array of named input/value pairs,
467 * with the values unfiltered.
471 public function getRawValues()
474 foreach ($this->inputs
as $name => $input) {
475 if ($input instanceof InputFilterInterface
) {
476 $values[$name] = $input->getRawValues();
479 $values[$name] = $input->getRawValue();
485 * Return a list of validation failure messages
487 * Should return an associative array of named input/message list pairs.
488 * Pairs should only be returned for inputs that failed validation.
492 public function getMessages()
495 foreach ($this->getInvalidInput() as $name => $input) {
496 $messages[$name] = $input->getMessages();
503 * Ensure all names of a validation group exist as input in the filter
505 * @param array $inputs
507 * @throws Exception\InvalidArgumentException
509 protected function validateValidationGroup(array $inputs)
511 foreach ($inputs as $name) {
512 if (!array_key_exists($name, $this->inputs
)) {
513 throw new Exception\
InvalidArgumentException(sprintf(
514 'setValidationGroup() expects a list of valid input names; "%s" was not found',
522 * Populate the values of all attached inputs
526 protected function populate()
528 foreach (array_keys($this->inputs
) as $name) {
529 $input = $this->inputs
[$name];
531 if (!isset($this->data
[$name])) {
532 // No value; clear value in this input
533 if ($input instanceof InputFilterInterface
) {
534 $input->setData(array());
538 $input->setValue(null);
542 $value = $this->data
[$name];
544 if ($input instanceof InputFilterInterface
) {
545 $input->setData($value);
549 $input->setValue($value);
554 * Is the data set has unknown input ?
556 * @throws Exception\RuntimeException
559 public function hasUnknown()
561 if (null === $this->data
) {
562 throw new Exception\
RuntimeException(sprintf(
563 '%s: no data present!',
568 $data = array_keys($this->data
);
569 $inputs = array_keys($this->inputs
);
570 $diff = array_diff($data, $inputs);
572 return count(array_intersect($diff, $inputs)) == 0;
579 * Return the unknown input
581 * @throws Exception\RuntimeException
584 public function getUnknown()
586 if (null === $this->data
) {
587 throw new Exception\
RuntimeException(sprintf(
588 '%s: no data present!',
593 $data = array_keys($this->data
);
594 $inputs = array_keys($this->inputs
);
595 $diff = array_diff($data, $inputs);
597 $unknownInputs = array();
598 $intersect = array_intersect($diff, $data);
599 if (!empty($intersect)) {
600 foreach ($intersect as $key) {
601 $unknownInputs[$key] = $this->data
[$key];
605 return $unknownInputs;
609 * Get an array of all inputs
613 public function getInputs()
615 return $this->inputs
;