Pragmatism in the real world

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!

3 thoughts on “Zend\Input fallback value

  1. 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!

Comments are closed.