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-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license   http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10namespace Zend\InputFilter;
11
12use Zend\Filter\FilterChain;
13use Zend\Validator\NotEmpty;
14use Zend\Validator\ValidatorChain;
15
16class Input implements
17    InputInterface,
18    EmptyContextInterface
19{
20    /**
21     * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain.
22     *
23     * @var bool
24     */
25    protected $allowEmpty = false;
26
27    /**
28     * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain.
29     *
30     * @var bool
31     */
32    protected $continueIfEmpty = false;
33
34    /**
35     * @var bool
36     */
37    protected $breakOnFailure = false;
38
39    /**
40     * @var string|null
41     */
42    protected $errorMessage;
43
44    /**
45     * @var FilterChain
46     */
47    protected $filterChain;
48
49    /**
50     * @var string
51     */
52    protected $name;
53
54    /**
55     * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain.
56     *
57     * @var bool
58     */
59    protected $notEmptyValidator = false;
60
61    /**
62     * @var bool
63     */
64    protected $required = true;
65
66    /**
67     * @var ValidatorChain
68     */
69    protected $validatorChain;
70
71    /**
72     * @var mixed
73     */
74    protected $value;
75
76    /**
77     * Flag for distinguish when $value contains the value previously set or the default one.
78     *
79     * @var bool
80     */
81    protected $hasValue = false;
82
83    /**
84     * @var mixed
85     */
86    protected $fallbackValue;
87
88    /**
89     * @var bool
90     */
91    protected $hasFallback = false;
92
93    public function __construct($name = null)
94    {
95        $this->name = $name;
96    }
97
98    /**
99     * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain and set this to `true`.
100     *
101     * @param  bool $allowEmpty
102     * @return Input
103     */
104    public function setAllowEmpty($allowEmpty)
105    {
106        $this->allowEmpty = (bool) $allowEmpty;
107        return $this;
108    }
109
110    /**
111     * @param  bool $breakOnFailure
112     * @return Input
113     */
114    public function setBreakOnFailure($breakOnFailure)
115    {
116        $this->breakOnFailure = (bool) $breakOnFailure;
117        return $this;
118    }
119
120    /**
121     * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain and set this to `true`.
122     *
123     * @param bool $continueIfEmpty
124     * @return Input
125     */
126    public function setContinueIfEmpty($continueIfEmpty)
127    {
128        $this->continueIfEmpty = (bool) $continueIfEmpty;
129        return $this;
130    }
131
132    /**
133     * @param  string|null $errorMessage
134     * @return Input
135     */
136    public function setErrorMessage($errorMessage)
137    {
138        $this->errorMessage = (null === $errorMessage) ? null : (string) $errorMessage;
139        return $this;
140    }
141
142    /**
143     * @param  FilterChain $filterChain
144     * @return Input
145     */
146    public function setFilterChain(FilterChain $filterChain)
147    {
148        $this->filterChain = $filterChain;
149        return $this;
150    }
151
152    /**
153     * @param  string $name
154     * @return Input
155     */
156    public function setName($name)
157    {
158        $this->name = (string) $name;
159        return $this;
160    }
161
162    /**
163     * @param  bool $required
164     * @return Input
165     */
166    public function setRequired($required)
167    {
168        $this->required = (bool) $required;
169        return $this;
170    }
171
172    /**
173     * @param  ValidatorChain $validatorChain
174     * @return Input
175     */
176    public function setValidatorChain(ValidatorChain $validatorChain)
177    {
178        $this->validatorChain = $validatorChain;
179        return $this;
180    }
181
182    /**
183     * Set the input value.
184     *
185     * If you want to remove/unset the current value use {@link Input::resetValue()}.
186     *
187     * @see Input::getValue() For retrieve the input value.
188     * @see Input::hasValue() For to know if input value was set.
189     * @see Input::resetValue() For reset the input value to the default state.
190     *
191     * @param  mixed $value
192     * @return Input
193     */
194    public function setValue($value)
195    {
196        $this->value = $value;
197        $this->hasValue = true;
198        return $this;
199    }
200
201    /**
202     * Reset input value to the default state.
203     *
204     * @see Input::hasValue() For to know if input value was set.
205     * @see Input::setValue() For set a new value.
206     *
207     * @return Input
208     */
209    public function resetValue()
210    {
211        $this->value = null;
212        $this->hasValue = false;
213        return $this;
214    }
215
216    /**
217     * @param  mixed $value
218     * @return Input
219     */
220    public function setFallbackValue($value)
221    {
222        $this->fallbackValue = $value;
223        $this->hasFallback = true;
224        return $this;
225    }
226
227    /**
228     * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain.
229     *
230     * @return bool
231     */
232    public function allowEmpty()
233    {
234        return $this->allowEmpty;
235    }
236
237    /**
238     * @return bool
239     */
240    public function breakOnFailure()
241    {
242        return $this->breakOnFailure;
243    }
244
245    /**
246     * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. Should always return `true`.
247     *
248     * @return bool
249     */
250    public function continueIfEmpty()
251    {
252        return $this->continueIfEmpty;
253    }
254
255    /**
256     * @return string|null
257     */
258    public function getErrorMessage()
259    {
260        return $this->errorMessage;
261    }
262
263    /**
264     * @return FilterChain
265     */
266    public function getFilterChain()
267    {
268        if (!$this->filterChain) {
269            $this->setFilterChain(new FilterChain());
270        }
271        return $this->filterChain;
272    }
273
274    /**
275     * @return string
276     */
277    public function getName()
278    {
279        return $this->name;
280    }
281
282    /**
283     * @return mixed
284     */
285    public function getRawValue()
286    {
287        return $this->value;
288    }
289
290    /**
291     * @return bool
292     */
293    public function isRequired()
294    {
295        return $this->required;
296    }
297
298    /**
299     * @return ValidatorChain
300     */
301    public function getValidatorChain()
302    {
303        if (!$this->validatorChain) {
304            $this->setValidatorChain(new ValidatorChain());
305        }
306        return $this->validatorChain;
307    }
308
309    /**
310     * @return mixed
311     */
312    public function getValue()
313    {
314        $filter = $this->getFilterChain();
315        return $filter->filter($this->value);
316    }
317
318    /**
319     * Flag for inform if input value was set.
320     *
321     * This flag used for distinguish when {@link Input::getValue()} will return the value previously set or the default.
322     *
323     * @see Input::getValue() For retrieve the input value.
324     * @see Input::setValue() For set a new value.
325     * @see Input::resetValue() For reset the input value to the default state.
326     *
327     * @return bool
328     */
329    public function hasValue()
330    {
331        return $this->hasValue;
332    }
333
334    /**
335     * @return mixed
336     */
337    public function getFallbackValue()
338    {
339        return $this->fallbackValue;
340    }
341
342    /**
343     * @return bool
344     */
345    public function hasFallback()
346    {
347        return $this->hasFallback;
348    }
349
350    public function clearFallbackValue()
351    {
352        $this->hasFallback = false;
353        $this->fallbackValue = null;
354    }
355
356    /**
357     * @param  InputInterface $input
358     * @return Input
359     */
360    public function merge(InputInterface $input)
361    {
362        $this->setBreakOnFailure($input->breakOnFailure());
363        if ($input instanceof Input) {
364            $this->setContinueIfEmpty($input->continueIfEmpty());
365        }
366        $this->setErrorMessage($input->getErrorMessage());
367        $this->setName($input->getName());
368        $this->setRequired($input->isRequired());
369        $this->setAllowEmpty($input->allowEmpty());
370        if (!($input instanceof Input) || $input->hasValue()) {
371            $this->setValue($input->getRawValue());
372        }
373
374        $filterChain = $input->getFilterChain();
375        $this->getFilterChain()->merge($filterChain);
376
377        $validatorChain = $input->getValidatorChain();
378        $this->getValidatorChain()->merge($validatorChain);
379        return $this;
380    }
381
382    /**
383     * @param  mixed $context Extra "context" to provide the validator
384     * @return bool
385     */
386    public function isValid($context = null)
387    {
388        $value           = $this->getValue();
389        $hasValue        = $this->hasValue();
390        $empty           = ($value === null || $value === '' || $value === array());
391        $required        = $this->isRequired();
392        $allowEmpty      = $this->allowEmpty();
393        $continueIfEmpty = $this->continueIfEmpty();
394
395        if (! $hasValue && $this->hasFallback()) {
396            $this->setValue($this->getFallbackValue());
397            return true;
398        }
399
400        if (! $hasValue && ! $required) {
401            return true;
402        }
403
404        if (! $hasValue && $required) {
405            if ($this->errorMessage === null) {
406                $this->errorMessage = $this->prepareRequiredValidationFailureMessage();
407            }
408            return false;
409        }
410
411        if ($empty && ! $required && ! $continueIfEmpty) {
412            return true;
413        }
414
415        if ($empty && $allowEmpty && ! $continueIfEmpty) {
416            return true;
417        }
418
419        // At this point, we need to run validators.
420        // If we do not allow empty and the "continue if empty" flag are
421        // BOTH false, we inject the "not empty" validator into the chain,
422        // which adds that logic into the validation routine.
423        if (! $allowEmpty && ! $continueIfEmpty) {
424            $this->injectNotEmptyValidator();
425        }
426
427        $validator = $this->getValidatorChain();
428        $result    = $validator->isValid($value, $context);
429        if (! $result && $this->hasFallback()) {
430            $this->setValue($this->getFallbackValue());
431            $result = true;
432        }
433
434        return $result;
435    }
436
437    /**
438     * @return string[]
439     */
440    public function getMessages()
441    {
442        if (null !== $this->errorMessage) {
443            return (array) $this->errorMessage;
444        }
445
446        if ($this->hasFallback()) {
447            return array();
448        }
449
450        $validator = $this->getValidatorChain();
451        return $validator->getMessages();
452    }
453
454    /**
455     * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain.
456     *
457     * @return void
458     */
459    protected function injectNotEmptyValidator()
460    {
461        if ((!$this->isRequired() && $this->allowEmpty()) || $this->notEmptyValidator) {
462            return;
463        }
464        $chain = $this->getValidatorChain();
465
466        // Check if NotEmpty validator is already in chain
467        $validators = $chain->getValidators();
468        foreach ($validators as $validator) {
469            if ($validator['instance'] instanceof NotEmpty) {
470                $this->notEmptyValidator = true;
471                return;
472            }
473        }
474
475        $this->notEmptyValidator = true;
476
477        if (class_exists('Zend\ServiceManager\AbstractPluginManager')) {
478            $chain->prependByName('NotEmpty', array(), true);
479
480            return;
481        }
482
483        $chain->prependValidator(new NotEmpty(), true);
484    }
485
486    /**
487     * Create and return the validation failure message for required input.
488     *
489     * @return string[]
490     */
491    protected function prepareRequiredValidationFailureMessage()
492    {
493        $notEmpty = new NotEmpty();
494        $templates = $notEmpty->getOption('messageTemplates');
495        return array(
496            NotEmpty::IS_EMPTY => $templates[NotEmpty::IS_EMPTY],
497        );
498    }
499}
500