diff --git a/src/ArrayInput.php b/src/ArrayInput.php index 4d1dc7b4..08876f91 100644 --- a/src/ArrayInput.php +++ b/src/ArrayInput.php @@ -50,11 +50,18 @@ public function getValue() */ public function isValid($context = null) { - $this->injectNotEmptyValidator(); + if (!$this->continueIfEmpty() && !$this->allowEmpty()) { + $this->injectNotEmptyValidator(); + } $validator = $this->getValidatorChain(); $values = $this->getValue(); $result = true; foreach ($values as $value) { + $empty = ($value === null || $value === '' || $value === array()); + if ($empty && $this->allowEmpty() && !$this->continueIfEmpty()) { + $result = true; + continue; + } $result = $validator->isValid($value, $context); if (!$result) { if ($this->hasFallback()) { diff --git a/src/BaseInputFilter.php b/src/BaseInputFilter.php index 13dd9001..3e41a24c 100644 --- a/src/BaseInputFilter.php +++ b/src/BaseInputFilter.php @@ -189,10 +189,11 @@ public function setData($data) /** * Is the data set valid? * + * @param mixed|null $context * @throws Exception\RuntimeException * @return bool */ - public function isValid() + public function isValid($context = null) { $data = $this->getRawValues(); if (null === $data) { @@ -203,17 +204,18 @@ public function isValid() } $inputs = $this->validationGroup ?: array_keys($this->inputs); - return $this->validateInputs($inputs, $data); + return $this->validateInputs($inputs, $data, $context); } /** * Validate a set of inputs against the current data * - * @param array $inputs - * @param array $data + * @param array $inputs + * @param array $data + * @param mixed|null $context * @return bool */ - protected function validateInputs(array $inputs, array $data = array()) + protected function validateInputs(array $inputs, array $data = array(), $context = null) { // backwards compatibility if (empty($data)) { @@ -226,109 +228,15 @@ protected function validateInputs(array $inputs, array $data = array()) foreach ($inputs as $name) { $input = $this->inputs[$name]; - $dataExists = array_key_exists($name, $data); - - // key doesn't exist, but input is not required; valid - if (!$dataExists - && $input instanceof InputInterface - && !$input->isRequired() - ) { - $this->validInputs[$name] = $input; - continue; - } - - // key doesn't exist, input is required, allows empty; valid if - // continueIfEmpty is false or input doesn't implement - // that interface; otherwise validation chain continues - if (!$dataExists - && $input instanceof InputInterface - && $input->isRequired() - && $input->allowEmpty() - ) { - if (!($input instanceof EmptyContextInterface && $input->continueIfEmpty())) { - $this->validInputs[$name] = $input; - continue; - } - } - - // key exists, is null, input is not required; valid - if ($dataExists - && null === $data[$name] - && $input instanceof InputInterface - && !$input->isRequired() - ) { - $this->validInputs[$name] = $input; - continue; - } - - // key exists, is null, input is required, allows empty; valid if - // continueIfEmpty is false or input doesn't implement - // that interface; otherwise validation chain continues - if ($dataExists - && null === $data[$name] - && $input instanceof InputInterface - && $input->isRequired() - && $input->allowEmpty() - ) { - if (!($input instanceof EmptyContextInterface && $input->continueIfEmpty())) { - $this->validInputs[$name] = $input; - continue; - } - } - - // key exists, empty string, input is not required, allows empty; valid - if ($dataExists - && '' === $data[$name] - && $input instanceof InputInterface - && !$input->isRequired() - && $input->allowEmpty() - ) { - $this->validInputs[$name] = $input; - continue; - } - - // key exists, empty string, input is required, allows empty; valid - // if continueIfEmpty is false, otherwise validation continues - if ($dataExists - && '' === $data[$name] - && $input instanceof InputInterface - && $input->isRequired() - && $input->allowEmpty() - ) { - if (!($input instanceof EmptyContextInterface && $input->continueIfEmpty())) { - $this->validInputs[$name] = $input; - continue; - } - } - // key exists, is array representing file, no file present, input not - // required or allows empty; valid - if ($dataExists - && is_array($data[$name]) - && ( - (isset($data[$name]['error']) - && $data[$name]['error'] === UPLOAD_ERR_NO_FILE) - || (count($data[$name]) === 1 - && isset($data[$name][0]) - && is_array($data[$name][0]) - && isset($data[$name][0]['error']) - && $data[$name][0]['error'] === UPLOAD_ERR_NO_FILE) - ) - && $input instanceof InputInterface - && (!$input->isRequired() || $input->allowEmpty()) - ) { - $this->validInputs[$name] = $input; - continue; - } - - // make sure we have a value (empty) for validation - if (!$dataExists) { + // 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()) { + if (!$input->isValid($context)) { $this->invalidInputs[$name] = $input; $valid = false; continue; @@ -339,7 +247,9 @@ protected function validateInputs(array $inputs, array $data = array()) // Validate an input if ($input instanceof InputInterface) { - if (!$input->isValid($data)) { + $inputContext = $context ?: $data; + + if (!$input->isValid($inputContext)) { // Validation failure $this->invalidInputs[$name] = $input; $valid = false; @@ -531,6 +441,7 @@ public function getRawValues() $values[$name] = $input->getRawValues(); continue; } + $values[$name] = $input->getRawValue(); } return $values; @@ -679,4 +590,18 @@ public function getInputs() { return $this->inputs; } + + /** + * Merges the inputs from an InputFilter into the current one + * + * @param BaseInputFilter $inputFilter + * + * @return self + */ + public function merge(BaseInputFilter $inputFilter) + { + foreach ($inputFilter->getInputs() as $name => $input) { + $this->add($input, $name); + } + } } diff --git a/src/Factory.php b/src/Factory.php index ff179651..244233ec 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -145,13 +145,17 @@ public function getInputFilterManager() /** * Factory for input objects * - * @param array|Traversable $inputSpecification + * @param array|Traversable|InputProviderInterface $inputSpecification * @throws Exception\InvalidArgumentException * @throws Exception\RuntimeException * @return InputInterface|InputFilterInterface */ public function createInput($inputSpecification) { + if ($inputSpecification instanceof InputProviderInterface) { + $inputSpecification = $inputSpecification->getInputSpecification(); + } + if (!is_array($inputSpecification) && !$inputSpecification instanceof Traversable) { throw new Exception\InvalidArgumentException(sprintf( '%s expects an array or Traversable; received "%s"', @@ -269,13 +273,17 @@ public function createInput($inputSpecification) /** * Factory for input filters * - * @param array|Traversable $inputFilterSpecification + * @param array|Traversable|InputFilterProviderInterface $inputFilterSpecification * @throws Exception\InvalidArgumentException * @throws Exception\RuntimeException * @return InputFilterInterface */ public function createInputFilter($inputFilterSpecification) { + if ($inputFilterSpecification instanceof InputFilterProviderInterface) { + $inputFilterSpecification = $inputFilterSpecification->getInputFilterSpecification(); + } + if (!is_array($inputFilterSpecification) && !$inputFilterSpecification instanceof Traversable) { throw new Exception\InvalidArgumentException(sprintf( '%s expects an array or Traversable; received "%s"', diff --git a/src/FileInput.php b/src/FileInput.php index 223d7cb9..3a11f934 100644 --- a/src/FileInput.php +++ b/src/FileInput.php @@ -83,16 +83,46 @@ public function getValue() return $value; } + /** + * Checks if the raw input value is an empty file input eg: no file was uploaded + * + * @param $rawValue + * @return bool + */ + public function isEmptyFile($rawValue) + { + if (!is_array($rawValue)) { + return true; + } + + if (isset($rawValue['error']) && $rawValue['error'] === UPLOAD_ERR_NO_FILE) { + return true; + } + + if (count($rawValue) === 1 && isset($rawValue[0])) { + return $this->isEmptyFile($rawValue[0]); + } + + return false; + } + /** * @param mixed $context Extra "context" to provide the validator * @return bool */ public function isValid($context = null) { + $rawValue = $this->getRawValue(); + $empty = $this->isEmptyFile($rawValue); + + if ($empty && $this->allowEmpty() && !$this->continueIfEmpty()) { + return true; + } + $this->injectUploadValidator(); $validator = $this->getValidatorChain(); //$value = $this->getValue(); // Do not run the filters yet for File uploads (see getValue()) - $rawValue = $this->getRawValue(); + if (!is_array($rawValue)) { // This can happen in an AJAX POST, where the input comes across as a string $rawValue = array( diff --git a/src/Input.php b/src/Input.php index a5dae88f..665af421 100644 --- a/src/Input.php +++ b/src/Input.php @@ -319,14 +319,21 @@ public function merge(InputInterface $input) */ public function isValid($context = null) { + $value = $this->getValue(); + $empty = ($value === null || $value === '' || $value === array()); + + if ($empty && $this->allowEmpty() && !$this->continueIfEmpty()) { + return true; + } + // Empty value needs further validation if continueIfEmpty is set // so don't inject NotEmpty validator which would always // mark that as false - if (!$this->continueIfEmpty()) { + if (!$this->continueIfEmpty() && !$this->allowEmpty()) { $this->injectNotEmptyValidator(); } $validator = $this->getValidatorChain(); - $value = $this->getValue(); + $result = $validator->isValid($value, $context); if (!$result && $this->hasFallback()) { $this->setValue($this->getFallbackValue()); @@ -372,7 +379,14 @@ protected function injectNotEmptyValidator() } } - $chain->prependByName('NotEmpty', array(), true); $this->notEmptyValidator = true; + + if (class_exists('Zend\ServiceManager\AbstractPluginManager')) { + $chain->prependByName('NotEmpty', array(), true); + + return; + } + + $chain->prependValidator(new NotEmpty(), true); } } diff --git a/src/InputFilterAbstractServiceFactory.php b/src/InputFilterAbstractServiceFactory.php new file mode 100644 index 00000000..19a53b7f --- /dev/null +++ b/src/InputFilterAbstractServiceFactory.php @@ -0,0 +1,112 @@ +getServiceLocator(); + if (! $services instanceof ServiceLocatorInterface + || ! $services->has('Config') + ) { + return false; + } + + $config = $services->get('Config'); + if (!isset($config['input_filter_specs'][$rName]) + || !is_array($config['input_filter_specs'][$rName]) + ) { + return false; + } + + return true; + } + + /** + * @param ServiceLocatorInterface $inputFilters + * @param string $cName + * @param string $rName + * @return \Zend\InputFilter\InputFilterInterface + */ + public function createServiceWithName(ServiceLocatorInterface $inputFilters, $cName, $rName) + { + $services = $inputFilters->getServiceLocator(); + $allConfig = $services->get('Config'); + $config = $allConfig['input_filter_specs'][$rName]; + + $factory = $this->getInputFilterFactory($services); + + return $factory->createInputFilter($config); + } + + /** + * @param ServiceLocatorInterface $services + * @return Factory + */ + protected function getInputFilterFactory(ServiceLocatorInterface $services) + { + if ($this->factory instanceof Factory) { + return $this->factory; + } + + $this->factory = new Factory(); + $this->factory + ->getDefaultFilterChain() + ->setPluginManager($this->getFilterPluginManager($services)); + $this->factory + ->getDefaultValidatorChain() + ->setPluginManager($this->getValidatorPluginManager($services)); + + return $this->factory; + } + + /** + * @param ServiceLocatorInterface $services + * @return FilterPluginManager + */ + protected function getFilterPluginManager(ServiceLocatorInterface $services) + { + if ($services->has('FilterManager')) { + return $services->get('FilterManager'); + } + + return new FilterPluginManager(); + } + + /** + * @param ServiceLocatorInterface $services + * @return ValidatorPluginManager + */ + protected function getValidatorPluginManager(ServiceLocatorInterface $services) + { + if ($services->has('ValidatorManager')) { + return $services->get('ValidatorManager'); + } + + return new ValidatorPluginManager(); + } +} diff --git a/src/InputInterface.php b/src/InputInterface.php index 134b06d8..14b0529a 100644 --- a/src/InputInterface.php +++ b/src/InputInterface.php @@ -14,26 +14,112 @@ interface InputInterface { + /** + * @param bool $allowEmpty + * @return self + */ public function setAllowEmpty($allowEmpty); + + /** + * @param bool $breakOnFailure + * @return self + */ public function setBreakOnFailure($breakOnFailure); + + /** + * @param string|null $errorMessage + * @return self + */ public function setErrorMessage($errorMessage); + + /** + * @param FilterChain $filterChain + * @return self + */ public function setFilterChain(FilterChain $filterChain); + + /** + * @param string $name + * @return self + */ public function setName($name); + + /** + * @param bool $required + * @return self + */ public function setRequired($required); + + /** + * @param ValidatorChain $validatorChain + * @return self + */ public function setValidatorChain(ValidatorChain $validatorChain); + + /** + * @param mixed $value + * @return self + */ public function setValue($value); + + /** + * @param InputInterface $input + * @return self + */ public function merge(InputInterface $input); + /** + * @return bool + */ public function allowEmpty(); + + /** + * @return bool + */ public function breakOnFailure(); + + /** + * @return string|null + */ public function getErrorMessage(); + + /** + * @return FilterChain + */ public function getFilterChain(); + + /** + * @return string + */ public function getName(); + + /** + * @return mixed + */ public function getRawValue(); + + /** + * @return bool + */ public function isRequired(); + + /** + * @return ValidatorChain + */ public function getValidatorChain(); + + /** + * @return mixed + */ public function getValue(); + /** + * @return bool + */ public function isValid(); + + /** + * @return array + */ public function getMessages(); } diff --git a/test/ArrayInputTest.php b/test/ArrayInputTest.php index a9c933be..7268d677 100644 --- a/test/ArrayInputTest.php +++ b/test/ArrayInputTest.php @@ -219,4 +219,31 @@ public function testFallbackValue($fallbackValue) $this->assertEmpty($this->input->getMessages()); $this->assertSame($fallbackValue, $this->input->getValue()); } + + public function emptyValuesProvider() + { + return array( + array(array(null)), + array(array('')), + array(array(array())), + ); + } + + public function testNotAllowEmptyWithFilterConvertsNonemptyToEmptyIsNotValid() + { + $this->input->setValue(array('nonempty')) + ->getFilterChain()->attach(new Filter\Callback(function () { + return ''; + })); + $this->assertFalse($this->input->isValid()); + } + + public function testNotAllowEmptyWithFilterConvertsEmptyToNonEmptyIsValid() + { + $this->input->setValue(array('')) + ->getFilterChain()->attach(new Filter\Callback(function () { + return 'nonempty'; + })); + $this->assertTrue($this->input->isValid()); + } } diff --git a/test/BaseInputFilterTest.php b/test/BaseInputFilterTest.php index f0c1ffa2..0edb5d8c 100644 --- a/test/BaseInputFilterTest.php +++ b/test/BaseInputFilterTest.php @@ -661,7 +661,7 @@ public function testValidationMarksInputValidWhenAllowEmptyFlagIsTrueAndContinue { $filter = new InputFilter(); - $data = array ( + $data = array( 'allowEmpty' => $allowEmpty, 'blankIsValid' => $blankIsValid, ); @@ -912,4 +912,72 @@ public function testPopulateSupportsArrayInputEvenIfDataMissing() $filter->add($arrayInput, 'arrayInput'); $filter->setData(array('foo' => 'bar')); } + + /** + * @group 6431 + */ + public function testMerge() + { + $inputFilter = new InputFilter(); + $originInputFilter = new InputFilter(); + + $inputFilter->add(new Input(), 'foo'); + $inputFilter->add(new Input(), 'bar'); + + $originInputFilter->add(new Input(), 'baz'); + + $inputFilter->merge($originInputFilter); + + $this->assertEquals( + array( + 'foo', + 'bar', + 'baz' + ), + array_keys($inputFilter->getInputs()) + ); + } + + public function testAllowEmptyTestsFilteredValueAndOverrulesValidatorChain() + { + $input = new Input('foo'); + $input->setAllowEmpty(true); + $input->setContinueIfEmpty(false); + // Filter chain produces empty value which should be evaluated instead of raw value + $input->getFilterChain()->attach(new Filter\Callback(function () { + return ''; + })); + // Validator chain says "not valid", but should not be invoked at all + $input->getValidatorChain()->attach(new Validator\Callback(function () { + return false; + })); + + $filter = new \Zend\InputFilter\InputFilter; + $filter->add($input) + ->setData(array('foo' => 'nonempty')); + + $this->assertTrue($filter->isValid()); + $this->assertEquals(array('foo' => ''), $filter->getValues()); + } + + public function testAllowEmptyTestsFilteredValueAndContinuesIfEmpty() + { + $input = new Input('foo'); + $input->setAllowEmpty(true); + $input->setContinueIfEmpty(true); + // Filter chain produces empty value which should be evaluated instead of raw value + $input->getFilterChain()->attach(new Filter\Callback(function () { + return ''; + })); + // Validator chain says "not valid", explicitly requested despite empty input + $input->getValidatorChain()->attach(new Validator\Callback(function () { + return false; + })); + + $filter = new \Zend\InputFilter\InputFilter; + $filter->add($input) + ->setData(array('foo' => 'nonempty')); + + $this->assertFalse($filter->isValid()); + } } diff --git a/test/FactoryTest.php b/test/FactoryTest.php index feac025a..c5afbcc2 100644 --- a/test/FactoryTest.php +++ b/test/FactoryTest.php @@ -610,4 +610,53 @@ public function testCanCreateInputFilterWithNullInputs() $this->assertFalse($inputFilter->has('bar')); $this->assertTrue($inputFilter->has('baz')); } + + /** + * @group 7010 + */ + public function testCanCreateInputFromProvider() + { + /* @group $provider \Zend\InputFilter\InputProviderInterface|\PHPUnit_Framework_MockObject_MockObject */ + $provider = $this->getMock('Zend\InputFilter\InputProviderInterface', array('getInputSpecification')); + + $provider + ->expects($this->any()) + ->method('getInputSpecification') + ->will($this->returnValue(array('name' => 'foo'))); + + $factory = new Factory(); + $input = $factory->createInput($provider); + + $this->assertInstanceOf('Zend\InputFilter\InputInterface', $input); + } + + /** + * @group 7010 + */ + public function testCanCreateInputFilterFromProvider() + { + /* @group $provider \Zend\InputFilter\InputFilterProviderInterface|\PHPUnit_Framework_MockObject_MockObject */ + $provider = $this->getMock( + 'Zend\InputFilter\InputFilterProviderInterface', + array('getInputFilterSpecification') + ); + $provider + ->expects($this->any()) + ->method('getInputFilterSpecification') + ->will($this->returnValue(array( + 'foo' => array( + 'name' => 'foo', + 'required' => false, + ), + 'baz' => array( + 'name' => 'baz', + 'required' => true, + ), + ))); + + $factory = new Factory(); + $inputFilter = $factory->createInputFilter($provider); + + $this->assertInstanceOf('Zend\InputFilter\InputFilterInterface', $inputFilter); + } } diff --git a/test/FileInputTest.php b/test/FileInputTest.php index 9d033601..27888b4c 100644 --- a/test/FileInputTest.php +++ b/test/FileInputTest.php @@ -344,4 +344,107 @@ public function testFallbackValue($fallbackValue = null) { $this->markTestSkipped('Not use fallback value'); } + + public function testIsEmptyFileNotArray() + { + $rawValue = 'file'; + $this->assertTrue($this->input->isEmptyFile($rawValue)); + } + + public function testIsEmptyFileUploadNoFile() + { + $rawValue = array( + 'tmp_name' => '', + 'error' => \UPLOAD_ERR_NO_FILE, + ); + $this->assertTrue($this->input->isEmptyFile($rawValue)); + } + + public function testIsEmptyFileOk() + { + $rawValue = array( + 'tmp_name' => 'name', + 'error' => \UPLOAD_ERR_OK, + ); + $this->assertFalse($this->input->isEmptyFile($rawValue)); + } + + public function testIsEmptyMultiFileUploadNoFile() + { + $rawValue = array(array( + 'tmp_name' => 'foo', + 'error' => \UPLOAD_ERR_NO_FILE + )); + $this->assertTrue($this->input->isEmptyFile($rawValue)); + } + + public function testIsEmptyFileMultiFileOk() + { + $rawValue = array( + array( + 'tmp_name' => 'foo', + 'error' => \UPLOAD_ERR_OK + ), + array( + 'tmp_name' => 'bar', + 'error' => \UPLOAD_ERR_OK + ), + ); + $this->assertFalse($this->input->isEmptyFile($rawValue)); + } + + public function emptyValuesProvider() + { + // Provide empty values specific for file input + return array( + array('file'), + array(array( + 'tmp_name' => '', + 'error' => \UPLOAD_ERR_NO_FILE, + )), + array(array(array( + 'tmp_name' => 'foo', + 'error' => \UPLOAD_ERR_NO_FILE + ))), + ); + } + + /** + * @dataProvider emptyValuesProvider + */ + public function testAllowEmptyOptionSet($emptyValue) + { + // UploadFile validator is disabled, pretend one + $validator = new Validator\Callback(function () { + return false; // This should never be called + }); + $this->input->getValidatorChain()->attach($validator); + parent::testAllowEmptyOptionSet($emptyValue); + } + + /** + * @dataProvider emptyValuesProvider + */ + public function testAllowEmptyOptionNotSet($emptyValue) + { + // UploadFile validator is disabled, pretend one + $message = 'pretend failing UploadFile validator'; + $validator = new Validator\Callback(function () { + return false; + }); + $validator->setMessage($message); + $this->input->getValidatorChain()->attach($validator); + parent::testAllowEmptyOptionNotSet($emptyValue); + $this->assertEquals(array('callbackValue' => $message), $this->input->getMessages()); + } + + public function testNotAllowEmptyWithFilterConvertsNonemptyToEmptyIsNotValid() + { + $this->markTestSkipped('does not apply to FileInput'); + } + + public function testNotAllowEmptyWithFilterConvertsEmptyToNonEmptyIsValid() + { + $this->markTestSkipped('does not apply to FileInput'); + } } diff --git a/test/InputFilterAbstractServiceFactoryTest.php b/test/InputFilterAbstractServiceFactoryTest.php new file mode 100644 index 00000000..0646044d --- /dev/null +++ b/test/InputFilterAbstractServiceFactoryTest.php @@ -0,0 +1,156 @@ +services = new ServiceManager(); + $this->filters = new InputFilterPluginManager(); + $this->filters->setServiceLocator($this->services); + $this->services->setService('InputFilterManager', $this->filters); + + $this->factory = new InputFilterAbstractServiceFactory(); + } + + public function testCannotCreateServiceIfNoConfigServicePresent() + { + $this->assertFalse($this->factory->canCreateServiceWithName($this->filters, 'filter', 'filter')); + } + + public function testCannotCreateServiceIfConfigServiceDoesNotHaveInputFiltersConfiguration() + { + $this->services->setService('Config', array()); + $this->assertFalse($this->factory->canCreateServiceWithName($this->filters, 'filter', 'filter')); + } + + public function testCannotCreateServiceIfConfigInputFiltersDoesNotContainMatchingServiceName() + { + $this->services->setService('Config', array( + 'input_filter_specs' => array(), + )); + $this->assertFalse($this->factory->canCreateServiceWithName($this->filters, 'filter', 'filter')); + } + + public function testCanCreateServiceIfConfigInputFiltersContainsMatchingServiceName() + { + $this->services->setService('Config', array( + 'input_filter_specs' => array( + 'filter' => array(), + ), + )); + $this->assertTrue($this->factory->canCreateServiceWithName($this->filters, 'filter', 'filter')); + } + + public function testCreatesInputFilterInstance() + { + $this->services->setService('Config', array( + 'input_filter_specs' => array( + 'filter' => array(), + ), + )); + $filter = $this->factory->createServiceWithName($this->filters, 'filter', 'filter'); + $this->assertInstanceOf('Zend\InputFilter\InputFilterInterface', $filter); + } + + /** + * @depends testCreatesInputFilterInstance + */ + public function testUsesConfiguredValidationAndFilterManagerServicesWhenCreatingInputFilter() + { + $filters = new FilterPluginManager(); + $filter = function ($value) { + }; + $filters->setService('foo', $filter); + + $validators = new ValidatorPluginManager(); + $validator = $this->getMock('Zend\Validator\ValidatorInterface'); + $validators->setService('foo', $validator); + + $this->services->setService('FilterManager', $filters); + $this->services->setService('ValidatorManager', $validators); + $this->services->setService('Config', array( + 'input_filter_specs' => array( + 'filter' => array( + 'input' => array( + 'name' => 'input', + 'required' => true, + 'filters' => array( + array( 'name' => 'foo' ), + ), + 'validators' => array( + array( 'name' => 'foo' ), + ), + ), + ), + ), + )); + + $inputFilter = $this->factory->createServiceWithName($this->filters, 'filter', 'filter'); + $this->assertTrue($inputFilter->has('input')); + + $input = $inputFilter->get('input'); + + $filterChain = $input->getFilterChain(); + $this->assertSame($filters, $filterChain->getPluginManager()); + $this->assertEquals(1, count($filterChain)); + $this->assertSame($filter, $filterChain->plugin('foo')); + $this->assertEquals(1, count($filterChain)); + + $validatorChain = $input->getvalidatorChain(); + $this->assertSame($validators, $validatorChain->getPluginManager()); + $this->assertEquals(1, count($validatorChain)); + $this->assertSame($validator, $validatorChain->plugin('foo')); + $this->assertEquals(1, count($validatorChain)); + } + + public function testRetrieveInputFilterFromInputFilterPluginManager() + { + $filters = new FilterPluginManager(); + $filter = function ($value) { + }; + $filters->setService('foo', $filter); + + $validators = new ValidatorPluginManager(); + $validator = $this->getMock('Zend\Validator\ValidatorInterface'); + $validators->setService('foo', $validator); + + $this->services->setService('FilterManager', $filters); + $this->services->setService('ValidatorManager', $validators); + $this->services->setService('Config', array( + 'input_filter_specs' => array( + 'foobar' => array( + 'input' => array( + 'name' => 'input', + 'required' => true, + 'filters' => array( + array( 'name' => 'foo' ), + ), + 'validators' => array( + array( 'name' => 'foo' ), + ), + ), + ), + ), + )); + $this->services->get('InputFilterManager')->addAbstractFactory('Zend\InputFilter\InputFilterAbstractServiceFactory'); + + $inputFilter = $this->services->get('InputFilterManager')->get('foobar'); + $this->assertInstanceOf('Zend\InputFilter\InputFilterInterface', $inputFilter); + } +} diff --git a/test/InputFilterTest.php b/test/InputFilterTest.php index b35ee27c..99a16dec 100644 --- a/test/InputFilterTest.php +++ b/test/InputFilterTest.php @@ -18,6 +18,11 @@ class InputFilterTest extends TestCase { + /** + * @var InputFilter + */ + protected $filter; + public function setUp() { $this->filter = new InputFilter(); @@ -96,4 +101,18 @@ public function testCountZeroValidateInternalInputWithCollectionInputFilter() $this->assertTrue($this->filter->isvalid()); $this->assertSame($data, $this->filter->getValues()); } + + public function testCanUseContextPassedToInputFilter() + { + $context = new \stdClass(); + + $input = $this->getMock('Zend\InputFilter\InputInterface'); + $input->expects($this->once())->method('isValid')->with($context)->will($this->returnValue(true)); + $input->expects($this->any())->method('getRawValue')->will($this->returnValue('Mwop')); + + $this->filter->add($input, 'username'); + $this->filter->setData(array('username' => 'Mwop')); + + $this->filter->isValid($context); + } } diff --git a/test/InputTest.php b/test/InputTest.php index 7f603a8c..6113f263 100644 --- a/test/InputTest.php +++ b/test/InputTest.php @@ -227,6 +227,87 @@ public function testRequiredNotEmptyValidatorNotAddedWhenOneExists() $this->assertEquals($notEmptyMock, $validators[0]['instance']); } + public function emptyValuesProvider() + { + return array( + array(null), + array(''), + array(array()), + ); + } + + /** + * @dataProvider emptyValuesProvider + */ + public function testValidatorSkippedIfValueIsEmptyAndAllowedAndNotContinue($emptyValue) + { + $this->input->setAllowEmpty(true) + ->setContinueIfEmpty(false) + ->setValue($emptyValue) + ->getValidatorChain()->attach(new Validator\Callback(function () { + return false; + })); + $this->assertTrue($this->input->isValid()); + } + + /** + * @dataProvider emptyValuesProvider + */ + public function testAllowEmptyOptionSet($emptyValue) + { + $this->input->setAllowEmpty(true); + $this->input->setValue($emptyValue); + $this->assertTrue($this->input->isValid()); + } + + /** + * @dataProvider emptyValuesProvider + */ + public function testAllowEmptyOptionNotSet($emptyValue) + { + $this->input->setAllowEmpty(false); + $this->input->setValue($emptyValue); + $this->assertFalse($this->input->isValid()); + } + + /** + * @dataProvider emptyValuesProvider + */ + public function testValidatorInvokedIfValueIsEmptyAndAllowedAndContinue($emptyValue) + { + $message = 'failure by explicit validator'; + $validator = new Validator\Callback(function ($value) { + return false; + }); + $validator->setMessage($message); + $this->input->setAllowEmpty(true) + ->setContinueIfEmpty(true) + ->setValue($emptyValue) + ->getValidatorChain()->attach($validator); + $this->assertFalse($this->input->isValid()); + // Test reason for validation failure; ensures that failure was not + // caused by accidentally injected NotEmpty validator + $this->assertEquals(array('callbackValue' => $message), $this->input->getMessages()); + } + + public function testNotAllowEmptyWithFilterConvertsNonemptyToEmptyIsNotValid() + { + $this->input->setValue('nonempty') + ->getFilterChain()->attach(new Filter\Callback(function () { + return ''; + })); + $this->assertFalse($this->input->isValid()); + } + + public function testNotAllowEmptyWithFilterConvertsEmptyToNonEmptyIsValid() + { + $this->input->setValue('') + ->getFilterChain()->attach(new Filter\Callback(function () { + return 'nonempty'; + })); + $this->assertTrue($this->input->isValid()); + } + public function testMerge() { $input = new Input('foo');