Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Commit

Permalink
Loop validation context is mutable
Browse files Browse the repository at this point in the history
  • Loading branch information
Maks3w committed Aug 20, 2015
1 parent b75c2a7 commit 5c81065
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 74 deletions.
9 changes: 2 additions & 7 deletions src/BaseInputFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ protected function validateInputs(array $inputs, $data = [], $context = null)
$data = $this->getRawValues();
}

$inputContext = $context ?: (array_merge($this->getRawValues(), (array) $data));

$this->validInputs = [];
$this->invalidInputs = [];
$valid = true;
Expand Down Expand Up @@ -274,11 +276,6 @@ protected function validateInputs(array $inputs, $data = [], $context = null)
continue;
}

// Make sure we have a value (empty) for validation of context
if (!array_key_exists($name, $data)) {
$data[$name] = null;
}

// Validate an input filter
if ($input instanceof InputFilterInterface) {
if (!$input->isValid($context)) {
Expand All @@ -292,8 +289,6 @@ protected function validateInputs(array $inputs, $data = [], $context = null)

// Validate an input
if ($input instanceof InputInterface) {
$inputContext = $context ?: $data;

if (!$input->isValid($inputContext)) {
// Validation failure
$this->invalidInputs[$name] = $input;
Expand Down
153 changes: 86 additions & 67 deletions test/BaseInputFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
namespace ZendTest\InputFilter;

use ArrayObject;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
use PHPUnit_Framework_TestCase as TestCase;
use stdClass;
use Zend\InputFilter\Input;
use Zend\InputFilter\InputInterface;
use Zend\InputFilter\FileInput;
use Zend\InputFilter\BaseInputFilter as InputFilter;
use Zend\Filter;
Expand Down Expand Up @@ -433,44 +435,103 @@ public function testCanGetValidationMessages()
}
}

/**
* Idea for this one is that one input may only need to be validated if another input is present.
*
* Commenting out for now, as validation context may make this irrelevant, and unsure what API to expose.
public function testCanConditionallyInvokeValidators()
/*
* Idea for this one is that validation may need to rely on context -- e.g., a "password confirmation"
* field may need to know what the original password entered was in order to compare.
*/

public function contextProvider()
{
$this->markTestIncomplete();
$data = ['fooInput' => 'fooValue'];
$arrayAccessData = new ArrayObject(['fooInput' => 'fooValue']);
$expectedFromRawValues = ['fooInput' => 'fooValue'];

return [
// Description => [$data, $expectedContext]
'by default get context from raw values (array)' => [$data, $expectedFromRawValues],
'by default get context from raw values (ArrayAccess)' => [$arrayAccessData, $expectedFromRawValues],
];
}
*/

/**
* Idea for this one is that validation may need to rely on context -- e.g., a "password confirmation"
* field may need to know what the original password entered was in order to compare.
* @dataProvider contextProvider
*
* @param mixed $data
* @param mixed $expectedContext
*/
public function testValidationCanUseContext()
public function testValidationUseContext($data, $expectedContext)
{
$filter = new InputFilter();

$store = new stdClass;
$foo = new Input();
$foo->getValidatorChain()->attach(new Validator\Callback(function ($value, $context) use ($store) {
$store->value = $value;
$store->context = $context;
return true;
}));
/** @var InputInterface|MockObject $input */
$input = $this->getMock(InputInterface::class);
$input->expects($this->once())
->method('isValid')
->with($expectedContext)
->willReturn(true)
;

$bar = new Input();
$bar->getValidatorChain()->attach(new Validator\Digits());
$filter->add($input, 'fooInput');

$filter->add($foo, 'foo')
->add($bar, 'bar');
$filter->setData($data);

$this->assertTrue($filter->isValid(), json_encode($filter->getMessages()));
}

public function testValidationUseCustomContext()
{
$data = ['fooInput' => 'fooValue'];
$expectedContext = ['custom context'];
$filter = new InputFilter();

/** @var InputInterface|MockObject $input */
$input = $this->getMock(InputInterface::class);
$input->expects($this->once())
->method('isValid')
->with($expectedContext)
->willReturn(true)
;

$filter->add($input, 'fooInput');

$data = ['foo' => 'foo', 'bar' => 123];
$filter->setData($data);

$this->assertTrue($filter->isValid());
$this->assertEquals('foo', $store->value);
$this->assertEquals($data, $store->context);
$this->assertTrue($filter->isValid($expectedContext), json_encode($filter->getMessages()));
}

public function testContextIsTheSameWhenARequiredInputIsGivenAndOptionalInputIsMissing()
{
$data = [
'inputRequired' => 'inputRequiredValue',
];
$expectedContext = [
'inputRequired' => 'inputRequiredValue',
'inputOptional' => null,
];
$filter = new InputFilter();

/** @var InputInterface|MockObject $inputRequired */
$inputRequired = $this->getMock(InputInterface::class);
$inputRequired->expects($this->once())
->method('isValid')
->with($expectedContext)
->willReturn(true)
;

/** @var InputInterface|MockObject $inputOptional */
$inputOptional = $this->getMock(InputInterface::class);
$inputOptional->expects($this->once())
->method('isValid')
->with($expectedContext)
->willReturn(true)
;

$filter->add($inputRequired, 'inputRequired');
$filter->add($inputOptional, 'inputOptional');

$filter->setData($data);

$this->assertTrue($filter->isValid(), json_encode($filter->getMessages()));
}

/**
Expand Down Expand Up @@ -627,48 +688,6 @@ public function testValidationMarksInputInvalidWhenRequiredAndAllowEmptyFlagIsFa
$this->assertFalse($filter->isValid());
}

public static function contextDataProvider()
{
return [
['', 'y', true],
['', 'n', false],
];
}

/**
* Idea here is that an empty field may or may not be valid based on
* context.
*/
/**
* @dataProvider contextDataProvider()
*/
// @codingStandardsIgnoreStart
public function testValidationMarksInputValidWhenAllowEmptyFlagIsTrueAndContinueIfEmptyIsTrueAndContextValidatesEmptyField($allowEmpty, $blankIsValid, $valid)
{
// @codingStandardsIgnoreEnd
$filter = new InputFilter();

$data = [
'allowEmpty' => $allowEmpty,
'blankIsValid' => $blankIsValid,
];

$allowEmpty = new Input();
$allowEmpty->setAllowEmpty(true)
->setContinueIfEmpty(true);

$blankIsValid = new Input();
$blankIsValid->getValidatorChain()->attach(new Validator\Callback(function ($value, $context) {
return ('y' === $value && empty($context['allowEmpty']));
}));

$filter->add($allowEmpty, 'allowEmpty')
->add($blankIsValid, 'blankIsValid');
$filter->setData($data);

$this->assertSame($valid, $filter->isValid());
}

public function testCanRetrieveRawValuesIndividuallyWithoutValidating()
{
if (!extension_loaded('intl')) {
Expand Down

0 comments on commit 5c81065

Please sign in to comment.