├── src ├── Exception │ ├── ExceptionInterface.php │ ├── ExtensionNotLoadedException.php │ ├── DomainException.php │ ├── RuntimeException.php │ ├── BadMethodCallException.php │ └── InvalidArgumentException.php ├── Word │ ├── CamelCaseToDash.php │ ├── DashToCamelCase.php │ ├── DashToUnderscore.php │ ├── UnderscoreToDash.php │ ├── CamelCaseToUnderscore.php │ ├── UnderscoreToCamelCase.php │ ├── SeparatorToDash.php │ ├── UnderscoreToSeparator.php │ ├── DashToSeparator.php │ ├── UnderscoreToStudlyCase.php │ ├── CamelCaseToSeparator.php │ ├── AbstractSeparator.php │ ├── Service │ │ └── SeparatorToSeparatorFactory.php │ ├── SeparatorToSeparator.php │ └── SeparatorToCamelCase.php ├── MonthSelect.php ├── DateSelect.php ├── FilterInterface.php ├── FilterProviderInterface.php ├── Dir.php ├── ToFloat.php ├── ToInt.php ├── StripNewlines.php ├── Decrypt.php ├── BaseName.php ├── Encrypt │ ├── EncryptionAlgorithmInterface.php │ └── BlockCipher.php ├── ConfigProvider.php ├── Int.php ├── Compress │ ├── CompressionAlgorithmInterface.php │ ├── AbstractCompressionAlgorithm.php │ ├── Lzf.php │ ├── Snappy.php │ ├── Bz2.php │ ├── Gz.php │ ├── Rar.php │ ├── Tar.php │ └── Zip.php ├── Null.php ├── Decompress.php ├── Module.php ├── Digits.php ├── StringToLower.php ├── StringToUpper.php ├── UpperCaseWords.php ├── AbstractUnicode.php ├── DateTimeSelect.php ├── File │ ├── LowerCase.php │ ├── UpperCase.php │ ├── Decrypt.php │ └── Encrypt.php ├── StringPrefix.php ├── Whitelist.php ├── Blacklist.php ├── StringSuffix.php ├── StaticFilter.php ├── FilterPluginManagerFactory.php ├── DateTimeFormatter.php ├── StringTrim.php ├── Callback.php ├── AbstractFilter.php ├── RealPath.php ├── AbstractDateDropdown.php ├── UriNormalize.php ├── ToNull.php ├── PregReplace.php ├── Encrypt.php ├── HtmlEntities.php ├── Compress.php ├── DataUnitFormatter.php ├── FilterChain.php └── Boolean.php ├── README.md ├── LICENSE.md ├── composer.json └── CHANGELOG.md /src/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | separator, $value); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zend-filter 2 | 3 | > ## Repository abandoned 2019-12-31 4 | > 5 | > This repository has moved to [laminas/laminas-filter](https://github.com/laminas/laminas-filter). 6 | 7 | [![Build Status](https://secure.travis-ci.org/zendframework/zend-filter.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-filter) 8 | [![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-filter/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-filter?branch=master) 9 | 10 | The `Zend\Filter` component provides a set of commonly needed data filters. It 11 | also provides a simple filter chaining mechanism by which multiple filters may 12 | be applied to a single datum in a user-defined order. 13 | 14 | 15 | - File issues at https://github.com/zendframework/zend-filter/issues 16 | - Documentation is at https://docs.zendframework.com/zend-filter/ 17 | -------------------------------------------------------------------------------- /src/ToFloat.php: -------------------------------------------------------------------------------- 1 | adapter->decrypt($value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/BaseName.php: -------------------------------------------------------------------------------- 1 | $this->getDependencyConfig(), 21 | ]; 22 | } 23 | 24 | /** 25 | * Return dependency mappings for this component. 26 | * 27 | * @return array 28 | */ 29 | public function getDependencyConfig() 30 | { 31 | return [ 32 | 'aliases' => [ 33 | 'FilterManager' => FilterPluginManager::class, 34 | ], 35 | 'factories' => [ 36 | FilterPluginManager::class => FilterPluginManagerFactory::class, 37 | ], 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Int.php: -------------------------------------------------------------------------------- 1 | filter($value); 28 | } 29 | 30 | /** 31 | * Defined by FilterInterface 32 | * 33 | * Decompresses the content $value with the defined settings 34 | * 35 | * @param string $value Content to decompress 36 | * @return string The decompressed content 37 | */ 38 | public function filter($value) 39 | { 40 | if (! is_string($value) && $value !== null) { 41 | return $value; 42 | } 43 | 44 | return $this->getAdapter()->decompress($value); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Word/CamelCaseToSeparator.php: -------------------------------------------------------------------------------- 1 | separator . '\1', $this->separator . '\1']; 31 | } else { 32 | $pattern = ['#(?<=(?:[A-Z]))([A-Z]+)([A-Z][a-z])#', '#(?<=(?:[a-z0-9]))([A-Z])#']; 33 | $replacement = ['\1' . $this->separator . '\2', $this->separator . '\1']; 34 | } 35 | 36 | return preg_replace($pattern, $replacement, $value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Module.php: -------------------------------------------------------------------------------- 1 | $provider->getDependencyConfig(), 21 | ]; 22 | } 23 | 24 | /** 25 | * Register a specification for the FilterManager with the ServiceListener. 26 | * 27 | * @param \Zend\ModuleManager\ModuleManager $moduleManager 28 | * @return void 29 | */ 30 | public function init($moduleManager) 31 | { 32 | $event = $moduleManager->getEvent(); 33 | $container = $event->getParam('ServiceManager'); 34 | $serviceListener = $container->get('ServiceListener'); 35 | 36 | $serviceListener->addServiceManager( 37 | 'FilterManager', 38 | 'filters', 39 | FilterProviderInterface::class, 40 | 'getFilterConfig' 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-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/Digits.php: -------------------------------------------------------------------------------- 1 | null, 21 | ]; 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param string|array|Traversable $encodingOrOptions OPTIONAL 27 | */ 28 | public function __construct($encodingOrOptions = null) 29 | { 30 | if ($encodingOrOptions !== null) { 31 | if (! static::isOptions($encodingOrOptions)) { 32 | $this->setEncoding($encodingOrOptions); 33 | } else { 34 | $this->setOptions($encodingOrOptions); 35 | } 36 | } 37 | } 38 | 39 | /** 40 | * Defined by Zend\Filter\FilterInterface 41 | * 42 | * Returns the string $value, converting characters to lowercase as necessary 43 | * 44 | * If the value provided is non-scalar, the value will remain unfiltered 45 | * 46 | * @param string $value 47 | * @return string|mixed 48 | */ 49 | public function filter($value) 50 | { 51 | if (! is_scalar($value)) { 52 | return $value; 53 | } 54 | $value = (string) $value; 55 | 56 | if (null !== $this->getEncoding()) { 57 | return mb_strtolower($value, $this->options['encoding']); 58 | } 59 | 60 | return strtolower($value); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/StringToUpper.php: -------------------------------------------------------------------------------- 1 | null, 21 | ]; 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param string|array|Traversable $encodingOrOptions OPTIONAL 27 | */ 28 | public function __construct($encodingOrOptions = null) 29 | { 30 | if ($encodingOrOptions !== null) { 31 | if (! static::isOptions($encodingOrOptions)) { 32 | $this->setEncoding($encodingOrOptions); 33 | } else { 34 | $this->setOptions($encodingOrOptions); 35 | } 36 | } 37 | } 38 | 39 | /** 40 | * Defined by Zend\Filter\FilterInterface 41 | * 42 | * Returns the string $value, converting characters to uppercase as necessary 43 | * 44 | * If the value provided is non-scalar, the value will remain unfiltered 45 | * 46 | * @param string $value 47 | * @return string|mixed 48 | */ 49 | public function filter($value) 50 | { 51 | if (! is_scalar($value)) { 52 | return $value; 53 | } 54 | $value = (string) $value; 55 | 56 | if (null !== $this->getEncoding()) { 57 | return mb_strtoupper($value, $this->options['encoding']); 58 | } 59 | 60 | return strtoupper($value); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Word/AbstractSeparator.php: -------------------------------------------------------------------------------- 1 | setSeparator($separator); 34 | } 35 | 36 | /** 37 | * Sets a new separator 38 | * 39 | * @param string $separator Separator 40 | * @return self 41 | * @throws Exception\InvalidArgumentException 42 | */ 43 | public function setSeparator($separator) 44 | { 45 | if (! is_string($separator)) { 46 | throw new Exception\InvalidArgumentException('"' . $separator . '" is not a valid separator.'); 47 | } 48 | $this->separator = $separator; 49 | return $this; 50 | } 51 | 52 | /** 53 | * Returns the actual set separator 54 | * 55 | * @return string 56 | */ 57 | public function getSeparator() 58 | { 59 | return $this->separator; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/UpperCaseWords.php: -------------------------------------------------------------------------------- 1 | null 19 | ]; 20 | 21 | /** 22 | * Constructor 23 | * 24 | * @param string|array|\Traversable $encodingOrOptions OPTIONAL 25 | */ 26 | public function __construct($encodingOrOptions = null) 27 | { 28 | if ($encodingOrOptions !== null) { 29 | if (static::isOptions($encodingOrOptions)) { 30 | $this->setOptions($encodingOrOptions); 31 | } else { 32 | $this->setEncoding($encodingOrOptions); 33 | } 34 | } 35 | } 36 | 37 | /** 38 | * {@inheritDoc} 39 | * 40 | * Returns the string $value, converting words to have an uppercase first character as necessary 41 | * 42 | * If the value provided is not a string, the value will remain unfiltered 43 | * 44 | * @param string|mixed $value 45 | * @return string|mixed 46 | */ 47 | public function filter($value) 48 | { 49 | if (! is_string($value)) { 50 | return $value; 51 | } 52 | 53 | $value = (string) $value; 54 | 55 | if ($this->options['encoding'] !== null) { 56 | return mb_convert_case($value, MB_CASE_TITLE, $this->options['encoding']); 57 | } 58 | 59 | return ucwords(strtolower($value)); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/AbstractUnicode.php: -------------------------------------------------------------------------------- 1 | options['encoding'] = $encoding; 43 | return $this; 44 | } 45 | 46 | /** 47 | * Returns the set encoding 48 | * 49 | * @return string 50 | */ 51 | public function getEncoding() 52 | { 53 | if ($this->options['encoding'] === null && function_exists('mb_internal_encoding')) { 54 | $this->options['encoding'] = mb_internal_encoding(); 55 | } 56 | 57 | return $this->options['encoding']; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Compress/AbstractCompressionAlgorithm.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 38 | } 39 | } 40 | 41 | /** 42 | * Returns one or all set options 43 | * 44 | * @param string|null $option Option to return 45 | * @return mixed 46 | */ 47 | public function getOptions($option = null) 48 | { 49 | if ($option === null) { 50 | return $this->options; 51 | } 52 | 53 | if (! isset($this->options[$option])) { 54 | return null; 55 | } 56 | 57 | return $this->options[$option]; 58 | } 59 | 60 | /** 61 | * Sets all or one option 62 | * 63 | * @param array $options 64 | * @return self 65 | */ 66 | public function setOptions(array $options) 67 | { 68 | foreach ($options as $key => $option) { 69 | $method = 'set' . $key; 70 | if (method_exists($this, $method)) { 71 | $this->$method($option); 72 | } 73 | } 74 | 75 | return $this; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Word/Service/SeparatorToSeparatorFactory.php: -------------------------------------------------------------------------------- 1 | creationOptions = $creationOptions; 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | public function __invoke(ContainerInterface $container, $requestedName, array $options = null) 46 | { 47 | return new SeparatorToSeparator( 48 | isset($options['search_separator']) ? $options['search_separator'] : ' ', 49 | isset($options['replacement_separator']) ? $options['replacement_separator'] : '-' 50 | ); 51 | } 52 | 53 | public function createService(ServiceLocatorInterface $serviceLocator) 54 | { 55 | return $this($serviceLocator, self::class, $this->creationOptions); 56 | } 57 | 58 | public function setCreationOptions(array $options) 59 | { 60 | $this->creationOptions = $options; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Compress/Lzf.php: -------------------------------------------------------------------------------- 1 | isNullOnEmpty() 39 | && ( 40 | empty($value['year']) 41 | || empty($value['month']) 42 | || empty($value['day']) 43 | || empty($value['hour']) 44 | || empty($value['minute']) 45 | || (isset($value['second']) && empty($value['second'])) 46 | ) 47 | ) { 48 | return; 49 | } 50 | 51 | if ($this->isNullOnAllEmpty() 52 | && ( 53 | empty($value['year']) 54 | && empty($value['month']) 55 | && empty($value['day']) 56 | && empty($value['hour']) 57 | && empty($value['minute']) 58 | && (! isset($value['second']) || empty($value['second'])) 59 | ) 60 | ) { 61 | // Cannot handle this value 62 | return; 63 | } 64 | 65 | if (! isset($value['second'])) { 66 | $value['second'] = '00'; 67 | } 68 | 69 | $this->filterable($value); 70 | 71 | ksort($value); 72 | 73 | $value = vsprintf($this->format, $value); 74 | 75 | return $value; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/File/LowerCase.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | protected $options = [ 18 | 'prefix' => null, 19 | ]; 20 | 21 | /** 22 | * @param string|array|Traversable $options 23 | */ 24 | public function __construct($options = null) 25 | { 26 | if ($options !== null) { 27 | $this->setOptions($options); 28 | } 29 | } 30 | 31 | /** 32 | * Set the prefix string 33 | * 34 | * @param string $prefix 35 | * @return self 36 | * @throws Exception\InvalidArgumentException 37 | */ 38 | public function setPrefix($prefix) 39 | { 40 | if (! is_string($prefix)) { 41 | throw new Exception\InvalidArgumentException(sprintf( 42 | '%s expects "prefix" to be string; received "%s"', 43 | __METHOD__, 44 | is_object($prefix) ? get_class($prefix) : gettype($prefix) 45 | )); 46 | } 47 | 48 | $this->options['prefix'] = $prefix; 49 | 50 | return $this; 51 | } 52 | 53 | /** 54 | * Returns the prefix string, which is appended at the beginning of the input value 55 | * 56 | * @return string 57 | */ 58 | public function getPrefix() 59 | { 60 | if (! isset($this->options['prefix'])) { 61 | throw new Exception\InvalidArgumentException(sprintf( 62 | '%s expects a "prefix" option; none given', 63 | __CLASS__ 64 | )); 65 | } 66 | 67 | return $this->options['prefix']; 68 | } 69 | 70 | /** 71 | * {@inheritdoc} 72 | */ 73 | public function filter($value) 74 | { 75 | if (! is_scalar($value)) { 76 | return $value; 77 | } 78 | 79 | $value = (string) $value; 80 | 81 | return $this->getPrefix() . $value; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Compress/Snappy.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 34 | } 35 | } 36 | 37 | /** 38 | * Determine whether the in_array() call should be "strict" or not. See in_array docs. 39 | * 40 | * @param bool $strict 41 | */ 42 | public function setStrict($strict = true) 43 | { 44 | $this->strict = (bool) $strict; 45 | } 46 | 47 | /** 48 | * Returns whether the in_array() call should be "strict" or not. See in_array docs. 49 | * 50 | * @return boolean 51 | */ 52 | public function getStrict() 53 | { 54 | return $this->strict; 55 | } 56 | 57 | /** 58 | * Set the list of items to white-list. 59 | * 60 | * @param array|Traversable $list 61 | */ 62 | public function setList($list = []) 63 | { 64 | if (! is_array($list)) { 65 | $list = ArrayUtils::iteratorToArray($list); 66 | } 67 | 68 | $this->list = $list; 69 | } 70 | 71 | 72 | /** 73 | * Get the list of items to white-list 74 | * 75 | * @return array 76 | */ 77 | public function getList() 78 | { 79 | return $this->list; 80 | } 81 | 82 | /** 83 | * {@inheritDoc} 84 | * 85 | * Will return $value if its present in the white-list. If $value is rejected then it will return null. 86 | */ 87 | public function filter($value) 88 | { 89 | return in_array($value, $this->getList(), $this->getStrict()) ? $value : null; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Blacklist.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 34 | } 35 | } 36 | 37 | /** 38 | * Determine whether the in_array() call should be "strict" or not. See in_array docs. 39 | * 40 | * @param bool $strict 41 | */ 42 | public function setStrict($strict = true) 43 | { 44 | $this->strict = (bool) $strict; 45 | } 46 | 47 | /** 48 | * Returns whether the in_array() call should be "strict" or not. See in_array docs. 49 | * 50 | * @return boolean 51 | */ 52 | public function getStrict() 53 | { 54 | return $this->strict; 55 | } 56 | 57 | /** 58 | * Set the list of items to black-list. 59 | * 60 | * @param array|Traversable $list 61 | */ 62 | public function setList($list = []) 63 | { 64 | if (! is_array($list)) { 65 | $list = ArrayUtils::iteratorToArray($list); 66 | } 67 | 68 | $this->list = $list; 69 | } 70 | 71 | /** 72 | * Get the list of items to black-list 73 | * 74 | * @return array 75 | */ 76 | public function getList() 77 | { 78 | return $this->list; 79 | } 80 | 81 | /** 82 | * {@inheritDoc} 83 | * 84 | * Will return null if $value is present in the black-list. If $value is NOT present then it will return $value. 85 | */ 86 | public function filter($value) 87 | { 88 | return in_array($value, $this->getList(), $this->getStrict()) ? null : $value; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/StringSuffix.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | protected $options = [ 18 | 'suffix' => null, 19 | ]; 20 | 21 | /** 22 | * @param string|array|Traversable $options 23 | */ 24 | public function __construct($options = null) 25 | { 26 | if ($options !== null) { 27 | $this->setOptions($options); 28 | } 29 | } 30 | 31 | /** 32 | * Set the suffix string 33 | * 34 | * @param string $suffix 35 | * 36 | * @return self 37 | * @throws Exception\InvalidArgumentException 38 | */ 39 | public function setSuffix($suffix) 40 | { 41 | if (! is_string($suffix)) { 42 | throw new Exception\InvalidArgumentException(sprintf( 43 | '%s expects "suffix" to be string; received "%s"', 44 | __METHOD__, 45 | is_object($suffix) ? get_class($suffix) : gettype($suffix) 46 | )); 47 | } 48 | 49 | $this->options['suffix'] = $suffix; 50 | 51 | return $this; 52 | } 53 | 54 | /** 55 | * Returns the suffix string, which is appended at the end of the input value 56 | * 57 | * @return string 58 | * @throws Exception\InvalidArgumentException 59 | */ 60 | public function getSuffix() 61 | { 62 | if (! isset($this->options['suffix'])) { 63 | throw new Exception\InvalidArgumentException(sprintf( 64 | '%s expects a "suffix" option; none given', 65 | __CLASS__ 66 | )); 67 | } 68 | 69 | return $this->options['suffix']; 70 | } 71 | 72 | /** 73 | * {@inheritdoc} 74 | */ 75 | public function filter($value) 76 | { 77 | if (! is_scalar($value)) { 78 | return $value; 79 | } 80 | 81 | $value = (string) $value; 82 | 83 | return $value . $this->getSuffix(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/StaticFilter.php: -------------------------------------------------------------------------------- 1 | get($classBaseName, $args); 67 | return $filter->filter($value); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/FilterPluginManagerFactory.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 filters configuration, nothing more to do 47 | if (! isset($config['filters']) || ! is_array($config['filters'])) { 48 | return $pluginManager; 49 | } 50 | 51 | // Wire service configuration for validators 52 | (new Config($config['filters']))->configureServiceManager($pluginManager); 53 | 54 | return $pluginManager; 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | * 60 | * @return FilterPluginManager 61 | */ 62 | public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null) 63 | { 64 | return $this($container, $requestedName ?: FilterPluginManager::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/DateTimeFormatter.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 32 | } 33 | } 34 | 35 | /** 36 | * Set the format string accepted by date() to use when formatting a string 37 | * 38 | * @param string $format 39 | * @return self 40 | */ 41 | public function setFormat($format) 42 | { 43 | $this->format = $format; 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * Filter a datetime string by normalizing it to the filters specified format 50 | * 51 | * @param DateTime|string|integer $value 52 | * @throws Exception\InvalidArgumentException 53 | * @return string 54 | */ 55 | public function filter($value) 56 | { 57 | try { 58 | $result = $this->normalizeDateTime($value); 59 | } catch (\Exception $e) { 60 | // DateTime threw an exception, an invalid date string was provided 61 | throw new Exception\InvalidArgumentException('Invalid date string provided', $e->getCode(), $e); 62 | } 63 | 64 | if ($result === false) { 65 | return $value; 66 | } 67 | 68 | return $result; 69 | } 70 | 71 | /** 72 | * Normalize the provided value to a formatted string 73 | * 74 | * @param string|int|DateTime $value 75 | * @return string 76 | */ 77 | protected function normalizeDateTime($value) 78 | { 79 | if ($value === '' || $value === null) { 80 | return $value; 81 | } 82 | 83 | if (! is_string($value) && ! is_int($value) && ! $value instanceof DateTime) { 84 | return $value; 85 | } 86 | 87 | if (is_int($value)) { 88 | //timestamp 89 | $value = new DateTime('@' . $value); 90 | } elseif (! $value instanceof DateTime) { 91 | $value = new DateTime($value); 92 | } 93 | 94 | return $value->format($this->format); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zendframework/zend-filter", 3 | "description": "Programmatically filter and normalize data and files", 4 | "license": "BSD-3-Clause", 5 | "keywords": [ 6 | "zf", 7 | "zendframework", 8 | "filter" 9 | ], 10 | "support": { 11 | "docs": "https://docs.zendframework.com/zend-filter/", 12 | "issues": "https://github.com/zendframework/zend-filter/issues", 13 | "source": "https://github.com/zendframework/zend-filter", 14 | "rss": "https://github.com/zendframework/zend-filter/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-stdlib": "^2.7.7 || ^3.1" 21 | }, 22 | "require-dev": { 23 | "pear/archive_tar": "^1.4.3", 24 | "phpunit/phpunit": "^5.7.23 || ^6.4.3", 25 | "psr/http-factory": "^1.0", 26 | "zendframework/zend-coding-standard": "~1.0.0", 27 | "zendframework/zend-crypt": "^3.2.1", 28 | "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", 29 | "zendframework/zend-uri": "^2.6" 30 | }, 31 | "conflict": { 32 | "zendframework/zend-validator": "<2.10.1" 33 | }, 34 | "suggest": { 35 | "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", 36 | "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", 37 | "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", 38 | "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", 39 | "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" 40 | }, 41 | "autoload": { 42 | "psr-4": { 43 | "Zend\\Filter\\": "src/" 44 | } 45 | }, 46 | "autoload-dev": { 47 | "psr-4": { 48 | "ZendTest\\Filter\\": "test/" 49 | } 50 | }, 51 | "config": { 52 | "sort-packages": true 53 | }, 54 | "extra": { 55 | "branch-alias": { 56 | "dev-master": "2.9.x-dev", 57 | "dev-develop": "2.10.x-dev" 58 | }, 59 | "zf": { 60 | "component": "Zend\\Filter", 61 | "config-provider": "Zend\\Filter\\ConfigProvider" 62 | } 63 | }, 64 | "scripts": { 65 | "check": [ 66 | "@cs-check", 67 | "@test" 68 | ], 69 | "cs-check": "phpcs", 70 | "cs-fix": "phpcbf", 71 | "test": "phpunit --colors=always", 72 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Word/SeparatorToSeparator.php: -------------------------------------------------------------------------------- 1 | setSearchSeparator($searchSeparator); 29 | $this->setReplacementSeparator($replacementSeparator); 30 | } 31 | 32 | /** 33 | * Sets a new separator to search for 34 | * 35 | * @param string $separator Separator to search for 36 | * @return self 37 | */ 38 | public function setSearchSeparator($separator) 39 | { 40 | $this->searchSeparator = $separator; 41 | return $this; 42 | } 43 | 44 | /** 45 | * Returns the actual set separator to search for 46 | * 47 | * @return string 48 | */ 49 | public function getSearchSeparator() 50 | { 51 | return $this->searchSeparator; 52 | } 53 | 54 | /** 55 | * Sets a new separator which replaces the searched one 56 | * 57 | * @param string $separator Separator which replaces the searched one 58 | * @return self 59 | */ 60 | public function setReplacementSeparator($separator) 61 | { 62 | $this->replacementSeparator = $separator; 63 | return $this; 64 | } 65 | 66 | /** 67 | * Returns the actual set separator which replaces the searched one 68 | * 69 | * @return string 70 | */ 71 | public function getReplacementSeparator() 72 | { 73 | return $this->replacementSeparator; 74 | } 75 | 76 | /** 77 | * Defined by Zend\Filter\Filter 78 | * 79 | * Returns the string $value, replacing the searched separators with the defined ones 80 | * 81 | * @param string|array $value 82 | * @return string|array 83 | */ 84 | public function filter($value) 85 | { 86 | if (! is_scalar($value) && ! is_array($value)) { 87 | return $value; 88 | } 89 | 90 | if ($this->searchSeparator === null) { 91 | throw new Exception\RuntimeException('You must provide a search separator for this filter to work.'); 92 | } 93 | 94 | $pattern = '#' . preg_quote($this->searchSeparator, '#') . '#'; 95 | return preg_replace($pattern, $this->replacementSeparator, $value); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Word/SeparatorToCamelCase.php: -------------------------------------------------------------------------------- 1 | separator, '#'); 30 | 31 | if (StringUtils::hasPcreUnicodeSupport()) { 32 | $patterns = [ 33 | '#(' . $pregQuotedSeparator.')(\P{Z}{1})#u', 34 | '#(^\P{Z}{1})#u', 35 | ]; 36 | if (! extension_loaded('mbstring')) { 37 | $replacements = [ 38 | // @codingStandardsIgnoreStart 39 | static function ($matches) { 40 | return strtoupper($matches[2]); 41 | }, 42 | static function ($matches) { 43 | return strtoupper($matches[1]); 44 | }, 45 | // @codingStandardsIgnoreEnd 46 | ]; 47 | } else { 48 | $replacements = [ 49 | // @codingStandardsIgnoreStart 50 | static function ($matches) { 51 | return mb_strtoupper($matches[2], 'UTF-8'); 52 | }, 53 | static function ($matches) { 54 | return mb_strtoupper($matches[1], 'UTF-8'); 55 | }, 56 | // @codingStandardsIgnoreEnd 57 | ]; 58 | } 59 | } else { 60 | $patterns = [ 61 | '#(' . $pregQuotedSeparator.')([\S]{1})#', 62 | '#(^[\S]{1})#', 63 | ]; 64 | $replacements = [ 65 | // @codingStandardsIgnoreStart 66 | static function ($matches) { 67 | return strtoupper($matches[2]); 68 | }, 69 | static function ($matches) { 70 | return strtoupper($matches[1]); 71 | }, 72 | // @codingStandardsIgnoreEnd 73 | ]; 74 | } 75 | 76 | $filtered = $value; 77 | foreach ($patterns as $index => $pattern) { 78 | $filtered = preg_replace_callback($pattern, $replacements[$index], $filtered); 79 | } 80 | return $filtered; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/StringTrim.php: -------------------------------------------------------------------------------- 1 | null, 21 | ]; 22 | 23 | /** 24 | * Sets filter options 25 | * 26 | * @param string|array|Traversable $charlistOrOptions 27 | */ 28 | public function __construct($charlistOrOptions = null) 29 | { 30 | if ($charlistOrOptions !== null) { 31 | if (! is_array($charlistOrOptions) && ! $charlistOrOptions instanceof Traversable) { 32 | $this->setCharList($charlistOrOptions); 33 | } else { 34 | $this->setOptions($charlistOrOptions); 35 | } 36 | } 37 | } 38 | 39 | /** 40 | * Sets the charList option 41 | * 42 | * @param string $charList 43 | * @return self Provides a fluent interface 44 | */ 45 | public function setCharList($charList) 46 | { 47 | if (! strlen($charList)) { 48 | $charList = null; 49 | } 50 | 51 | $this->options['charlist'] = $charList; 52 | 53 | return $this; 54 | } 55 | 56 | /** 57 | * Returns the charList option 58 | * 59 | * @return string|null 60 | */ 61 | public function getCharList() 62 | { 63 | return $this->options['charlist']; 64 | } 65 | 66 | /** 67 | * Defined by Zend\Filter\FilterInterface 68 | * 69 | * Returns the string $value with characters stripped from the beginning and end 70 | * 71 | * @param string $value 72 | * @return string 73 | */ 74 | public function filter($value) 75 | { 76 | if (! is_string($value)) { 77 | return $value; 78 | } 79 | $value = (string) $value; 80 | 81 | if (null === $this->options['charlist']) { 82 | return $this->unicodeTrim($value); 83 | } 84 | 85 | return $this->unicodeTrim($value, $this->options['charlist']); 86 | } 87 | 88 | /** 89 | * Unicode aware trim method 90 | * Fixes a PHP problem 91 | * 92 | * @param string $value 93 | * @param string $charlist 94 | * @return string 95 | */ 96 | protected function unicodeTrim($value, $charlist = '\\\\s') 97 | { 98 | $chars = preg_replace( 99 | ['/[\^\-\]\\\]/S', '/\\\{4}/S', '/\//'], 100 | ['\\\\\\0', '\\', '\/'], 101 | $charlist 102 | ); 103 | 104 | $pattern = '/^[' . $chars . ']+|[' . $chars . ']+$/usSD'; 105 | 106 | return preg_replace($pattern, '', $value); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Callback.php: -------------------------------------------------------------------------------- 1 | null, 21 | 'callback_params' => [] 22 | ]; 23 | 24 | /** 25 | * @param callable|array|string|Traversable $callbackOrOptions 26 | * @param array $callbackParams 27 | */ 28 | public function __construct($callbackOrOptions = [], $callbackParams = []) 29 | { 30 | if (is_callable($callbackOrOptions) || is_string($callbackOrOptions)) { 31 | $this->setCallback($callbackOrOptions); 32 | $this->setCallbackParams($callbackParams); 33 | } else { 34 | $this->setOptions($callbackOrOptions); 35 | } 36 | } 37 | 38 | /** 39 | * Sets a new callback for this filter 40 | * 41 | * @param callable $callback 42 | * @throws Exception\InvalidArgumentException 43 | * @return self 44 | */ 45 | public function setCallback($callback) 46 | { 47 | if (is_string($callback) && class_exists($callback)) { 48 | $callback = new $callback(); 49 | } 50 | 51 | if (! is_callable($callback)) { 52 | throw new Exception\InvalidArgumentException( 53 | 'Invalid parameter for callback: must be callable' 54 | ); 55 | } 56 | 57 | $this->options['callback'] = $callback; 58 | return $this; 59 | } 60 | 61 | /** 62 | * Returns the set callback 63 | * 64 | * @return callable 65 | */ 66 | public function getCallback() 67 | { 68 | return $this->options['callback']; 69 | } 70 | 71 | /** 72 | * Sets parameters for the callback 73 | * 74 | * @param array $params 75 | * @return self 76 | */ 77 | public function setCallbackParams($params) 78 | { 79 | $this->options['callback_params'] = (array) $params; 80 | return $this; 81 | } 82 | 83 | /** 84 | * Get parameters for the callback 85 | * 86 | * @return array 87 | */ 88 | public function getCallbackParams() 89 | { 90 | return $this->options['callback_params']; 91 | } 92 | 93 | /** 94 | * Calls the filter per callback 95 | * 96 | * @param mixed $value Options for the set callable 97 | * @return mixed Result from the filter which was called 98 | */ 99 | public function filter($value) 100 | { 101 | $params = (array) $this->options['callback_params']; 102 | array_unshift($params, $value); 103 | 104 | return call_user_func_array($this->options['callback'], $params); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/AbstractFilter.php: -------------------------------------------------------------------------------- 1 | $value) { 49 | $setter = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))); 50 | if (method_exists($this, $setter)) { 51 | $this->{$setter}($value); 52 | } elseif (array_key_exists($key, $this->options)) { 53 | $this->options[$key] = $value; 54 | } else { 55 | throw new Exception\InvalidArgumentException( 56 | sprintf( 57 | 'The option "%s" does not have a matching %s setter method or options[%s] array key', 58 | $key, 59 | $setter, 60 | $key 61 | ) 62 | ); 63 | } 64 | } 65 | return $this; 66 | } 67 | 68 | /** 69 | * Retrieve options representing object state 70 | * 71 | * @return array 72 | */ 73 | public function getOptions() 74 | { 75 | return $this->options; 76 | } 77 | 78 | /** 79 | * Invoke filter as a command 80 | * 81 | * Proxies to {@link filter()} 82 | * 83 | * @param mixed $value 84 | * @throws Exception\ExceptionInterface If filtering $value is impossible 85 | * @return mixed 86 | */ 87 | public function __invoke($value) 88 | { 89 | return $this->filter($value); 90 | } 91 | 92 | /** 93 | * @param mixed $options 94 | * @return bool 95 | */ 96 | protected static function isOptions($options) 97 | { 98 | return (is_array($options) || $options instanceof Traversable); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/File/Decrypt.php: -------------------------------------------------------------------------------- 1 | filename; 35 | } 36 | 37 | /** 38 | * Sets the new filename where the content will be stored 39 | * 40 | * @param string $filename (Optional) New filename to set 41 | * @return self 42 | */ 43 | public function setFilename($filename = null) 44 | { 45 | $this->filename = $filename; 46 | return $this; 47 | } 48 | 49 | /** 50 | * Defined by Zend\Filter\FilterInterface 51 | * 52 | * Decrypts the file $value with the defined settings 53 | * 54 | * @param string|array $value Full path of file to change or $_FILES data array 55 | * @return string|array The filename which has been set 56 | * @throws Exception\InvalidArgumentException 57 | * @throws Exception\RuntimeException 58 | */ 59 | public function filter($value) 60 | { 61 | if (! is_scalar($value) && ! is_array($value)) { 62 | return $value; 63 | } 64 | 65 | // An uploaded file? Retrieve the 'tmp_name' 66 | $isFileUpload = false; 67 | if (is_array($value)) { 68 | if (! isset($value['tmp_name'])) { 69 | return $value; 70 | } 71 | 72 | $isFileUpload = true; 73 | $uploadData = $value; 74 | $value = $value['tmp_name']; 75 | } 76 | 77 | if (! file_exists($value)) { 78 | throw new Exception\InvalidArgumentException("File '$value' not found"); 79 | } 80 | 81 | if (! isset($this->filename)) { 82 | $this->filename = $value; 83 | } 84 | 85 | if (file_exists($this->filename) && ! is_writable($this->filename)) { 86 | throw new Exception\RuntimeException("File '{$this->filename}' is not writable"); 87 | } 88 | 89 | $content = file_get_contents($value); 90 | if (! $content) { 91 | throw new Exception\RuntimeException("Problem while reading file '$value'"); 92 | } 93 | 94 | $decrypted = parent::filter($content); 95 | $result = file_put_contents($this->filename, $decrypted); 96 | 97 | if (! $result) { 98 | throw new Exception\RuntimeException("Problem while writing file '{$this->filename}'"); 99 | } 100 | 101 | if ($isFileUpload) { 102 | $uploadData['tmp_name'] = $this->filename; 103 | return $uploadData; 104 | } 105 | return $this->filename; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/File/Encrypt.php: -------------------------------------------------------------------------------- 1 | filename; 35 | } 36 | 37 | /** 38 | * Sets the new filename where the content will be stored 39 | * 40 | * @param string $filename (Optional) New filename to set 41 | * @return self 42 | */ 43 | public function setFilename($filename = null) 44 | { 45 | $this->filename = $filename; 46 | return $this; 47 | } 48 | 49 | /** 50 | * Defined by Zend\Filter\Filter 51 | * 52 | * Encrypts the file $value with the defined settings 53 | * 54 | * @param string|array $value Full path of file to change or $_FILES data array 55 | * @return string|array The filename which has been set, or false when there were errors 56 | * @throws Exception\InvalidArgumentException 57 | * @throws Exception\RuntimeException 58 | */ 59 | public function filter($value) 60 | { 61 | if (! is_scalar($value) && ! is_array($value)) { 62 | return $value; 63 | } 64 | 65 | // An uploaded file? Retrieve the 'tmp_name' 66 | $isFileUpload = false; 67 | if (is_array($value)) { 68 | if (! isset($value['tmp_name'])) { 69 | return $value; 70 | } 71 | 72 | $isFileUpload = true; 73 | $uploadData = $value; 74 | $value = $value['tmp_name']; 75 | } 76 | 77 | if (! file_exists($value)) { 78 | throw new Exception\InvalidArgumentException("File '$value' not found"); 79 | } 80 | 81 | if (! isset($this->filename)) { 82 | $this->filename = $value; 83 | } 84 | 85 | if (file_exists($this->filename) && ! is_writable($this->filename)) { 86 | throw new Exception\RuntimeException("File '{$this->filename}' is not writable"); 87 | } 88 | 89 | $content = file_get_contents($value); 90 | if (! $content) { 91 | throw new Exception\RuntimeException("Problem while reading file '$value'"); 92 | } 93 | 94 | $encrypted = parent::filter($content); 95 | $result = file_put_contents($this->filename, $encrypted); 96 | 97 | if (! $result) { 98 | throw new Exception\RuntimeException("Problem while writing file '{$this->filename}'"); 99 | } 100 | 101 | if ($isFileUpload) { 102 | $uploadData['tmp_name'] = $this->filename; 103 | return $uploadData; 104 | } 105 | return $this->filename; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/RealPath.php: -------------------------------------------------------------------------------- 1 | true 22 | ]; 23 | 24 | /** 25 | * Class constructor 26 | * 27 | * @param bool|Traversable $existsOrOptions Options to set 28 | */ 29 | public function __construct($existsOrOptions = true) 30 | { 31 | if ($existsOrOptions !== null) { 32 | if (! static::isOptions($existsOrOptions)) { 33 | $this->setExists($existsOrOptions); 34 | } else { 35 | $this->setOptions($existsOrOptions); 36 | } 37 | } 38 | } 39 | 40 | /** 41 | * Sets if the path has to exist 42 | * TRUE when the path must exist 43 | * FALSE when not existing paths can be given 44 | * 45 | * @param bool $flag Path must exist 46 | * @return self 47 | */ 48 | public function setExists($flag = true) 49 | { 50 | $this->options['exists'] = (bool) $flag; 51 | return $this; 52 | } 53 | 54 | /** 55 | * Returns true if the filtered path must exist 56 | * 57 | * @return bool 58 | */ 59 | public function getExists() 60 | { 61 | return $this->options['exists']; 62 | } 63 | 64 | /** 65 | * Defined by Zend\Filter\FilterInterface 66 | * 67 | * Returns realpath($value) 68 | * 69 | * If the value provided is non-scalar, the value will remain unfiltered 70 | * 71 | * @param string $value 72 | * @return string|mixed 73 | */ 74 | public function filter($value) 75 | { 76 | if (! is_string($value)) { 77 | return $value; 78 | } 79 | $path = (string) $value; 80 | 81 | if ($this->options['exists']) { 82 | return realpath($path); 83 | } 84 | 85 | ErrorHandler::start(); 86 | $realpath = realpath($path); 87 | ErrorHandler::stop(); 88 | if ($realpath) { 89 | return $realpath; 90 | } 91 | 92 | $drive = ''; 93 | if (stripos(PHP_OS, 'WIN') === 0) { 94 | $path = preg_replace('/[\\\\\/]/', DIRECTORY_SEPARATOR, $path); 95 | if (preg_match('/([a-zA-Z]\:)(.*)/', $path, $matches)) { 96 | list(, $drive, $path) = $matches; 97 | } else { 98 | $cwd = getcwd(); 99 | $drive = substr($cwd, 0, 2); 100 | if (strpos($path, DIRECTORY_SEPARATOR) !== 0) { 101 | $path = substr($cwd, 3) . DIRECTORY_SEPARATOR . $path; 102 | } 103 | } 104 | } elseif (strpos($path, DIRECTORY_SEPARATOR) !== 0) { 105 | $path = getcwd() . DIRECTORY_SEPARATOR . $path; 106 | } 107 | 108 | $stack = []; 109 | $parts = explode(DIRECTORY_SEPARATOR, $path); 110 | foreach ($parts as $dir) { 111 | if ($dir !== '' && $dir !== '.') { 112 | if ($dir === '..') { 113 | array_pop($stack); 114 | } else { 115 | $stack[] = $dir; 116 | } 117 | } 118 | } 119 | 120 | return $drive . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $stack); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/AbstractDateDropdown.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 50 | } 51 | } 52 | 53 | /** 54 | * @param bool $nullOnAllEmpty 55 | * @return self 56 | */ 57 | public function setNullOnAllEmpty($nullOnAllEmpty) 58 | { 59 | $this->nullOnAllEmpty = $nullOnAllEmpty; 60 | return $this; 61 | } 62 | 63 | /** 64 | * @return bool 65 | */ 66 | public function isNullOnAllEmpty() 67 | { 68 | return $this->nullOnAllEmpty; 69 | } 70 | 71 | /** 72 | * @param bool $nullOnEmpty 73 | * @return self 74 | */ 75 | public function setNullOnEmpty($nullOnEmpty) 76 | { 77 | $this->nullOnEmpty = $nullOnEmpty; 78 | return $this; 79 | } 80 | 81 | /** 82 | * @return bool 83 | */ 84 | public function isNullOnEmpty() 85 | { 86 | return $this->nullOnEmpty; 87 | } 88 | 89 | /** 90 | * Attempts to filter an array of date/time information to a formatted 91 | * string. 92 | * 93 | * @param mixed $value 94 | * @return mixed 95 | * @throws Exception\RuntimeException If filtering $value is impossible 96 | */ 97 | public function filter($value) 98 | { 99 | if (! is_array($value)) { 100 | // nothing to do 101 | return $value; 102 | } 103 | 104 | // Convert the date to a specific format 105 | if ($this->isNullOnEmpty() 106 | && array_reduce($value, __CLASS__ . '::reduce', false) 107 | ) { 108 | return null; 109 | } 110 | 111 | if ($this->isNullOnAllEmpty() 112 | && array_reduce($value, __CLASS__ . '::reduce', true) 113 | ) { 114 | return null; 115 | } 116 | 117 | $this->filterable($value); 118 | 119 | ksort($value); 120 | $value = vsprintf($this->format, $value); 121 | 122 | return $value; 123 | } 124 | 125 | /** 126 | * Ensures there are enough inputs in the array to properly format the date. 127 | * 128 | * @param $value 129 | * @throws Exception\RuntimeException 130 | */ 131 | protected function filterable($value) 132 | { 133 | if (count($value) !== $this->expectedInputs) { 134 | throw new Exception\RuntimeException( 135 | sprintf( 136 | 'There are not enough values in the array to filter this date (Required: %d, Received: %d)', 137 | $this->expectedInputs, 138 | count($value) 139 | ) 140 | ); 141 | } 142 | } 143 | 144 | /** 145 | * Reduce to a single value 146 | * 147 | * @param string $soFar 148 | * @param string $value 149 | * @return bool 150 | */ 151 | public static function reduce($soFar, $value) 152 | { 153 | return $soFar || empty($value); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/UriNormalize.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 41 | } 42 | } 43 | 44 | /** 45 | * Set the default scheme to use when parsing scheme-less URIs 46 | * 47 | * The scheme used when parsing URIs may affect the specific object used to 48 | * normalize the URI and thus may affect the resulting normalize URI. 49 | * 50 | * @param string $defaultScheme 51 | * @return self 52 | */ 53 | public function setDefaultScheme($defaultScheme) 54 | { 55 | $this->defaultScheme = $defaultScheme; 56 | return $this; 57 | } 58 | 59 | /** 60 | * Set a URI scheme to enforce on schemeless URIs 61 | * 62 | * This allows forcing input values such as 'www.example.com/foo' into 63 | * 'http://www.example.com/foo'. 64 | * 65 | * This should be used with caution, as a standard-compliant URI parser 66 | * would regard 'www.example.com' in the above input URI to be the path and 67 | * not host part of the URI. While this option can assist in solving 68 | * real-world user mishaps, it may yield unexpected results at times. 69 | * 70 | * @param string $enforcedScheme 71 | * @return self 72 | */ 73 | public function setEnforcedScheme($enforcedScheme) 74 | { 75 | $this->enforcedScheme = $enforcedScheme; 76 | return $this; 77 | } 78 | 79 | /** 80 | * Filter the URL by normalizing it and applying a default scheme if set 81 | * 82 | * @param string $value 83 | * @return string 84 | */ 85 | public function filter($value) 86 | { 87 | if (! is_scalar($value)) { 88 | return $value; 89 | } 90 | $value = (string) $value; 91 | 92 | $defaultScheme = $this->defaultScheme ?: $this->enforcedScheme; 93 | 94 | // Reset default scheme if it is not a known scheme 95 | if (! UriFactory::getRegisteredSchemeClass($defaultScheme)) { 96 | $defaultScheme = null; 97 | } 98 | 99 | try { 100 | $uri = UriFactory::factory($value, $defaultScheme); 101 | if ($this->enforcedScheme && ! $uri->getScheme()) { 102 | $this->enforceScheme($uri); 103 | } 104 | } catch (UriException $ex) { 105 | // We are unable to parse / enfore scheme with the given config and input 106 | return $value; 107 | } 108 | 109 | $uri->normalize(); 110 | 111 | if (! $uri->isValid()) { 112 | return $value; 113 | } 114 | 115 | return $uri->toString(); 116 | } 117 | 118 | /** 119 | * Enforce the defined scheme on the URI 120 | * 121 | * This will also adjust the host and path parts of the URI as expected in 122 | * the case of scheme-less network URIs 123 | * 124 | * @param Uri $uri 125 | */ 126 | protected function enforceScheme(Uri $uri) 127 | { 128 | $path = $uri->getPath(); 129 | if (strpos($path, '/') !== false) { 130 | list($host, $path) = explode('/', $path, 2); 131 | $path = '/' . $path; 132 | } else { 133 | $host = $path; 134 | $path = ''; 135 | } 136 | 137 | // We have nothing to do if we have no host 138 | if (! $host) { 139 | return; 140 | } 141 | 142 | $uri->setScheme($this->enforcedScheme) 143 | ->setHost($host) 144 | ->setPath($path); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/Compress/Bz2.php: -------------------------------------------------------------------------------- 1 | Blocksize to use from 0-9 23 | * 'archive' => Archive to use 24 | * ) 25 | * 26 | * @var array 27 | */ 28 | protected $options = [ 29 | 'blocksize' => 4, 30 | 'archive' => null, 31 | ]; 32 | 33 | /** 34 | * Class constructor 35 | * 36 | * @param null|array|\Traversable $options (Optional) Options to set 37 | * @throws Exception\ExtensionNotLoadedException if bz2 extension not loaded 38 | */ 39 | public function __construct($options = null) 40 | { 41 | if (! extension_loaded('bz2')) { 42 | throw new Exception\ExtensionNotLoadedException('This filter needs the bz2 extension'); 43 | } 44 | parent::__construct($options); 45 | } 46 | 47 | /** 48 | * Returns the set blocksize 49 | * 50 | * @return int 51 | */ 52 | public function getBlocksize() 53 | { 54 | return $this->options['blocksize']; 55 | } 56 | 57 | /** 58 | * Sets a new blocksize 59 | * 60 | * @param int $blocksize 61 | * @throws Exception\InvalidArgumentException 62 | * @return self 63 | */ 64 | public function setBlocksize($blocksize) 65 | { 66 | if (($blocksize < 0) || ($blocksize > 9)) { 67 | throw new Exception\InvalidArgumentException('Blocksize must be between 0 and 9'); 68 | } 69 | 70 | $this->options['blocksize'] = (int) $blocksize; 71 | return $this; 72 | } 73 | 74 | /** 75 | * Returns the set archive 76 | * 77 | * @return string 78 | */ 79 | public function getArchive() 80 | { 81 | return $this->options['archive']; 82 | } 83 | 84 | /** 85 | * Sets the archive to use for de-/compression 86 | * 87 | * @param string $archive Archive to use 88 | * @return self 89 | */ 90 | public function setArchive($archive) 91 | { 92 | $this->options['archive'] = (string) $archive; 93 | return $this; 94 | } 95 | 96 | /** 97 | * Compresses the given content 98 | * 99 | * @param string $content 100 | * @return string 101 | * @throws Exception\RuntimeException 102 | */ 103 | public function compress($content) 104 | { 105 | $archive = $this->getArchive(); 106 | if (! empty($archive)) { 107 | $file = bzopen($archive, 'w'); 108 | if (! $file) { 109 | throw new Exception\RuntimeException("Error opening the archive '" . $archive . "'"); 110 | } 111 | 112 | bzwrite($file, $content); 113 | bzclose($file); 114 | $compressed = true; 115 | } else { 116 | $compressed = bzcompress($content, $this->getBlocksize()); 117 | } 118 | 119 | if (is_int($compressed)) { 120 | throw new Exception\RuntimeException('Error during compression'); 121 | } 122 | 123 | return $compressed; 124 | } 125 | 126 | /** 127 | * Decompresses the given content 128 | * 129 | * @param string $content 130 | * @return string 131 | * @throws Exception\RuntimeException 132 | */ 133 | public function decompress($content) 134 | { 135 | $archive = $this->getArchive(); 136 | 137 | //check if there are null byte characters before doing a file_exists check 138 | if (false === strpos($content, "\0") && file_exists($content)) { 139 | $archive = $content; 140 | } 141 | 142 | if (file_exists($archive)) { 143 | $file = bzopen($archive, 'r'); 144 | if (! $file) { 145 | throw new Exception\RuntimeException("Error opening the archive '" . $content . "'"); 146 | } 147 | 148 | $compressed = bzread($file); 149 | bzclose($file); 150 | } else { 151 | $compressed = bzdecompress($content); 152 | } 153 | 154 | if (is_int($compressed)) { 155 | throw new Exception\RuntimeException('Error during decompression'); 156 | } 157 | 158 | return $compressed; 159 | } 160 | 161 | /** 162 | * Returns the adapter name 163 | * 164 | * @return string 165 | */ 166 | public function toString() 167 | { 168 | return 'Bz2'; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/ToNull.php: -------------------------------------------------------------------------------- 1 | 'boolean', 29 | self::TYPE_INTEGER => 'integer', 30 | self::TYPE_EMPTY_ARRAY => 'array', 31 | self::TYPE_STRING => 'string', 32 | self::TYPE_ZERO_STRING => 'zero', 33 | self::TYPE_FLOAT => 'float', 34 | self::TYPE_ALL => 'all', 35 | ]; 36 | 37 | /** 38 | * @var array 39 | */ 40 | protected $options = [ 41 | 'type' => self::TYPE_ALL, 42 | ]; 43 | 44 | /** 45 | * Constructor 46 | * 47 | * @param int|string|array|Traversable|null $typeOrOptions 48 | */ 49 | public function __construct($typeOrOptions = null) 50 | { 51 | if ($typeOrOptions !== null) { 52 | if ($typeOrOptions instanceof Traversable) { 53 | $typeOrOptions = iterator_to_array($typeOrOptions); 54 | } 55 | 56 | if (is_array($typeOrOptions)) { 57 | if (isset($typeOrOptions['type'])) { 58 | $this->setOptions($typeOrOptions); 59 | } else { 60 | $this->setType($typeOrOptions); 61 | } 62 | } else { 63 | $this->setType($typeOrOptions); 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * Set boolean types 70 | * 71 | * @param int|string|array $type 72 | * @throws Exception\InvalidArgumentException 73 | * @return self 74 | */ 75 | public function setType($type = null) 76 | { 77 | if (is_array($type)) { 78 | $detected = 0; 79 | foreach ($type as $value) { 80 | if (is_int($value)) { 81 | $detected |= $value; 82 | } elseif (($found = array_search($value, $this->constants, true)) !== false) { 83 | $detected |= $found; 84 | } 85 | } 86 | 87 | $type = $detected; 88 | } elseif (is_string($type) && ($found = array_search($type, $this->constants, true)) !== false) { 89 | $type = $found; 90 | } 91 | 92 | if (! is_int($type) || ($type < 0) || ($type > self::TYPE_ALL)) { 93 | throw new Exception\InvalidArgumentException(sprintf( 94 | 'Unknown type value "%s" (%s)', 95 | $type, 96 | gettype($type) 97 | )); 98 | } 99 | 100 | $this->options['type'] = $type; 101 | return $this; 102 | } 103 | 104 | /** 105 | * Returns defined boolean types 106 | * 107 | * @return int 108 | */ 109 | public function getType() 110 | { 111 | return $this->options['type']; 112 | } 113 | 114 | /** 115 | * Defined by Zend\Filter\FilterInterface 116 | * 117 | * Returns null representation of $value, if value is empty and matches 118 | * types that should be considered null. 119 | * 120 | * @param null|array|bool|float|int|string $value 121 | * @return null|mixed 122 | */ 123 | public function filter($value) 124 | { 125 | $type = $this->getType(); 126 | 127 | // FLOAT (0.0) 128 | if ($type & self::TYPE_FLOAT) { 129 | if (is_float($value) && $value === 0.0) { 130 | return null; 131 | } 132 | } 133 | 134 | // STRING ZERO ('0') 135 | if ($type & self::TYPE_ZERO_STRING) { 136 | if (is_string($value) && $value === '0') { 137 | return null; 138 | } 139 | } 140 | 141 | // STRING ('') 142 | if ($type & self::TYPE_STRING) { 143 | if (is_string($value) && $value === '') { 144 | return null; 145 | } 146 | } 147 | 148 | // EMPTY_ARRAY (array()) 149 | if ($type & self::TYPE_EMPTY_ARRAY) { 150 | if (is_array($value) && $value === []) { 151 | return null; 152 | } 153 | } 154 | 155 | // INTEGER (0) 156 | if ($type & self::TYPE_INTEGER) { 157 | if (is_int($value) && $value === 0) { 158 | return null; 159 | } 160 | } 161 | 162 | // BOOLEAN (false) 163 | if ($type & self::TYPE_BOOLEAN) { 164 | if (is_bool($value) && $value === false) { 165 | return null; 166 | } 167 | } 168 | 169 | return $value; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/PregReplace.php: -------------------------------------------------------------------------------- 1 | null, 18 | 'replacement' => '', 19 | ]; 20 | 21 | /** 22 | * Constructor 23 | * Supported options are 24 | * 'pattern' => matching pattern 25 | * 'replacement' => replace with this 26 | * 27 | * @param array|Traversable|string|null $options 28 | */ 29 | public function __construct($options = null) 30 | { 31 | if ($options instanceof Traversable) { 32 | $options = iterator_to_array($options); 33 | } 34 | 35 | if (! is_array($options) || (! isset($options['pattern']) && ! isset($options['replacement']))) { 36 | $args = func_get_args(); 37 | if (isset($args[0])) { 38 | $this->setPattern($args[0]); 39 | } 40 | if (isset($args[1])) { 41 | $this->setReplacement($args[1]); 42 | } 43 | } else { 44 | $this->setOptions($options); 45 | } 46 | } 47 | 48 | /** 49 | * Set the regex pattern to search for 50 | * @see preg_replace() 51 | * 52 | * @param string|array $pattern - same as the first argument of preg_replace 53 | * @return self 54 | * @throws Exception\InvalidArgumentException 55 | */ 56 | public function setPattern($pattern) 57 | { 58 | if (! is_array($pattern) && ! is_string($pattern)) { 59 | throw new Exception\InvalidArgumentException(sprintf( 60 | '%s expects pattern to be array or string; received "%s"', 61 | __METHOD__, 62 | (is_object($pattern) ? get_class($pattern) : gettype($pattern)) 63 | )); 64 | } 65 | 66 | if (is_array($pattern)) { 67 | foreach ($pattern as $p) { 68 | $this->validatePattern($p); 69 | } 70 | } 71 | 72 | if (is_string($pattern)) { 73 | $this->validatePattern($pattern); 74 | } 75 | 76 | $this->options['pattern'] = $pattern; 77 | return $this; 78 | } 79 | 80 | /** 81 | * Get currently set match pattern 82 | * 83 | * @return string|array 84 | */ 85 | public function getPattern() 86 | { 87 | return $this->options['pattern']; 88 | } 89 | 90 | /** 91 | * Set the replacement array/string 92 | * @see preg_replace() 93 | * 94 | * @param array|string $replacement - same as the second argument of preg_replace 95 | * @return self 96 | * @throws Exception\InvalidArgumentException 97 | */ 98 | public function setReplacement($replacement) 99 | { 100 | if (! is_array($replacement) && ! is_string($replacement)) { 101 | throw new Exception\InvalidArgumentException(sprintf( 102 | '%s expects replacement to be array or string; received "%s"', 103 | __METHOD__, 104 | (is_object($replacement) ? get_class($replacement) : gettype($replacement)) 105 | )); 106 | } 107 | $this->options['replacement'] = $replacement; 108 | return $this; 109 | } 110 | 111 | /** 112 | * Get currently set replacement value 113 | * 114 | * @return string|array 115 | */ 116 | public function getReplacement() 117 | { 118 | return $this->options['replacement']; 119 | } 120 | 121 | /** 122 | * Perform regexp replacement as filter 123 | * 124 | * @param mixed $value 125 | * @return mixed 126 | * @throws Exception\RuntimeException 127 | */ 128 | public function filter($value) 129 | { 130 | if (! is_scalar($value) && ! is_array($value)) { 131 | return $value; 132 | } 133 | 134 | if ($this->options['pattern'] === null) { 135 | throw new Exception\RuntimeException(sprintf( 136 | 'Filter %s does not have a valid pattern set', 137 | get_class($this) 138 | )); 139 | } 140 | 141 | return preg_replace($this->options['pattern'], $this->options['replacement'], $value); 142 | } 143 | 144 | /** 145 | * Validate a pattern and ensure it does not contain the "e" modifier 146 | * 147 | * @param string $pattern 148 | * @return bool 149 | * @throws Exception\InvalidArgumentException 150 | */ 151 | protected function validatePattern($pattern) 152 | { 153 | if (! preg_match('/(?[imsxeADSUXJu]+)$/', $pattern, $matches)) { 154 | return true; 155 | } 156 | 157 | if (false !== strpos($matches['modifier'], 'e')) { 158 | throw new Exception\InvalidArgumentException(sprintf( 159 | 'Pattern for a PregReplace filter may not contain the "e" pattern modifier; received "%s"', 160 | $pattern 161 | )); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/Encrypt.php: -------------------------------------------------------------------------------- 1 | setAdapter($options); 39 | } 40 | 41 | /** 42 | * Returns the adapter instance 43 | * 44 | * @throws Exception\RuntimeException 45 | * @throws Exception\InvalidArgumentException 46 | * @return Encrypt\EncryptionAlgorithmInterface 47 | */ 48 | public function getAdapterInstance() 49 | { 50 | if ($this->adapter instanceof Encrypt\EncryptionAlgorithmInterface) { 51 | return $this->adapter; 52 | } 53 | 54 | $adapter = $this->adapter; 55 | $options = $this->getOptions(); 56 | if (! class_exists($adapter)) { 57 | $adapter = __CLASS__ . '\\' . ucfirst($adapter); 58 | if (! class_exists($adapter)) { 59 | throw new Exception\RuntimeException(sprintf( 60 | '%s unable to load adapter; class "%s" not found', 61 | __METHOD__, 62 | $this->adapter 63 | )); 64 | } 65 | } 66 | 67 | $this->adapter = new $adapter($options); 68 | if (! $this->adapter instanceof Encrypt\EncryptionAlgorithmInterface) { 69 | throw new Exception\InvalidArgumentException(sprintf( 70 | 'Encryption adapter "%s" does not implement %s\\EncryptionAlgorithmInterface', 71 | $adapter, 72 | __CLASS__ 73 | )); 74 | } 75 | return $this->adapter; 76 | } 77 | 78 | /** 79 | * Returns the name of the set adapter 80 | * 81 | * @return string 82 | */ 83 | public function getAdapter() 84 | { 85 | return $this->adapter->toString(); 86 | } 87 | 88 | /** 89 | * Sets new encryption options 90 | * 91 | * @param string|array $options (Optional) Encryption options 92 | * @return self 93 | * @throws Exception\DomainException 94 | * @throws Exception\InvalidArgumentException 95 | */ 96 | public function setAdapter($options = null) 97 | { 98 | if (is_string($options)) { 99 | $adapter = $options; 100 | } elseif (isset($options['adapter'])) { 101 | $adapter = $options['adapter']; 102 | unset($options['adapter']); 103 | } else { 104 | $adapter = 'BlockCipher'; 105 | } 106 | 107 | if (! is_array($options)) { 108 | $options = []; 109 | } 110 | 111 | if (class_exists('Zend\Filter\Encrypt\\' . ucfirst($adapter))) { 112 | $adapter = 'Zend\Filter\Encrypt\\' . ucfirst($adapter); 113 | } elseif (! class_exists($adapter)) { 114 | throw new Exception\DomainException( 115 | sprintf( 116 | '%s expects a valid registry class name; received "%s", which did not resolve', 117 | __METHOD__, 118 | $adapter 119 | ) 120 | ); 121 | } 122 | 123 | $this->adapter = new $adapter($options); 124 | if (! $this->adapter instanceof Encrypt\EncryptionAlgorithmInterface) { 125 | throw new Exception\InvalidArgumentException( 126 | "Encoding adapter '" . $adapter 127 | . "' does not implement Zend\\Filter\\Encrypt\\EncryptionAlgorithmInterface" 128 | ); 129 | } 130 | 131 | return $this; 132 | } 133 | 134 | /** 135 | * Calls adapter methods 136 | * 137 | * @param string $method Method to call 138 | * @param string|array $options Options for this method 139 | * @return mixed 140 | * @throws Exception\BadMethodCallException 141 | */ 142 | public function __call($method, $options) 143 | { 144 | $part = substr($method, 0, 3); 145 | if (($part !== 'get' && $part !== 'set') || ! method_exists($this->adapter, $method)) { 146 | throw new Exception\BadMethodCallException("Unknown method '{$method}'"); 147 | } 148 | 149 | return call_user_func_array([$this->adapter, $method], $options); 150 | } 151 | 152 | /** 153 | * Defined by Zend\Filter\Filter 154 | * 155 | * Encrypts the content $value with the defined settings 156 | * 157 | * @param string $value Content to encrypt 158 | * @return string The encrypted content 159 | */ 160 | public function filter($value) 161 | { 162 | if (! is_string($value) && ! is_numeric($value)) { 163 | return $value; 164 | } 165 | 166 | return $this->adapter->encrypt($value); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/HtmlEntities.php: -------------------------------------------------------------------------------- 1 | setQuoteStyle($options['quotestyle']); 74 | $this->setEncoding($options['encoding']); 75 | $this->setDoubleQuote($options['doublequote']); 76 | } 77 | 78 | /** 79 | * Returns the quoteStyle option 80 | * 81 | * @return int 82 | */ 83 | public function getQuoteStyle() 84 | { 85 | return $this->quoteStyle; 86 | } 87 | 88 | /** 89 | * Sets the quoteStyle option 90 | * 91 | * @param int $quoteStyle 92 | * @return self Provides a fluent interface 93 | */ 94 | public function setQuoteStyle($quoteStyle) 95 | { 96 | $this->quoteStyle = $quoteStyle; 97 | return $this; 98 | } 99 | 100 | /** 101 | * Get encoding 102 | * 103 | * @return string 104 | */ 105 | public function getEncoding() 106 | { 107 | return $this->encoding; 108 | } 109 | 110 | /** 111 | * Set encoding 112 | * 113 | * @param string $value 114 | * @return self 115 | */ 116 | public function setEncoding($value) 117 | { 118 | $this->encoding = (string) $value; 119 | return $this; 120 | } 121 | 122 | /** 123 | * Returns the charSet option 124 | * 125 | * Proxies to {@link getEncoding()} 126 | * 127 | * @return string 128 | */ 129 | public function getCharSet() 130 | { 131 | return $this->getEncoding(); 132 | } 133 | 134 | /** 135 | * Sets the charSet option 136 | * 137 | * Proxies to {@link setEncoding()} 138 | * 139 | * @param string $charSet 140 | * @return self Provides a fluent interface 141 | */ 142 | public function setCharSet($charSet) 143 | { 144 | return $this->setEncoding($charSet); 145 | } 146 | 147 | /** 148 | * Returns the doubleQuote option 149 | * 150 | * @return bool 151 | */ 152 | public function getDoubleQuote() 153 | { 154 | return $this->doubleQuote; 155 | } 156 | 157 | /** 158 | * Sets the doubleQuote option 159 | * 160 | * @param bool $doubleQuote 161 | * @return self Provides a fluent interface 162 | */ 163 | public function setDoubleQuote($doubleQuote) 164 | { 165 | $this->doubleQuote = (bool) $doubleQuote; 166 | return $this; 167 | } 168 | 169 | /** 170 | * Defined by Zend\Filter\FilterInterface 171 | * 172 | * Returns the string $value, converting characters to their corresponding HTML entity 173 | * equivalents where they exist 174 | * 175 | * If the value provided is non-scalar, the value will remain unfiltered 176 | * 177 | * @param string $value 178 | * @return string|mixed 179 | * @throws Exception\DomainException on encoding mismatches 180 | */ 181 | public function filter($value) 182 | { 183 | if (! is_scalar($value)) { 184 | return $value; 185 | } 186 | $value = (string) $value; 187 | 188 | $filtered = htmlentities($value, $this->getQuoteStyle(), $this->getEncoding(), $this->getDoubleQuote()); 189 | if (strlen($value) && ! strlen($filtered)) { 190 | if (! function_exists('iconv')) { 191 | throw new Exception\DomainException('Encoding mismatch has resulted in htmlentities errors'); 192 | } 193 | $enc = $this->getEncoding(); 194 | $value = iconv('', $this->getEncoding() . '//IGNORE', $value); 195 | $filtered = htmlentities($value, $this->getQuoteStyle(), $enc, $this->getDoubleQuote()); 196 | if (! strlen($filtered)) { 197 | throw new Exception\DomainException('Encoding mismatch has resulted in htmlentities errors'); 198 | } 199 | } 200 | return $filtered; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/Compress/Gz.php: -------------------------------------------------------------------------------- 1 | Compression level 0-9 23 | * 'mode' => Compression mode, can be 'compress', 'deflate' 24 | * 'archive' => Archive to use 25 | * ) 26 | * 27 | * @var array 28 | */ 29 | protected $options = [ 30 | 'level' => 9, 31 | 'mode' => 'compress', 32 | 'archive' => null, 33 | ]; 34 | 35 | /** 36 | * Class constructor 37 | * 38 | * @param null|array|\Traversable $options (Optional) Options to set 39 | * @throws Exception\ExtensionNotLoadedException if zlib extension not loaded 40 | */ 41 | public function __construct($options = null) 42 | { 43 | if (! extension_loaded('zlib')) { 44 | throw new Exception\ExtensionNotLoadedException('This filter needs the zlib extension'); 45 | } 46 | parent::__construct($options); 47 | } 48 | 49 | /** 50 | * Returns the set compression level 51 | * 52 | * @return int 53 | */ 54 | public function getLevel() 55 | { 56 | return $this->options['level']; 57 | } 58 | 59 | /** 60 | * Sets a new compression level 61 | * 62 | * @param int $level 63 | * @throws Exception\InvalidArgumentException 64 | * @return self 65 | */ 66 | public function setLevel($level) 67 | { 68 | if (($level < 0) || ($level > 9)) { 69 | throw new Exception\InvalidArgumentException('Level must be between 0 and 9'); 70 | } 71 | 72 | $this->options['level'] = (int) $level; 73 | return $this; 74 | } 75 | 76 | /** 77 | * Returns the set compression mode 78 | * 79 | * @return string 80 | */ 81 | public function getMode() 82 | { 83 | return $this->options['mode']; 84 | } 85 | 86 | /** 87 | * Sets a new compression mode 88 | * 89 | * @param string $mode Supported are 'compress', 'deflate' and 'file' 90 | * @return self 91 | * @throws Exception\InvalidArgumentException for invalid $mode value 92 | */ 93 | public function setMode($mode) 94 | { 95 | if ($mode !== 'compress' && $mode !== 'deflate') { 96 | throw new Exception\InvalidArgumentException('Given compression mode not supported'); 97 | } 98 | 99 | $this->options['mode'] = $mode; 100 | return $this; 101 | } 102 | 103 | /** 104 | * Returns the set archive 105 | * 106 | * @return string 107 | */ 108 | public function getArchive() 109 | { 110 | return $this->options['archive']; 111 | } 112 | 113 | /** 114 | * Sets the archive to use for de-/compression 115 | * 116 | * @param string $archive Archive to use 117 | * @return self 118 | */ 119 | public function setArchive($archive) 120 | { 121 | $this->options['archive'] = (string) $archive; 122 | return $this; 123 | } 124 | 125 | /** 126 | * Compresses the given content 127 | * 128 | * @param string $content 129 | * @return string 130 | * @throws Exception\RuntimeException if unable to open archive or error during decompression 131 | */ 132 | public function compress($content) 133 | { 134 | $archive = $this->getArchive(); 135 | if (! empty($archive)) { 136 | $file = gzopen($archive, 'w' . $this->getLevel()); 137 | if (! $file) { 138 | throw new Exception\RuntimeException("Error opening the archive '" . $this->options['archive'] . "'"); 139 | } 140 | 141 | gzwrite($file, $content); 142 | gzclose($file); 143 | $compressed = true; 144 | } elseif ($this->options['mode'] === 'deflate') { 145 | $compressed = gzdeflate($content, $this->getLevel()); 146 | } else { 147 | $compressed = gzcompress($content, $this->getLevel()); 148 | } 149 | 150 | if (! $compressed) { 151 | throw new Exception\RuntimeException('Error during compression'); 152 | } 153 | 154 | return $compressed; 155 | } 156 | 157 | /** 158 | * Decompresses the given content 159 | * 160 | * @param string $content 161 | * @return string 162 | * @throws Exception\RuntimeException if unable to open archive or error during decompression 163 | */ 164 | public function decompress($content) 165 | { 166 | $archive = $this->getArchive(); 167 | $mode = $this->getMode(); 168 | 169 | //check if there are null byte characters before doing a file_exists check 170 | if (false === strpos($content, "\0") && file_exists($content)) { 171 | $archive = $content; 172 | } 173 | 174 | if (file_exists($archive)) { 175 | $handler = fopen($archive, 'rb'); 176 | if (! $handler) { 177 | throw new Exception\RuntimeException("Error opening the archive '" . $archive . "'"); 178 | } 179 | 180 | fseek($handler, -4, SEEK_END); 181 | $packet = fread($handler, 4); 182 | $bytes = unpack('V', $packet); 183 | $size = end($bytes); 184 | fclose($handler); 185 | 186 | $file = gzopen($archive, 'r'); 187 | $compressed = gzread($file, $size); 188 | gzclose($file); 189 | } elseif ($mode === 'deflate') { 190 | $compressed = gzinflate($content); 191 | } else { 192 | $compressed = gzuncompress($content); 193 | } 194 | 195 | if ($compressed === false) { 196 | throw new Exception\RuntimeException('Error during decompression'); 197 | } 198 | 199 | return $compressed; 200 | } 201 | 202 | /** 203 | * Returns the adapter name 204 | * 205 | * @return string 206 | */ 207 | public function toString() 208 | { 209 | return 'Gz'; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/Compress/Rar.php: -------------------------------------------------------------------------------- 1 | Callback for compression 23 | * 'archive' => Archive to use 24 | * 'password' => Password to use 25 | * 'target' => Target to write the files to 26 | * ) 27 | * 28 | * @var array 29 | */ 30 | protected $options = [ 31 | 'callback' => null, 32 | 'archive' => null, 33 | 'password' => null, 34 | 'target' => '.', 35 | ]; 36 | 37 | /** 38 | * Class constructor 39 | * 40 | * @param array $options (Optional) Options to set 41 | * @throws Exception\ExtensionNotLoadedException if rar extension not loaded 42 | */ 43 | public function __construct($options = null) 44 | { 45 | if (! extension_loaded('rar')) { 46 | throw new Exception\ExtensionNotLoadedException('This filter needs the rar extension'); 47 | } 48 | parent::__construct($options); 49 | } 50 | 51 | /** 52 | * Returns the set callback for compression 53 | * 54 | * @return string 55 | */ 56 | public function getCallback() 57 | { 58 | return $this->options['callback']; 59 | } 60 | 61 | /** 62 | * Sets the callback to use 63 | * 64 | * @param string $callback 65 | * @return self 66 | * @throws Exception\InvalidArgumentException if invalid callback provided 67 | */ 68 | public function setCallback($callback) 69 | { 70 | if (! is_callable($callback)) { 71 | throw new Exception\InvalidArgumentException('Invalid callback provided'); 72 | } 73 | 74 | $this->options['callback'] = $callback; 75 | return $this; 76 | } 77 | 78 | /** 79 | * Returns the set archive 80 | * 81 | * @return string 82 | */ 83 | public function getArchive() 84 | { 85 | return $this->options['archive']; 86 | } 87 | 88 | /** 89 | * Sets the archive to use for de-/compression 90 | * 91 | * @param string $archive Archive to use 92 | * @return self 93 | */ 94 | public function setArchive($archive) 95 | { 96 | $archive = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $archive); 97 | $this->options['archive'] = (string) $archive; 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * Returns the set password 104 | * 105 | * @return string 106 | */ 107 | public function getPassword() 108 | { 109 | return $this->options['password']; 110 | } 111 | 112 | /** 113 | * Sets the password to use 114 | * 115 | * @param string $password 116 | * @return self 117 | */ 118 | public function setPassword($password) 119 | { 120 | $this->options['password'] = (string) $password; 121 | return $this; 122 | } 123 | 124 | /** 125 | * Returns the set targetpath 126 | * 127 | * @return string 128 | */ 129 | public function getTarget() 130 | { 131 | return $this->options['target']; 132 | } 133 | 134 | /** 135 | * Sets the targetpath to use 136 | * 137 | * @param string $target 138 | * @return self 139 | * @throws Exception\InvalidArgumentException if specified target directory does not exist 140 | */ 141 | public function setTarget($target) 142 | { 143 | if (! file_exists(dirname($target))) { 144 | throw new Exception\InvalidArgumentException("The directory '$target' does not exist"); 145 | } 146 | 147 | $target = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, (string) $target); 148 | $this->options['target'] = $target; 149 | return $this; 150 | } 151 | 152 | /** 153 | * Compresses the given content 154 | * 155 | * @param string|array $content 156 | * @return string 157 | * @throws Exception\RuntimeException if no callback available, or error during compression 158 | */ 159 | public function compress($content) 160 | { 161 | $callback = $this->getCallback(); 162 | if ($callback === null) { 163 | throw new Exception\RuntimeException('No compression callback available'); 164 | } 165 | 166 | $options = $this->getOptions(); 167 | unset($options['callback']); 168 | 169 | $result = $callback($options, $content); 170 | if ($result !== true) { 171 | throw new Exception\RuntimeException('Error compressing the RAR Archive'); 172 | } 173 | 174 | return $this->getArchive(); 175 | } 176 | 177 | /** 178 | * Decompresses the given content 179 | * 180 | * @param string $content 181 | * @return bool 182 | * @throws Exception\RuntimeException if archive not found, cannot be opened, 183 | * or error during decompression 184 | */ 185 | public function decompress($content) 186 | { 187 | if (! file_exists($content)) { 188 | throw new Exception\RuntimeException('RAR Archive not found'); 189 | } 190 | 191 | $archive = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, realpath($content)); 192 | $password = $this->getPassword(); 193 | if ($password !== null) { 194 | $archive = rar_open($archive, $password); 195 | } else { 196 | $archive = rar_open($archive); 197 | } 198 | 199 | if (! $archive) { 200 | throw new Exception\RuntimeException('Error opening the RAR Archive'); 201 | } 202 | 203 | $target = $this->getTarget(); 204 | if (! is_dir($target)) { 205 | $target = dirname($target); 206 | } 207 | 208 | $filelist = rar_list($archive); 209 | if (! $filelist) { 210 | throw new Exception\RuntimeException("Error reading the RAR Archive"); 211 | } 212 | 213 | foreach ($filelist as $file) { 214 | $file->extract($target); 215 | } 216 | 217 | rar_close($archive); 218 | return true; 219 | } 220 | 221 | /** 222 | * Returns the adapter name 223 | * 224 | * @return string 225 | */ 226 | public function toString() 227 | { 228 | return 'Rar'; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/Compress.php: -------------------------------------------------------------------------------- 1 | setAdapter($options); 42 | } elseif ($options instanceof Compress\CompressionAlgorithmInterface) { 43 | $this->setAdapter($options); 44 | } elseif (is_array($options)) { 45 | $this->setOptions($options); 46 | } 47 | } 48 | 49 | /** 50 | * Set filter setate 51 | * 52 | * @param array $options 53 | * @throws Exception\InvalidArgumentException if options is not an array or Traversable 54 | * @return self 55 | */ 56 | public function setOptions($options) 57 | { 58 | if (! is_array($options) && ! $options instanceof Traversable) { 59 | throw new Exception\InvalidArgumentException(sprintf( 60 | '"%s" expects an array or Traversable; received "%s"', 61 | __METHOD__, 62 | (is_object($options) ? get_class($options) : gettype($options)) 63 | )); 64 | } 65 | 66 | foreach ($options as $key => $value) { 67 | if ($key === 'options') { 68 | $key = 'adapterOptions'; 69 | } 70 | $method = 'set' . ucfirst($key); 71 | if (method_exists($this, $method)) { 72 | $this->$method($value); 73 | } 74 | } 75 | return $this; 76 | } 77 | 78 | /** 79 | * Returns the current adapter, instantiating it if necessary 80 | * 81 | * @throws Exception\RuntimeException 82 | * @throws Exception\InvalidArgumentException 83 | * @return Compress\CompressionAlgorithmInterface 84 | */ 85 | public function getAdapter() 86 | { 87 | if ($this->adapter instanceof Compress\CompressionAlgorithmInterface) { 88 | return $this->adapter; 89 | } 90 | 91 | $adapter = $this->adapter; 92 | $options = $this->getAdapterOptions(); 93 | if (! class_exists($adapter)) { 94 | $adapter = 'Zend\\Filter\\Compress\\' . ucfirst($adapter); 95 | if (! class_exists($adapter)) { 96 | throw new Exception\RuntimeException(sprintf( 97 | '%s unable to load adapter; class "%s" not found', 98 | __METHOD__, 99 | $this->adapter 100 | )); 101 | } 102 | } 103 | 104 | $this->adapter = new $adapter($options); 105 | if (! $this->adapter instanceof Compress\CompressionAlgorithmInterface) { 106 | throw new Exception\InvalidArgumentException( 107 | "Compression adapter '" . $adapter 108 | . "' does not implement Zend\\Filter\\Compress\\CompressionAlgorithmInterface" 109 | ); 110 | } 111 | return $this->adapter; 112 | } 113 | 114 | /** 115 | * Retrieve adapter name 116 | * 117 | * @return string 118 | */ 119 | public function getAdapterName() 120 | { 121 | return $this->getAdapter()->toString(); 122 | } 123 | 124 | /** 125 | * Sets compression adapter 126 | * 127 | * @param string|Compress\CompressionAlgorithmInterface $adapter Adapter to use 128 | * @return self 129 | * @throws Exception\InvalidArgumentException 130 | */ 131 | public function setAdapter($adapter) 132 | { 133 | if ($adapter instanceof Compress\CompressionAlgorithmInterface) { 134 | $this->adapter = $adapter; 135 | return $this; 136 | } 137 | if (! is_string($adapter)) { 138 | throw new Exception\InvalidArgumentException( 139 | 'Invalid adapter provided; must be string or instance of ' 140 | . 'Zend\\Filter\\Compress\\CompressionAlgorithmInterface' 141 | ); 142 | } 143 | $this->adapter = $adapter; 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * Retrieve adapter options 150 | * 151 | * @return array 152 | */ 153 | public function getAdapterOptions() 154 | { 155 | return $this->adapterOptions; 156 | } 157 | 158 | /** 159 | * Set adapter options 160 | * 161 | * @param array $options 162 | * @return self 163 | */ 164 | public function setAdapterOptions(array $options) 165 | { 166 | $this->adapterOptions = $options; 167 | return $this; 168 | } 169 | 170 | /** 171 | * Get individual or all options from underlying adapter 172 | * 173 | * @param null|string $option 174 | * @return mixed 175 | */ 176 | public function getOptions($option = null) 177 | { 178 | $adapter = $this->getAdapter(); 179 | return $adapter->getOptions($option); 180 | } 181 | 182 | /** 183 | * Calls adapter methods 184 | * 185 | * @param string $method Method to call 186 | * @param string|array $options Options for this method 187 | * @return mixed 188 | * @throws Exception\BadMethodCallException 189 | */ 190 | public function __call($method, $options) 191 | { 192 | $adapter = $this->getAdapter(); 193 | if (! method_exists($adapter, $method)) { 194 | throw new Exception\BadMethodCallException("Unknown method '{$method}'"); 195 | } 196 | 197 | return call_user_func_array([$adapter, $method], $options); 198 | } 199 | 200 | /** 201 | * Defined by Zend\Filter\FilterInterface 202 | * 203 | * Compresses the content $value with the defined settings 204 | * 205 | * @param string $value Content to compress 206 | * @return string The compressed content 207 | */ 208 | public function filter($value) 209 | { 210 | if (! is_string($value)) { 211 | return $value; 212 | } 213 | 214 | return $this->getAdapter()->compress($value); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/DataUnitFormatter.php: -------------------------------------------------------------------------------- 1 | ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'], 41 | // decimal SI units: 42 | self::MODE_DECIMAL => ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 43 | ]; 44 | 45 | /** 46 | * Default options: 47 | * 48 | * @var array 49 | */ 50 | protected $options = [ 51 | 'mode' => self::MODE_DECIMAL, 52 | 'unit' => '', 53 | 'precision' => 2, 54 | 'prefixes' => [], 55 | ]; 56 | 57 | /** 58 | * @param array $options 59 | */ 60 | public function __construct($options = []) 61 | { 62 | if (! static::isOptions($options)) { 63 | throw new InvalidArgumentException('The unit filter needs options to work.'); 64 | } 65 | 66 | if (! isset($options['unit'])) { 67 | throw new InvalidArgumentException('The unit filter needs a unit to work with.'); 68 | } 69 | 70 | $this->setOptions($options); 71 | } 72 | 73 | /** 74 | * Define the mode of the filter. Possible values can be fount at self::$modes. 75 | * 76 | * @param string $mode 77 | * 78 | * @throws InvalidArgumentException 79 | */ 80 | protected function setMode($mode) 81 | { 82 | $mode = strtolower($mode); 83 | if (! in_array($mode, self::$modes, true)) { 84 | throw new InvalidArgumentException(sprintf('Invalid binary mode: %s', $mode)); 85 | } 86 | $this->options['mode'] = $mode; 87 | } 88 | 89 | /** 90 | * Get current filter mode 91 | * 92 | * @return string 93 | */ 94 | protected function getMode() 95 | { 96 | return $this->options['mode']; 97 | } 98 | 99 | /** 100 | * Find out if the filter is in decimal mode. 101 | * 102 | * @return bool 103 | */ 104 | protected function isDecimalMode() 105 | { 106 | return $this->getMode() === self::MODE_DECIMAL; 107 | } 108 | 109 | /** 110 | * Find out if the filter is in binary mode. 111 | * 112 | * @return bool 113 | */ 114 | protected function isBinaryMode() 115 | { 116 | return $this->getMode() === self::MODE_BINARY; 117 | } 118 | 119 | /** 120 | * Define the unit of the filter. Possible values can be fount at self::$types. 121 | * 122 | * @param string $unit 123 | */ 124 | protected function setUnit($unit) 125 | { 126 | $this->options['unit'] = (string) $unit; 127 | } 128 | 129 | /** 130 | * Get current filter type 131 | * 132 | * @return string 133 | */ 134 | protected function getUnit() 135 | { 136 | return $this->options['unit']; 137 | } 138 | 139 | /** 140 | * Set the precision of the filtered result. 141 | * 142 | * @param $precision 143 | */ 144 | protected function setPrecision($precision) 145 | { 146 | $this->options['precision'] = (int) $precision; 147 | } 148 | 149 | /** 150 | * Get the precision of the filtered result. 151 | * 152 | * @return int 153 | */ 154 | protected function getPrecision() 155 | { 156 | return $this->options['precision']; 157 | } 158 | 159 | /** 160 | * Set the precision of the result. 161 | * 162 | * @param array $prefixes 163 | */ 164 | protected function setPrefixes(array $prefixes) 165 | { 166 | $this->options['prefixes'] = $prefixes; 167 | } 168 | 169 | /** 170 | * Get the predefined prefixes or use the build-in standardized lists of prefixes. 171 | * 172 | * @return array 173 | */ 174 | protected function getPrefixes() 175 | { 176 | $prefixes = $this->options['prefixes']; 177 | if ($prefixes) { 178 | return $prefixes; 179 | } 180 | 181 | return self::$standardizedPrefixes[$this->getMode()]; 182 | } 183 | 184 | /** 185 | * Find the prefix at a specific location in the prefixes array. 186 | * 187 | * @param $index 188 | * 189 | * @return string|null 190 | */ 191 | protected function getPrefixAt($index) 192 | { 193 | $prefixes = $this->getPrefixes(); 194 | return isset($prefixes[$index]) ? $prefixes[$index] : null; 195 | } 196 | 197 | /** 198 | * Defined by Zend\Filter\FilterInterface 199 | * 200 | * Returns a human readable format of the amount of bits or bytes. 201 | * 202 | * If the value provided is not numeric, the value will remain unfiltered 203 | * 204 | * @param string $value 205 | * @return string|mixed 206 | */ 207 | public function filter($value) 208 | { 209 | if (! is_numeric($value)) { 210 | return $value; 211 | } 212 | 213 | // Parse to float and check if value is not zero 214 | $amount = (float) $value; 215 | if ($amount === 0.0) { 216 | return $this->formatAmount($amount); 217 | } 218 | 219 | // Calculate the correct size and prefix: 220 | $base = $this->isBinaryMode() ? self::BASE_BINARY : self::BASE_DECIMAL; 221 | $power = floor(log($amount, $base)); 222 | $prefix = $this->getPrefixAt((int)$power); 223 | 224 | // When the amount is too big, no prefix can be found: 225 | if ($prefix === null) { 226 | return $this->formatAmount($amount); 227 | } 228 | 229 | // return formatted value: 230 | $result = ($amount / pow($base, $power)); 231 | $formatted = number_format($result, $this->getPrecision()); 232 | return $this->formatAmount($formatted, $prefix); 233 | } 234 | 235 | /** 236 | * @param $amount 237 | * @param null $prefix 238 | * 239 | * @return string 240 | */ 241 | protected function formatAmount($amount, $prefix = null) 242 | { 243 | return sprintf('%s %s%s', $amount, $prefix, $this->getUnit()); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/Compress/Tar.php: -------------------------------------------------------------------------------- 1 | Archive to use 26 | * 'target' => Target to write the files to 27 | * ) 28 | * 29 | * @var array 30 | */ 31 | protected $options = [ 32 | 'archive' => null, 33 | 'target' => '.', 34 | 'mode' => null, 35 | ]; 36 | 37 | /** 38 | * Class constructor 39 | * 40 | * @param array $options (Optional) Options to set 41 | * @throws Exception\ExtensionNotLoadedException if Archive_Tar component not available 42 | */ 43 | public function __construct($options = null) 44 | { 45 | if (! class_exists('Archive_Tar')) { 46 | throw new Exception\ExtensionNotLoadedException( 47 | 'This filter needs PEAR\'s Archive_Tar component. ' 48 | . 'Ensure loading Archive_Tar (registering autoload or require_once)' 49 | ); 50 | } 51 | 52 | parent::__construct($options); 53 | } 54 | 55 | /** 56 | * Returns the set archive 57 | * 58 | * @return string 59 | */ 60 | public function getArchive() 61 | { 62 | return $this->options['archive']; 63 | } 64 | 65 | /** 66 | * Sets the archive to use for de-/compression 67 | * 68 | * @param string $archive Archive to use 69 | * @return self 70 | */ 71 | public function setArchive($archive) 72 | { 73 | $archive = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, (string) $archive); 74 | $this->options['archive'] = $archive; 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * Returns the set target path 81 | * 82 | * @return string 83 | */ 84 | public function getTarget() 85 | { 86 | return $this->options['target']; 87 | } 88 | 89 | /** 90 | * Sets the target path to use 91 | * 92 | * @param string $target 93 | * @return self 94 | * @throws Exception\InvalidArgumentException if target path does not exist 95 | */ 96 | public function setTarget($target) 97 | { 98 | if (! file_exists(dirname($target))) { 99 | throw new Exception\InvalidArgumentException("The directory '$target' does not exist"); 100 | } 101 | 102 | $target = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, (string) $target); 103 | $this->options['target'] = $target; 104 | return $this; 105 | } 106 | 107 | /** 108 | * Returns the set compression mode 109 | * 110 | * @return string 111 | */ 112 | public function getMode() 113 | { 114 | return $this->options['mode']; 115 | } 116 | 117 | /** 118 | * Compression mode to use 119 | * 120 | * Either Gz or Bz2. 121 | * 122 | * @param string $mode 123 | * @return self 124 | * @throws Exception\InvalidArgumentException for invalid $mode values 125 | * @throws Exception\ExtensionNotLoadedException if bz2 mode selected but extension not loaded 126 | * @throws Exception\ExtensionNotLoadedException if gz mode selected but extension not loaded 127 | */ 128 | public function setMode($mode) 129 | { 130 | $mode = strtolower($mode); 131 | if ($mode !== 'bz2' && $mode !== 'gz') { 132 | throw new Exception\InvalidArgumentException("The mode '$mode' is unknown"); 133 | } 134 | 135 | if ($mode === 'bz2' && ! extension_loaded('bz2')) { 136 | throw new Exception\ExtensionNotLoadedException('This mode needs the bz2 extension'); 137 | } 138 | 139 | if ($mode === 'gz' && ! extension_loaded('zlib')) { 140 | throw new Exception\ExtensionNotLoadedException('This mode needs the zlib extension'); 141 | } 142 | 143 | $this->options['mode'] = $mode; 144 | return $this; 145 | } 146 | 147 | /** 148 | * Compresses the given content 149 | * 150 | * @param string $content 151 | * @return string 152 | * @throws Exception\RuntimeException if unable to create temporary file 153 | * @throws Exception\RuntimeException if unable to create archive 154 | */ 155 | public function compress($content) 156 | { 157 | $archive = new Archive_Tar($this->getArchive(), $this->getMode()); 158 | if (! file_exists($content)) { 159 | $file = $this->getTarget(); 160 | if (is_dir($file)) { 161 | $file .= DIRECTORY_SEPARATOR . 'tar.tmp'; 162 | } 163 | 164 | $result = file_put_contents($file, $content); 165 | if ($result === false) { 166 | throw new Exception\RuntimeException('Error creating the temporary file'); 167 | } 168 | 169 | $content = $file; 170 | } 171 | 172 | if (is_dir($content)) { 173 | // collect all file infos 174 | foreach (new RecursiveIteratorIterator( 175 | new RecursiveDirectoryIterator($content, RecursiveDirectoryIterator::KEY_AS_PATHNAME), 176 | RecursiveIteratorIterator::SELF_FIRST 177 | ) as $directory => $info) { 178 | if ($info->isFile()) { 179 | $file[] = $directory; 180 | } 181 | } 182 | 183 | $content = $file; 184 | } 185 | 186 | $result = $archive->create($content); 187 | if ($result === false) { 188 | throw new Exception\RuntimeException('Error creating the Tar archive'); 189 | } 190 | 191 | return $this->getArchive(); 192 | } 193 | 194 | /** 195 | * Decompresses the given content 196 | * 197 | * @param string $content 198 | * @return string 199 | * @throws Exception\RuntimeException if unable to find archive 200 | * @throws Exception\RuntimeException if error occurs decompressing archive 201 | */ 202 | public function decompress($content) 203 | { 204 | $archive = $this->getArchive(); 205 | if (file_exists($content)) { 206 | $archive = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, realpath($content)); 207 | } elseif (empty($archive) || ! file_exists($archive)) { 208 | throw new Exception\RuntimeException('Tar Archive not found'); 209 | } 210 | 211 | $archive = new Archive_Tar($archive, $this->getMode()); 212 | $target = $this->getTarget(); 213 | if (! is_dir($target)) { 214 | $target = dirname($target) . DIRECTORY_SEPARATOR; 215 | } 216 | 217 | $result = $archive->extract($target); 218 | if ($result === false) { 219 | throw new Exception\RuntimeException('Error while extracting the Tar archive'); 220 | } 221 | 222 | return $target; 223 | } 224 | 225 | /** 226 | * Returns the adapter name 227 | * 228 | * @return string 229 | */ 230 | public function toString() 231 | { 232 | return 'Tar'; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/FilterChain.php: -------------------------------------------------------------------------------- 1 | filters = new PriorityQueue(); 44 | 45 | if (null !== $options) { 46 | $this->setOptions($options); 47 | } 48 | } 49 | 50 | /** 51 | * @param array|Traversable $options 52 | * @return self 53 | * @throws Exception\InvalidArgumentException 54 | */ 55 | public function setOptions($options) 56 | { 57 | if (! is_array($options) && ! $options instanceof Traversable) { 58 | throw new Exception\InvalidArgumentException(sprintf( 59 | 'Expected array or Traversable; received "%s"', 60 | (is_object($options) ? get_class($options) : gettype($options)) 61 | )); 62 | } 63 | 64 | foreach ($options as $key => $value) { 65 | switch (strtolower($key)) { 66 | case 'callbacks': 67 | foreach ($value as $spec) { 68 | $callback = isset($spec['callback']) ? $spec['callback'] : false; 69 | $priority = isset($spec['priority']) ? $spec['priority'] : static::DEFAULT_PRIORITY; 70 | if ($callback) { 71 | $this->attach($callback, $priority); 72 | } 73 | } 74 | break; 75 | case 'filters': 76 | foreach ($value as $spec) { 77 | $name = isset($spec['name']) ? $spec['name'] : false; 78 | $options = isset($spec['options']) ? $spec['options'] : []; 79 | $priority = isset($spec['priority']) ? $spec['priority'] : static::DEFAULT_PRIORITY; 80 | if ($name) { 81 | $this->attachByName($name, $options, $priority); 82 | } 83 | } 84 | break; 85 | default: 86 | // ignore other options 87 | break; 88 | } 89 | } 90 | 91 | return $this; 92 | } 93 | 94 | /** 95 | * Return the count of attached filters 96 | * 97 | * @return int 98 | */ 99 | public function count() 100 | { 101 | return count($this->filters); 102 | } 103 | 104 | /** 105 | * Get plugin manager instance 106 | * 107 | * @return FilterPluginManager 108 | */ 109 | public function getPluginManager() 110 | { 111 | if (! $this->plugins) { 112 | $this->setPluginManager(new FilterPluginManager(new ServiceManager())); 113 | } 114 | return $this->plugins; 115 | } 116 | 117 | /** 118 | * Set plugin manager instance 119 | * 120 | * @param FilterPluginManager $plugins 121 | * @return self 122 | */ 123 | public function setPluginManager(FilterPluginManager $plugins) 124 | { 125 | $this->plugins = $plugins; 126 | return $this; 127 | } 128 | 129 | /** 130 | * Retrieve a filter plugin by name 131 | * 132 | * @param mixed $name 133 | * @param array $options 134 | * @return FilterInterface 135 | */ 136 | public function plugin($name, array $options = []) 137 | { 138 | $plugins = $this->getPluginManager(); 139 | return $plugins->get($name, $options); 140 | } 141 | 142 | /** 143 | * Attach a filter to the chain 144 | * 145 | * @param callable|FilterInterface $callback A Filter implementation or valid PHP callback 146 | * @param int $priority Priority at which to enqueue filter; defaults to 1000 (higher executes earlier) 147 | * @throws Exception\InvalidArgumentException 148 | * @return self 149 | */ 150 | public function attach($callback, $priority = self::DEFAULT_PRIORITY) 151 | { 152 | if (! is_callable($callback)) { 153 | if (! $callback instanceof FilterInterface) { 154 | throw new Exception\InvalidArgumentException(sprintf( 155 | 'Expected a valid PHP callback; received "%s"', 156 | (is_object($callback) ? get_class($callback) : gettype($callback)) 157 | )); 158 | } 159 | $callback = [$callback, 'filter']; 160 | } 161 | $this->filters->insert($callback, $priority); 162 | return $this; 163 | } 164 | 165 | /** 166 | * Attach a filter to the chain using a short name 167 | * 168 | * Retrieves the filter from the attached plugin manager, and then calls attach() 169 | * with the retrieved instance. 170 | * 171 | * @param string $name 172 | * @param mixed $options 173 | * @param int $priority Priority at which to enqueue filter; defaults to 1000 (higher executes earlier) 174 | * @return self 175 | */ 176 | public function attachByName($name, $options = [], $priority = self::DEFAULT_PRIORITY) 177 | { 178 | if (! is_array($options)) { 179 | $options = (array) $options; 180 | } elseif (empty($options)) { 181 | $options = null; 182 | } 183 | $filter = $this->getPluginManager()->get($name, $options); 184 | return $this->attach($filter, $priority); 185 | } 186 | 187 | /** 188 | * Merge the filter chain with the one given in parameter 189 | * 190 | * @param FilterChain $filterChain 191 | * @return self 192 | */ 193 | public function merge(FilterChain $filterChain) 194 | { 195 | foreach ($filterChain->filters->toArray(PriorityQueue::EXTR_BOTH) as $item) { 196 | $this->attach($item['data'], $item['priority']); 197 | } 198 | 199 | return $this; 200 | } 201 | 202 | /** 203 | * Get all the filters 204 | * 205 | * @return PriorityQueue 206 | */ 207 | public function getFilters() 208 | { 209 | return $this->filters; 210 | } 211 | 212 | /** 213 | * Returns $value filtered through each filter in the chain 214 | * 215 | * Filters are run in the order in which they were added to the chain (FIFO) 216 | * 217 | * @param mixed $value 218 | * @return mixed 219 | */ 220 | public function filter($value) 221 | { 222 | $chain = clone $this->filters; 223 | 224 | $valueFiltered = $value; 225 | foreach ($chain as $filter) { 226 | $valueFiltered = call_user_func($filter, $valueFiltered); 227 | } 228 | 229 | return $valueFiltered; 230 | } 231 | 232 | /** 233 | * Clone filters 234 | */ 235 | public function __clone() 236 | { 237 | $this->filters = clone $this->filters; 238 | } 239 | 240 | /** 241 | * Prepare filter chain for serialization 242 | * 243 | * Plugin manager (property 'plugins') cannot 244 | * be serialized. On wakeup the property remains unset 245 | * and next invocation to getPluginManager() sets 246 | * the default plugin manager instance (FilterPluginManager). 247 | */ 248 | public function __sleep() 249 | { 250 | return ['filters']; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /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.9.3 - 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.9.2 - 2019-08-19 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 | - [#89](https://github.com/zendframework/zend-filter/pull/89) fixes infinite 48 | loop on malformed HTML comments in StripTags filter. 49 | 50 | - [#92](https://github.com/zendframework/zend-filter/pull/92) fixes Tar adapter 51 | to not require `archive` in options when decompressing. 52 | 53 | ## 2.9.1 - 2018-12-17 54 | 55 | ### Added 56 | 57 | - Nothing. 58 | 59 | ### Changed 60 | 61 | - Nothing. 62 | 63 | ### Deprecated 64 | 65 | - Nothing. 66 | 67 | ### Removed 68 | 69 | - Nothing. 70 | 71 | ### Fixed 72 | 73 | - [#79](https://github.com/zendframework/zend-filter/pull/79) fixes a regression introduced in 2.9.0 when using 74 | `Zend\Filter\File\RenameUpload` via the traditional SAPI. 75 | 76 | ## 2.9.0 - 2018-12-12 77 | 78 | ### Added 79 | 80 | - [#70](https://github.com/zendframework/zend-inputfilter/pull/70) Adds compatibility with the PSR-7 `UploadedFileInterface` to the 81 | `RenameUpload` filter. The functionality requires PHP 7 and a 82 | psr/http-factory-implementation in your application. When present, 83 | `RenameUpload` will accept a PSR-7 `UploadedFileInterface`, and return a new 84 | one representing the renamed file. 85 | 86 | - [#71](https://github.com/zendframework/zend-filter/pull/71) adds the `ToFloat` filter, to complement the `ToInt` filter. 87 | 88 | - [#69](https://github.com/zendframework/zend-filter/pull/69) adds `Zend\Filter\StringSufix`; when provided with a string `suffix` 89 | option, it will suffix scalar values with that string. 90 | 91 | - [#69](https://github.com/zendframework/zend-filter/pull/69) adds `Zend\Filter\StringPrefix`; when provided with a string `prefix` 92 | option, it will prefix scalar values with that string. 93 | 94 | ### Changed 95 | 96 | - [#66](https://github.com/zendframework/zend-filter/pull/66) modifies how the FilterPluginManager is registered with the dependency 97 | injection container. Previously, it was registered only under the name 98 | `FilterManager`. Now it regisers `Zend\Filter\FilterPluginManager` as a 99 | factory service, and `FilterManager` as an alias to that service. 100 | 101 | ### Deprecated 102 | 103 | - Nothing. 104 | 105 | ### Removed 106 | 107 | - Nothing. 108 | 109 | ### Fixed 110 | 111 | - Nothing. 112 | 113 | ## 2.8.0 - 2018-04-11 114 | 115 | ### Added 116 | 117 | - [#26](https://github.com/zendframework/zend-filter/pull/26) adds the interface 118 | `Zend\Filter\FilterProviderInterface`, which can be used to provide 119 | configuration for the `FilterPluginManager` via zend-mvc `Module` classes. 120 | 121 | - [#61](https://github.com/zendframework/zend-filter/pull/61) adds support for 122 | PHP 7.2. 123 | 124 | ### Deprecated 125 | 126 | - Nothing. 127 | 128 | ### Removed 129 | 130 | - [#61](https://github.com/zendframework/zend-filter/pull/61) removes support 131 | for PHP 5.5. 132 | 133 | - [#61](https://github.com/zendframework/zend-filter/pull/61) removes support 134 | for HHVM. 135 | 136 | - [#61](https://github.com/zendframework/zend-filter/pull/61) removes support 137 | for zend-crypt versions prior to 3.0. This was done as PHP deprecated the 138 | mcrypt extension starting in PHP 7.1, and does not ship it by default 139 | starting in PHP 7.2. zend-crypt 3.0 adds an OpenSSL adapter for its 140 | BlockCipher capabilities, and acts as a polyfill for mcrypt usage. Since this 141 | functionality has been used by default since 2.7.2, users should be able to 142 | upgrade seamlessly. 143 | 144 | ### Fixed 145 | 146 | - Nothing. 147 | 148 | ## 2.7.2 - 2017-05-17 149 | 150 | ### Added 151 | 152 | - Nothing. 153 | 154 | ### Changes 155 | 156 | - [#40](https://github.com/zendframework/zend-filter/pull/40) updates the 157 | `Callback` filter's `setCallback()` method to allow passing a string name of a 158 | class that is instantiable without constructor arguments, and which defines 159 | `__invoke()`. 160 | - [#43](https://github.com/zendframework/zend-filter/pull/43) updates the 161 | exception thrown by the `File\Rename` filter when the target already exists to 162 | indicate the target filename path. 163 | 164 | ### Deprecated 165 | 166 | - Nothing. 167 | 168 | ### Removed 169 | 170 | - Nothing. 171 | 172 | ### Fixed 173 | 174 | - [#56](https://github.com/zendframework/zend-filter/pull/56) fixes how the 175 | `FilterPluginManagerFactory` factory initializes the plugin manager instance, 176 | ensuring it is injecting the relevant configuration from the `config` service 177 | and thus seeding it with configured translator loader services. This means 178 | that the `filters` configuration will now be honored in non-zend-mvc contexts. 179 | - [#36](https://github.com/zendframework/zend-filter/pull/36) fixes an issue in 180 | the constructor whereby a discovered option was not removed from the options 181 | list after being used to set the compression algorithm. 182 | - [#49](https://github.com/zendframework/zend-filter/pull/49) and 183 | [#51](https://github.com/zendframework/zend-filter/pull/51) fix logic within 184 | the `Boolean` and `ToNull` filters to use boolean rather than arithmetic 185 | operations, ensuring that if the same type is specified multiple times via the 186 | options, it will be aggregated correctly internally, and thus ensure correct 187 | operation of the filter. 188 | - [#55](https://github.com/zendframework/zend-filter/pull/55) adds a missing 189 | import statement to the `Word\SeparatorToSeparatorFactory`. 190 | 191 | ## 2.7.1 - 2016-04-18 192 | 193 | ### Added 194 | 195 | - Nothing. 196 | 197 | ### Deprecated 198 | 199 | - Nothing. 200 | 201 | ### Removed 202 | 203 | - Nothing. 204 | 205 | ### Fixed 206 | 207 | - [#27](https://github.com/zendframework/zend-filter/pull/27) fixes the 208 | `Module::init()` method to properly receive a `ModuleManager` instance, and 209 | not expect a `ModuleEvent`. 210 | 211 | ## 2.7.0 - 2016-04-06 212 | 213 | ### Added 214 | 215 | - [#25](https://github.com/zendframework/zend-filter/pull/25) exposes the 216 | package as a ZF component and/or generic configuration provider, by adding the 217 | following: 218 | - `FilterPluginManagerFactory`, which can be consumed by container-interop / 219 | zend-servicemanager to create and return a `FilterPluginManager` instance. 220 | - `ConfigProvider`, which maps the service `FilterManager` to the above 221 | factory. 222 | - `Module`, which does the same as `ConfigProvider`, but specifically for 223 | zend-mvc applications. It also provices a specification to 224 | `Zend\ModuleManager\Listener\ServiceListener` to allow modules to provide 225 | filter configuration. 226 | 227 | ### Deprecated 228 | 229 | - Nothing. 230 | 231 | ### Removed 232 | 233 | - Nothing. 234 | 235 | ### Fixed 236 | 237 | - Nothing. 238 | 239 | ## 2.6.1 - 2016-02-08 240 | 241 | ### Added 242 | 243 | - Nothing. 244 | 245 | ### Deprecated 246 | 247 | - Nothing. 248 | 249 | ### Removed 250 | 251 | - Nothing. 252 | 253 | ### Fixed 254 | 255 | - [#24](https://github.com/zendframework/zend-filter/pull/24) updates the 256 | `FilterPluginManager` to reference the `NumberFormat` **filter**, instead of 257 | the **view helper**. 258 | 259 | ## 2.6.0 - 2016-02-04 260 | 261 | ### Added 262 | 263 | - [#14](https://github.com/zendframework/zend-filter/pull/14) adds the 264 | `UpperCaseWords` filter to the default list of filters known to the 265 | `FilterPluginManager`. 266 | - [#22](https://github.com/zendframework/zend-filter/pull/22) adds 267 | documentation, and automatically publishes it to 268 | https://zendframework.github.io/zend-filter/ 269 | 270 | ### Deprecated 271 | 272 | - Nothing. 273 | 274 | ### Removed 275 | 276 | - Nothing. 277 | 278 | ### Fixed 279 | 280 | - [#15](https://github.com/zendframework/zend-filter/pull/15), 281 | [#19](https://github.com/zendframework/zend-filter/pull/19), and 282 | [#21](https://github.com/zendframework/zend-filter/pull/21) 283 | update the component to be forwards-compatible with zend-servicemanager v3, 284 | and reduce the number of development dependencies required for testing. 285 | -------------------------------------------------------------------------------- /src/Boolean.php: -------------------------------------------------------------------------------- 1 | 'boolean', 34 | self::TYPE_INTEGER => 'integer', 35 | self::TYPE_FLOAT => 'float', 36 | self::TYPE_STRING => 'string', 37 | self::TYPE_ZERO_STRING => 'zero', 38 | self::TYPE_EMPTY_ARRAY => 'array', 39 | self::TYPE_NULL => 'null', 40 | self::TYPE_PHP => 'php', 41 | self::TYPE_FALSE_STRING => 'false', 42 | self::TYPE_LOCALIZED => 'localized', 43 | self::TYPE_ALL => 'all', 44 | ]; 45 | 46 | /** 47 | * @var array 48 | */ 49 | protected $options = [ 50 | 'type' => self::TYPE_PHP, 51 | 'casting' => true, 52 | 'translations' => [], 53 | ]; 54 | 55 | /** 56 | * Constructor 57 | * 58 | * @param int|string|array|Traversable|null $typeOrOptions 59 | * @param bool $casting 60 | * @param array $translations 61 | */ 62 | public function __construct($typeOrOptions = null, $casting = true, $translations = []) 63 | { 64 | if ($typeOrOptions !== null) { 65 | if ($typeOrOptions instanceof Traversable) { 66 | $typeOrOptions = ArrayUtils::iteratorToArray($typeOrOptions); 67 | } 68 | 69 | if (is_array($typeOrOptions)) { 70 | if (isset($typeOrOptions['type']) 71 | || isset($typeOrOptions['casting']) 72 | || isset($typeOrOptions['translations']) 73 | ) { 74 | $this->setOptions($typeOrOptions); 75 | } else { 76 | $this->setType($typeOrOptions); 77 | $this->setCasting($casting); 78 | $this->setTranslations($translations); 79 | } 80 | } else { 81 | $this->setType($typeOrOptions); 82 | $this->setCasting($casting); 83 | $this->setTranslations($translations); 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * Set boolean types 90 | * 91 | * @param int|string|array $type 92 | * @throws Exception\InvalidArgumentException 93 | * @return self 94 | */ 95 | public function setType($type = null) 96 | { 97 | if (is_array($type)) { 98 | $detected = 0; 99 | foreach ($type as $value) { 100 | if (is_int($value)) { 101 | $detected |= $value; 102 | } elseif (($found = array_search($value, $this->constants, true)) !== false) { 103 | $detected |= $found; 104 | } 105 | } 106 | 107 | $type = $detected; 108 | } elseif (is_string($type) && ($found = array_search($type, $this->constants, true)) !== false) { 109 | $type = $found; 110 | } 111 | 112 | if (! is_int($type) || ($type < 0) || ($type > self::TYPE_ALL)) { 113 | throw new Exception\InvalidArgumentException(sprintf( 114 | 'Unknown type value "%s" (%s)', 115 | $type, 116 | gettype($type) 117 | )); 118 | } 119 | 120 | $this->options['type'] = $type; 121 | return $this; 122 | } 123 | 124 | /** 125 | * Returns defined boolean types 126 | * 127 | * @return int 128 | */ 129 | public function getType() 130 | { 131 | return $this->options['type']; 132 | } 133 | 134 | /** 135 | * Set the working mode 136 | * 137 | * @param bool $flag When true this filter works like cast 138 | * When false it recognises only true and false 139 | * and all other values are returned as is 140 | * @return self 141 | */ 142 | public function setCasting($flag = true) 143 | { 144 | $this->options['casting'] = (bool) $flag; 145 | return $this; 146 | } 147 | 148 | /** 149 | * Returns the casting option 150 | * 151 | * @return bool 152 | */ 153 | public function getCasting() 154 | { 155 | return $this->options['casting']; 156 | } 157 | 158 | /** 159 | * @param array|Traversable $translations 160 | * @throws Exception\InvalidArgumentException 161 | * @return self 162 | */ 163 | public function setTranslations($translations) 164 | { 165 | if (! is_array($translations) && ! $translations instanceof Traversable) { 166 | throw new Exception\InvalidArgumentException(sprintf( 167 | '"%s" expects an array or Traversable; received "%s"', 168 | __METHOD__, 169 | (is_object($translations) ? get_class($translations) : gettype($translations)) 170 | )); 171 | } 172 | 173 | foreach ($translations as $message => $flag) { 174 | $this->options['translations'][$message] = (bool) $flag; 175 | } 176 | 177 | return $this; 178 | } 179 | 180 | /** 181 | * @return array 182 | */ 183 | public function getTranslations() 184 | { 185 | return $this->options['translations']; 186 | } 187 | 188 | /** 189 | * Defined by Zend\Filter\FilterInterface 190 | * 191 | * Returns a boolean representation of $value 192 | * 193 | * @param null|array|bool|float|int|string $value 194 | * @return bool|mixed 195 | */ 196 | public function filter($value) 197 | { 198 | $type = $this->getType(); 199 | $casting = $this->getCasting(); 200 | 201 | // LOCALIZED 202 | if ($type & self::TYPE_LOCALIZED) { 203 | if (is_string($value)) { 204 | if (isset($this->options['translations'][$value])) { 205 | return (bool) $this->options['translations'][$value]; 206 | } 207 | } 208 | } 209 | 210 | // FALSE_STRING ('false') 211 | if ($type & self::TYPE_FALSE_STRING) { 212 | if (is_string($value) && strtolower($value) === 'false') { 213 | return false; 214 | } 215 | 216 | if (! $casting && is_string($value) && strtolower($value) === 'true') { 217 | return true; 218 | } 219 | } 220 | 221 | // NULL (null) 222 | if ($type & self::TYPE_NULL) { 223 | if ($value === null) { 224 | return false; 225 | } 226 | } 227 | 228 | // EMPTY_ARRAY (array()) 229 | if ($type & self::TYPE_EMPTY_ARRAY) { 230 | if (is_array($value) && $value === []) { 231 | return false; 232 | } 233 | } 234 | 235 | // ZERO_STRING ('0') 236 | if ($type & self::TYPE_ZERO_STRING) { 237 | if (is_string($value) && $value === '0') { 238 | return false; 239 | } 240 | 241 | if (! $casting && is_string($value) && $value === '1') { 242 | return true; 243 | } 244 | } 245 | 246 | // STRING ('') 247 | if ($type & self::TYPE_STRING) { 248 | if (is_string($value) && $value === '') { 249 | return false; 250 | } 251 | } 252 | 253 | // FLOAT (0.0) 254 | if ($type & self::TYPE_FLOAT) { 255 | if (is_float($value) && $value === 0.0) { 256 | return false; 257 | } 258 | 259 | if (! $casting && is_float($value) && $value === 1.0) { 260 | return true; 261 | } 262 | } 263 | 264 | // INTEGER (0) 265 | if ($type & self::TYPE_INTEGER) { 266 | if (is_int($value) && $value === 0) { 267 | return false; 268 | } 269 | 270 | if (! $casting && is_int($value) && $value === 1) { 271 | return true; 272 | } 273 | } 274 | 275 | // BOOLEAN (false) 276 | if ($type & self::TYPE_BOOLEAN) { 277 | if (is_bool($value)) { 278 | return $value; 279 | } 280 | } 281 | 282 | if ($casting) { 283 | return true; 284 | } 285 | 286 | return $value; 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/Encrypt/BlockCipher.php: -------------------------------------------------------------------------------- 1 | encryption key string 30 | * 'key_iteration' => the number of iterations for the PBKDF2 key generation 31 | * 'algorithm => cipher algorithm to use 32 | * 'hash' => algorithm to use for the authentication 33 | * 'vector' => initialization vector 34 | * ) 35 | */ 36 | protected $encryption = [ 37 | 'key_iteration' => 5000, 38 | 'algorithm' => 'aes', 39 | 'hash' => 'sha256', 40 | ]; 41 | 42 | /** 43 | * BlockCipher 44 | * 45 | * @var BlockCipher 46 | */ 47 | protected $blockCipher; 48 | 49 | /** 50 | * Internal compression 51 | * 52 | * @var array 53 | */ 54 | protected $compression; 55 | 56 | /** 57 | * Class constructor 58 | * 59 | * @param string|array|Traversable $options Encryption Options 60 | * @throws Exception\RuntimeException 61 | * @throws Exception\InvalidArgumentException 62 | */ 63 | public function __construct($options) 64 | { 65 | $cipherPluginManager = CryptBlockCipher::getSymmetricPluginManager(); 66 | $cipherType = $cipherPluginManager->has('openssl') ? 'openssl' : 'mcrypt'; 67 | try { 68 | $this->blockCipher = CryptBlockCipher::factory($cipherType, $this->encryption); 69 | } catch (SymmetricException\RuntimeException $e) { 70 | throw new Exception\RuntimeException(sprintf( 71 | 'The BlockCipher cannot be used without the %s extension', 72 | $cipherType 73 | )); 74 | } 75 | 76 | if ($options instanceof Traversable) { 77 | $options = ArrayUtils::iteratorToArray($options); 78 | } elseif (is_string($options)) { 79 | $options = ['key' => $options]; 80 | } elseif (! is_array($options)) { 81 | throw new Exception\InvalidArgumentException('Invalid options argument provided to filter'); 82 | } 83 | 84 | if (array_key_exists('compression', $options)) { 85 | $this->setCompression($options['compression']); 86 | unset($options['compress']); 87 | } 88 | 89 | $this->setEncryption($options); 90 | } 91 | 92 | /** 93 | * Returns the set encryption options 94 | * 95 | * @return array 96 | */ 97 | public function getEncryption() 98 | { 99 | return $this->encryption; 100 | } 101 | 102 | /** 103 | * Sets new encryption options 104 | * 105 | * @param string|array $options Encryption options 106 | * @return self 107 | * @throws Exception\InvalidArgumentException 108 | */ 109 | public function setEncryption($options) 110 | { 111 | if (is_string($options)) { 112 | $this->blockCipher->setKey($options); 113 | $this->encryption['key'] = $options; 114 | return $this; 115 | } 116 | 117 | if (! is_array($options)) { 118 | throw new Exception\InvalidArgumentException('Invalid options argument provided to filter'); 119 | } 120 | 121 | $options += $this->encryption; 122 | 123 | if (isset($options['key'])) { 124 | $this->blockCipher->setKey($options['key']); 125 | } 126 | 127 | if (isset($options['algorithm'])) { 128 | try { 129 | $this->blockCipher->setCipherAlgorithm($options['algorithm']); 130 | } catch (CryptException\InvalidArgumentException $e) { 131 | throw new Exception\InvalidArgumentException( 132 | "The algorithm '{$options['algorithm']}' is not supported" 133 | ); 134 | } 135 | } 136 | 137 | if (isset($options['hash'])) { 138 | try { 139 | $this->blockCipher->setHashAlgorithm($options['hash']); 140 | } catch (CryptException\InvalidArgumentException $e) { 141 | throw new Exception\InvalidArgumentException("The algorithm '{$options['hash']}' is not supported"); 142 | } 143 | } 144 | 145 | if (isset($options['vector'])) { 146 | $this->setVector($options['vector']); 147 | } 148 | 149 | if (isset($options['key_iteration'])) { 150 | $this->blockCipher->setKeyIteration($options['key_iteration']); 151 | } 152 | 153 | $this->encryption = $options; 154 | 155 | return $this; 156 | } 157 | 158 | /** 159 | * Returns the initialization vector 160 | * 161 | * @return string 162 | */ 163 | public function getVector() 164 | { 165 | return $this->encryption['vector']; 166 | } 167 | 168 | /** 169 | * Set the inizialization vector 170 | * 171 | * @param string $vector 172 | * @return self 173 | * @throws Exception\InvalidArgumentException 174 | */ 175 | public function setVector($vector) 176 | { 177 | try { 178 | $this->blockCipher->setSalt($vector); 179 | } catch (CryptException\InvalidArgumentException $e) { 180 | throw new Exception\InvalidArgumentException($e->getMessage()); 181 | } 182 | $this->encryption['vector'] = $vector; 183 | return $this; 184 | } 185 | 186 | /** 187 | * Set the encryption key 188 | * 189 | * @param string $key 190 | * @return self 191 | * @throws Exception\InvalidArgumentException 192 | */ 193 | public function setKey($key) 194 | { 195 | try { 196 | $this->blockCipher->setKey($key); 197 | } catch (CryptException\InvalidArgumentException $e) { 198 | throw new Exception\InvalidArgumentException($e->getMessage()); 199 | } 200 | $this->encryption['key'] = $key; 201 | return $this; 202 | } 203 | 204 | /** 205 | * Get the encryption key 206 | * 207 | * @return string 208 | */ 209 | public function getKey() 210 | { 211 | return $this->encryption['key']; 212 | } 213 | 214 | /** 215 | * Returns the compression 216 | * 217 | * @return array 218 | */ 219 | public function getCompression() 220 | { 221 | return $this->compression; 222 | } 223 | 224 | /** 225 | * Sets an internal compression for values to encrypt 226 | * 227 | * @param string|array $compression 228 | * @return self 229 | */ 230 | public function setCompression($compression) 231 | { 232 | if (is_string($this->compression)) { 233 | $compression = ['adapter' => $compression]; 234 | } 235 | 236 | $this->compression = $compression; 237 | return $this; 238 | } 239 | 240 | /** 241 | * Defined by Zend\Filter\FilterInterface 242 | * 243 | * Encrypts $value with the defined settings 244 | * 245 | * @param string $value The content to encrypt 246 | * @throws Exception\InvalidArgumentException 247 | * @return string The encrypted content 248 | */ 249 | public function encrypt($value) 250 | { 251 | // compress prior to encryption 252 | if (! empty($this->compression)) { 253 | $compress = new Compress($this->compression); 254 | $value = $compress($value); 255 | } 256 | 257 | try { 258 | $encrypted = $this->blockCipher->encrypt($value); 259 | } catch (CryptException\InvalidArgumentException $e) { 260 | throw new Exception\InvalidArgumentException($e->getMessage()); 261 | } 262 | return $encrypted; 263 | } 264 | 265 | /** 266 | * Defined by Zend\Filter\FilterInterface 267 | * 268 | * Decrypts $value with the defined settings 269 | * 270 | * @param string $value Content to decrypt 271 | * @return string The decrypted content 272 | */ 273 | public function decrypt($value) 274 | { 275 | $decrypted = $this->blockCipher->decrypt($value); 276 | 277 | // decompress after decryption 278 | if (! empty($this->compression)) { 279 | $decompress = new Decompress($this->compression); 280 | $decrypted = $decompress($decrypted); 281 | } 282 | 283 | return $decrypted; 284 | } 285 | 286 | /** 287 | * Returns the adapter name 288 | * 289 | * @return string 290 | */ 291 | public function toString() 292 | { 293 | return 'BlockCipher'; 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/Compress/Zip.php: -------------------------------------------------------------------------------- 1 | Archive to use 24 | * 'password' => Password to use 25 | * 'target' => Target to write the files to 26 | * ) 27 | * 28 | * @var array 29 | */ 30 | protected $options = [ 31 | 'archive' => null, 32 | 'target' => null, 33 | ]; 34 | 35 | /** 36 | * Class constructor 37 | * 38 | * @param null|array|\Traversable $options (Optional) Options to set 39 | * @throws Exception\ExtensionNotLoadedException if zip extension not loaded 40 | */ 41 | public function __construct($options = null) 42 | { 43 | if (! extension_loaded('zip')) { 44 | throw new Exception\ExtensionNotLoadedException('This filter needs the zip extension'); 45 | } 46 | parent::__construct($options); 47 | } 48 | 49 | /** 50 | * Returns the set archive 51 | * 52 | * @return string 53 | */ 54 | public function getArchive() 55 | { 56 | return $this->options['archive']; 57 | } 58 | 59 | /** 60 | * Sets the archive to use for de-/compression 61 | * 62 | * @param string $archive Archive to use 63 | * @return self 64 | */ 65 | public function setArchive($archive) 66 | { 67 | $archive = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, (string) $archive); 68 | $this->options['archive'] = $archive; 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * Returns the set targetpath 75 | * 76 | * @return string 77 | */ 78 | public function getTarget() 79 | { 80 | return $this->options['target']; 81 | } 82 | 83 | /** 84 | * Sets the target to use 85 | * 86 | * @param string $target 87 | * @throws Exception\InvalidArgumentException 88 | * @return self 89 | */ 90 | public function setTarget($target) 91 | { 92 | if (! file_exists(dirname($target))) { 93 | throw new Exception\InvalidArgumentException("The directory '$target' does not exist"); 94 | } 95 | 96 | $target = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, (string) $target); 97 | $this->options['target'] = $target; 98 | return $this; 99 | } 100 | 101 | /** 102 | * Compresses the given content 103 | * 104 | * @param string $content 105 | * @return string Compressed archive 106 | * @throws Exception\RuntimeException if unable to open zip archive, or error during compression 107 | */ 108 | public function compress($content) 109 | { 110 | $zip = new ZipArchive(); 111 | $res = $zip->open($this->getArchive(), ZipArchive::CREATE | ZipArchive::OVERWRITE); 112 | 113 | if ($res !== true) { 114 | throw new Exception\RuntimeException($this->errorString($res)); 115 | } 116 | 117 | if (file_exists($content)) { 118 | $content = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, realpath($content)); 119 | $basename = substr($content, strrpos($content, DIRECTORY_SEPARATOR) + 1); 120 | if (is_dir($content)) { 121 | $index = strrpos($content, DIRECTORY_SEPARATOR) + 1; 122 | $content .= DIRECTORY_SEPARATOR; 123 | $stack = [$content]; 124 | while (! empty($stack)) { 125 | $current = array_pop($stack); 126 | $files = []; 127 | 128 | $dir = dir($current); 129 | while (false !== ($node = $dir->read())) { 130 | if ($node === '.' || $node === '..') { 131 | continue; 132 | } 133 | 134 | if (is_dir($current . $node)) { 135 | $stack[] = $current . $node . DIRECTORY_SEPARATOR; 136 | } 137 | 138 | if (is_file($current . $node)) { 139 | $files[] = $node; 140 | } 141 | } 142 | 143 | $local = substr($current, $index); 144 | $zip->addEmptyDir(substr($local, 0, -1)); 145 | 146 | foreach ($files as $file) { 147 | $zip->addFile($current . $file, $local . $file); 148 | if ($res !== true) { 149 | throw new Exception\RuntimeException($this->errorString($res)); 150 | } 151 | } 152 | } 153 | } else { 154 | $res = $zip->addFile($content, $basename); 155 | if ($res !== true) { 156 | throw new Exception\RuntimeException($this->errorString($res)); 157 | } 158 | } 159 | } else { 160 | $file = $this->getTarget(); 161 | if (! is_dir($file)) { 162 | $file = basename($file); 163 | } else { 164 | $file = 'zip.tmp'; 165 | } 166 | 167 | $res = $zip->addFromString($file, $content); 168 | if ($res !== true) { 169 | throw new Exception\RuntimeException($this->errorString($res)); 170 | } 171 | } 172 | 173 | $zip->close(); 174 | return $this->options['archive']; 175 | } 176 | 177 | /** 178 | * Decompresses the given content 179 | * 180 | * @param string $content 181 | * @return string 182 | * @throws Exception\RuntimeException If archive file not found, target directory not found, 183 | * or error during decompression 184 | */ 185 | public function decompress($content) 186 | { 187 | $archive = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, realpath($content)); 188 | 189 | if (empty($archive) || ! file_exists($archive)) { 190 | throw new Exception\RuntimeException('ZIP Archive not found'); 191 | } 192 | 193 | $zip = new ZipArchive(); 194 | $res = $zip->open($archive); 195 | 196 | $target = $this->getTarget(); 197 | if (! empty($target) && ! is_dir($target)) { 198 | $target = dirname($target); 199 | } 200 | 201 | if (! empty($target)) { 202 | $target = rtrim($target, '/\\') . DIRECTORY_SEPARATOR; 203 | } 204 | 205 | if (empty($target) || ! is_dir($target)) { 206 | throw new Exception\RuntimeException('No target for ZIP decompression set'); 207 | } 208 | 209 | if ($res !== true) { 210 | throw new Exception\RuntimeException($this->errorString($res)); 211 | } 212 | 213 | $res = $zip->extractTo($target); 214 | if ($res !== true) { 215 | throw new Exception\RuntimeException($this->errorString($res)); 216 | } 217 | 218 | $zip->close(); 219 | return $target; 220 | } 221 | 222 | /** 223 | * Returns the proper string based on the given error constant 224 | * 225 | * @param string $error 226 | * @return string 227 | */ 228 | public function errorString($error) 229 | { 230 | switch ($error) { 231 | case ZipArchive::ER_MULTIDISK: 232 | return 'Multidisk ZIP Archives not supported'; 233 | 234 | case ZipArchive::ER_RENAME: 235 | return 'Failed to rename the temporary file for ZIP'; 236 | 237 | case ZipArchive::ER_CLOSE: 238 | return 'Failed to close the ZIP Archive'; 239 | 240 | case ZipArchive::ER_SEEK: 241 | return 'Failure while seeking the ZIP Archive'; 242 | 243 | case ZipArchive::ER_READ: 244 | return 'Failure while reading the ZIP Archive'; 245 | 246 | case ZipArchive::ER_WRITE: 247 | return 'Failure while writing the ZIP Archive'; 248 | 249 | case ZipArchive::ER_CRC: 250 | return 'CRC failure within the ZIP Archive'; 251 | 252 | case ZipArchive::ER_ZIPCLOSED: 253 | return 'ZIP Archive already closed'; 254 | 255 | case ZipArchive::ER_NOENT: 256 | return 'No such file within the ZIP Archive'; 257 | 258 | case ZipArchive::ER_EXISTS: 259 | return 'ZIP Archive already exists'; 260 | 261 | case ZipArchive::ER_OPEN: 262 | return 'Can not open ZIP Archive'; 263 | 264 | case ZipArchive::ER_TMPOPEN: 265 | return 'Failure creating temporary ZIP Archive'; 266 | 267 | case ZipArchive::ER_ZLIB: 268 | return 'ZLib Problem'; 269 | 270 | case ZipArchive::ER_MEMORY: 271 | return 'Memory allocation problem while working on a ZIP Archive'; 272 | 273 | case ZipArchive::ER_CHANGED: 274 | return 'ZIP Entry has been changed'; 275 | 276 | case ZipArchive::ER_COMPNOTSUPP: 277 | return 'Compression method not supported within ZLib'; 278 | 279 | case ZipArchive::ER_EOF: 280 | return 'Premature EOF within ZIP Archive'; 281 | 282 | case ZipArchive::ER_INVAL: 283 | return 'Invalid argument for ZLIB'; 284 | 285 | case ZipArchive::ER_NOZIP: 286 | return 'Given file is no zip archive'; 287 | 288 | case ZipArchive::ER_INTERNAL: 289 | return 'Internal error while working on a ZIP Archive'; 290 | 291 | case ZipArchive::ER_INCONS: 292 | return 'Inconsistent ZIP archive'; 293 | 294 | case ZipArchive::ER_REMOVE: 295 | return 'Can not remove ZIP Archive'; 296 | 297 | case ZipArchive::ER_DELETED: 298 | return 'ZIP Entry has been deleted'; 299 | 300 | default: 301 | return 'Unknown error within ZIP Archive'; 302 | } 303 | } 304 | 305 | /** 306 | * Returns the adapter name 307 | * 308 | * @return string 309 | */ 310 | public function toString() 311 | { 312 | return 'Zip'; 313 | } 314 | } 315 | --------------------------------------------------------------------------------