2

i was just reading a great presentation about Quality Assurance for PHP Projects by Michelangelo van Dam aka DragonBe. In his presentation he uses Zend-Framework as an example, which i am familiar with on a basic level.

Since i want to improve myself, im starting with unit testing, though i feel the problem is on ZFs side.

The Problem:

With given code-artifacts whenever i assign a boolean value of false the Zend_Filter or Zend_Validator changes this into null. I can't figure out why this is the case.

Please see update on bottom for newer code and test

First the Tests

public function goodData() {
  return array(
    array('DU-IT', 'Sample Description', true, false),
    array('Mercüß', 'bla"ß"trager', false, true),
    array('Mia123', '728 Tage im Jahr', false, false)
  );
}

/**
 * @dataProvider goodData()
 */
public function testModelAcceptsValidData($name, $description, $flag_active, $flag_deleted)
{
  $data = array(
    'id'            => 0,
    'name'          => $name,
    'description'   => $description,
    'flag_active'   => $flag_active,
    'flag_deleted'  => $flag_deleted
  );

  try {
    $this->_model->populate($data);
  } catch (Zend_Exception $e) {
    $this->fail('Unexpected Exception: '.$e->getMessage());
  }

  $this->assertSame($data, $this->_model->toArray());
}

The Model

public function __construct($props = null)
{
  // Set Filters
  $this->_filters = array(
    'id'            => array('Int'),
    'name'          => array('StringTrim', 'StripTags'),
    'description'   => array('StringTrim', 'StripTags'),
    'flag_active'   => array(new Zend_Filter_Boolean()),
    'flag_deleted'  => array(new Zend_Filter_Boolean())
  );

  // Set Validators
  $this->_validators = array(
    'id'            => array('Int'),
    'name'          => array(new Zend_Validate_StringLength(array('min'=>4, 'max'=>50))),
    'description'   => array(new Zend_Validate_StringLength(array('max'=>5000))),
    'flag_active'   => array(new Zend_Validate_InArray(array(true, false))),
    'flag_deleted'  => array(new Zend_Validate_InArray(array(true, false)))
  );

  // Set Properties
  if (!is_null($props)) {
    $this->populate($props);
  }
}

public function setFlagActive($arg)
{
  $input = new Zend_Filter_Input($this->_filters, $this->_validators);
  $input->setData(array('flag_active'=>$arg));
  if (!$input->isValid('flag_active')) {
    throw new Zend_Exception('Invalid FLAG_ACTIVE provided'. gettype($input->flag_active));
  }
  $this->_flag_active = (bool) $input->flag_active;
  return $this;
}

As far as the model is concerned, i tried leaving the validator for flag_active and flag_deleted empty, but this did not change my results. The error message of phpunit remains the same:

Unexpected Exception: Invalid FLAG_ACTIVE providedNULL

Whereas NULL is the type of the variable, WHENEVER i pass false as the argument for the flag via the data-provider goodData() (same happens with badData, too).

My guess is this has something to do with Zend_Filter, but i just can't figure this out. And hints are greatly appreciated!

UPDATE Since Drew gave a little hint i've tested a little further, yet the problem remains. I have pasted the updated Class and unit test onto pastebin for better readability.

PHPUnit gives out the following error: You must give a non-empty value for field 'flag_active' same for flag_deleted. A second error on half the tests is <null> does not match expected type "boolean". I'm stuck. No matter if i use filters or an inArrayValidator(array(0=>true,1=>false)) it doesn't change a thing :\

Sam
  • 16,435
  • 6
  • 55
  • 89

1 Answers1

3

If you examine the contents of $input->getMessages(), it should provide some explanation as to why that particular value is coming through as invalid.

When you use Zend_Filter_Input, any value that did not pass validation will not be magically accessible by its name (e.g. $input->flag_active). The value will always be NULL if it didn't pass the validation step.

Certain filters can almost guarantee validation in some cases (i.e. an Int filter with an Int validator) because the filter will filter the data and return a valid value for the validator. Passing hello through the Int filter returns 0 which may validate with a simple Int validator even though the original input data is not valid.

The reason you are most likely getting a null value within your exception is because the value is not available since it didn't validate; therefore check the input messages for any hints as to why.

EDIT:

I think the problem you are having is that Zend_Filter_Input is not allowing empty values by default. Therefore if the value coming into the filter is false or NULL, it is causing an error with the filter.

Check out this code:

    <?php

    // set up a boolean validator and inarray validator like you have
    $filters    = array('test' => array(new Zend_Filter_Boolean()));
    $validators = array('test' => array(new Zend_Validate_InArray(array(true, false))));

    // set up the input filter with default options
    $input      = new Zend_Filter_Input($filters, $validators);

    // our "input" value which is (bool)false
    $test       = false;

    $input->setData(array('test' => $test));

    // run through input filter using default values
    // should throw error about a non-empty value
    if (!$input->isValid('test')) {
        echo "Test is not valid.  <br />Original Value: ";
        var_dump($test);
        echo "<br />";
        print_r($input->getMessages());
    } else {
        echo "Test is valid.<br />Original Value: ";
        var_dump($test);
        echo "<br />Value now: ";
        var_dump($input->test);
    }

    // now allowing empty values...
    echo "<hr />\n";

    // tell Zend_Filter_Input to allow empty values on input
    $options = array(Zend_Filter_Input::ALLOW_EMPTY => true);

    // set up new input filter with option
    $input   = new Zend_Filter_Input($filters, $validators, null, $options);

    $input->setData(array('test' => $test));

    // run through input filter allowing empty values
    // this should NOT throw an error
    if (!$input->isValid('test')) {
        echo "Test is not valid.  <br />Original Value: ";
        var_dump($test);
        echo "<br />";
        print_r($input->getMessages());
    } else {
        echo "Test is valid.<br />Original Value: ";
        var_dump($test);
        echo "<br />Value now: ";
        var_dump($input->test);
    }

So the problem seems to be that Zend_Filter_Input doesn't like empty values unless you specify an option. Therefore sending a valid (false) value into the filter causes an error unless you set Zend_Filter_Input::ALLOW_EMPTY to true.

drew010
  • 68,777
  • 11
  • 134
  • 162
  • First of all thank you for your answer. Sadly the known error message doesn't help out too much, as it's just a better written 'Hey, you got a null here' - see updated question for details with new class and test code. The question remains: **can boolean values be VALIDATED by Zend_Filter_Input** ? Even with empty filter/validator, as long as i input "false", the inputFilter fails badly, returning null. – Sam Jun 26 '12 at 05:49
  • Have you confirmed that `$arg` coming into the set functions is what you expect? I tested some things with `Zend_Filter_Input` and with a boolean filter, pretty much any value gets converted to bool for me so it's strange that this is failing. – drew010 Jun 26 '12 at 17:08
  • There in fact was an error in the class that didn't set the attribute properly, but i managed to find it last 5 minutes of work. I will check further later - i didn't get it to work before that. May you be able to provide your example that worked with both true and false boolean value? – Sam Jun 26 '12 at 17:54
  • Here's a [paste](http://pastebin.com/rbiJS5fi) of what I used. I'm going to update the answer in a moment with some helpful information I think. – drew010 Jun 26 '12 at 18:24
  • Ok, it is good to know that there is an option, i will check this out in two days (sadly, only then). As far as i see it, empty value will allow for `NULL` to be assigned, too, right? I guess this can't be changed then. NULL would then have to be treated as FALSE, i guess i can live with that. Though it feels weird :) Thanks upfront, i will let you know on thursday on how it went on my case and propperly assign the (little) award ;) – Sam Jun 26 '12 at 19:32
  • I tried that before and a `NULL` value will pass the filter and become a `false` value. If that isn't desirable, you could check for null before you run it through zend_filter_input, but that's all I can think of at the moment. – drew010 Jun 26 '12 at 19:58
  • I just rebuilt this at home and it works as expected and explained by you. After some more research, this pretty much boils down to how PHP handles array with `false` as a boolean value. As in some cases it simply becomes `null`. No clue as to why that is, but this is pretty much the 'problem' i encountered here. So allowing nullable values will work out well enough for me :) THANK YOU – Sam Jun 27 '12 at 09:46