diff --git a/src/BaseInputFilter.php b/src/BaseInputFilter.php index 4297873e..a407a566 100644 --- a/src/BaseInputFilter.php +++ b/src/BaseInputFilter.php @@ -165,6 +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) + // 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/src/FileInput.php b/src/FileInput.php new file mode 100644 index 00000000..52e2a02c --- /dev/null +++ b/src/FileInput.php @@ -0,0 +1,62 @@ +getFilterChain(); + $value = (is_array($this->value) && isset($this->value['tmp_name'])) + ? $this->value['tmp_name'] : $this->value; + if ($this->isValid) { + $value = $filter->filter($value); + } + return $value; + } + + /** + * @param mixed $context Extra "context" to provide the validator + * @return boolean + */ + public function isValid($context = null) + { + $this->injectNotEmptyValidator(); + $validator = $this->getValidatorChain(); + //$value = $this->getValue(); // Do not run the filters yet for File uploads + $this->isValid = $validator->isValid($this->getRawValue(), $context); + return $this->isValid; + } + + /** + * @return void + */ + protected function injectNotEmptyValidator() + { + $this->notEmptyValidator = true; + } +} 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) { diff --git a/test/BaseInputFilterTest.php b/test/BaseInputFilterTest.php index 2187e9e0..59fceab2 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,65 @@ 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 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(); 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]); + } +}