Zend\Input fallback value
Recently an issue was reported against Zend\InputFilter where the reporter has discovered a regression where the fallback value wasn’t being populated correctly. Matthew investigated, fixed it and asked me to review it.
I was fascinated as I didn’t realise (or had completely forgotten!) that Zend\Input and Zend\InputFilter supported fallback values so I looked into it and it turns out that it’s simple and works exactly as its name implies.
For the basic case of using an Input directly, you use it like this:
<?php $name = new \Zend\InputFilter\Input('name'); $validators = $name->getValidatorChain(); $validators->addValidator(new \Zend\Validator\StringLength(5), true); $name->setFallbackValue('Rob Allen'); $name->setValue(''); $isValid = $name->isValid(); var_dump($name->getValue());
The output is “Rob Allen“.
That is, when the value supplied is empty, then, the fallback value is set instead and used.
A wrinkle
There is, however, a wrinkle! Sometimes $name->getValue() returns an empty string and this occurs due to particular combinations of required, allow_empty & continue_if_empty as I’ve already discussed.
If continue_if_empty is false then the combination of required & allow_empty affects whether you get the fallback value or an empty string when calling getValue() after isValid():
required | allow_empty | What happens |
---|---|---|
true | false | With an empty value, the fallback value is returned in getValue(). |
true | true | The fallback value is ignored and an empty value is returned in getValue(). |
false | false | The fallback value is ignored and an empty value is returned in getValue(). |
false | true | The fallback value is ignored and an empty value is returned in getValue(). |
If you compare this table to the one in the last post, you’ll notice that in the three cases where the validators are not run, the fallback value is not set. This is not surprising as those combinations result in a short-circuit of isValid().
Test app
Again, I used a test application to check this out
<php require 'vendor/autoload.php'; $values = [ // [contine_if_empty, required, allow_empty] [false, true, false], [false, true, true], [false, false, false], [false, false, true], [true, true, false], [true, true, true], [true, false, false], [true, false, true], ]; foreach ($values as $row) { test(...$row); } function test($continueIfEmpty, $required, $allowEmpty) { // set up Input with a StringLength validator so we'll know if the // validators have run as they will always fail $name = new \Zend\InputFilter\Input('name'); $validators = $name->getValidatorChain(); $validators->addValidator(new \Zend\Validator\StringLength(5), true); $name->setFallbackValue('Rob Allen'); $name->setValue(''); $name->setRequired($required); $name->setAllowEmpty($allowEmpty); $name->setContinueIfEmpty($continueIfEmpty); // Test echo "continue_if_empty: " . (int)$continueIfEmpty; echo ", required: " . (int)$required; echo ", allow_empty: " . (int)$allowEmpty; $isValid = (int)$name->isValid(); echo " - Result: isValid() = $isValid"; if (!$isValid) { echo " " . current($name->getMessages()); } else { echo ", value = " . $name->getValue(); } echo "\n"; }
As before, this app simply runs through all combinations of required, allow_empty & continue_if_empty against a Zend\InputFilter\Input with a fallback value set and sees what happens.
This is the output:
$ php test.php continue_if_empty: 0, required: 1, allow_empty: 0 - Result: isValid() = 1, value = Rob Allen continue_if_empty: 0, required: 1, allow_empty: 1 - Result: isValid() = 1, value = continue_if_empty: 0, required: 0, allow_empty: 0 - Result: isValid() = 1, value = continue_if_empty: 0, required: 0, allow_empty: 1 - Result: isValid() = 1, value = continue_if_empty: 1, required: 1, allow_empty: 0 - Result: isValid() = 1, value = Rob Allen continue_if_empty: 1, required: 1, allow_empty: 1 - Result: isValid() = 1, value = Rob Allen continue_if_empty: 1, required: 0, allow_empty: 0 - Result: isValid() = 1, value = Rob Allen continue_if_empty: 1, required: 0, allow_empty: 1 - Result: isValid() = 1, value = Rob Allen\
Zend\InputFilter
Note that with the fix discussed at the start of this post, Zend\InputFilter works exactly the same as Zend\Input, as you’d expect. This fix was back ported to the 2.4 release too, so if you are using fallback values, ensure that you’re using the latest 2.4 or 2.5 version.
Conclusion
If you want to use a fallback value with Zend\Input make sure that you set required to true and allow_empty to false. Fortunately this is the default, so that’s probably what you’re doing anyway!
Hi Alen. How a set (fallback) will look in a Traversable object.
Thanks for this! It was actually really helpful, because it wasn't taking a fallback value when required = false, and I couldn't figure out why!