├── src ├── Exception │ ├── ExceptionInterface.php │ ├── RuntimeException.php │ └── InvalidArgumentException.php ├── InputProviderInterface.php ├── InputFilterProviderInterface.php ├── ReplaceableInputInterface.php ├── UnfilteredDataInterface.php ├── InputFilterAwareInterface.php ├── UnknownInputsCapableInterface.php ├── EmptyContextInterface.php ├── InputFilterAwareTrait.php ├── FileInput │ ├── FileInputDecoratorInterface.php │ ├── PsrFileInputDecorator.php │ └── HttpServerFileInputDecorator.php ├── Module.php ├── ConfigProvider.php ├── InputFilter.php ├── OptionalInputFilter.php ├── InputFilterPluginManagerFactory.php ├── InputInterface.php ├── ArrayInput.php ├── InputFilterInterface.php ├── InputFilterAbstractServiceFactory.php ├── InputFilterPluginManager.php ├── FileInput.php ├── CollectionInputFilter.php ├── Input.php ├── Factory.php └── BaseInputFilter.php ├── README.md ├── LICENSE.md ├── composer.json └── CHANGELOG.md /src/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | inputFilter = $inputFilter; 28 | 29 | return $this; 30 | } 31 | 32 | /** 33 | * Retrieve input filter 34 | * 35 | * @return InputFilterInterface 36 | */ 37 | public function getInputFilter() 38 | { 39 | return $this->inputFilter; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zend-inputfilter 2 | 3 | > ## Repository abandoned 2019-12-31 4 | > 5 | > This repository has moved to [laminas/laminas-inputfilter](https://github.com/laminas/laminas-inputfilter). 6 | 7 | [![Build Status](https://secure.travis-ci.org/zendframework/zend-inputfilter.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-inputfilter) 8 | [![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-inputfilter/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-inputfilter?branch=master) 9 | 10 | The zend-inputfilter component can be used to filter and validate generic sets 11 | of input data. For instance, you could use it to filter `$_GET` or `$_POST` 12 | values, CLI arguments, etc. 13 | 14 | ## Installation 15 | 16 | Run the following to install this library: 17 | 18 | ```bash 19 | $ composer require zendframework/zend-inputfilter 20 | ``` 21 | 22 | ## Documentation 23 | 24 | Browse the documentation online at https://docs.zendframework.com/zend-inputfilter/ 25 | 26 | ## Support 27 | 28 | * [Issues](https://github.com/zendframework/zend-inputfilter/issues/) 29 | * [Chat](https://zendframework-slack.herokuapp.com/) 30 | * [Forum](https://discourse.zendframework.com/) 31 | -------------------------------------------------------------------------------- /src/FileInput/FileInputDecoratorInterface.php: -------------------------------------------------------------------------------- 1 | $provider->getDependencyConfig(), 21 | 'input_filters' => $provider->getInputFilterConfig(), 22 | ]; 23 | } 24 | 25 | /** 26 | * Register a specification for the InputFilterManager with the ServiceListener. 27 | * 28 | * @param \Zend\ModuleManager\ModuleManager $moduleManager 29 | * @return void 30 | */ 31 | public function init($moduleManager) 32 | { 33 | $event = $moduleManager->getEvent(); 34 | $container = $event->getParam('ServiceManager'); 35 | $serviceListener = $container->get('ServiceListener'); 36 | 37 | $serviceListener->addServiceManager( 38 | 'InputFilterManager', 39 | 'input_filters', 40 | 'Zend\ModuleManager\Feature\InputFilterProviderInterface', 41 | 'getInputFilterConfig' 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ConfigProvider.php: -------------------------------------------------------------------------------- 1 | $this->getDependencyConfig(), 21 | 'input_filters' => $this->getInputFilterConfig(), 22 | ]; 23 | } 24 | 25 | /** 26 | * Return dependency mappings for this component. 27 | * 28 | * @return array 29 | */ 30 | public function getDependencyConfig() 31 | { 32 | return [ 33 | 'aliases' => [ 34 | 'InputFilterManager' => InputFilterPluginManager::class, 35 | ], 36 | 'factories' => [ 37 | InputFilterPluginManager::class => InputFilterPluginManagerFactory::class, 38 | ], 39 | ]; 40 | } 41 | 42 | /** 43 | * Get input filter configuration 44 | * 45 | * @return array 46 | */ 47 | public function getInputFilterConfig() 48 | { 49 | return [ 50 | 'abstract_factories' => [ 51 | InputFilterAbstractServiceFactory::class, 52 | ], 53 | ]; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2019, Zend Technologies USA, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | - Neither the name of Zend Technologies USA, Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/InputFilter.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 30 | return $this; 31 | } 32 | 33 | /** 34 | * Get factory to use when adding inputs and filters by spec 35 | * 36 | * Lazy-loads a Factory instance if none attached. 37 | * 38 | * @return Factory 39 | */ 40 | public function getFactory() 41 | { 42 | if (null === $this->factory) { 43 | $this->setFactory(new Factory()); 44 | } 45 | return $this->factory; 46 | } 47 | 48 | /** 49 | * Add an input to the input filter 50 | * 51 | * @param array|Traversable|InputInterface|InputFilterInterface $input 52 | * @param null|string $name 53 | * @return InputFilter 54 | */ 55 | public function add($input, $name = null) 56 | { 57 | if (is_array($input) 58 | || ($input instanceof Traversable && ! $input instanceof InputFilterInterface) 59 | ) { 60 | $factory = $this->getFactory(); 61 | $input = $factory->createInput($input); 62 | } 63 | return parent::add($input, $name); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/OptionalInputFilter.php: -------------------------------------------------------------------------------- 1 | setRequired(false) 17 | */ 18 | class OptionalInputFilter extends InputFilter 19 | { 20 | /** 21 | * Set data to use when validating and filtering 22 | * 23 | * @param iterable|mixed $data 24 | * must be a non-empty iterable in order trigger actual validation, else it is always valid 25 | * @throws Exception\InvalidArgumentException 26 | * @return InputFilterInterface 27 | */ 28 | public function setData($data) 29 | { 30 | return parent::setData($data ?: []); 31 | } 32 | 33 | /** 34 | * Run validation, or return true if the data was empty 35 | * 36 | * {@inheritDoc} 37 | */ 38 | public function isValid($context = null) 39 | { 40 | if ($this->data) { 41 | return parent::isValid($context); 42 | } 43 | 44 | return true; 45 | } 46 | 47 | /** 48 | * Return a list of filtered values, or null if the data was missing entirely 49 | * Null is returned instead of an empty array to prevent it being passed to a hydrator, 50 | * which would likely cause failures later on in your program 51 | * Fallbacks for the inputs are not respected by design 52 | * 53 | * @return array|null 54 | */ 55 | public function getValues() 56 | { 57 | return $this->data ? parent::getValues() : null; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zendframework/zend-inputfilter", 3 | "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", 4 | "license": "BSD-3-Clause", 5 | "keywords": [ 6 | "zf", 7 | "zendframework", 8 | "inputfilter" 9 | ], 10 | "support": { 11 | "docs": "https://docs.zendframework.com/zend-inputfilter/", 12 | "issues": "https://github.com/zendframework/zend-inputfilter/issues", 13 | "source": "https://github.com/zendframework/zend-inputfilter", 14 | "rss": "https://github.com/zendframework/zend-inputfilter/releases.atom", 15 | "chat": "https://zendframework-slack.herokuapp.com", 16 | "forum": "https://discourse.zendframework.com/c/questions/components" 17 | }, 18 | "require": { 19 | "php": "^5.6 || ^7.0", 20 | "zendframework/zend-filter": "^2.9.1", 21 | "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", 22 | "zendframework/zend-stdlib": "^2.7 || ^3.0", 23 | "zendframework/zend-validator": "^2.11" 24 | }, 25 | "require-dev": { 26 | "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", 27 | "psr/http-message": "^1.0", 28 | "zendframework/zend-coding-standard": "~1.0.0" 29 | }, 30 | "suggest": { 31 | "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Zend\\InputFilter\\": "src/" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "ZendTest\\InputFilter\\": "test/" 41 | } 42 | }, 43 | "config": { 44 | "sort-packages": true 45 | }, 46 | "extra": { 47 | "branch-alias": { 48 | "dev-master": "2.10.x-dev", 49 | "dev-develop": "2.11.x-dev" 50 | }, 51 | "zf": { 52 | "component": "Zend\\InputFilter", 53 | "config-provider": "Zend\\InputFilter\\ConfigProvider" 54 | } 55 | }, 56 | "scripts": { 57 | "check": [ 58 | "@cs-check", 59 | "@test" 60 | ], 61 | "cs-check": "phpcs", 62 | "cs-fix": "phpcbf", 63 | "test": "phpunit --colors=always", 64 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/InputFilterPluginManagerFactory.php: -------------------------------------------------------------------------------- 1 | has('ServiceListener')) { 36 | return $pluginManager; 37 | } 38 | 39 | // If we do not have a config service, nothing more to do 40 | if (! $container->has('config')) { 41 | return $pluginManager; 42 | } 43 | 44 | $config = $container->get('config'); 45 | 46 | // If we do not have input_filters configuration, nothing more to do 47 | if (! isset($config['input_filters']) || ! is_array($config['input_filters'])) { 48 | return $pluginManager; 49 | } 50 | 51 | // Wire service configuration for input_filters 52 | (new Config($config['input_filters']))->configureServiceManager($pluginManager); 53 | 54 | return $pluginManager; 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | * 60 | * @return InputFilterPluginManager 61 | */ 62 | public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null) 63 | { 64 | return $this($container, $requestedName ?: InputFilterPluginManager::class, $this->creationOptions); 65 | } 66 | 67 | /** 68 | * zend-servicemanager v2 support for invocation options. 69 | * 70 | * @param array $options 71 | * @return void 72 | */ 73 | public function setCreationOptions(array $options) 74 | { 75 | $this->creationOptions = $options; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/InputInterface.php: -------------------------------------------------------------------------------- 1 | value = []; 41 | $this->hasValue = false; 42 | return $this; 43 | } 44 | 45 | /** 46 | * @return array 47 | */ 48 | public function getValue() 49 | { 50 | $filter = $this->getFilterChain(); 51 | $result = []; 52 | foreach ($this->value as $key => $value) { 53 | $result[$key] = $filter->filter($value); 54 | } 55 | return $result; 56 | } 57 | 58 | /** 59 | * @param mixed $context Extra "context" to provide the validator 60 | * @return bool 61 | */ 62 | public function isValid($context = null) 63 | { 64 | $hasValue = $this->hasValue(); 65 | $required = $this->isRequired(); 66 | $hasFallback = $this->hasFallback(); 67 | 68 | if (! $hasValue && $hasFallback) { 69 | $this->setValue($this->getFallbackValue()); 70 | return true; 71 | } 72 | 73 | if (! $hasValue && $required) { 74 | if ($this->errorMessage === null) { 75 | $this->errorMessage = $this->prepareRequiredValidationFailureMessage(); 76 | } 77 | return false; 78 | } 79 | 80 | if (! $this->continueIfEmpty() && ! $this->allowEmpty()) { 81 | $this->injectNotEmptyValidator(); 82 | } 83 | $validator = $this->getValidatorChain(); 84 | $values = $this->getValue(); 85 | $result = true; 86 | 87 | if ($required && empty($values)) { 88 | if ($this->errorMessage === null) { 89 | $this->errorMessage = $this->prepareRequiredValidationFailureMessage(); 90 | } 91 | return false; 92 | } 93 | 94 | foreach ($values as $value) { 95 | $empty = ($value === null || $value === '' || $value === []); 96 | if ($empty && ! $this->isRequired() && ! $this->continueIfEmpty()) { 97 | $result = true; 98 | continue; 99 | } 100 | if ($empty && $this->allowEmpty() && ! $this->continueIfEmpty()) { 101 | $result = true; 102 | continue; 103 | } 104 | $result = $validator->isValid($value, $context); 105 | if (! $result) { 106 | if ($hasFallback) { 107 | $this->setValue($this->getFallbackValue()); 108 | return true; 109 | } 110 | break; 111 | } 112 | } 113 | 114 | return $result; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/InputFilterInterface.php: -------------------------------------------------------------------------------- 1 | getError() === UPLOAD_ERR_NO_FILE; 49 | } 50 | 51 | public function __construct(FileInput $subject) 52 | { 53 | $this->subject = $subject; 54 | } 55 | 56 | /** 57 | * @return UploadedFileInterface|UploadedFileInterface[] 58 | */ 59 | public function getValue() 60 | { 61 | $value = $this->subject->value; 62 | 63 | // Run filters ~after~ validation, so that is_uploaded_file() 64 | // validation is not affected by filters. 65 | if (! $this->subject->isValid) { 66 | return $value; 67 | } 68 | 69 | $filter = $this->subject->getFilterChain(); 70 | 71 | if (is_array($value)) { 72 | // Multi file input (multiple attribute set) 73 | $newValue = []; 74 | foreach ($value as $fileData) { 75 | $newValue[] = $filter->filter($fileData); 76 | } 77 | return $newValue; 78 | } 79 | 80 | // Single file input 81 | return $filter->filter($value); 82 | } 83 | 84 | /** 85 | * @param mixed $context Extra "context" to provide the validator 86 | * @return bool 87 | */ 88 | public function isValid($context = null) 89 | { 90 | $rawValue = $this->subject->getRawValue(); 91 | $validator = $this->injectUploadValidator($this->subject->getValidatorChain()); 92 | 93 | if (is_array($rawValue)) { 94 | // Multi file input (multiple attribute set) 95 | $this->subject->isValid = true; 96 | foreach ($rawValue as $value) { 97 | if (! $validator->isValid($value, $context)) { 98 | $this->subject->isValid = false; 99 | return false; // Do not continue processing files if validation fails 100 | } 101 | } 102 | return true; // We return early from the loop if validation fails 103 | } 104 | 105 | // Single file input 106 | $this->subject->isValid = $validator->isValid($rawValue, $context); 107 | return $this->subject->isValid; 108 | } 109 | 110 | /** 111 | * @return ValidatorChain 112 | */ 113 | protected function injectUploadValidator(ValidatorChain $chain) 114 | { 115 | if (! $this->subject->autoPrependUploadValidator) { 116 | return $chain; 117 | } 118 | 119 | // Check if Upload validator is already first in chain 120 | $validators = $chain->getValidators(); 121 | if (isset($validators[0]['instance']) 122 | && $validators[0]['instance'] instanceof UploadValidator 123 | ) { 124 | $this->subject->autoPrependUploadValidator = false; 125 | return $chain; 126 | } 127 | 128 | $chain->prependByName(UploadValidator::class, [], true); 129 | $this->subject->autoPrependUploadValidator = false; 130 | 131 | return $chain; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/InputFilterAbstractServiceFactory.php: -------------------------------------------------------------------------------- 1 | get('config'); 35 | $config = $allConfig['input_filter_specs'][$rName]; 36 | $factory = $this->getInputFilterFactory($services); 37 | 38 | return $factory->createInputFilter($config); 39 | } 40 | 41 | /** 42 | * 43 | * @param ContainerInterface $services 44 | * @param string $rName 45 | * @return bool 46 | */ 47 | public function canCreate(ContainerInterface $services, $rName) 48 | { 49 | if (! $services->has('config')) { 50 | return false; 51 | } 52 | 53 | $config = $services->get('config'); 54 | if (! isset($config['input_filter_specs'][$rName]) 55 | || ! is_array($config['input_filter_specs'][$rName]) 56 | ) { 57 | return false; 58 | } 59 | 60 | return true; 61 | } 62 | 63 | /** 64 | * Determine if we can create a service with name (v2) 65 | * 66 | * @param ServiceLocatorInterface $container 67 | * @param $name 68 | * @param $requestedName 69 | * @return bool 70 | */ 71 | public function canCreateServiceWithName(ServiceLocatorInterface $container, $name, $requestedName) 72 | { 73 | // v2 => may need to get parent service locator 74 | if ($container instanceof AbstractPluginManager) { 75 | $container = $container->getServiceLocator() ?: $container; 76 | } 77 | 78 | return $this->canCreate($container, $requestedName); 79 | } 80 | 81 | /** 82 | * Create the requested service (v2) 83 | * 84 | * @param ServiceLocatorInterface $container 85 | * @param string $cName 86 | * @param string $rName 87 | * @return InputFilterInterface 88 | */ 89 | public function createServiceWithName(ServiceLocatorInterface $container, $cName, $rName) 90 | { 91 | // v2 => may need to get parent service locator 92 | if ($container instanceof AbstractPluginManager) { 93 | $container = $container->getServiceLocator() ?: $container; 94 | } 95 | 96 | return $this($container, $rName); 97 | } 98 | 99 | /** 100 | * @param ContainerInterface $container 101 | * @return Factory 102 | */ 103 | protected function getInputFilterFactory(ContainerInterface $container) 104 | { 105 | if ($this->factory instanceof Factory) { 106 | return $this->factory; 107 | } 108 | 109 | $this->factory = new Factory(); 110 | $this->factory 111 | ->getDefaultFilterChain() 112 | ->setPluginManager($this->getFilterPluginManager($container)); 113 | $this->factory 114 | ->getDefaultValidatorChain() 115 | ->setPluginManager($this->getValidatorPluginManager($container)); 116 | 117 | $this->factory->setInputFilterManager($container->get('InputFilterManager')); 118 | 119 | return $this->factory; 120 | } 121 | 122 | /** 123 | * @param ContainerInterface $container 124 | * @return FilterPluginManager 125 | */ 126 | protected function getFilterPluginManager(ContainerInterface $container) 127 | { 128 | if ($container->has('FilterManager')) { 129 | return $container->get('FilterManager'); 130 | } 131 | 132 | return new FilterPluginManager($container); 133 | } 134 | 135 | /** 136 | * @param ContainerInterface $container 137 | * @return ValidatorPluginManager 138 | */ 139 | protected function getValidatorPluginManager(ContainerInterface $container) 140 | { 141 | if ($container->has('ValidatorManager')) { 142 | return $container->get('ValidatorManager'); 143 | } 144 | 145 | return new ValidatorPluginManager($container); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/FileInput/HttpServerFileInputDecorator.php: -------------------------------------------------------------------------------- 1 | subject = $subject; 62 | } 63 | 64 | /** 65 | * @return mixed 66 | */ 67 | public function getValue() 68 | { 69 | $value = $this->subject->value; 70 | 71 | if (! $this->subject->isValid || ! is_array($value)) { 72 | return $value; 73 | } 74 | 75 | // Run filters ~after~ validation, so that is_uploaded_file() 76 | // validation is not affected by filters. 77 | $filter = $this->subject->getFilterChain(); 78 | if (isset($value['tmp_name'])) { 79 | // Single file input 80 | $value = $filter->filter($value); 81 | return $value; 82 | } 83 | 84 | // Multi file input (multiple attribute set) 85 | $newValue = []; 86 | foreach ($value as $fileData) { 87 | if (is_array($fileData) && isset($fileData['tmp_name'])) { 88 | $newValue[] = $filter->filter($fileData); 89 | } 90 | } 91 | 92 | return $newValue; 93 | } 94 | 95 | /** 96 | * @param mixed $context Extra "context" to provide the validator 97 | * @return bool 98 | */ 99 | public function isValid($context = null) 100 | { 101 | $rawValue = $this->subject->getRawValue(); 102 | $validator = $this->injectUploadValidator($this->subject->getValidatorChain()); 103 | 104 | if (! is_array($rawValue)) { 105 | // This can happen in an AJAX POST, where the input comes across as a string 106 | $rawValue = [ 107 | 'tmp_name' => $rawValue, 108 | 'name' => $rawValue, 109 | 'size' => 0, 110 | 'type' => '', 111 | 'error' => UPLOAD_ERR_NO_FILE, 112 | ]; 113 | } elseif (! isset($rawValue['tmp_name']) && ! isset($rawValue[0]['tmp_name'])) { 114 | // This can happen when sent not file and just array 115 | $rawValue = [ 116 | 'tmp_name' => '', 117 | 'name' => '', 118 | 'size' => 0, 119 | 'type' => '', 120 | 'error' => UPLOAD_ERR_NO_FILE, 121 | ]; 122 | } 123 | 124 | if (is_array($rawValue) && isset($rawValue['tmp_name'])) { 125 | // Single file input 126 | $this->subject->isValid = $validator->isValid($rawValue, $context); 127 | return $this->subject->isValid; 128 | } 129 | 130 | if (is_array($rawValue) && isset($rawValue[0]['tmp_name'])) { 131 | // Multi file input (multiple attribute set) 132 | $this->subject->isValid = true; 133 | 134 | foreach ($rawValue as $value) { 135 | if (! $validator->isValid($value, $context)) { 136 | $this->subject->isValid = false; 137 | return false; // Do not continue processing files if validation fails 138 | } 139 | } 140 | 141 | return true; // We return early from the loop if validation fails 142 | } 143 | 144 | return $this->subject->isValid; 145 | } 146 | 147 | /** 148 | * @return ValidatorChain 149 | */ 150 | protected function injectUploadValidator(ValidatorChain $chain) 151 | { 152 | if (! $this->subject->autoPrependUploadValidator) { 153 | return $chain; 154 | } 155 | 156 | // Check if Upload validator is already first in chain 157 | $validators = $chain->getValidators(); 158 | if (isset($validators[0]['instance']) 159 | && $validators[0]['instance'] instanceof UploadValidator 160 | ) { 161 | $this->subject->autoPrependUploadValidator = false; 162 | return $chain; 163 | } 164 | 165 | $chain->prependByName('fileuploadfile', [], true); 166 | $this->subject->autoPrependUploadValidator = false; 167 | 168 | return $chain; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/InputFilterPluginManager.php: -------------------------------------------------------------------------------- 1 | InputFilter::class, 32 | 'inputFilter' => InputFilter::class, 33 | 'InputFilter' => InputFilter::class, 34 | 'collection' => CollectionInputFilter::class, 35 | 'Collection' => CollectionInputFilter::class, 36 | 'optionalinputfilter' => OptionalInputFilter::class, 37 | 'optionalInputFilter' => OptionalInputFilter::class, 38 | 'OptionalInputFilter' => OptionalInputFilter::class, 39 | ]; 40 | 41 | /** 42 | * Default set of plugins 43 | * 44 | * @var string[] 45 | */ 46 | protected $factories = [ 47 | InputFilter::class => InvokableFactory::class, 48 | CollectionInputFilter::class => InvokableFactory::class, 49 | OptionalInputFilter::class => InvokableFactory::class, 50 | // v2 canonical FQCN 51 | 'zendinputfilterinputfilter' => InvokableFactory::class, 52 | 'zendinputfiltercollectioninputfilter' => InvokableFactory::class, 53 | 'zendinputfilteroptionalinputfilter' => InvokableFactory::class, 54 | ]; 55 | 56 | /** 57 | * Whether or not to share by default (v3) 58 | * 59 | * @var bool 60 | */ 61 | protected $sharedByDefault = false; 62 | 63 | /** 64 | * Whether or not to share by default (v2) 65 | * 66 | * @var bool 67 | */ 68 | protected $shareByDefault = false; 69 | 70 | /** 71 | * @param null|\Zend\ServiceManager\ConfigInterface|ContainerInterface $configOrContainer 72 | * For zend-servicemanager v2, null or a ConfigInterface instance are 73 | * allowed; for v3, a ContainerInterface is expected. 74 | * @param array $v3config Optional configuration array (zend-servicemanager v3 only) 75 | */ 76 | public function __construct($configOrContainer = null, array $v3config = []) 77 | { 78 | $this->initializers[] = [$this, 'populateFactory']; 79 | parent::__construct($configOrContainer, $v3config); 80 | } 81 | 82 | /** 83 | * Inject this and populate the factory with filter chain and validator chain 84 | * 85 | * @param ContainerInterface|InputFilter $containerOrInputFilter When using ServiceManager v3 86 | * this will be the plugin manager instance 87 | * @param InputFilter $inputFilter This is only used with ServiceManager v3 88 | */ 89 | public function populateFactory($containerOrInputFilter, $inputFilter = null) 90 | { 91 | $inputFilter = $containerOrInputFilter instanceof ContainerInterface ? $inputFilter : $containerOrInputFilter; 92 | 93 | if (! $inputFilter instanceof InputFilter) { 94 | return; 95 | } 96 | 97 | $factory = $inputFilter->getFactory(); 98 | $factory->setInputFilterManager($this); 99 | } 100 | 101 | /** 102 | * Populate the filter and validator managers for the default filter/validator chains. 103 | * 104 | * @param Factory $factory 105 | * @return void 106 | */ 107 | public function populateFactoryPluginManagers(Factory $factory) 108 | { 109 | $container = property_exists($this, 'creationContext') 110 | ? $this->creationContext // v3 111 | : $this->serviceLocator; // v2 112 | 113 | if ($container && $container->has('FilterManager')) { 114 | $factory->getDefaultFilterChain()->setPluginManager($container->get('FilterManager')); 115 | } 116 | 117 | if ($container && $container->has('ValidatorManager')) { 118 | $factory->getDefaultValidatorChain()->setPluginManager($container->get('ValidatorManager')); 119 | } 120 | } 121 | 122 | /** 123 | * {@inheritDoc} (v3) 124 | */ 125 | public function validate($plugin) 126 | { 127 | if ($plugin instanceof InputFilterInterface || $plugin instanceof InputInterface) { 128 | // Hook to perform various initialization, when the inputFilter is not created through the factory 129 | if ($plugin instanceof InitializableInterface) { 130 | $plugin->init(); 131 | } 132 | 133 | // we're okay 134 | return; 135 | } 136 | 137 | throw new InvalidServiceException(sprintf( 138 | 'Plugin of type %s is invalid; must implement %s or %s', 139 | (is_object($plugin) ? get_class($plugin) : gettype($plugin)), 140 | InputFilterInterface::class, 141 | InputInterface::class 142 | )); 143 | } 144 | 145 | /** 146 | * Validate the plugin (v2) 147 | * 148 | * Checks that the filter loaded is either a valid callback or an instance 149 | * of FilterInterface. 150 | * 151 | * @param mixed $plugin 152 | * @return void 153 | * @throws Exception\RuntimeException if invalid 154 | */ 155 | public function validatePlugin($plugin) 156 | { 157 | try { 158 | $this->validate($plugin); 159 | } catch (InvalidServiceException $e) { 160 | throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/FileInput.php: -------------------------------------------------------------------------------- 1 | implementation = $this->createDecoratorImplementation($value); 52 | parent::setValue($value); 53 | return $this; 54 | } 55 | 56 | public function resetValue() 57 | { 58 | $this->implementation = null; 59 | return parent::resetValue(); 60 | } 61 | 62 | /** 63 | * @param bool $value Enable/Disable automatically prepending an Upload validator 64 | * 65 | * @return FileInput 66 | */ 67 | public function setAutoPrependUploadValidator($value) 68 | { 69 | $this->autoPrependUploadValidator = $value; 70 | return $this; 71 | } 72 | 73 | /** 74 | * @return bool 75 | */ 76 | public function getAutoPrependUploadValidator() 77 | { 78 | return $this->autoPrependUploadValidator; 79 | } 80 | 81 | /** 82 | * @return mixed 83 | */ 84 | public function getValue() 85 | { 86 | if ($this->implementation === null) { 87 | return $this->value; 88 | } 89 | return $this->implementation->getValue(); 90 | } 91 | 92 | /** 93 | * Checks if the raw input value is an empty file input eg: no file was uploaded 94 | * 95 | * @param $rawValue 96 | * @return bool 97 | */ 98 | public function isEmptyFile($rawValue) 99 | { 100 | if ($rawValue instanceof UploadedFileInterface) { 101 | return FileInput\PsrFileInputDecorator::isEmptyFileDecorator($rawValue); 102 | } 103 | 104 | if (is_array($rawValue)) { 105 | if (isset($rawValue[0]) && $rawValue[0] instanceof UploadedFileInterface) { 106 | return FileInput\PsrFileInputDecorator::isEmptyFileDecorator($rawValue); 107 | } 108 | 109 | return FileInput\HttpServerFileInputDecorator::isEmptyFileDecorator($rawValue); 110 | } 111 | 112 | return true; 113 | } 114 | 115 | /** 116 | * @param mixed $context Extra "context" to provide the validator 117 | * @return bool 118 | */ 119 | public function isValid($context = null) 120 | { 121 | $rawValue = $this->getRawValue(); 122 | $hasValue = $this->hasValue(); 123 | $empty = $this->isEmptyFile($rawValue); 124 | $required = $this->isRequired(); 125 | $allowEmpty = $this->allowEmpty(); 126 | $continueIfEmpty = $this->continueIfEmpty(); 127 | 128 | if (! $hasValue && ! $required) { 129 | return true; 130 | } 131 | 132 | if (! $hasValue && $required && ! $this->hasFallback()) { 133 | if ($this->errorMessage === null) { 134 | $this->errorMessage = $this->prepareRequiredValidationFailureMessage(); 135 | } 136 | return false; 137 | } 138 | 139 | if ($empty && ! $required && ! $continueIfEmpty) { 140 | return true; 141 | } 142 | 143 | if ($empty && $allowEmpty && ! $continueIfEmpty) { 144 | return true; 145 | } 146 | 147 | return $this->implementation->isValid($context); 148 | } 149 | 150 | /** 151 | * @param InputInterface $input 152 | * 153 | * @return FileInput 154 | */ 155 | public function merge(InputInterface $input) 156 | { 157 | parent::merge($input); 158 | if ($input instanceof FileInput) { 159 | $this->setAutoPrependUploadValidator($input->getAutoPrependUploadValidator()); 160 | } 161 | return $this; 162 | } 163 | 164 | /** 165 | * @deprecated 2.4.8 See note on parent class. Removal does not affect this class. 166 | * 167 | * No-op, NotEmpty validator does not apply for FileInputs. 168 | * See also: BaseInputFilter::isValid() 169 | * 170 | * @return void 171 | */ 172 | protected function injectNotEmptyValidator() 173 | { 174 | $this->notEmptyValidator = true; 175 | } 176 | 177 | /** 178 | * @param mixed $value 179 | * @return FileInput\FileInputDecoratorInterface 180 | */ 181 | private function createDecoratorImplementation($value) 182 | { 183 | // Single PSR-7 instance 184 | if ($value instanceof UploadedFileInterface) { 185 | return new FileInput\PsrFileInputDecorator($this); 186 | } 187 | 188 | if (is_array($value)) { 189 | if (isset($value[0]) && $value[0] instanceof UploadedFileInterface) { 190 | // Array of PSR-7 instances 191 | return new FileInput\PsrFileInputDecorator($this); 192 | } 193 | 194 | // Single or multiple SAPI file upload arrays 195 | return new FileInput\HttpServerFileInputDecorator($this); 196 | } 197 | 198 | // AJAX/XHR/Fetch case 199 | return new FileInput\HttpServerFileInputDecorator($this); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/CollectionInputFilter.php: -------------------------------------------------------------------------------- 1 | getFactory()->createInputFilter($inputFilter); 63 | } 64 | 65 | if (! $inputFilter instanceof BaseInputFilter) { 66 | throw new Exception\RuntimeException(sprintf( 67 | '%s expects an instance of %s; received "%s"', 68 | __METHOD__, 69 | BaseInputFilter::class, 70 | (is_object($inputFilter) ? get_class($inputFilter) : gettype($inputFilter)) 71 | )); 72 | } 73 | 74 | $this->inputFilter = $inputFilter; 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * Get the input filter used when looping the data 81 | * 82 | * @return BaseInputFilter 83 | */ 84 | public function getInputFilter() 85 | { 86 | if (null === $this->inputFilter) { 87 | $this->setInputFilter(new InputFilter()); 88 | } 89 | 90 | return $this->inputFilter; 91 | } 92 | 93 | /** 94 | * Set if the collection can be empty 95 | * 96 | * @param bool $isRequired 97 | * @return CollectionInputFilter 98 | */ 99 | public function setIsRequired($isRequired) 100 | { 101 | $this->isRequired = $isRequired; 102 | 103 | return $this; 104 | } 105 | 106 | /** 107 | * Get if collection can be empty 108 | * 109 | * @return bool 110 | */ 111 | public function getIsRequired() 112 | { 113 | return $this->isRequired; 114 | } 115 | 116 | /** 117 | * Set the count of data to validate 118 | * 119 | * @param int $count 120 | * @return CollectionInputFilter 121 | */ 122 | public function setCount($count) 123 | { 124 | $this->count = $count > 0 ? $count : 0; 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * Get the count of data to validate, use the count of data by default 131 | * 132 | * @return int 133 | */ 134 | public function getCount() 135 | { 136 | if (null === $this->count) { 137 | return count($this->data); 138 | } 139 | 140 | return $this->count; 141 | } 142 | 143 | /** 144 | * {@inheritdoc} 145 | */ 146 | public function setData($data) 147 | { 148 | if (! (is_array($data) || $data instanceof Traversable)) { 149 | throw new Exception\InvalidArgumentException(sprintf( 150 | '%s expects an array or Traversable collection; invalid collection of type %s provided', 151 | __METHOD__, 152 | is_object($data) ? get_class($data) : gettype($data) 153 | )); 154 | } 155 | 156 | $this->setUnfilteredData($data); 157 | 158 | foreach ($data as $item) { 159 | if (is_array($item) || $item instanceof Traversable) { 160 | continue; 161 | } 162 | 163 | throw new Exception\InvalidArgumentException(sprintf( 164 | '%s expects each item in a collection to be an array or Traversable; ' 165 | . 'invalid item in collection of type %s detected', 166 | __METHOD__, 167 | is_object($item) ? get_class($item) : gettype($item) 168 | )); 169 | } 170 | 171 | $this->data = $data; 172 | return $this; 173 | } 174 | 175 | /** 176 | * Retrieve the NotEmpty validator to use for failed "required" validations. 177 | * 178 | * This validator will be used to produce a validation failure message in 179 | * cases where the collection is empty but required. 180 | * 181 | * @return NotEmpty 182 | */ 183 | public function getNotEmptyValidator() 184 | { 185 | if ($this->notEmptyValidator === null) { 186 | $this->notEmptyValidator = new NotEmpty(); 187 | } 188 | 189 | return $this->notEmptyValidator; 190 | } 191 | 192 | /** 193 | * Set the NotEmpty validator to use for failed "required" validations. 194 | * 195 | * This validator will be used to produce a validation failure message in 196 | * cases where the collection is empty but required. 197 | * 198 | * @param NotEmpty $notEmptyValidator 199 | * @return $this 200 | */ 201 | public function setNotEmptyValidator(NotEmpty $notEmptyValidator) 202 | { 203 | $this->notEmptyValidator = $notEmptyValidator; 204 | 205 | return $this; 206 | } 207 | 208 | /** 209 | * {@inheritdoc} 210 | * @param mixed $context Ignored, but present to retain signature compatibility. 211 | */ 212 | public function isValid($context = null) 213 | { 214 | $this->collectionMessages = []; 215 | $inputFilter = $this->getInputFilter(); 216 | $valid = true; 217 | 218 | if ($this->getCount() < 1 && $this->isRequired) { 219 | $this->collectionMessages[] = $this->prepareRequiredValidationFailureMessage(); 220 | $valid = false; 221 | } 222 | 223 | if (count($this->data) < $this->getCount()) { 224 | $valid = false; 225 | } 226 | 227 | if (! $this->data) { 228 | $this->clearValues(); 229 | $this->clearRawValues(); 230 | 231 | return $valid; 232 | } 233 | 234 | foreach ($this->data as $key => $data) { 235 | $inputFilter->setData($data); 236 | 237 | if (null !== $this->validationGroup) { 238 | $inputFilter->setValidationGroup($this->validationGroup[$key]); 239 | } 240 | 241 | if ($inputFilter->isValid()) { 242 | $this->validInputs[$key] = $inputFilter->getValidInput(); 243 | } else { 244 | $valid = false; 245 | $this->collectionMessages[$key] = $inputFilter->getMessages(); 246 | $this->invalidInputs[$key] = $inputFilter->getInvalidInput(); 247 | } 248 | 249 | $this->collectionValues[$key] = $inputFilter->getValues(); 250 | $this->collectionRawValues[$key] = $inputFilter->getRawValues(); 251 | } 252 | 253 | return $valid; 254 | } 255 | 256 | /** 257 | * {@inheritdoc} 258 | */ 259 | public function setValidationGroup($name) 260 | { 261 | if ($name === self::VALIDATE_ALL) { 262 | $name = null; 263 | } 264 | $this->validationGroup = $name; 265 | 266 | return $this; 267 | } 268 | 269 | /** 270 | * {@inheritdoc} 271 | */ 272 | public function getValues() 273 | { 274 | return $this->collectionValues; 275 | } 276 | 277 | /** 278 | * {@inheritdoc} 279 | */ 280 | public function getRawValues() 281 | { 282 | return $this->collectionRawValues; 283 | } 284 | 285 | /** 286 | * Clear collectionValues 287 | * 288 | * @return array[] 289 | */ 290 | public function clearValues() 291 | { 292 | return $this->collectionValues = []; 293 | } 294 | 295 | /** 296 | * Clear collectionRawValues 297 | * 298 | * @return array[] 299 | */ 300 | public function clearRawValues() 301 | { 302 | return $this->collectionRawValues = []; 303 | } 304 | 305 | /** 306 | * {@inheritdoc} 307 | */ 308 | public function getMessages() 309 | { 310 | return $this->collectionMessages; 311 | } 312 | 313 | /** 314 | * {@inheritdoc} 315 | */ 316 | public function getUnknown() 317 | { 318 | if (! $this->data) { 319 | throw new Exception\RuntimeException(sprintf( 320 | '%s: no data present!', 321 | __METHOD__ 322 | )); 323 | } 324 | 325 | $inputFilter = $this->getInputFilter(); 326 | 327 | $unknownInputs = []; 328 | foreach ($this->data as $key => $data) { 329 | $inputFilter->setData($data); 330 | 331 | if ($unknown = $inputFilter->getUnknown()) { 332 | $unknownInputs[$key] = $unknown; 333 | } 334 | } 335 | 336 | return $unknownInputs; 337 | } 338 | 339 | /** 340 | * @return array 341 | */ 342 | protected function prepareRequiredValidationFailureMessage() 343 | { 344 | $notEmptyValidator = $this->getNotEmptyValidator(); 345 | $templates = $notEmptyValidator->getOption('messageTemplates'); 346 | $message = $templates[NotEmpty::IS_EMPTY]; 347 | $translator = $notEmptyValidator->getTranslator(); 348 | 349 | return [ 350 | NotEmpty::IS_EMPTY => $translator 351 | ? $translator->translate($message, $notEmptyValidator->getTranslatorTextDomain()) 352 | : $message, 353 | ]; 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/Input.php: -------------------------------------------------------------------------------- 1 | name = $name; 97 | } 98 | 99 | /** 100 | * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain and set this to `true`. 101 | * 102 | * @param bool $allowEmpty 103 | * @return Input 104 | */ 105 | public function setAllowEmpty($allowEmpty) 106 | { 107 | $this->allowEmpty = (bool) $allowEmpty; 108 | return $this; 109 | } 110 | 111 | /** 112 | * @param bool $breakOnFailure 113 | * @return Input 114 | */ 115 | public function setBreakOnFailure($breakOnFailure) 116 | { 117 | $this->breakOnFailure = (bool) $breakOnFailure; 118 | return $this; 119 | } 120 | 121 | /** 122 | * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain and set this to `true`. 123 | * 124 | * @param bool $continueIfEmpty 125 | * @return Input 126 | */ 127 | public function setContinueIfEmpty($continueIfEmpty) 128 | { 129 | $this->continueIfEmpty = (bool) $continueIfEmpty; 130 | return $this; 131 | } 132 | 133 | /** 134 | * @param string|null $errorMessage 135 | * @return Input 136 | */ 137 | public function setErrorMessage($errorMessage) 138 | { 139 | $this->errorMessage = (null === $errorMessage) ? null : (string) $errorMessage; 140 | return $this; 141 | } 142 | 143 | /** 144 | * @param FilterChain $filterChain 145 | * @return Input 146 | */ 147 | public function setFilterChain(FilterChain $filterChain) 148 | { 149 | $this->filterChain = $filterChain; 150 | return $this; 151 | } 152 | 153 | /** 154 | * @param string $name 155 | * @return Input 156 | */ 157 | public function setName($name) 158 | { 159 | $this->name = (string) $name; 160 | return $this; 161 | } 162 | 163 | /** 164 | * @param bool $required 165 | * @return Input 166 | */ 167 | public function setRequired($required) 168 | { 169 | $this->required = (bool) $required; 170 | return $this; 171 | } 172 | 173 | /** 174 | * @param ValidatorChain $validatorChain 175 | * @return Input 176 | */ 177 | public function setValidatorChain(ValidatorChain $validatorChain) 178 | { 179 | $this->validatorChain = $validatorChain; 180 | return $this; 181 | } 182 | 183 | /** 184 | * Set the input value. 185 | * 186 | * If you want to remove/unset the current value use {@link Input::resetValue()}. 187 | * 188 | * @see Input::getValue() For retrieve the input value. 189 | * @see Input::hasValue() For to know if input value was set. 190 | * @see Input::resetValue() For reset the input value to the default state. 191 | * 192 | * @param mixed $value 193 | * @return Input 194 | */ 195 | public function setValue($value) 196 | { 197 | $this->value = $value; 198 | $this->hasValue = true; 199 | return $this; 200 | } 201 | 202 | /** 203 | * Reset input value to the default state. 204 | * 205 | * @see Input::hasValue() For to know if input value was set. 206 | * @see Input::setValue() For set a new value. 207 | * 208 | * @return Input 209 | */ 210 | public function resetValue() 211 | { 212 | $this->value = null; 213 | $this->hasValue = false; 214 | return $this; 215 | } 216 | 217 | /** 218 | * @param mixed $value 219 | * @return Input 220 | */ 221 | public function setFallbackValue($value) 222 | { 223 | $this->fallbackValue = $value; 224 | $this->hasFallback = true; 225 | return $this; 226 | } 227 | 228 | /** 229 | * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. 230 | * 231 | * @return bool 232 | */ 233 | public function allowEmpty() 234 | { 235 | return $this->allowEmpty; 236 | } 237 | 238 | /** 239 | * @return bool 240 | */ 241 | public function breakOnFailure() 242 | { 243 | return $this->breakOnFailure; 244 | } 245 | 246 | /** 247 | * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. Should always return `true`. 248 | * 249 | * @return bool 250 | */ 251 | public function continueIfEmpty() 252 | { 253 | return $this->continueIfEmpty; 254 | } 255 | 256 | /** 257 | * @return string|null 258 | */ 259 | public function getErrorMessage() 260 | { 261 | return $this->errorMessage; 262 | } 263 | 264 | /** 265 | * @return FilterChain 266 | */ 267 | public function getFilterChain() 268 | { 269 | if (! $this->filterChain) { 270 | $this->setFilterChain(new FilterChain()); 271 | } 272 | return $this->filterChain; 273 | } 274 | 275 | /** 276 | * @return string 277 | */ 278 | public function getName() 279 | { 280 | return $this->name; 281 | } 282 | 283 | /** 284 | * @return mixed 285 | */ 286 | public function getRawValue() 287 | { 288 | return $this->value; 289 | } 290 | 291 | /** 292 | * @return bool 293 | */ 294 | public function isRequired() 295 | { 296 | return $this->required; 297 | } 298 | 299 | /** 300 | * @return ValidatorChain 301 | */ 302 | public function getValidatorChain() 303 | { 304 | if (! $this->validatorChain) { 305 | $this->setValidatorChain(new ValidatorChain()); 306 | } 307 | return $this->validatorChain; 308 | } 309 | 310 | /** 311 | * @return mixed 312 | */ 313 | public function getValue() 314 | { 315 | $filter = $this->getFilterChain(); 316 | return $filter->filter($this->value); 317 | } 318 | 319 | /** 320 | * Flag for inform if input value was set. 321 | * 322 | * This flag used for distinguish when {@link Input::getValue()} 323 | * will return the value previously set or the default. 324 | * 325 | * @see Input::getValue() For retrieve the input value. 326 | * @see Input::setValue() For set a new value. 327 | * @see Input::resetValue() For reset the input value to the default state. 328 | * 329 | * @return bool 330 | */ 331 | public function hasValue() 332 | { 333 | return $this->hasValue; 334 | } 335 | 336 | /** 337 | * @return mixed 338 | */ 339 | public function getFallbackValue() 340 | { 341 | return $this->fallbackValue; 342 | } 343 | 344 | /** 345 | * @return bool 346 | */ 347 | public function hasFallback() 348 | { 349 | return $this->hasFallback; 350 | } 351 | 352 | public function clearFallbackValue() 353 | { 354 | $this->hasFallback = false; 355 | $this->fallbackValue = null; 356 | } 357 | 358 | /** 359 | * @param InputInterface $input 360 | * @return Input 361 | */ 362 | public function merge(InputInterface $input) 363 | { 364 | $this->setBreakOnFailure($input->breakOnFailure()); 365 | if ($input instanceof Input) { 366 | $this->setContinueIfEmpty($input->continueIfEmpty()); 367 | } 368 | $this->setErrorMessage($input->getErrorMessage()); 369 | $this->setName($input->getName()); 370 | $this->setRequired($input->isRequired()); 371 | $this->setAllowEmpty($input->allowEmpty()); 372 | if (! $input instanceof Input || $input->hasValue()) { 373 | $this->setValue($input->getRawValue()); 374 | } 375 | 376 | $filterChain = $input->getFilterChain(); 377 | $this->getFilterChain()->merge($filterChain); 378 | 379 | $validatorChain = $input->getValidatorChain(); 380 | $this->getValidatorChain()->merge($validatorChain); 381 | return $this; 382 | } 383 | 384 | /** 385 | * @param mixed $context Extra "context" to provide the validator 386 | * @return bool 387 | */ 388 | public function isValid($context = null) 389 | { 390 | if (is_array($this->errorMessage)) { 391 | $this->errorMessage = null; 392 | } 393 | 394 | $value = $this->getValue(); 395 | $hasValue = $this->hasValue(); 396 | $empty = ($value === null || $value === '' || $value === []); 397 | $required = $this->isRequired(); 398 | $allowEmpty = $this->allowEmpty(); 399 | $continueIfEmpty = $this->continueIfEmpty(); 400 | 401 | if (! $hasValue && $this->hasFallback()) { 402 | $this->setValue($this->getFallbackValue()); 403 | return true; 404 | } 405 | 406 | if (! $hasValue && ! $required) { 407 | return true; 408 | } 409 | 410 | if (! $hasValue && $required) { 411 | if ($this->errorMessage === null) { 412 | $this->errorMessage = $this->prepareRequiredValidationFailureMessage(); 413 | } 414 | return false; 415 | } 416 | 417 | if ($empty && ! $required && ! $continueIfEmpty) { 418 | return true; 419 | } 420 | 421 | if ($empty && $allowEmpty && ! $continueIfEmpty) { 422 | return true; 423 | } 424 | 425 | // At this point, we need to run validators. 426 | // If we do not allow empty and the "continue if empty" flag are 427 | // BOTH false, we inject the "not empty" validator into the chain, 428 | // which adds that logic into the validation routine. 429 | if (! $allowEmpty && ! $continueIfEmpty) { 430 | $this->injectNotEmptyValidator(); 431 | } 432 | 433 | $validator = $this->getValidatorChain(); 434 | $result = $validator->isValid($value, $context); 435 | if (! $result && $this->hasFallback()) { 436 | $this->setValue($this->getFallbackValue()); 437 | $result = true; 438 | } 439 | 440 | return $result; 441 | } 442 | 443 | /** 444 | * @return string[] 445 | */ 446 | public function getMessages() 447 | { 448 | if (null !== $this->errorMessage) { 449 | return (array) $this->errorMessage; 450 | } 451 | 452 | if ($this->hasFallback()) { 453 | return []; 454 | } 455 | 456 | $validator = $this->getValidatorChain(); 457 | return $validator->getMessages(); 458 | } 459 | 460 | /** 461 | * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. 462 | * 463 | * @return void 464 | */ 465 | protected function injectNotEmptyValidator() 466 | { 467 | if ((! $this->isRequired() && $this->allowEmpty()) || $this->notEmptyValidator) { 468 | return; 469 | } 470 | $chain = $this->getValidatorChain(); 471 | 472 | // Check if NotEmpty validator is already in chain 473 | $validators = $chain->getValidators(); 474 | foreach ($validators as $validator) { 475 | if ($validator['instance'] instanceof NotEmpty) { 476 | $this->notEmptyValidator = true; 477 | return; 478 | } 479 | } 480 | 481 | $this->notEmptyValidator = true; 482 | 483 | if (class_exists(AbstractPluginManager::class)) { 484 | $chain->prependByName('NotEmpty', [], true); 485 | 486 | return; 487 | } 488 | 489 | $chain->prependValidator(new NotEmpty(), true); 490 | } 491 | 492 | /** 493 | * Create and return the validation failure message for required input. 494 | * 495 | * @return string[] 496 | */ 497 | protected function prepareRequiredValidationFailureMessage() 498 | { 499 | $chain = $this->getValidatorChain(); 500 | $notEmpty = $chain->plugin(NotEmpty::class); 501 | 502 | foreach ($chain->getValidators() as $validator) { 503 | if ($validator['instance'] instanceof NotEmpty) { 504 | $notEmpty = $validator['instance']; 505 | break; 506 | } 507 | } 508 | 509 | $templates = $notEmpty->getOption('messageTemplates'); 510 | $message = $templates[NotEmpty::IS_EMPTY]; 511 | $translator = $notEmpty->getTranslator(); 512 | 513 | if ($translator) { 514 | $message = $translator->translate($message, $notEmpty->getTranslatorTextDomain()); 515 | } 516 | 517 | return [ 518 | NotEmpty::IS_EMPTY => $message, 519 | ]; 520 | } 521 | } 522 | -------------------------------------------------------------------------------- /src/Factory.php: -------------------------------------------------------------------------------- 1 | defaultFilterChain = new FilterChain(); 42 | $this->defaultValidatorChain = new ValidatorChain(); 43 | 44 | if ($inputFilterManager) { 45 | $this->setInputFilterManager($inputFilterManager); 46 | } 47 | } 48 | 49 | /** 50 | * Set default filter chain to use 51 | * 52 | * @param FilterChain $filterChain 53 | * @return Factory 54 | */ 55 | public function setDefaultFilterChain(FilterChain $filterChain) 56 | { 57 | $this->defaultFilterChain = $filterChain; 58 | return $this; 59 | } 60 | 61 | /** 62 | * Get default filter chain, if any 63 | * 64 | * @return null|FilterChain 65 | */ 66 | public function getDefaultFilterChain() 67 | { 68 | return $this->defaultFilterChain; 69 | } 70 | 71 | /** 72 | * Clear the default filter chain (i.e., don't inject one into new inputs) 73 | * 74 | * @return void 75 | */ 76 | public function clearDefaultFilterChain() 77 | { 78 | $this->defaultFilterChain = null; 79 | } 80 | 81 | /** 82 | * Set default validator chain to use 83 | * 84 | * @param ValidatorChain $validatorChain 85 | * @return Factory 86 | */ 87 | public function setDefaultValidatorChain(ValidatorChain $validatorChain) 88 | { 89 | $this->defaultValidatorChain = $validatorChain; 90 | return $this; 91 | } 92 | 93 | /** 94 | * Get default validator chain, if any 95 | * 96 | * @return null|ValidatorChain 97 | */ 98 | public function getDefaultValidatorChain() 99 | { 100 | return $this->defaultValidatorChain; 101 | } 102 | 103 | /** 104 | * Clear the default validator chain (i.e., don't inject one into new inputs) 105 | * 106 | * @return void 107 | */ 108 | public function clearDefaultValidatorChain() 109 | { 110 | $this->defaultValidatorChain = null; 111 | } 112 | 113 | /** 114 | * @param InputFilterPluginManager $inputFilterManager 115 | * @return self 116 | */ 117 | public function setInputFilterManager(InputFilterPluginManager $inputFilterManager) 118 | { 119 | $this->inputFilterManager = $inputFilterManager; 120 | $inputFilterManager->populateFactoryPluginManagers($this); 121 | return $this; 122 | } 123 | 124 | /** 125 | * @return InputFilterPluginManager 126 | */ 127 | public function getInputFilterManager() 128 | { 129 | if (null === $this->inputFilterManager) { 130 | $this->inputFilterManager = new InputFilterPluginManager(new ServiceManager()); 131 | } 132 | 133 | return $this->inputFilterManager; 134 | } 135 | 136 | /** 137 | * Factory for input objects 138 | * 139 | * @param array|Traversable|InputProviderInterface $inputSpecification 140 | * @throws Exception\InvalidArgumentException 141 | * @throws Exception\RuntimeException 142 | * @return InputInterface|InputFilterInterface 143 | */ 144 | public function createInput($inputSpecification) 145 | { 146 | if ($inputSpecification instanceof InputProviderInterface) { 147 | $inputSpecification = $inputSpecification->getInputSpecification(); 148 | } 149 | 150 | if (! is_array($inputSpecification) && ! $inputSpecification instanceof Traversable) { 151 | throw new Exception\InvalidArgumentException(sprintf( 152 | '%s expects an array or Traversable; received "%s"', 153 | __METHOD__, 154 | (is_object($inputSpecification) ? get_class($inputSpecification) : gettype($inputSpecification)) 155 | )); 156 | } 157 | if ($inputSpecification instanceof Traversable) { 158 | $inputSpecification = ArrayUtils::iteratorToArray($inputSpecification); 159 | } 160 | 161 | $class = Input::class; 162 | 163 | if (isset($inputSpecification['type'])) { 164 | $class = $inputSpecification['type']; 165 | } 166 | 167 | $managerInstance = null; 168 | if ($this->getInputFilterManager()->has($class)) { 169 | $managerInstance = $this->getInputFilterManager()->get($class); 170 | } 171 | if (! $managerInstance && ! class_exists($class)) { 172 | throw new Exception\RuntimeException(sprintf( 173 | 'Input factory expects the "type" to be a valid class or a plugin name; received "%s"', 174 | $class 175 | )); 176 | } 177 | 178 | $input = $managerInstance ?: new $class(); 179 | 180 | if ($input instanceof InputFilterInterface) { 181 | return $this->createInputFilter($inputSpecification); 182 | } 183 | 184 | if (! $input instanceof InputInterface) { 185 | throw new Exception\RuntimeException(sprintf( 186 | 'Input factory expects the "type" to be a class implementing %s; received "%s"', 187 | InputInterface::class, 188 | $class 189 | )); 190 | } 191 | 192 | $managerInstance 193 | ? $this->injectFilterAndValidatorChainsWithPluginManagers($input) 194 | : $this->injectDefaultFilterAndValidatorChains($input); 195 | 196 | foreach ($inputSpecification as $key => $value) { 197 | switch ($key) { 198 | case 'name': 199 | $input->setName($value); 200 | break; 201 | case 'required': 202 | $input->setRequired($value); 203 | break; 204 | case 'allow_empty': 205 | $input->setAllowEmpty($value); 206 | if (! isset($inputSpecification['required'])) { 207 | $input->setRequired(! $value); 208 | } 209 | break; 210 | case 'continue_if_empty': 211 | if (! $input instanceof Input) { 212 | throw new Exception\RuntimeException(sprintf( 213 | '%s "continue_if_empty" can only set to inputs of type "%s"', 214 | __METHOD__, 215 | Input::class 216 | )); 217 | } 218 | $input->setContinueIfEmpty($inputSpecification['continue_if_empty']); 219 | break; 220 | case 'error_message': 221 | $input->setErrorMessage($value); 222 | break; 223 | case 'fallback_value': 224 | if (! $input instanceof Input) { 225 | throw new Exception\RuntimeException(sprintf( 226 | '%s "fallback_value" can only set to inputs of type "%s"', 227 | __METHOD__, 228 | Input::class 229 | )); 230 | } 231 | $input->setFallbackValue($value); 232 | break; 233 | case 'break_on_failure': 234 | $input->setBreakOnFailure($value); 235 | break; 236 | case 'filters': 237 | if ($value instanceof FilterChain) { 238 | $input->setFilterChain($value); 239 | break; 240 | } 241 | if (! is_array($value) && ! $value instanceof Traversable) { 242 | throw new Exception\RuntimeException(sprintf( 243 | '%s expects the value associated with "filters" to be an array/Traversable of filters' 244 | . ' or filter specifications, or a FilterChain; received "%s"', 245 | __METHOD__, 246 | (is_object($value) ? get_class($value) : gettype($value)) 247 | )); 248 | } 249 | $this->populateFilters($input->getFilterChain(), $value); 250 | break; 251 | case 'validators': 252 | if ($value instanceof ValidatorChain) { 253 | $input->setValidatorChain($value); 254 | break; 255 | } 256 | if (! is_array($value) && ! $value instanceof Traversable) { 257 | throw new Exception\RuntimeException(sprintf( 258 | '%s expects the value associated with "validators" to be an array/Traversable of validators' 259 | . ' or validator specifications, or a ValidatorChain; received "%s"', 260 | __METHOD__, 261 | (is_object($value) ? get_class($value) : gettype($value)) 262 | )); 263 | } 264 | $this->populateValidators($input->getValidatorChain(), $value); 265 | break; 266 | default: 267 | // ignore unknown keys 268 | break; 269 | } 270 | } 271 | 272 | return $input; 273 | } 274 | 275 | /** 276 | * Factory for input filters 277 | * 278 | * @param array|Traversable|InputFilterProviderInterface $inputFilterSpecification 279 | * @throws Exception\InvalidArgumentException 280 | * @throws Exception\RuntimeException 281 | * @return InputFilterInterface 282 | */ 283 | public function createInputFilter($inputFilterSpecification) 284 | { 285 | if ($inputFilterSpecification instanceof InputFilterProviderInterface) { 286 | $inputFilterSpecification = $inputFilterSpecification->getInputFilterSpecification(); 287 | } 288 | 289 | if (! is_array($inputFilterSpecification) && ! $inputFilterSpecification instanceof Traversable) { 290 | throw new Exception\InvalidArgumentException(sprintf( 291 | '%s expects an array or Traversable; received "%s"', 292 | __METHOD__, 293 | is_object($inputFilterSpecification) 294 | ? get_class($inputFilterSpecification) 295 | : gettype($inputFilterSpecification) 296 | )); 297 | } 298 | if ($inputFilterSpecification instanceof Traversable) { 299 | $inputFilterSpecification = ArrayUtils::iteratorToArray($inputFilterSpecification); 300 | } 301 | 302 | $type = InputFilter::class; 303 | 304 | if (isset($inputFilterSpecification['type']) && is_string($inputFilterSpecification['type'])) { 305 | $type = $inputFilterSpecification['type']; 306 | unset($inputFilterSpecification['type']); 307 | } 308 | 309 | $inputFilter = $this->getInputFilterManager()->get($type); 310 | 311 | if ($inputFilter instanceof CollectionInputFilter) { 312 | $inputFilter->setFactory($this); 313 | if (isset($inputFilterSpecification['input_filter'])) { 314 | $inputFilter->setInputFilter($inputFilterSpecification['input_filter']); 315 | } 316 | if (isset($inputFilterSpecification['count'])) { 317 | $inputFilter->setCount($inputFilterSpecification['count']); 318 | } 319 | if (isset($inputFilterSpecification['required'])) { 320 | $inputFilter->setIsRequired($inputFilterSpecification['required']); 321 | } 322 | if (isset($inputFilterSpecification['required_message'])) { 323 | $inputFilter->getNotEmptyValidator()->setMessage($inputFilterSpecification['required_message']); 324 | } 325 | return $inputFilter; 326 | } 327 | 328 | foreach ($inputFilterSpecification as $key => $value) { 329 | if (null === $value) { 330 | continue; 331 | } 332 | 333 | if ($value instanceof InputInterface 334 | || $value instanceof InputFilterInterface 335 | ) { 336 | $inputFilter->add($value, $key); 337 | continue; 338 | } 339 | 340 | // Patch to enable nested, integer indexed input_filter_specs. 341 | // Check type and name are in spec, and that composed type is 342 | // an input filter... 343 | if ((isset($value['type']) && is_string($value['type'])) 344 | && (isset($value['name']) && is_string($value['name'])) 345 | && $this->getInputFilterManager()->get($value['type']) instanceof InputFilter 346 | ) { 347 | // If $key is an integer, reset it to the specified name. 348 | if (is_integer($key)) { 349 | $key = $value['name']; 350 | } 351 | 352 | // Remove name from specification. InputFilter doesn't have a 353 | // name property! 354 | unset($value['name']); 355 | } 356 | 357 | $inputFilter->add($this->createInput($value), $key); 358 | } 359 | 360 | return $inputFilter; 361 | } 362 | 363 | /** 364 | * @param FilterChain $chain 365 | * @param array|Traversable $filters 366 | * @throws Exception\RuntimeException 367 | * @return void 368 | */ 369 | protected function populateFilters(FilterChain $chain, $filters) 370 | { 371 | foreach ($filters as $filter) { 372 | if (is_object($filter) || is_callable($filter)) { 373 | $chain->attach($filter); 374 | continue; 375 | } 376 | 377 | if (is_array($filter)) { 378 | if (! isset($filter['name'])) { 379 | throw new Exception\RuntimeException( 380 | 'Invalid filter specification provided; does not include "name" key' 381 | ); 382 | } 383 | $name = $filter['name']; 384 | $priority = isset($filter['priority']) ? $filter['priority'] : FilterChain::DEFAULT_PRIORITY; 385 | $options = []; 386 | if (isset($filter['options'])) { 387 | $options = $filter['options']; 388 | } 389 | $chain->attachByName($name, $options, $priority); 390 | continue; 391 | } 392 | 393 | throw new Exception\RuntimeException( 394 | 'Invalid filter specification provided; was neither a filter instance nor an array specification' 395 | ); 396 | } 397 | } 398 | 399 | /** 400 | * @param ValidatorChain $chain 401 | * @param string[]|ValidatorInterface[] $validators 402 | * @throws Exception\RuntimeException 403 | * @return void 404 | */ 405 | protected function populateValidators(ValidatorChain $chain, $validators) 406 | { 407 | foreach ($validators as $validator) { 408 | if ($validator instanceof ValidatorInterface) { 409 | $chain->attach($validator); 410 | continue; 411 | } 412 | 413 | if (is_array($validator)) { 414 | if (! isset($validator['name'])) { 415 | throw new Exception\RuntimeException( 416 | 'Invalid validator specification provided; does not include "name" key' 417 | ); 418 | } 419 | $name = $validator['name']; 420 | $options = []; 421 | if (isset($validator['options'])) { 422 | $options = $validator['options']; 423 | } 424 | $breakChainOnFailure = false; 425 | if (isset($validator['break_chain_on_failure'])) { 426 | $breakChainOnFailure = $validator['break_chain_on_failure']; 427 | } 428 | $priority = isset($validator['priority']) ? $validator['priority'] : ValidatorChain::DEFAULT_PRIORITY; 429 | $chain->attachByName($name, $options, $breakChainOnFailure, $priority); 430 | continue; 431 | } 432 | 433 | throw new Exception\RuntimeException( 434 | 'Invalid validator specification provided; was neither a validator instance nor an array specification' 435 | ); 436 | } 437 | } 438 | 439 | /** 440 | * Inject the default filter and validator chains into the input, if present. 441 | * 442 | * This ensures custom plugins are made available to the input instance. 443 | * 444 | * @param InputInterface $input 445 | * @return void 446 | */ 447 | protected function injectDefaultFilterAndValidatorChains(InputInterface $input) 448 | { 449 | if ($this->defaultFilterChain) { 450 | $input->setFilterChain(clone $this->defaultFilterChain); 451 | } 452 | 453 | if ($this->defaultValidatorChain) { 454 | $input->setValidatorChain(clone $this->defaultValidatorChain); 455 | } 456 | } 457 | 458 | /** 459 | * Inject filter and validator chains with the plugin managers from 460 | * the default chains, if present. 461 | * 462 | * This ensures custom plugins are made available to the input instance. 463 | * 464 | * @param InputInterface $input 465 | * @return void 466 | */ 467 | protected function injectFilterAndValidatorChainsWithPluginManagers(InputInterface $input) 468 | { 469 | if ($this->defaultFilterChain) { 470 | $input->getFilterChain() 471 | ? $input->getFilterChain()->setPluginManager($this->defaultFilterChain->getPluginManager()) 472 | : $input->setFilterChain(clone $this->defaultFilterChain); 473 | } 474 | 475 | if ($this->defaultValidatorChain) { 476 | $input->getValidatorChain() 477 | ? $input->getValidatorChain()->setPluginManager($this->defaultValidatorChain->getPluginManager()) 478 | : $input->setValidatorChain(clone $this->defaultValidatorChain); 479 | } 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /src/BaseInputFilter.php: -------------------------------------------------------------------------------- 1 | inputs); 74 | } 75 | 76 | /** 77 | * Add an input to the input filter 78 | * 79 | * @param InputInterface|InputFilterInterface $input 80 | * @param null|string $name Name used to retrieve this input 81 | * @throws Exception\InvalidArgumentException 82 | * @return InputFilterInterface 83 | */ 84 | public function add($input, $name = null) 85 | { 86 | if (! $input instanceof InputInterface && ! $input instanceof InputFilterInterface) { 87 | throw new Exception\InvalidArgumentException(sprintf( 88 | '%s expects an instance of %s or %s as its first argument; received "%s"', 89 | __METHOD__, 90 | InputInterface::class, 91 | InputFilterInterface::class, 92 | (is_object($input) ? get_class($input) : gettype($input)) 93 | )); 94 | } 95 | 96 | if ($input instanceof InputInterface && (empty($name) || is_int($name))) { 97 | $name = $input->getName(); 98 | } 99 | 100 | if (isset($this->inputs[$name]) && $this->inputs[$name] instanceof InputInterface) { 101 | // The element already exists, so merge the config. Please note 102 | // that this merges the new input into the original. 103 | $original = $this->inputs[$name]; 104 | $original->merge($input); 105 | return $this; 106 | } 107 | 108 | $this->inputs[$name] = $input; 109 | return $this; 110 | } 111 | 112 | /** 113 | * Replace a named input 114 | * 115 | * @param mixed $input Any of the input types allowed on add() method. 116 | * @param string $name Name of the input to replace 117 | * @throws Exception\InvalidArgumentException If input to replace not exists. 118 | * @return self 119 | */ 120 | public function replace($input, $name) 121 | { 122 | if (! array_key_exists($name, $this->inputs)) { 123 | throw new Exception\InvalidArgumentException(sprintf( 124 | '%s: no input found matching "%s"', 125 | __METHOD__, 126 | $name 127 | )); 128 | } 129 | 130 | $this->remove($name); 131 | $this->add($input, $name); 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * Retrieve a named input 138 | * 139 | * @param string $name 140 | * @throws Exception\InvalidArgumentException 141 | * @return InputInterface|InputFilterInterface 142 | */ 143 | public function get($name) 144 | { 145 | if (! array_key_exists($name, $this->inputs)) { 146 | throw new Exception\InvalidArgumentException(sprintf( 147 | '%s: no input found matching "%s"', 148 | __METHOD__, 149 | $name 150 | )); 151 | } 152 | return $this->inputs[$name]; 153 | } 154 | 155 | /** 156 | * Test if an input or input filter by the given name is attached 157 | * 158 | * @param string $name 159 | * @return bool 160 | */ 161 | public function has($name) 162 | { 163 | return array_key_exists($name, $this->inputs); 164 | } 165 | 166 | /** 167 | * Remove a named input 168 | * 169 | * @param string $name 170 | * @return InputFilterInterface 171 | */ 172 | public function remove($name) 173 | { 174 | unset($this->inputs[$name]); 175 | return $this; 176 | } 177 | 178 | /** 179 | * Set data to use when validating and filtering 180 | * 181 | * @param null|array|Traversable $data null is cast to an empty array. 182 | * @throws Exception\InvalidArgumentException 183 | * @return InputFilterInterface 184 | */ 185 | public function setData($data) 186 | { 187 | // A null value indicates an empty set 188 | if (null === $data) { 189 | $data = []; 190 | } 191 | 192 | if ($data instanceof Traversable) { 193 | $data = ArrayUtils::iteratorToArray($data); 194 | } 195 | 196 | if (! is_array($data)) { 197 | throw new Exception\InvalidArgumentException(sprintf( 198 | '%s expects an array or Traversable argument; received %s', 199 | __METHOD__, 200 | (is_object($data) ? get_class($data) : gettype($data)) 201 | )); 202 | } 203 | 204 | $this->setUnfilteredData($data); 205 | 206 | $this->data = $data; 207 | $this->populate(); 208 | 209 | return $this; 210 | } 211 | 212 | /** 213 | * Is the data set valid? 214 | * 215 | * @param mixed|null $context 216 | * @throws Exception\RuntimeException 217 | * @return bool 218 | */ 219 | public function isValid($context = null) 220 | { 221 | if (null === $this->data) { 222 | throw new Exception\RuntimeException(sprintf( 223 | '%s: no data present to validate!', 224 | __METHOD__ 225 | )); 226 | } 227 | 228 | $inputs = $this->validationGroup ?: array_keys($this->inputs); 229 | return $this->validateInputs($inputs, $this->data, $context); 230 | } 231 | 232 | /** 233 | * Validate a set of inputs against the current data 234 | * 235 | * @param string[] $inputs Array of input names. 236 | * @param array|ArrayAccess $data 237 | * @param mixed|null $context 238 | * @return bool 239 | */ 240 | protected function validateInputs(array $inputs, $data = [], $context = null) 241 | { 242 | $inputContext = $context ?: (array_merge($this->getRawValues(), (array) $data)); 243 | 244 | $this->validInputs = []; 245 | $this->invalidInputs = []; 246 | $valid = true; 247 | 248 | foreach ($inputs as $name) { 249 | $input = $this->inputs[$name]; 250 | 251 | // Validate an input filter 252 | if ($input instanceof InputFilterInterface) { 253 | if (! $input->isValid($context)) { 254 | $this->invalidInputs[$name] = $input; 255 | $valid = false; 256 | continue; 257 | } 258 | $this->validInputs[$name] = $input; 259 | continue; 260 | } 261 | 262 | // If input is not InputInterface then silently continue (BC safe) 263 | if (! $input instanceof InputInterface) { 264 | continue; 265 | } 266 | 267 | // If input is optional (not required), and value is not set, then ignore. 268 | if (! array_key_exists($name, $data) 269 | && ! $input->isRequired() 270 | ) { 271 | continue; 272 | } 273 | 274 | // Validate an input 275 | if (! $input->isValid($inputContext)) { 276 | // Validation failure 277 | $this->invalidInputs[$name] = $input; 278 | $valid = false; 279 | 280 | if ($input->breakOnFailure()) { 281 | return false; 282 | } 283 | continue; 284 | } 285 | $this->validInputs[$name] = $input; 286 | } 287 | 288 | return $valid; 289 | } 290 | 291 | /** 292 | * Provide a list of one or more elements indicating the complete set to validate 293 | * 294 | * When provided, calls to {@link isValid()} will only validate the provided set. 295 | * 296 | * If the initial value is {@link VALIDATE_ALL}, the current validation group, if 297 | * any, should be cleared. 298 | * 299 | * Implementations should allow passing a single array value, or multiple arguments, 300 | * each specifying a single input. 301 | * 302 | * @param mixed $name 303 | * @throws Exception\InvalidArgumentException 304 | * @return InputFilterInterface 305 | */ 306 | public function setValidationGroup($name) 307 | { 308 | if ($name === self::VALIDATE_ALL) { 309 | $this->validationGroup = null; 310 | foreach ($this->getInputs() as $input) { 311 | if ($input instanceof InputFilterInterface) { 312 | $input->setValidationGroup(self::VALIDATE_ALL); 313 | } 314 | } 315 | return $this; 316 | } 317 | 318 | if (is_array($name)) { 319 | $inputs = []; 320 | foreach ($name as $key => $value) { 321 | if (! $this->has($key)) { 322 | $inputs[] = $value; 323 | continue; 324 | } 325 | 326 | $inputs[] = $key; 327 | 328 | if ($this->inputs[$key] instanceof InputFilterInterface) { 329 | // Recursively populate validation groups for sub input filters 330 | $this->inputs[$key]->setValidationGroup($value); 331 | } 332 | } 333 | } else { 334 | $inputs = func_get_args(); 335 | } 336 | 337 | if (! empty($inputs)) { 338 | $this->validateValidationGroup($inputs); 339 | $this->validationGroup = $inputs; 340 | } 341 | 342 | return $this; 343 | } 344 | 345 | /** 346 | * Return a list of inputs that were invalid. 347 | * 348 | * Implementations should return an associative array of name/input pairs 349 | * that failed validation. 350 | * 351 | * @return InputInterface[] 352 | */ 353 | public function getInvalidInput() 354 | { 355 | return is_array($this->invalidInputs) ? $this->invalidInputs : []; 356 | } 357 | 358 | /** 359 | * Return a list of inputs that were valid. 360 | * 361 | * Implementations should return an associative array of name/input pairs 362 | * that passed validation. 363 | * 364 | * @return InputInterface[] 365 | */ 366 | public function getValidInput() 367 | { 368 | return is_array($this->validInputs) ? $this->validInputs : []; 369 | } 370 | 371 | /** 372 | * Retrieve a value from a named input 373 | * 374 | * @param string $name 375 | * @throws Exception\InvalidArgumentException 376 | * @return mixed 377 | */ 378 | public function getValue($name) 379 | { 380 | if (! array_key_exists($name, $this->inputs)) { 381 | throw new Exception\InvalidArgumentException(sprintf( 382 | '%s expects a valid input name; "%s" was not found in the filter', 383 | __METHOD__, 384 | $name 385 | )); 386 | } 387 | $input = $this->inputs[$name]; 388 | 389 | if ($input instanceof InputFilterInterface) { 390 | return $input->getValues(); 391 | } 392 | 393 | return $input->getValue(); 394 | } 395 | 396 | /** 397 | * Return a list of filtered values 398 | * 399 | * List should be an associative array, with the values filtered. If 400 | * validation failed, this should raise an exception. 401 | * 402 | * @return array 403 | */ 404 | public function getValues() 405 | { 406 | $inputs = $this->validationGroup ?: array_keys($this->inputs); 407 | $values = []; 408 | foreach ($inputs as $name) { 409 | $input = $this->inputs[$name]; 410 | 411 | if ($input instanceof InputFilterInterface) { 412 | $values[$name] = $input->getValues(); 413 | continue; 414 | } 415 | $values[$name] = $input->getValue(); 416 | } 417 | return $values; 418 | } 419 | 420 | /** 421 | * Retrieve a raw (unfiltered) value from a named input 422 | * 423 | * @param string $name 424 | * @throws Exception\InvalidArgumentException 425 | * @return mixed 426 | */ 427 | public function getRawValue($name) 428 | { 429 | if (! array_key_exists($name, $this->inputs)) { 430 | throw new Exception\InvalidArgumentException(sprintf( 431 | '%s expects a valid input name; "%s" was not found in the filter', 432 | __METHOD__, 433 | $name 434 | )); 435 | } 436 | $input = $this->inputs[$name]; 437 | if ($input instanceof InputFilterInterface) { 438 | return $input->getRawValues(); 439 | } 440 | return $input->getRawValue(); 441 | } 442 | 443 | /** 444 | * Return a list of unfiltered values 445 | * 446 | * List should be an associative array of named input/value pairs, 447 | * with the values unfiltered. 448 | * 449 | * @return array 450 | */ 451 | public function getRawValues() 452 | { 453 | $values = []; 454 | foreach ($this->inputs as $name => $input) { 455 | if ($input instanceof InputFilterInterface) { 456 | $values[$name] = $input->getRawValues(); 457 | continue; 458 | } 459 | 460 | $values[$name] = $input->getRawValue(); 461 | } 462 | return $values; 463 | } 464 | 465 | /** 466 | * Return a list of validation failure messages 467 | * 468 | * Should return an associative array of named input/message list pairs. 469 | * Pairs should only be returned for inputs that failed validation. 470 | * 471 | * @return array 472 | */ 473 | public function getMessages() 474 | { 475 | $messages = []; 476 | foreach ($this->getInvalidInput() as $name => $input) { 477 | $messages[$name] = $input->getMessages(); 478 | } 479 | 480 | return $messages; 481 | } 482 | 483 | /** 484 | * Ensure all names of a validation group exist as input in the filter 485 | * 486 | * @param string[] $inputs Input names 487 | * @return void 488 | * @throws Exception\InvalidArgumentException 489 | */ 490 | protected function validateValidationGroup(array $inputs) 491 | { 492 | foreach ($inputs as $name) { 493 | if (! array_key_exists($name, $this->inputs)) { 494 | throw new Exception\InvalidArgumentException(sprintf( 495 | 'setValidationGroup() expects a list of valid input names; "%s" was not found', 496 | $name 497 | )); 498 | } 499 | } 500 | } 501 | 502 | /** 503 | * Populate the values of all attached inputs 504 | * 505 | * @return void 506 | */ 507 | protected function populate() 508 | { 509 | foreach (array_keys($this->inputs) as $name) { 510 | $input = $this->inputs[$name]; 511 | 512 | if ($input instanceof CollectionInputFilter) { 513 | $input->clearValues(); 514 | $input->clearRawValues(); 515 | } 516 | 517 | if (! array_key_exists($name, $this->data)) { 518 | // No value; clear value in this input 519 | if ($input instanceof InputFilterInterface) { 520 | $input->setData([]); 521 | continue; 522 | } 523 | 524 | if ($input instanceof Input) { 525 | $input->resetValue(); 526 | continue; 527 | } 528 | 529 | $input->setValue(null); 530 | continue; 531 | } 532 | 533 | $value = $this->data[$name]; 534 | 535 | if ($input instanceof InputFilterInterface) { 536 | // Fixes #159 537 | if (! is_array($value) && ! $value instanceof Traversable) { 538 | $value = []; 539 | } 540 | 541 | $input->setData($value); 542 | continue; 543 | } 544 | 545 | $input->setValue($value); 546 | } 547 | } 548 | 549 | /** 550 | * Is the data set has unknown input ? 551 | * 552 | * @throws Exception\RuntimeException 553 | * @return bool 554 | */ 555 | public function hasUnknown() 556 | { 557 | return $this->getUnknown() ? true : false; 558 | } 559 | 560 | /** 561 | * Return the unknown input 562 | * 563 | * @throws Exception\RuntimeException 564 | * @return array 565 | */ 566 | public function getUnknown() 567 | { 568 | if (null === $this->data) { 569 | throw new Exception\RuntimeException(sprintf( 570 | '%s: no data present!', 571 | __METHOD__ 572 | )); 573 | } 574 | 575 | $data = array_keys($this->data); 576 | $inputs = array_keys($this->inputs); 577 | $diff = array_diff($data, $inputs); 578 | 579 | $unknownInputs = []; 580 | $intersect = array_intersect($diff, $data); 581 | if (! empty($intersect)) { 582 | foreach ($intersect as $key) { 583 | $unknownInputs[$key] = $this->data[$key]; 584 | } 585 | } 586 | 587 | return $unknownInputs; 588 | } 589 | 590 | /** 591 | * Get an array of all inputs 592 | * 593 | * @return InputInterface[]|InputFilterInterface[] 594 | */ 595 | public function getInputs() 596 | { 597 | return $this->inputs; 598 | } 599 | 600 | /** 601 | * Merges the inputs from an InputFilter into the current one 602 | * 603 | * @param BaseInputFilter $inputFilter 604 | * 605 | * @return self 606 | */ 607 | public function merge(BaseInputFilter $inputFilter) 608 | { 609 | foreach ($inputFilter->getInputs() as $name => $input) { 610 | $this->add($input, $name); 611 | } 612 | 613 | return $this; 614 | } 615 | 616 | /** 617 | * @return array|object 618 | */ 619 | public function getUnfilteredData() 620 | { 621 | return $this->unfilteredData; 622 | } 623 | 624 | /** 625 | * @param array|object $data 626 | * @return $this 627 | */ 628 | public function setUnfilteredData($data) 629 | { 630 | $this->unfilteredData = $data; 631 | return $this; 632 | } 633 | } 634 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file, in reverse chronological order by release. 4 | 5 | ## 2.10.2 - TBD 6 | 7 | ### Added 8 | 9 | - Nothing. 10 | 11 | ### Changed 12 | 13 | - Nothing. 14 | 15 | ### Deprecated 16 | 17 | - Nothing. 18 | 19 | ### Removed 20 | 21 | - Nothing. 22 | 23 | ### Fixed 24 | 25 | - Nothing. 26 | 27 | ## 2.10.1 - 2019-08-28 28 | 29 | ### Added 30 | 31 | - Nothing. 32 | 33 | ### Changed 34 | 35 | - Nothing. 36 | 37 | ### Deprecated 38 | 39 | - Nothing. 40 | 41 | ### Removed 42 | 43 | - Nothing. 44 | 45 | ### Fixed 46 | 47 | - [#185](https://github.com/zendframework/zend-inputfilter/pull/185) fixes 48 | validation response on invalid file upload request. 49 | 50 | - [#181](https://github.com/zendframework/zend-inputfilter/pull/181) fixes 51 | missing abstract service factory registration in `Module` as per the 52 | [latest documentation](https://docs.zendframework.com/zend-inputfilter/specs/#setup). 53 | In particular, it ensures that the `InputFilterAbstractFactory` is registered 54 | under the `input_filters` configuration. 55 | 56 | - [#180](https://github.com/zendframework/zend-inputfilter/pull/180) fixes 57 | attaching validators on creation of InputFilter - `priority` value is now used. 58 | 59 | ## 2.10.0 - 2019-01-30 60 | 61 | ### Added 62 | 63 | - [#176](https://github.com/zendframework/zend-inputfilter/pull/176) adds the interface `UnfilteredDataInterface`, with the following methods: 64 | 65 | ```php 66 | public function getUnfilteredData() : array|object; 67 | public function setUnfilteredData(array|object $data) : $this; 68 | ``` 69 | 70 | By default, the `BaseInputFilter` now implements this interface. 71 | 72 | The primary purpose of the interface is to allow the ability to access ALL 73 | original raw data, and not just the data the input filter knows about. This is 74 | particularly useful with collections. 75 | 76 | ### Changed 77 | 78 | - Nothing. 79 | 80 | ### Deprecated 81 | 82 | - Nothing. 83 | 84 | ### Removed 85 | 86 | - Nothing. 87 | 88 | ### Fixed 89 | 90 | - Nothing. 91 | 92 | ## 2.9.1 - 2019-01-07 93 | 94 | ### Added 95 | 96 | - [#174](https://github.com/zendframework/zend-inputfilter/pull/174) adds support for PHP 7.3. 97 | 98 | ### Changed 99 | 100 | - Nothing. 101 | 102 | ### Deprecated 103 | 104 | - Nothing. 105 | 106 | ### Removed 107 | 108 | - Nothing. 109 | 110 | ### Fixed 111 | 112 | - [#175](https://github.com/zendframework/zend-inputfilter/pull/175) fixes a regression introduced in 2.9.0 when overriding the default 113 | validator of a `FileInput`. 2.9.0 changed the default to use the 114 | fully-qualified class name of `Zend\Validator\File\Upload` as the service, 115 | instead of the previous 'fileuploadfile`; this release returns to the original 116 | behavior. 117 | 118 | ## 2.9.0 - 2018-12-17 119 | 120 | ### Added 121 | 122 | - [#172](https://github.com/zendframework/zend-inputfilter/pull/172) adds support for PSR-7 `UploadedFileInterface` to `Zend\InputFilter\FileInput`. 123 | It adds a new interface, `Zend\InputFilter\FileInput\FileInputDecoratorInterface`, 124 | which defines methods required for validating and filtering file uploads. It 125 | also provides two implementations of it, one for standard SAPI file uploads, 126 | and the other for PSR-7 uploads. The `FileInput` class does detection on the 127 | value being tested and decorates itself using the appropriate decorator, which 128 | then performs the work of validating and filtering the upload or uploads. 129 | 130 | - [#170](https://github.com/zendframework/zend-inputfilter/pull/170) adds the ability to set a "required" message on a `CollectionInputFilter`. 131 | By default, such instances will lazy-load a `NotEmpty` validator, and use its 132 | messages to report that the collection was empty if it is marked as required. 133 | If you wish to set a different message, you have two options: 134 | 135 | - provide a custom `NotEmpty` validator via the new method 136 | `setNotEmptyValidator()`. 137 | 138 | - if using a factory, provide the key `required_message` as a sibling to 139 | `required`, containing the custom message. This will replace the typical 140 | `IS_EMPTY` message. 141 | 142 | ### Changed 143 | 144 | - Nothing. 145 | 146 | ### Deprecated 147 | 148 | - Nothing. 149 | 150 | ### Removed 151 | 152 | - Nothing. 153 | 154 | ### Fixed 155 | 156 | - Nothing. 157 | 158 | ## 2.8.3 - 2018-12-13 159 | 160 | ### Added 161 | 162 | - Nothing. 163 | 164 | ### Changed 165 | 166 | - Nothing. 167 | 168 | ### Deprecated 169 | 170 | - Nothing. 171 | 172 | ### Removed 173 | 174 | - Nothing. 175 | 176 | ### Fixed 177 | 178 | - [#167](https://github.com/zendframework/zend-inputfilter/pull/167) fixes the combination of marking an `ArrayInput` required, and passing an 179 | empty array for validation; it now correctly detects these as invalid. 180 | 181 | ## 2.8.2 - 2018-05-14 182 | 183 | ### Added 184 | 185 | - Nothing. 186 | 187 | ### Changed 188 | 189 | - Nothing. 190 | 191 | ### Deprecated 192 | 193 | - Nothing. 194 | 195 | ### Removed 196 | 197 | - Nothing. 198 | 199 | ### Fixed 200 | 201 | - [#163](https://github.com/zendframework/zend-inputfilter/pull/163) adds code to `BaseInputFilter::populate()` to detect non-iterable, 202 | non-null values passed as a value for a composed input filter. Previously, these would trigger 203 | an exception; they now instead result in an empty array being used to populate the 204 | input filter, which will generally result in invalidation without causing an 205 | exception. 206 | 207 | - [#162](https://github.com/zendframework/zend-inputfilter/pull/162) fixes incorrect abstract service factory registration in `ConfigProvider`as per 208 | the [latest documentation](https://docs.zendframework.com/zend-inputfilter/specs/#setup). In particular, it ensures that the `InputFilterAbstractFactory` 209 | is registered under the `input_filters` configuration instead of the 210 | `dependencies` configuration. 211 | 212 | ## 2.8.1 - 2018-01-22 213 | 214 | ### Added 215 | 216 | - Nothing. 217 | 218 | ### Changed 219 | 220 | - Nothing. 221 | 222 | ### Deprecated 223 | 224 | - Nothing. 225 | 226 | ### Removed 227 | 228 | - Nothing. 229 | 230 | ### Fixed 231 | 232 | - [#160](https://github.com/zendframework/zend-inputfilter/pull/160) adds 233 | zend-servicemanager as a direct requirement, rather than a suggestion. The 234 | package has not worked without it since [#67](https://github.com/zendframework/zend-inputfilter/pull/67) 235 | was merged for the 2.6.1 release. 236 | 237 | - [#161](https://github.com/zendframework/zend-inputfilter/pull/161) fixes an 238 | issue whereby an input filter receiving a `null` value to `setData()` would 239 | raise an exception, instead of being treated as an empty data set. 240 | 241 | ## 2.8.0 - 2017-12-04 242 | 243 | ### Added 244 | 245 | - [#135](https://github.com/zendframework/zend-inputfilter/pull/135) adds 246 | `Zend\InputFilter\OptionalInputFilter`, which allows defining optional sets of 247 | data. This acts like a standard input filter, but is considered valid if no 248 | data, `null` data, or empty data sets are provided to it; if a non-empty data 249 | set is provided, it will run normal validations. 250 | 251 | - [#142](https://github.com/zendframework/zend-inputfilter/pull/142) adds 252 | support for PHP 7.2. 253 | 254 | ### Changed 255 | 256 | - Nothing. 257 | 258 | ### Deprecated 259 | 260 | - Nothing. 261 | 262 | ### Removed 263 | 264 | - [#142](https://github.com/zendframework/zend-inputfilter/pull/142) removes 265 | support for HHVM. 266 | 267 | ### Fixed 268 | 269 | - Nothing. 270 | 271 | ## 2.7.6 - 2017-12-04 272 | 273 | ### Added 274 | 275 | - Nothing. 276 | 277 | ### Changed 278 | 279 | - Nothing. 280 | 281 | ### Deprecated 282 | 283 | - Nothing. 284 | 285 | ### Removed 286 | 287 | - Nothing. 288 | 289 | ### Fixed 290 | 291 | - [#156](https://github.com/zendframework/zend-inputfilter/pull/156) fixes an 292 | issue introduced in 2.7.5 whereby the filter and validator chains composed in 293 | inputs pulled from the `InputFilterPluginManager` were not receiving the 294 | default filter and validator plugin manager instances. A solution was created 295 | that preserves the original behavior as well as the bugfix that created the 296 | regression. 297 | 298 | ## 2.7.5 - 2017-11-07 299 | 300 | ### Added 301 | 302 | - Nothing. 303 | 304 | ### Deprecated 305 | 306 | - Nothing. 307 | 308 | ### Removed 309 | 310 | - Nothing. 311 | 312 | ### Fixed 313 | 314 | - [#151](https://github.com/zendframework/zend-inputfilter/pull/151) fixes an 315 | issue in `Factory::createInput()` introduced in 316 | [#2](https://github.com/zendframework/zend-inputfilter/pull/2) whereby an 317 | input pulled from the input filter manager would be injected with the default 318 | filter and validator chains, overwriting any chains that were set during 319 | instantiation and/or `init()`. They are now never overwritten. 320 | 321 | - [#149](https://github.com/zendframework/zend-inputfilter/pull/149) fixes an 322 | issue with how error messages for collection input field items were reported; 323 | previously, as soon as one item in the collection failed, the same validation 324 | message was propagated to all other items. This is now resolved. 325 | 326 | - [#131](https://github.com/zendframework/zend-inputfilter/pull/131) fixes a 327 | regression introduced in version 2.2.6 within 328 | `BaseInputFilter::setValidatorGroup()` whereby it began emitting exceptions if 329 | a given input was not an input filter. This raises issues when mixing input 330 | filters and inputs in the same validator group specification, as you will 331 | generally provide the input names as keys instead of values. The patch provide 332 | ensures both styles work going forwards. 333 | 334 | ## 2.7.4 - 2017-05-18 335 | 336 | ### Added 337 | 338 | - Nothing. 339 | 340 | ### Changes 341 | 342 | - [#122](https://github.com/zendframework/zend-inputfilter/pull/122) maps the 343 | `Zend\InputFilter\InputFilterPluginManager` service to 344 | `Zend\InputFilter\InputFilterPluginManagerFactory`, and adds an alias from 345 | `InputFitlerPluginManager` to the fully qualified class name. This change 346 | allows you to request the service using either the original short name, or the 347 | fully qualified class name. 348 | 349 | ### Deprecated 350 | 351 | - Nothing. 352 | 353 | ### Removed 354 | 355 | - Nothing. 356 | 357 | ### Fixed 358 | 359 | - [#137](https://github.com/zendframework/zend-inputfilter/pull/137) fixes how the 360 | `InputFilterPluginManagerFactory` factory initializes the plugin manager 361 | instance, ensuring it is injecting the relevant configuration from the 362 | `config` service and thus seeding it with configured input filter services. 363 | This means that the `input_filters` configuration will now be honored in 364 | non-zend-mvc contexts. 365 | 366 | ## 2.7.3 - 2016-08-18 367 | 368 | ### Added 369 | 370 | - Nothing. 371 | 372 | ### Deprecated 373 | 374 | - Nothing. 375 | 376 | ### Removed 377 | 378 | - Nothing. 379 | 380 | ### Fixed 381 | 382 | - [#115](https://github.com/zendframework/zend-inputfilter/pull/115) fixes 383 | retrieval of unknown fields when using a `CollectionInputFilter`. Previously, 384 | it returned all fields in the collection, not just the unknown fields, which 385 | was a different behavior from all other input filters. Now it will return only 386 | the unknown fields for each collection. 387 | - [#108](https://github.com/zendframework/zend-inputfilter/pull/108) fixes 388 | the `InputFilterPluginManager::populateFactory()` method to restore behavior 389 | from prior to the 2.7 series; specifically, previously it would inject itself 390 | as the plugin manager to input filter factories when under zend-servicemanager 391 | v2; it now will do so again. 392 | - [#116](https://github.com/zendframework/zend-inputfilter/pull/116) fixes the 393 | behavior of `CollectionInputFilter::setData()`. Prior to this release, it 394 | would validate whether the data represented a collection (i.e., it was an 395 | array or traversable) and whether individual items in the collection were data 396 | sets (i.e., arrays or traversables) only during `isValid()` and/or 397 | `getUnknown()` calls, raising exceptions during runtime. These should have 398 | been considered invalid arguments when the data was provided; they now are. As 399 | such, `setData()` now raises `Zend\InputFilter\Exception\InvalidArgumentException` 400 | for invalid data, ensuring that `isValid()` and `getUnknown()` only ever 401 | operate on usable collections and collection sets. 402 | - [#118](https://github.com/zendframework/zend-inputfilter/pull/118) fixes 403 | aggregation of error messages when validating collections to ensure only the 404 | error messages specific to a given datum are presented. 405 | 406 | ## 2.7.2 - 2016-06-11 407 | 408 | ### Added 409 | 410 | - [#105](https://github.com/zendframework/zend-inputfilter/pull/105) adds and 411 | publishes the documentation to https://zendframework.github.io/zend-inputfilter 412 | 413 | ### Deprecated 414 | 415 | - Nothing. 416 | 417 | ### Removed 418 | 419 | - Nothing. 420 | 421 | ### Fixed 422 | 423 | - [#110](https://github.com/zendframework/zend-inputfilter/pull/110) fixes an 424 | issue with `InputFilterAbstractServiceFactory` whereby it was not working when 425 | the provided container is not a plugin manager, but rather the application 426 | container. 427 | 428 | ## 2.7.1 - 2016-04-18 429 | 430 | ### Added 431 | 432 | - Nothing. 433 | 434 | ### Deprecated 435 | 436 | - Nothing. 437 | 438 | ### Removed 439 | 440 | - Nothing. 441 | 442 | ### Fixed 443 | 444 | - [#104](https://github.com/zendframework/zend-inputfilter/pull/104) fixes the 445 | `Module::init()` method to properly receive a `ModuleManager` instance, and 446 | not expect a `ModuleEvent`. 447 | 448 | ## 2.7.0 - 2016-04-07 449 | 450 | ### Added 451 | 452 | - [#3](https://github.com/zendframework/zend-inputfilter/pull/3) updates the 453 | `InputFilterAbstractServiceFactory` to inject the created input filter factory 454 | with the `InputFilterManager` service, ensuring that the generated factory can 455 | pull named input filters and inputs from the container as needed. 456 | - [#100](https://github.com/zendframework/zend-inputfilter/pull/100) adds a 457 | number of classes, in order to better allow usage as a standalone component: 458 | - `InputFilterPluginManagerFactory`, ported from zend-mvc, allows creating and 459 | returning an `InputFilterPluginManager`. 460 | - `ConfigProvider` maps the `InputFilterManager` service to the above factory, 461 | and enables the `InputFilterAbstractServiceFactory`. 462 | - `Module` does the same as `ConfigProvider`, within a zend-mvc context, and 463 | also registers a specification with the zend-modulemanager `ServiceListener` 464 | to allow modules to configure the input filter plugin manager. 465 | 466 | ### Deprecated 467 | 468 | - Nothing. 469 | 470 | ### Removed 471 | 472 | - Nothing. 473 | 474 | ### Fixed 475 | 476 | - Nothing. 477 | 478 | ## 2.6.1 - 2016-04-07 479 | 480 | ### Added 481 | 482 | - [#68](https://github.com/zendframework/zend-inputfilter/pull/68) adds support 483 | for using *either* named keys *or* a `name` element in input filter specs 484 | parsed by the `InputFilterAbstractServiceFactory`. 485 | 486 | ### Deprecated 487 | 488 | - Nothing. 489 | 490 | ### Removed 491 | 492 | - Nothing. 493 | 494 | ### Fixed 495 | 496 | - [#67](https://github.com/zendframework/zend-inputfilter/pull/67) and 497 | [#73](https://github.com/zendframework/zend-inputfilter/pull/73) fix 498 | localization of the `NotEmpty` validation error message (created for any 499 | required input for which a value was not provided). 500 | 501 | ## 2.6.0 - 2016-02-18 502 | 503 | ### Added 504 | 505 | - Nothing. 506 | 507 | ### Deprecated 508 | 509 | - Nothing. 510 | 511 | ### Removed 512 | 513 | - Nothing. 514 | 515 | ### Fixed 516 | 517 | - [#86](https://github.com/zendframework/zend-inputfilter/pull/86), 518 | [#95](https://github.com/zendframework/zend-inputfilter/pull/95), and 519 | [#96](https://github.com/zendframework/zend-inputfilter/pull/96) update the 520 | component to be forwards-compatible with zend-servicemanager v3. 521 | - [#72](https://github.com/zendframework/zend-inputfilter/pull/72) `ArrayInput` 522 | value is properly reset after `BaseInputFilter::setData()` 523 | 524 | ## 2.5.5 - 2015-09-03 525 | 526 | ### Added 527 | 528 | - Nothing. 529 | 530 | ### Deprecated 531 | 532 | - Nothing. 533 | 534 | ### Removed 535 | 536 | - Nothing. 537 | 538 | ### Fixed 539 | 540 | - [#22](https://github.com/zendframework/zend-inputfilter/pull/22) adds tests to 541 | verify two conditions around inputs with fallback values: 542 | - If the input was not in the data set, it should not be represented in either 543 | the list of valid *or* invalid inputs. 544 | - If the input *was* in the data set, but empty, it should be represented in 545 | the list of valid inputs. 546 | - [#31](https://github.com/zendframework/zend-inputfilter/pull/31) updates the 547 | `InputFilterInterface::add()` docblock to match existing, shipped implementations. 548 | - [#25](https://github.com/zendframework/zend-inputfilter/pull/25) updates the 549 | input filter to prevent validation of missing optional fields (a BC break 550 | since 2.3.9). This change likely requires changes to your inputs as follows: 551 | 552 | ```php 553 | $input = new Input(); 554 | $input->setAllowEmpty(true); // Disable BC Break logic related to treat `null` values as valid empty value instead *not set*. 555 | $input->setContinueIfEmpty(true); // Disable BC Break logic related to treat `null` values as valid empty value instead *not set*. 556 | $input->getValidatorChain()->attach( 557 | new Zend\Validator\NotEmpty(), 558 | true // break chain on failure 559 | 560 | ); 561 | ``` 562 | 563 | ```php 564 | $inputSpecification = [ 565 | 'allow_empty' => true, 566 | 'continue_if_empty' => true, 567 | 'validators' => [ 568 | [ 569 | 'break_chain_on_failure' => true, 570 | 'name' => 'Zend\\Validator\\NotEmpty', 571 | ], 572 | ], 573 | ]; 574 | ``` 575 | - [Numerous fixes](https://github.com/zendframework/zend-inputfilter/milestones/2.4.8) 576 | aimed at bringing the functionality back to the pre-2.4 code, and improving 577 | quality overall of the component via increased testing and test coverage. 578 | 579 | ## 2.5.4 - 2015-08-11 580 | 581 | ### Added 582 | 583 | - Nothing. 584 | 585 | ### Deprecated 586 | 587 | - Nothing. 588 | 589 | ### Removed 590 | 591 | - Nothing. 592 | 593 | ### Fixed 594 | 595 | - [#15](https://github.com/zendframework/zend-inputfilter/pull/15) ensures that 596 | `ArrayAccess` data provided to an input filter using `setData()` can be 597 | validated, a scenario that broke with [#7](https://github.com/zendframework/zend-inputfilter/pull/7). 598 | 599 | ## 2.5.3 - 2015-08-03 600 | 601 | ### Added 602 | 603 | - Nothing. 604 | 605 | ### Deprecated 606 | 607 | - Nothing. 608 | 609 | ### Removed 610 | 611 | - Nothing. 612 | 613 | ### Fixed 614 | 615 | - [#10](https://github.com/zendframework/zend-inputfilter/pull/10) fixes an 616 | issue with with the combination of `required`, `allow_empty`, and presence of 617 | a fallback value on an input introduced in 2.4.5. Prior to the fix, the 618 | fallback value was no longer considered when the value was required but no 619 | value was provided; it now is. 620 | 621 | ## 2.5.2 - 2015-07-28 622 | 623 | ### Added 624 | 625 | - [#2](https://github.com/zendframework/zend-inputfilter/pull/2) adds support 626 | in `Zend\InputFilter\Factory` for using the composed `InputFilterManager` to 627 | retrieve an input of a given `type` based on configuration; only if the type 628 | is not available in the factory will it attempt to directly instantiate it. 629 | 630 | ### Deprecated 631 | 632 | - Nothing. 633 | 634 | ### Removed 635 | 636 | - Nothing. 637 | 638 | ### Fixed 639 | 640 | - [#7](https://github.com/zendframework/zend-inputfilter/pull/7) fixes an issue 641 | with the combination of `required` and `allow_empty`, now properly 642 | invalidating a data set if the `required` input is missing entirely 643 | (previously, it would consider the data set valid, and auto-initialize the 644 | missing input to `null`). 645 | 646 | ## 2.4.8 - TBD 647 | 648 | ### Added 649 | 650 | - Nothing. 651 | 652 | ### Deprecated 653 | 654 | - [#26](https://github.com/zendframework/zend-inputfilter/pull/26) Deprecate magic logic for auto attach a NonEmpty 655 | validator with breakChainOnFailure = true. Instead append NonEmpty validator when desired. 656 | 657 | ```php 658 | $input = new Zend\InputFilter\Input(); 659 | $input->setContinueIfEmpty(true); 660 | $input->setAllowEmpty(true); 661 | $input->getValidatorChain()->attach(new Zend\Validator\NotEmpty(), /* break chain on failure */ true); 662 | ``` 663 | 664 | ### Removed 665 | 666 | - Nothing. 667 | 668 | ### Fixed 669 | 670 | - Nothing. 671 | 672 | ## 2.4.7 - 2015-08-11 673 | 674 | ### Added 675 | 676 | - Nothing. 677 | 678 | ### Deprecated 679 | 680 | - Nothing. 681 | 682 | ### Removed 683 | 684 | - Nothing. 685 | 686 | ### Fixed 687 | 688 | - [#15](https://github.com/zendframework/zend-inputfilter/pull/15) ensures that 689 | `ArrayAccess` data provided to an input filter using `setData()` can be 690 | validated, a scenario that broke with [#7](https://github.com/zendframework/zend-inputfilter/pull/7). 691 | 692 | ## 2.4.6 - 2015-08-03 693 | 694 | ### Added 695 | 696 | - Nothing. 697 | 698 | ### Deprecated 699 | 700 | - Nothing. 701 | 702 | ### Removed 703 | 704 | - Nothing. 705 | 706 | ### Fixed 707 | 708 | - [#10](https://github.com/zendframework/zend-inputfilter/pull/10) fixes an 709 | issue with with the combination of `required`, `allow_empty`, and presence of 710 | a fallback value on an input introduced in 2.4.5. Prior to the fix, the 711 | fallback value was no longer considered when the value was required but no 712 | value was provided; it now is. 713 | 714 | ## 2.4.5 - 2015-07-28 715 | 716 | ### Added 717 | 718 | - Nothing. 719 | 720 | ### Deprecated 721 | 722 | - Nothing. 723 | 724 | ### Removed 725 | 726 | - Nothing. 727 | 728 | ### Fixed 729 | 730 | - [#7](https://github.com/zendframework/zend-inputfilter/pull/7) fixes an issue 731 | with the combination of `required` and `allow_empty`, now properly 732 | invalidating a data set if the `required` input is missing entirely 733 | (previously, it would consider the data set valid, and auto-initialize the 734 | missing input to `null`). 735 | --------------------------------------------------------------------------------