From 86b3acf8fd26870fea8248aaae350b335844fa66 Mon Sep 17 00:00:00 2001 From: Christopher Martin Date: Tue, 2 Oct 2012 00:20:44 -0400 Subject: [PATCH 1/7] File Validators refactored --- src/FileInput.php | 4 +++- src/Input.php | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/FileInput.php b/src/FileInput.php index 3bb6cbd8..a4170c3a 100644 --- a/src/FileInput.php +++ b/src/FileInput.php @@ -52,9 +52,11 @@ public function isValid($context = null) return $this->isValid; } + /** + * @return void + */ protected function injectNotEmptyValidator() { $this->notEmptyValidator = true; - // TODO: Could do something like automatically add the Upload validator here } } diff --git a/src/Input.php b/src/Input.php index 42390c95..bc354c96 100644 --- a/src/Input.php +++ b/src/Input.php @@ -309,6 +309,9 @@ public function getMessages() return $validator->getMessages(); } + /** + * @return void + */ protected function injectNotEmptyValidator() { if ((!$this->isRequired() && $this->allowEmpty()) || $this->notEmptyValidator) { From 49ba1c985b83731a66651840511c9d2f442b4061 Mon Sep 17 00:00:00 2001 From: Christopher Martin Date: Fri, 5 Oct 2012 00:55:13 -0400 Subject: [PATCH 2/7] Unit test coverage for InputFilter, File Element, and FormFile view helper changes --- src/FileInput.php | 2 +- test/BaseInputFilterTest.php | 29 +++++ test/FileInputTest.php | 242 +++++++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 test/FileInputTest.php diff --git a/src/FileInput.php b/src/FileInput.php index a4170c3a..52e2a02c 100644 --- a/src/FileInput.php +++ b/src/FileInput.php @@ -31,7 +31,7 @@ class FileInput extends Input public function getValue() { $filter = $this->getFilterChain(); - $value = (isset($this->value['tmp_name'])) + $value = (is_array($this->value) && isset($this->value['tmp_name'])) ? $this->value['tmp_name'] : $this->value; if ($this->isValid) { $value = $filter->filter($value); diff --git a/test/BaseInputFilterTest.php b/test/BaseInputFilterTest.php index 2187e9e0..4801edd1 100644 --- a/test/BaseInputFilterTest.php +++ b/test/BaseInputFilterTest.php @@ -13,6 +13,7 @@ use PHPUnit_Framework_TestCase as TestCase; use stdClass; use Zend\InputFilter\Input; +use Zend\InputFilter\FileInput; use Zend\InputFilter\BaseInputFilter as InputFilter; use Zend\Filter; use Zend\Validator; @@ -414,6 +415,34 @@ public function testValidationSkipsFieldsMarkedNotRequiredWhenNoDataPresent() $this->assertTrue($filter->isValid()); } + public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoFileDataIsPresent() + { + $filter = new InputFilter(); + + $foo = new FileInput(); + $foo->getValidatorChain()->addValidator(new Validator\File\Upload()); + $foo->setRequired(false); + + $filter->add($foo, 'foo'); + + $data = array( + 'foo' => array( + 'tmp_name' => '/tmp/barfile', + 'name' => 'barfile', + 'type' => 'text', + 'size' => 0, + 'error' => 4, // UPLOAD_ERR_NO_FILE + ) + ); + $filter->setData($data); + $this->assertTrue($filter->isValid()); + + // Negative test + $foo->setRequired(true); + $filter->setData($data); + $this->assertFalse($filter->isValid()); + } + public function testValidationAllowsEmptyValuesToRequiredInputWhenAllowEmptyFlagIsTrue() { $filter = new InputFilter(); diff --git a/test/FileInputTest.php b/test/FileInputTest.php new file mode 100644 index 00000000..8748ba9c --- /dev/null +++ b/test/FileInputTest.php @@ -0,0 +1,242 @@ +assertEquals('foo', $input->getName()); + } + + public function testInputHasEmptyFilterChainByDefault() + { + $input = new FileInput('foo'); + $filters = $input->getFilterChain(); + $this->assertInstanceOf('Zend\Filter\FilterChain', $filters); + $this->assertEquals(0, count($filters)); + } + + public function testInputHasEmptyValidatorChainByDefault() + { + $input = new FileInput('foo'); + $validators = $input->getValidatorChain(); + $this->assertInstanceOf('Zend\Validator\ValidatorChain', $validators); + $this->assertEquals(0, count($validators)); + } + + public function testCanInjectFilterChain() + { + $input = new FileInput('foo'); + $chain = new Filter\FilterChain(); + $input->setFilterChain($chain); + $this->assertSame($chain, $input->getFilterChain()); + } + + public function testCanInjectValidatorChain() + { + $input = new FileInput('foo'); + $chain = new Validator\ValidatorChain(); + $input->setValidatorChain($chain); + $this->assertSame($chain, $input->getValidatorChain()); + } + + public function testInputIsMarkedAsRequiredByDefault() + { + $input = new FileInput('foo'); + $this->assertTrue($input->isRequired()); + } + + public function testRequiredFlagIsMutable() + { + $input = new FileInput('foo'); + $input->setRequired(false); + $this->assertFalse($input->isRequired()); + } + + public function testInputDoesNotAllowEmptyValuesByDefault() + { + $input = new FileInput('foo'); + $this->assertFalse($input->allowEmpty()); + } + + public function testAllowEmptyFlagIsMutable() + { + $input = new FileInput('foo'); + $input->setAllowEmpty(true); + $this->assertTrue($input->allowEmpty()); + } + + public function testValueIsNullByDefault() + { + $input = new FileInput('foo'); + $this->assertNull($input->getValue()); + } + + public function testValueMayBeInjected() + { + $input = new FileInput('foo'); + $input->setValue('bar'); + $this->assertEquals('bar', $input->getValue()); + } + + public function testRetrievingValueFiltersTheValueOnlyAfterValidating() + { + $input = new FileInput('foo'); + $input->setValue('bar'); + $filter = new Filter\StringToUpper(); + $input->getFilterChain()->attach($filter); + $this->assertEquals('bar', $input->getValue()); + $this->assertTrue($input->isValid()); + $this->assertEquals('BAR', $input->getValue()); + } + + public function testCanRetrieveRawValue() + { + $input = new FileInput('foo'); + $input->setValue('bar'); + $filter = new Filter\StringToUpper(); + $input->getFilterChain()->attach($filter); + $this->assertEquals('bar', $input->getRawValue()); + } + + public function testIsValidReturnsFalseIfValidationChainFails() + { + $input = new FileInput('foo'); + $input->setValue('bar'); + $validator = new Validator\Digits(); + $input->getValidatorChain()->addValidator($validator); + $this->assertFalse($input->isValid()); + } + + public function testIsValidReturnsTrueIfValidationChainSucceeds() + { + $input = new FileInput('foo'); + $input->setValue('123'); + $validator = new Validator\Digits(); + $input->getValidatorChain()->addValidator($validator); + $this->assertTrue($input->isValid()); + } + + public function testValidationOperatesBeforeFiltering() + { + $input = new FileInput('foo'); + $input->setValue(' 123 '); + $filter = new Filter\StringTrim(); + $input->getFilterChain()->attach($filter); + $validator = new Validator\Digits(); + $input->getValidatorChain()->addValidator($validator); + $this->assertFalse($input->isValid()); + $input->setValue('123'); + $this->assertTrue($input->isValid()); + } + + public function testGetMessagesReturnsValidationMessages() + { + $input = new FileInput('foo'); + $input->setValue('bar'); + $validator = new Validator\Digits(); + $input->getValidatorChain()->addValidator($validator); + $this->assertFalse($input->isValid()); + $messages = $input->getMessages(); + $this->assertArrayHasKey(Validator\Digits::NOT_DIGITS, $messages); + } + + public function testSpecifyingMessagesToInputReturnsThoseOnFailedValidation() + { + $input = new FileInput('foo'); + $input->setValue('bar'); + $validator = new Validator\Digits(); + $input->getValidatorChain()->addValidator($validator); + $input->setErrorMessage('Please enter only digits'); + $this->assertFalse($input->isValid()); + $messages = $input->getMessages(); + $this->assertArrayNotHasKey(Validator\Digits::NOT_DIGITS, $messages); + $this->assertContains('Please enter only digits', $messages); + } + + public function testBreakOnFailureFlagIsOffByDefault() + { + $input = new FileInput('foo'); + $this->assertFalse($input->breakOnFailure()); + } + + public function testBreakOnFailureFlagIsMutable() + { + $input = new FileInput('foo'); + $input->setBreakOnFailure(true); + $this->assertTrue($input->breakOnFailure()); + } + + public function testNotEmptyValidatorIsNotAddedWhenIsValidIsCalled() + { + $input = new FileInput('foo'); + $this->assertTrue($input->isRequired()); + $input->setValue(''); + $validatorChain = $input->getValidatorChain(); + $this->assertEquals(0, count($validatorChain->getValidators())); + + $this->assertTrue($input->isValid()); + $messages = $input->getMessages(); + $this->assertEquals(0, count($validatorChain->getValidators())); + } + + public function testRequiredNotEmptyValidatorNotAddedWhenOneExists() + { + $input = new FileInput('foo'); + $this->assertTrue($input->isRequired()); + $input->setValue(''); + + $notEmptyMock = $this->getMock('Zend\Validator\NotEmpty', array('isValid')); + $notEmptyMock->expects($this->exactly(1)) + ->method('isValid') + ->will($this->returnValue(false)); + + $validatorChain = $input->getValidatorChain(); + $validatorChain->prependValidator($notEmptyMock); + $this->assertFalse($input->isValid()); + + $validators = $validatorChain->getValidators(); + $this->assertEquals(1, count($validators)); + $this->assertEquals($notEmptyMock, $validators[0]['instance']); + } + + public function testMerge() + { + $input = new FileInput('foo'); + $input->setValue(' 123 '); + $filter = new Filter\StringTrim(); + $input->getFilterChain()->attach($filter); + $validator = new Validator\Digits(); + $input->getValidatorChain()->addValidator($validator); + + $input2 = new FileInput('bar'); + $input2->merge($input); + $validatorChain = $input->getValidatorChain(); + $filterChain = $input->getFilterChain(); + + $this->assertEquals(' 123 ', $input2->getRawValue()); + $this->assertEquals(1, $validatorChain->count()); + $this->assertEquals(1, $filterChain->count()); + + $validators = $validatorChain->getValidators(); + $this->assertInstanceOf('Zend\Validator\Digits', $validators[0]['instance']); + + $filters = $filterChain->getFilters()->toArray(); + $this->assertInstanceOf('Zend\Filter\StringTrim', $filters[0]); + } +} From fbed0a37eb689496dc72891c7a4138668ca5edba Mon Sep 17 00:00:00 2001 From: Christopher Martin Date: Sat, 6 Oct 2012 15:15:39 -0400 Subject: [PATCH 3/7] Fix for HTML5 multiple attribute on file element --- src/BaseInputFilter.php | 6 +++++- test/BaseInputFilterTest.php | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/BaseInputFilter.php b/src/BaseInputFilter.php index c1334fb2..a407a566 100644 --- a/src/BaseInputFilter.php +++ b/src/BaseInputFilter.php @@ -165,7 +165,11 @@ public function isValid() if (!array_key_exists($name, $this->data) || (null === $this->data[$name]) || (is_string($this->data[$name]) && strlen($this->data[$name]) === 0) - || (isset($this->data[$name]['error']) && $this->data[$name]['error'] === UPLOAD_ERR_NO_FILE) + // Single and Multi File Uploads + || (is_array($this->data[$name]) + && isset($this->data[$name]['error']) && $this->data[$name]['error'] === UPLOAD_ERR_NO_FILE) + || (is_array($this->data[$name]) && count($this->data[$name]) === 1 + && isset($this->data[$name][0]['error']) && $this->data[$name][0]['error'] === UPLOAD_ERR_NO_FILE) ) { if ($input instanceof InputInterface) { // - test if input is required diff --git a/test/BaseInputFilterTest.php b/test/BaseInputFilterTest.php index 4801edd1..59fceab2 100644 --- a/test/BaseInputFilterTest.php +++ b/test/BaseInputFilterTest.php @@ -443,6 +443,37 @@ public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoFileDataIsPr $this->assertFalse($filter->isValid()); } + public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoMultiFileDataIsPresent() + { + $filter = new InputFilter(); + + $explode = new Validator\File\Explode(); + $explode->setValidator(new Validator\File\Upload()); + + $foo = new FileInput(); + $foo->getValidatorChain()->addValidator($explode); + $foo->setRequired(false); + + $filter->add($foo, 'foo'); + + $data = array( + 'foo' => array(array( + 'tmp_name' => '/tmp/barfile', + 'name' => 'barfile', + 'type' => 'text', + 'size' => 0, + 'error' => 4, // UPLOAD_ERR_NO_FILE + )), + ); + $filter->setData($data); + $this->assertTrue($filter->isValid()); + + // Negative test + $foo->setRequired(true); + $filter->setData($data); + $this->assertFalse($filter->isValid()); + } + public function testValidationAllowsEmptyValuesToRequiredInputWhenAllowEmptyFlagIsTrue() { $filter = new InputFilter(); From 6a5f8be9a14eb0bcc6a7fb081043f38bc168fa80 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 13 Nov 2012 17:17:33 -0600 Subject: [PATCH 4/7] [zendframework/zf2#2875] Added UnknownInputsCapableInterface - Provides hasUnknown and getUnknown - BaseInputFilter now implements this additional interface --- src/BaseInputFilter.php | 20 ++++++++++---------- src/UnknownInputsCapableInterface.php | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 src/UnknownInputsCapableInterface.php diff --git a/src/BaseInputFilter.php b/src/BaseInputFilter.php index e01e2c2c..be9e3d06 100644 --- a/src/BaseInputFilter.php +++ b/src/BaseInputFilter.php @@ -20,7 +20,7 @@ * @category Zend * @package Zend_InputFilter */ -class BaseInputFilter implements InputFilterInterface +class BaseInputFilter implements InputFilterInterface, UnknownInputsCapableInterface { protected $data; protected $inputs = array(); @@ -155,11 +155,11 @@ public function isValid() )); } - $this->validInputs = array(); + $this->validInputs = array(); $this->invalidInputs = array(); - $valid = true; + $valid = true; - $inputs = $this->validationGroup ? : array_keys($this->inputs); + $inputs = $this->validationGroup ?: array_keys($this->inputs); foreach ($inputs as $name) { $input = $this->inputs[$name]; if (!array_key_exists($name, $this->data) @@ -320,7 +320,7 @@ public function getValue($name) */ public function getValues() { - $inputs = $this->validationGroup ? : array_keys($this->inputs); + $inputs = $this->validationGroup ?: array_keys($this->inputs); $values = array(); foreach ($inputs as $name) { $input = $this->inputs[$name]; @@ -458,9 +458,9 @@ public function hasUnknown() )); } - $datas = array_keys($this->data); + $data = array_keys($this->data); $inputs = array_keys($this->inputs); - $diff = array_diff($datas, $inputs); + $diff = array_diff($data, $inputs); if (!empty($diff)) { return count(array_intersect($diff, $inputs)) == 0; } @@ -483,12 +483,12 @@ public function getUnknown() )); } - $datas = array_keys($this->data); + $data = array_keys($this->data); $inputs = array_keys($this->inputs); - $diff = array_diff($datas, $inputs); + $diff = array_diff($data, $inputs); $unknownInputs = array(); - $intersect = array_intersect($diff, $datas); + $intersect = array_intersect($diff, $data); if (!empty($intersect)) { foreach ($intersect as $key) { $unknownInputs[$key] = $this->data[$key]; diff --git a/src/UnknownInputsCapableInterface.php b/src/UnknownInputsCapableInterface.php new file mode 100644 index 00000000..8e854000 --- /dev/null +++ b/src/UnknownInputsCapableInterface.php @@ -0,0 +1,24 @@ + Date: Fri, 9 Nov 2012 15:33:33 +0000 Subject: [PATCH 5/7] Improve coding standards on new traits, add tests --- src/InputFilterAwareTrait.php | 14 +++--- test/InputFilterAwareTraitTest.php | 49 ++++++++++++++++++++ test/TestAsset/MockInputFilterAwareTrait.php | 19 ++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 test/InputFilterAwareTraitTest.php create mode 100644 test/TestAsset/MockInputFilterAwareTrait.php diff --git a/src/InputFilterAwareTrait.php b/src/InputFilterAwareTrait.php index d254844a..9e30dd2b 100644 --- a/src/InputFilterAwareTrait.php +++ b/src/InputFilterAwareTrait.php @@ -19,19 +19,19 @@ trait InputFilterAwareTrait { /** - * @var \Zend\InputFilter\InputFilterInterface + * @var InputFilterInterface */ - protected $input_filter = null; + protected $inputFilter = null; /** * setInputFilter * - * @param \Zend\InputFilter\InputFilterInterface $inputFilter - * @return + * @param InputFilterInterface $inputFilter + * @return mixed */ public function setInputFilter(InputFilterInterface $inputFilter) { - $this->input_filter = $inputFilter; + $this->inputFilter = $inputFilter; return $this; } @@ -39,10 +39,10 @@ public function setInputFilter(InputFilterInterface $inputFilter) /** * getInputFilter * - * @return \Zend\InputFilter\InputFilterInterface + * @return InputFilterInterface */ public function getInputFilter() { - return $this->input_filter; + return $this->inputFilter; } } diff --git a/test/InputFilterAwareTraitTest.php b/test/InputFilterAwareTraitTest.php new file mode 100644 index 00000000..5abab6a4 --- /dev/null +++ b/test/InputFilterAwareTraitTest.php @@ -0,0 +1,49 @@ +assertAttributeEquals(null, 'inputFilter', $object); + + $inputFilter = new InputFilter; + + $object->setInputFilter($inputFilter); + + $this->assertAttributeEquals($inputFilter, 'inputFilter', $object); + } + + /** + * @requires PHP 5.4 + */ + public function testGetInputFilter() + { + $object = new TestAsset\MockInputFilterAwareTrait; + + $this->assertNull($object->getInputFilter()); + + $inputFilter = new InputFilter; + + $object->setInputFilter($inputFilter); + + $this->assertEquals($inputFilter, $object->getInputFilter()); + } +} diff --git a/test/TestAsset/MockInputFilterAwareTrait.php b/test/TestAsset/MockInputFilterAwareTrait.php new file mode 100644 index 00000000..374f1838 --- /dev/null +++ b/test/TestAsset/MockInputFilterAwareTrait.php @@ -0,0 +1,19 @@ + Date: Mon, 19 Nov 2012 17:30:14 +0000 Subject: [PATCH 6/7] Improve function comment quality, clean up tests a little --- src/InputFilterAwareTrait.php | 4 ++-- test/InputFilterAwareTraitTest.php | 13 +++++-------- test/TestAsset/MockInputFilterAwareTrait.php | 19 ------------------- 3 files changed, 7 insertions(+), 29 deletions(-) delete mode 100644 test/TestAsset/MockInputFilterAwareTrait.php diff --git a/src/InputFilterAwareTrait.php b/src/InputFilterAwareTrait.php index 9e30dd2b..4613eb05 100644 --- a/src/InputFilterAwareTrait.php +++ b/src/InputFilterAwareTrait.php @@ -24,7 +24,7 @@ trait InputFilterAwareTrait protected $inputFilter = null; /** - * setInputFilter + * Set input filter * * @param InputFilterInterface $inputFilter * @return mixed @@ -37,7 +37,7 @@ public function setInputFilter(InputFilterInterface $inputFilter) } /** - * getInputFilter + * Retrieve input filter * * @return InputFilterInterface */ diff --git a/test/InputFilterAwareTraitTest.php b/test/InputFilterAwareTraitTest.php index 5abab6a4..aaa8c491 100644 --- a/test/InputFilterAwareTraitTest.php +++ b/test/InputFilterAwareTraitTest.php @@ -13,14 +13,14 @@ use \PHPUnit_Framework_TestCase as TestCase; use \Zend\InputFilter\InputFilter; +/** + * @requires PHP 5.4 + */ class InputFilterAwareTraitTest extends TestCase { - /** - * @requires PHP 5.4 - */ public function testSetInputFilter() { - $object = new TestAsset\MockInputFilterAwareTrait; + $object = $this->getObjectForTrait('\Zend\InputFilter\InputFilterAwareTrait'); $this->assertAttributeEquals(null, 'inputFilter', $object); @@ -31,12 +31,9 @@ public function testSetInputFilter() $this->assertAttributeEquals($inputFilter, 'inputFilter', $object); } - /** - * @requires PHP 5.4 - */ public function testGetInputFilter() { - $object = new TestAsset\MockInputFilterAwareTrait; + $object = $this->getObjectForTrait('\Zend\InputFilter\InputFilterAwareTrait'); $this->assertNull($object->getInputFilter()); diff --git a/test/TestAsset/MockInputFilterAwareTrait.php b/test/TestAsset/MockInputFilterAwareTrait.php deleted file mode 100644 index 374f1838..00000000 --- a/test/TestAsset/MockInputFilterAwareTrait.php +++ /dev/null @@ -1,19 +0,0 @@ - Date: Wed, 21 Nov 2012 00:23:28 -0500 Subject: [PATCH 7/7] FileInput validation processing refactored to support multiple attribute --- src/FileInput.php | 91 +++++++++++- test/BaseInputFilterTest.php | 8 +- test/FileInputTest.php | 275 ++++++++++++++++++++--------------- 3 files changed, 246 insertions(+), 128 deletions(-) diff --git a/src/FileInput.php b/src/FileInput.php index 7148d703..77a7c5d3 100644 --- a/src/FileInput.php +++ b/src/FileInput.php @@ -10,9 +10,7 @@ namespace Zend\InputFilter; -use Zend\Filter\FilterChain; -use Zend\Validator\ValidatorChain; -use Zend\Validator\NotEmpty; +use Zend\Validator\File\Upload as UploadValidator; /** * @category Zend @@ -25,6 +23,29 @@ class FileInput extends Input */ protected $isValid = false; + /** + * @var boolean + */ + protected $autoPrependUploadValidator = true; + + /** + * @param boolean $value Enable/Disable automatically prepending an Upload validator + * @return FileInput + */ + public function setAutoPrependUploadValidator($value) + { + $this->autoPrependUploadValidator = $value; + return $this; + } + + /** + * @return boolean + */ + public function getAutoPrependUploadValidator() + { + return $this->autoPrependUploadValidator; + } + /** * @return mixed */ @@ -55,18 +76,76 @@ public function getValue() */ public function isValid($context = null) { - $this->injectNotEmptyValidator(); + $this->injectUploadValidator(); $validator = $this->getValidatorChain(); - //$value = $this->getValue(); // Do not run the filters yet for File uploads - $this->isValid = $validator->isValid($this->getRawValue(), $context); + //$value = $this->getValue(); // Do not run the filters yet for File uploads + + $rawValue = $this->getRawValue(); + if (is_array($rawValue) && isset($rawValue['tmp_name'])) { + // Single file input + $this->isValid = $validator->isValid($rawValue, $context); + } elseif (is_array($rawValue) && !empty($rawValue) && isset($rawValue[0]['tmp_name'])) { + // Multi file input (multiple attribute set) + $this->isValid = true; + foreach ($rawValue as $value) { + if (!$validator->isValid($value, $context)) { + $this->isValid = false; + break; // Do not continue processing files if validation fails + } + } + } else { + // RawValue does not contain a file data array + $this->setErrorMessage('Invalid type given. File array expected'); + $this->isValid = false; + } + return $this->isValid; } /** * @return void */ + protected function injectUploadValidator() + { + if (!$this->autoPrependUploadValidator) { + return; + } + $chain = $this->getValidatorChain(); + + // Check if Upload validator is already first in chain + $validators = $chain->getValidators(); + if (isset($validators[0]['instance']) + && $validators[0]['instance'] instanceof UploadValidator + ) { + $this->autoPrependUploadValidator = false; + return; + } + + $chain->prependByName('fileupload', array(), true); + $this->autoPrependUploadValidator = false; + } + + /** + * No-op, NotEmpty validator does not apply for FileInputs. + * See also: BaseInputFilter::isValid() + * + * @return void + */ protected function injectNotEmptyValidator() { $this->notEmptyValidator = true; } + + /** + * @param InputInterface $input + * @return FileInput + */ + public function merge(InputInterface $input) + { + parent::merge($input); + if ($input instanceof FileInput) { + $this->setAutoPrependUploadValidator($input->getAutoPrependUploadValidator()); + } + return $this; + } } diff --git a/test/BaseInputFilterTest.php b/test/BaseInputFilterTest.php index 024e6fa8..812847ba 100644 --- a/test/BaseInputFilterTest.php +++ b/test/BaseInputFilterTest.php @@ -446,14 +446,8 @@ public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoFileDataIsPr public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoMultiFileDataIsPresent() { $filter = new InputFilter(); - - $explode = new Validator\File\Explode(); - $explode->setValidator(new Validator\File\Upload()); - - $foo = new FileInput(); - $foo->getValidatorChain()->addValidator($explode); + $foo = new FileInput(); $foo->setRequired(false); - $filter->add($foo, 'foo'); $data = array( diff --git a/test/FileInputTest.php b/test/FileInputTest.php index 50c2858e..56ae2703 100644 --- a/test/FileInputTest.php +++ b/test/FileInputTest.php @@ -17,248 +17,292 @@ class FileInputTest extends TestCase { + public function setUp() + { + $this->input = new FileInput('foo'); + // Upload validator does not work in CLI test environment, disable + $this->input->setAutoPrependUploadValidator(false); + } + public function testConstructorRequiresAName() { - $input = new FileInput('foo'); - $this->assertEquals('foo', $input->getName()); + $this->assertEquals('foo', $this->input->getName()); } public function testInputHasEmptyFilterChainByDefault() { - $input = new FileInput('foo'); - $filters = $input->getFilterChain(); + $filters = $this->input->getFilterChain(); $this->assertInstanceOf('Zend\Filter\FilterChain', $filters); $this->assertEquals(0, count($filters)); } public function testInputHasEmptyValidatorChainByDefault() { - $input = new FileInput('foo'); - $validators = $input->getValidatorChain(); + $validators = $this->input->getValidatorChain(); $this->assertInstanceOf('Zend\Validator\ValidatorChain', $validators); $this->assertEquals(0, count($validators)); } public function testCanInjectFilterChain() { - $input = new FileInput('foo'); $chain = new Filter\FilterChain(); - $input->setFilterChain($chain); - $this->assertSame($chain, $input->getFilterChain()); + $this->input->setFilterChain($chain); + $this->assertSame($chain, $this->input->getFilterChain()); } public function testCanInjectValidatorChain() { - $input = new FileInput('foo'); $chain = new Validator\ValidatorChain(); - $input->setValidatorChain($chain); - $this->assertSame($chain, $input->getValidatorChain()); + $this->input->setValidatorChain($chain); + $this->assertSame($chain, $this->input->getValidatorChain()); } public function testInputIsMarkedAsRequiredByDefault() { - $input = new FileInput('foo'); - $this->assertTrue($input->isRequired()); + $this->assertTrue($this->input->isRequired()); } public function testRequiredFlagIsMutable() { - $input = new FileInput('foo'); - $input->setRequired(false); - $this->assertFalse($input->isRequired()); + $this->input->setRequired(false); + $this->assertFalse($this->input->isRequired()); } public function testInputDoesNotAllowEmptyValuesByDefault() { - $input = new FileInput('foo'); - $this->assertFalse($input->allowEmpty()); + $this->assertFalse($this->input->allowEmpty()); } public function testAllowEmptyFlagIsMutable() { - $input = new FileInput('foo'); - $input->setAllowEmpty(true); - $this->assertTrue($input->allowEmpty()); + $this->input->setAllowEmpty(true); + $this->assertTrue($this->input->allowEmpty()); } public function testValueIsNullByDefault() { - $input = new FileInput('foo'); - $this->assertNull($input->getValue()); + $this->assertNull($this->input->getValue()); } public function testValueMayBeInjected() { - $input = new FileInput('foo'); - $input->setValue('bar'); - $this->assertEquals('bar', $input->getValue()); + $this->input->setValue(array('tmp_name' => 'bar')); + $this->assertEquals('bar', $this->input->getValue()); } public function testRetrievingValueFiltersTheValueOnlyAfterValidating() { - $input = new FileInput('foo'); - $input->setValue('bar'); + $this->input->setValue(array('tmp_name' => 'bar')); $filter = new Filter\StringToUpper(); - $input->getFilterChain()->attach($filter); - $this->assertEquals('bar', $input->getValue()); - $this->assertTrue($input->isValid()); - $this->assertEquals('BAR', $input->getValue()); - } - - public function testCanFilterArrayOfStrings() - { - $input = new FileInput('foo'); - $values = array('foo', 'bar', 'baz'); - $input->setValue($values); - $filter = new Filter\StringToUpper(); - $input->getFilterChain()->attach($filter); - $this->assertEquals($values, $input->getValue()); - $this->assertTrue($input->isValid()); - $this->assertEquals(array('FOO', 'BAR', 'BAZ'), $input->getValue()); + $this->input->getFilterChain()->attach($filter); + $this->assertEquals('bar', $this->input->getValue()); + $this->assertTrue($this->input->isValid()); + $this->assertEquals('BAR', $this->input->getValue()); } public function testCanFilterArrayOfFileData() { - $input = new FileInput('foo'); $value = array('tmp_name' => 'foo'); - $input->setValue($value); + $this->input->setValue($value); $filter = new Filter\StringToUpper(); - $input->getFilterChain()->attach($filter); - $this->assertEquals('foo', $input->getValue()); - $this->assertTrue($input->isValid()); - $this->assertEquals('FOO', $input->getValue()); + $this->input->getFilterChain()->attach($filter); + $this->assertEquals('foo', $this->input->getValue()); + $this->assertTrue($this->input->isValid()); + $this->assertEquals('FOO', $this->input->getValue()); } public function testCanFilterArrayOfMultiFileData() { - $input = new FileInput('foo'); $values = array( array('tmp_name' => 'foo'), array('tmp_name' => 'bar'), array('tmp_name' => 'baz'), ); - $input->setValue($values); + $this->input->setValue($values); $filter = new Filter\StringToUpper(); - $input->getFilterChain()->attach($filter); - $this->assertEquals(array('foo', 'bar', 'baz'), $input->getValue()); - $this->assertTrue($input->isValid()); - $this->assertEquals(array('FOO', 'BAR', 'BAZ'), $input->getValue()); + $this->input->getFilterChain()->attach($filter); + $this->assertEquals(array('foo', 'bar', 'baz'), $this->input->getValue()); + $this->assertTrue($this->input->isValid()); + $this->assertEquals(array('FOO', 'BAR', 'BAZ'), $this->input->getValue()); } public function testCanRetrieveRawValue() { - $input = new FileInput('foo'); - $input->setValue('bar'); + $value = array('tmp_name' => 'bar'); + $this->input->setValue($value); $filter = new Filter\StringToUpper(); - $input->getFilterChain()->attach($filter); - $this->assertEquals('bar', $input->getRawValue()); + $this->input->getFilterChain()->attach($filter); + $this->assertEquals($value, $this->input->getRawValue()); } public function testIsValidReturnsFalseIfValidationChainFails() { - $input = new FileInput('foo'); - $input->setValue('bar'); + $this->input->setValue(array('tmp_name' => 'bar')); $validator = new Validator\Digits(); - $input->getValidatorChain()->addValidator($validator); - $this->assertFalse($input->isValid()); + $this->input->getValidatorChain()->addValidator($validator); + $this->assertFalse($this->input->isValid()); } public function testIsValidReturnsTrueIfValidationChainSucceeds() { - $input = new FileInput('foo'); - $input->setValue('123'); - $validator = new Validator\Digits(); - $input->getValidatorChain()->addValidator($validator); - $this->assertTrue($input->isValid()); + $this->input->setValue(array('tmp_name' => 'bar')); + $validator = new Validator\NotEmpty(); + $this->input->getValidatorChain()->addValidator($validator); + $this->assertTrue($this->input->isValid()); } public function testValidationOperatesBeforeFiltering() { - $input = new FileInput('foo'); - $input->setValue(' 123 '); + $this->input->setValue(array( + 'tmp_name' => ' ' . __FILE__ . ' ', + 'name' => 'foo', + 'size' => 1, + 'error' => 0, + )); $filter = new Filter\StringTrim(); - $input->getFilterChain()->attach($filter); - $validator = new Validator\Digits(); - $input->getValidatorChain()->addValidator($validator); - $this->assertFalse($input->isValid()); - $input->setValue('123'); - $this->assertTrue($input->isValid()); + $this->input->getFilterChain()->attach($filter); + $validator = new Validator\File\Exists(); + $this->input->getValidatorChain()->addValidator($validator); + $this->assertFalse($this->input->isValid()); + $this->input->setValue(array( + 'tmp_name' => __FILE__, + 'name' => 'foo', + 'size' => 1, + 'error' => 0, + )); + $this->assertTrue($this->input->isValid()); } public function testGetMessagesReturnsValidationMessages() { - $input = new FileInput('foo'); - $input->setValue('bar'); - $validator = new Validator\Digits(); - $input->getValidatorChain()->addValidator($validator); - $this->assertFalse($input->isValid()); - $messages = $input->getMessages(); - $this->assertArrayHasKey(Validator\Digits::NOT_DIGITS, $messages); + $this->input->setAutoPrependUploadValidator(true); + $this->input->setValue(array( + 'tmp_name' => __FILE__, + 'name' => 'foo', + 'size' => 1, + 'error' => 0, + )); + $this->assertFalse($this->input->isValid()); + $messages = $this->input->getMessages(); + $this->assertArrayHasKey(Validator\File\Upload::ATTACK, $messages); + } + + public function testCanValidateArrayOfMultiFileData() + { + $values = array( + array( + 'tmp_name' => __FILE__, + 'name' => 'foo', + ), + array( + 'tmp_name' => __FILE__, + 'name' => 'bar', + ), + array( + 'tmp_name' => __FILE__, + 'name' => 'baz', + ), + ); + $this->input->setValue($values); + $validator = new Validator\File\Exists(); + $this->input->getValidatorChain()->addValidator($validator); + $this->assertTrue($this->input->isValid()); + + // Negative test + $values[1]['tmp_name'] = 'file-not-found'; + $this->input->setValue($values); + $this->assertFalse($this->input->isValid()); } public function testSpecifyingMessagesToInputReturnsThoseOnFailedValidation() { - $input = new FileInput('foo'); - $input->setValue('bar'); + $this->input->setValue(array('tmp_name' => 'bar')); $validator = new Validator\Digits(); - $input->getValidatorChain()->addValidator($validator); - $input->setErrorMessage('Please enter only digits'); - $this->assertFalse($input->isValid()); - $messages = $input->getMessages(); + $this->input->getValidatorChain()->addValidator($validator); + $this->input->setErrorMessage('Please enter only digits'); + $this->assertFalse($this->input->isValid()); + $messages = $this->input->getMessages(); $this->assertArrayNotHasKey(Validator\Digits::NOT_DIGITS, $messages); $this->assertContains('Please enter only digits', $messages); } public function testBreakOnFailureFlagIsOffByDefault() { - $input = new FileInput('foo'); - $this->assertFalse($input->breakOnFailure()); + $this->assertFalse($this->input->breakOnFailure()); } public function testBreakOnFailureFlagIsMutable() { - $input = new FileInput('foo'); - $input->setBreakOnFailure(true); - $this->assertTrue($input->breakOnFailure()); + $this->input->setBreakOnFailure(true); + $this->assertTrue($this->input->breakOnFailure()); } - public function testNotEmptyValidatorIsNotAddedWhenIsValidIsCalled() + public function testAutoPrependUploadValidatorIsOnByDefault() { $input = new FileInput('foo'); - $this->assertTrue($input->isRequired()); - $input->setValue(''); - $validatorChain = $input->getValidatorChain(); + $this->assertTrue($input->getAutoPrependUploadValidator()); + } + + public function testUploadValidatorIsAddedWhenIsValidIsCalled() + { + $this->input->setAutoPrependUploadValidator(true); + $this->assertTrue($this->input->getAutoPrependUploadValidator()); + $this->assertTrue($this->input->isRequired()); + $this->input->setValue(array( + 'tmp_name' => __FILE__, + 'name' => 'foo', + 'size' => 1, + 'error' => 0, + )); + $validatorChain = $this->input->getValidatorChain(); + $this->assertEquals(0, count($validatorChain->getValidators())); + + $this->assertFalse($this->input->isValid()); + $validators = $validatorChain->getValidators(); + $this->assertEquals(1, count($validators)); + $this->assertInstanceOf('Zend\Validator\File\Upload', $validators[0]['instance']); + } + + public function testUploadValidatorIsNotAddedWhenIsValidIsCalled() + { + $this->assertFalse($this->input->getAutoPrependUploadValidator()); + $this->assertTrue($this->input->isRequired()); + $this->input->setValue(array('tmp_name' => 'bar')); + $validatorChain = $this->input->getValidatorChain(); $this->assertEquals(0, count($validatorChain->getValidators())); - $this->assertTrue($input->isValid()); - $messages = $input->getMessages(); + $this->assertTrue($this->input->isValid()); $this->assertEquals(0, count($validatorChain->getValidators())); } - public function testRequiredNotEmptyValidatorNotAddedWhenOneExists() + public function testRequiredUploadValidatorValidatorNotAddedWhenOneExists() { - $input = new FileInput('foo'); - $this->assertTrue($input->isRequired()); - $input->setValue(''); + $this->input->setAutoPrependUploadValidator(true); + $this->assertTrue($this->input->getAutoPrependUploadValidator()); + $this->assertTrue($this->input->isRequired()); + $this->input->setValue(array('tmp_name' => 'bar')); - $notEmptyMock = $this->getMock('Zend\Validator\NotEmpty', array('isValid')); - $notEmptyMock->expects($this->exactly(1)) + $uploadMock = $this->getMock('Zend\Validator\File\Upload', array('isValid')); + $uploadMock->expects($this->exactly(1)) ->method('isValid') - ->will($this->returnValue(false)); + ->will($this->returnValue(true)); - $validatorChain = $input->getValidatorChain(); - $validatorChain->prependValidator($notEmptyMock); - $this->assertFalse($input->isValid()); + $validatorChain = $this->input->getValidatorChain(); + $validatorChain->prependValidator($uploadMock); + $this->assertTrue($this->input->isValid()); $validators = $validatorChain->getValidators(); $this->assertEquals(1, count($validators)); - $this->assertEquals($notEmptyMock, $validators[0]['instance']); + $this->assertEquals($uploadMock, $validators[0]['instance']); } public function testMerge() { + $value = array('tmp_name' => 'bar'); + $input = new FileInput('foo'); - $input->setValue(' 123 '); + $input->setAutoPrependUploadValidator(false); + $input->setValue($value); $filter = new Filter\StringTrim(); $input->getFilterChain()->attach($filter); $validator = new Validator\Digits(); @@ -269,7 +313,8 @@ public function testMerge() $validatorChain = $input->getValidatorChain(); $filterChain = $input->getFilterChain(); - $this->assertEquals(' 123 ', $input2->getRawValue()); + $this->assertFalse($input2->getAutoPrependUploadValidator()); + $this->assertEquals($value, $input2->getRawValue()); $this->assertEquals(1, $validatorChain->count()); $this->assertEquals(1, $filterChain->count());