├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── bin └── update_hostname_validator.php ├── composer.json └── src ├── AbstractValidator.php ├── Barcode.php ├── Barcode ├── AbstractAdapter.php ├── AdapterInterface.php ├── Codabar.php ├── Code128.php ├── Code25.php ├── Code25interleaved.php ├── Code39.php ├── Code39ext.php ├── Code93.php ├── Code93ext.php ├── Ean12.php ├── Ean13.php ├── Ean14.php ├── Ean18.php ├── Ean2.php ├── Ean5.php ├── Ean8.php ├── Gtin12.php ├── Gtin13.php ├── Gtin14.php ├── Identcode.php ├── Intelligentmail.php ├── Issn.php ├── Itf14.php ├── Leitcode.php ├── Planet.php ├── Postnet.php ├── Royalmail.php ├── Sscc.php ├── Upca.php └── Upce.php ├── Between.php ├── Bitwise.php ├── Callback.php ├── ConfigProvider.php ├── CreditCard.php ├── Csrf.php ├── Date.php ├── DateStep.php ├── Db ├── AbstractDb.php ├── NoRecordExists.php └── RecordExists.php ├── Digits.php ├── EmailAddress.php ├── Exception ├── BadMethodCallException.php ├── ExceptionInterface.php ├── ExtensionNotLoadedException.php ├── InvalidArgumentException.php ├── InvalidMagicMimeFileException.php └── RuntimeException.php ├── Explode.php ├── File ├── Count.php ├── Crc32.php ├── ExcludeExtension.php ├── ExcludeMimeType.php ├── Exists.php ├── Extension.php ├── FileInformationTrait.php ├── FilesSize.php ├── Hash.php ├── ImageSize.php ├── IsCompressed.php ├── IsImage.php ├── Md5.php ├── MimeType.php ├── NotExists.php ├── Sha1.php ├── Size.php ├── Upload.php ├── UploadFile.php └── WordCount.php ├── GpsPoint.php ├── GreaterThan.php ├── Hex.php ├── Hostname.php ├── Hostname ├── Biz.php ├── Cn.php ├── Com.php └── Jp.php ├── Iban.php ├── Identical.php ├── InArray.php ├── Ip.php ├── IsCountable.php ├── IsInstanceOf.php ├── Isbn.php ├── Isbn ├── Isbn10.php └── Isbn13.php ├── LessThan.php ├── Module.php ├── NotEmpty.php ├── Regex.php ├── Sitemap ├── Changefreq.php ├── Lastmod.php ├── Loc.php └── Priority.php ├── StaticValidator.php ├── Step.php ├── StringLength.php ├── Timezone.php ├── Translator ├── TranslatorAwareInterface.php └── TranslatorInterface.php ├── UndisclosedPassword.php ├── Uri.php ├── Uuid.php ├── ValidatorChain.php ├── ValidatorInterface.php ├── ValidatorPluginManager.php ├── ValidatorPluginManagerAwareInterface.php ├── ValidatorPluginManagerFactory.php └── ValidatorProviderInterface.php /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zend-validator 2 | 3 | > ## Repository abandoned 2019-12-31 4 | > 5 | > This repository has moved to [laminas/laminas-validator](https://github.com/laminas/laminas-validator). 6 | 7 | [![Build Status](https://secure.travis-ci.org/zendframework/zend-validator.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-validator) 8 | [![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-validator/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-validator?branch=master) 9 | 10 | zend-validator provides a set of commonly needed validators. It also provides a 11 | simple validator chaining mechanism by which multiple validators may be applied 12 | to a single datum in a user-defined order. 13 | 14 | ## Installation 15 | 16 | Run the following to install this library: 17 | 18 | ```bash 19 | $ composer require zendframework/zend-validator 20 | ``` 21 | 22 | ## Documentation 23 | 24 | Browse the documentation online at https://docs.zendframework.com/zend-validator/ 25 | 26 | ## Support 27 | 28 | * [Issues](https://github.com/zendframework/zend-validator/issues/) 29 | * [Chat](https://zendframework-slack.herokuapp.com/) 30 | * [Forum](https://discourse.zendframework.com/) 31 | -------------------------------------------------------------------------------- /bin/update_hostname_validator.php: -------------------------------------------------------------------------------- 1 | getBody(), "\n")); 35 | $validatorVersion = getVersionFromString('IanaVersion', file_get_contents(ZF2_HOSTNAME_VALIDATOR_FILE)); 36 | 37 | if ($checkOnly && $ianaVersion > $validatorVersion) { 38 | printf( 39 | 'TLDs must be updated, please run `php bin/update_hostname_validator.php` and push your changes%s', 40 | PHP_EOL 41 | ); 42 | exit(1); 43 | } 44 | 45 | if ($checkOnly) { 46 | printf('TLDs are up-to-date%s', PHP_EOL); 47 | exit(0); 48 | } 49 | 50 | foreach (file(ZF2_HOSTNAME_VALIDATOR_FILE) as $line) { 51 | // Replace old version number with new one 52 | if (preg_match('/\*\s+IanaVersion\s+\d+/', $line, $matches)) { 53 | $newFileContent[] = sprintf(" * IanaVersion %s\n", $ianaVersion); 54 | continue; 55 | } 56 | 57 | if ($insertDone === $insertFinish) { 58 | // Outside of $validTlds definition; keep line as-is 59 | $newFileContent[] = $line; 60 | } 61 | 62 | if ($insertFinish) { 63 | continue; 64 | } 65 | 66 | if ($insertDone) { 67 | // Detect where the $validTlds declaration ends 68 | if (preg_match('/^\s+\];\s*$/', $line)) { 69 | $newFileContent[] = $line; 70 | $insertFinish = true; 71 | } 72 | 73 | continue; 74 | } 75 | 76 | // Detect where the $validTlds declaration begins 77 | if (preg_match('/^\s+protected\s+\$validTlds\s+=\s+\[\s*$/', $line)) { 78 | $newFileContent = array_merge($newFileContent, getNewValidTlds($response->getBody())); 79 | $insertDone = true; 80 | } 81 | } 82 | 83 | if (! $insertDone) { 84 | printf('Error: cannot find line with "protected $validTlds"%s', PHP_EOL); 85 | exit(1); 86 | } 87 | 88 | if (!$insertFinish) { 89 | printf('Error: cannot find end of $validTlds declaration%s', PHP_EOL); 90 | exit(1); 91 | } 92 | 93 | if (false === @file_put_contents(ZF2_HOSTNAME_VALIDATOR_FILE, $newFileContent)) { 94 | printf('Error: cannot write info file "%s"%s', ZF2_HOSTNAME_VALIDATOR_FILE, PHP_EOL); 95 | exit(1); 96 | } 97 | 98 | printf('Validator TLD file updated.%s', PHP_EOL); 99 | exit(0); 100 | 101 | /** 102 | * Get Official TLDs 103 | * 104 | * @return \Zend\Http\Response 105 | * @throws Exception 106 | */ 107 | function getOfficialTLDs() 108 | { 109 | $client = new Client(); 110 | $client->setOptions([ 111 | 'adapter' => 'Zend\Http\Client\Adapter\Curl', 112 | ]); 113 | $client->setUri(IANA_URL); 114 | $client->setMethod('GET'); 115 | 116 | $response = $client->send(); 117 | if (! $response->isSuccess()) { 118 | throw new \Exception(sprintf("Error: cannot get '%s'%s", IANA_URL, PHP_EOL)); 119 | } 120 | return $response; 121 | } 122 | 123 | /** 124 | * Extract the first match of a string like 125 | * "Version 2015072300" from the given string 126 | * 127 | * @param string $prefix 128 | * @param string $string 129 | * @return string 130 | * @throws Exception 131 | */ 132 | function getVersionFromString($prefix, $string) 133 | { 134 | $matches = []; 135 | if (! preg_match(sprintf('/%s\s+((\d+)?)/', $prefix), $string, $matches)) { 136 | throw new Exception('Error: cannot get last update date'); 137 | } 138 | 139 | return $matches[1]; 140 | } 141 | 142 | /** 143 | * Extract new Valid TLDs from a string containing one per line. 144 | * 145 | * @param string $string 146 | * @return array 147 | */ 148 | function getNewValidTlds($string) 149 | { 150 | $decodePunycode = getPunycodeDecoder(); 151 | 152 | // Get new TLDs from the list previously fetched 153 | $newValidTlds = []; 154 | foreach (preg_grep('/^[^#]/', preg_split("#\r?\n#", $string)) as $line) { 155 | $newValidTlds []= sprintf( 156 | "%s'%s',\n", 157 | str_repeat(' ', 8), 158 | $decodePunycode(strtolower($line)) 159 | ); 160 | } 161 | 162 | return $newValidTlds; 163 | } 164 | 165 | /** 166 | * Retrieve and return a punycode decoder. 167 | * 168 | * TLDs are puny encoded. 169 | * 170 | * We need a decodePunycode function to translate TLDs to UTF-8: 171 | * 172 | * - use idn_to_utf8 if available 173 | * - otherwise, use Hostname::decodePunycode() 174 | * 175 | * @return callable 176 | */ 177 | function getPunycodeDecoder() 178 | { 179 | if (function_exists('idn_to_utf8')) { 180 | return function ($domain) { 181 | return idn_to_utf8($domain, 0, INTL_IDNA_VARIANT_UTS46); 182 | }; 183 | } 184 | 185 | $hostnameValidator = new Hostname(); 186 | $reflection = new ReflectionClass(get_class($hostnameValidator)); 187 | $decodePunyCode = $reflection->getMethod('decodePunycode'); 188 | $decodePunyCode->setAccessible(true); 189 | 190 | return function ($encode) use ($hostnameValidator, $decodePunyCode) { 191 | if (strpos($encode, 'xn--') === 0) { 192 | return $decodePunyCode->invokeArgs($hostnameValidator, [substr($encode, 4)]); 193 | } 194 | return $encode; 195 | }; 196 | } 197 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zendframework/zend-validator", 3 | "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", 4 | "license": "BSD-3-Clause", 5 | "keywords": [ 6 | "zendframework", 7 | "zf", 8 | "validator" 9 | ], 10 | "support": { 11 | "docs": "https://docs.zendframework.com/zend-validator/", 12 | "issues": "https://github.com/zendframework/zend-validator/issues", 13 | "source": "https://github.com/zendframework/zend-validator", 14 | "rss": "https://github.com/zendframework/zend-validator/releases.atom", 15 | "chat": "https://zendframework-slack.herokuapp.com", 16 | "forum": "https://discourse.zendframework.com/c/questions/components" 17 | }, 18 | "require": { 19 | "php": "^7.1", 20 | "zendframework/zend-stdlib": "^3.2.1", 21 | "container-interop/container-interop": "^1.1" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "^6.0.8 || ^5.7.15", 25 | "psr/http-message": "^1.0", 26 | "psr/http-client": "^1.0", 27 | "psr/http-factory": "^1.0", 28 | "zendframework/zend-cache": "^2.6.1", 29 | "zendframework/zend-coding-standard": "~1.0.0", 30 | "zendframework/zend-config": "^2.6", 31 | "zendframework/zend-db": "^2.7", 32 | "zendframework/zend-filter": "^2.6", 33 | "zendframework/zend-http": "^2.5.4", 34 | "zendframework/zend-i18n": "^2.6", 35 | "zendframework/zend-math": "^2.6", 36 | "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", 37 | "zendframework/zend-session": "^2.8", 38 | "zendframework/zend-uri": "^2.5" 39 | }, 40 | "suggest": { 41 | "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", 42 | "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", 43 | "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", 44 | "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", 45 | "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", 46 | "zendframework/zend-i18n-resources": "Translations of validator messages", 47 | "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", 48 | "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", 49 | "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" 50 | }, 51 | "autoload": { 52 | "psr-4": { 53 | "Zend\\Validator\\": "src/" 54 | } 55 | }, 56 | "autoload-dev": { 57 | "psr-4": { 58 | "ZendTest\\Validator\\": "test/" 59 | } 60 | }, 61 | "config": { 62 | "sort-packages": true 63 | }, 64 | "extra": { 65 | "branch-alias": { 66 | "dev-master": "2.13.x-dev", 67 | "dev-develop": "2.14.x-dev" 68 | }, 69 | "zf": { 70 | "component": "Zend\\Validator", 71 | "config-provider": "Zend\\Validator\\ConfigProvider" 72 | } 73 | }, 74 | "scripts": { 75 | "check": [ 76 | "@cs-check", 77 | "@test" 78 | ], 79 | "cs-check": "phpcs", 80 | "cs-fix": "phpcbf", 81 | "test": "phpunit --colors=always", 82 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Barcode.php: -------------------------------------------------------------------------------- 1 | "The input failed checksum validation", 23 | self::INVALID_CHARS => "The input contains invalid characters", 24 | self::INVALID_LENGTH => "The input should have a length of %length% characters", 25 | self::INVALID => "Invalid type given. String expected", 26 | ]; 27 | 28 | /** 29 | * Additional variables available for validation failure messages 30 | * 31 | * @var array 32 | */ 33 | protected $messageVariables = [ 34 | 'length' => ['options' => 'length'], 35 | ]; 36 | 37 | protected $options = [ 38 | 'adapter' => null, // Barcode adapter Zend\Validator\Barcode\AbstractAdapter 39 | 'options' => null, // Options for this adapter 40 | 'length' => null, 41 | 'useChecksum' => null, 42 | ]; 43 | 44 | /** 45 | * Constructor for barcodes 46 | * 47 | * @param array|string $options Options to use 48 | */ 49 | public function __construct($options = null) 50 | { 51 | if ($options === null) { 52 | $options = []; 53 | } 54 | 55 | if (! is_array($options) && ! ($options instanceof Traversable)) { 56 | $options = ['adapter' => $options]; 57 | } 58 | 59 | if (array_key_exists('options', $options)) { 60 | $options['options'] = ['options' => $options['options']]; 61 | } 62 | 63 | parent::__construct($options); 64 | } 65 | 66 | /** 67 | * Returns the set adapter 68 | * 69 | * @return Barcode\AbstractAdapter 70 | */ 71 | public function getAdapter() 72 | { 73 | if (! ($this->options['adapter'] instanceof Barcode\AdapterInterface)) { 74 | $this->setAdapter('Ean13'); 75 | } 76 | 77 | return $this->options['adapter']; 78 | } 79 | 80 | /** 81 | * Sets a new barcode adapter 82 | * 83 | * @param string|Barcode\AbstractAdapter $adapter Barcode adapter to use 84 | * @param array $options Options for this adapter 85 | * @return Barcode 86 | * @throws Exception\InvalidArgumentException 87 | */ 88 | public function setAdapter($adapter, $options = null) 89 | { 90 | if (is_string($adapter)) { 91 | $adapter = ucfirst(strtolower($adapter)); 92 | $adapter = 'Zend\\Validator\\Barcode\\' . $adapter; 93 | 94 | if (! class_exists($adapter)) { 95 | throw new Exception\InvalidArgumentException('Barcode adapter matching "' . $adapter . '" not found'); 96 | } 97 | 98 | $adapter = new $adapter($options); 99 | } 100 | 101 | if (! $adapter instanceof Barcode\AdapterInterface) { 102 | throw new Exception\InvalidArgumentException( 103 | sprintf( 104 | "Adapter %s does not implement Zend\\Validator\\Barcode\\AdapterInterface", 105 | (is_object($adapter) ? get_class($adapter) : gettype($adapter)) 106 | ) 107 | ); 108 | } 109 | 110 | $this->options['adapter'] = $adapter; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Returns the checksum option 117 | * 118 | * @return string 119 | */ 120 | public function getChecksum() 121 | { 122 | return $this->getAdapter()->getChecksum(); 123 | } 124 | 125 | /** 126 | * Sets if checksum should be validated, if no value is given the actual setting is returned 127 | * 128 | * @param bool $checksum 129 | * @return bool 130 | */ 131 | public function useChecksum($checksum = null) 132 | { 133 | return $this->getAdapter()->useChecksum($checksum); 134 | } 135 | 136 | /** 137 | * Defined by Zend\Validator\ValidatorInterface 138 | * 139 | * Returns true if and only if $value contains a valid barcode 140 | * 141 | * @param string $value 142 | * @return bool 143 | */ 144 | public function isValid($value) 145 | { 146 | if (! is_string($value)) { 147 | $this->error(self::INVALID); 148 | return false; 149 | } 150 | 151 | $this->setValue($value); 152 | $adapter = $this->getAdapter(); 153 | $this->options['length'] = $adapter->getLength(); 154 | $result = $adapter->hasValidLength($value); 155 | if (! $result) { 156 | if (is_array($this->options['length'])) { 157 | $temp = $this->options['length']; 158 | $this->options['length'] = ""; 159 | foreach ($temp as $length) { 160 | $this->options['length'] .= "/"; 161 | $this->options['length'] .= $length; 162 | } 163 | 164 | $this->options['length'] = substr($this->options['length'], 1); 165 | } 166 | 167 | $this->error(self::INVALID_LENGTH); 168 | return false; 169 | } 170 | 171 | $result = $adapter->hasValidCharacters($value); 172 | if (! $result) { 173 | $this->error(self::INVALID_CHARS); 174 | return false; 175 | } 176 | 177 | if ($this->useChecksum(null)) { 178 | $result = $adapter->hasValidChecksum($value); 179 | if (! $result) { 180 | $this->error(self::FAILED); 181 | return false; 182 | } 183 | } 184 | 185 | return true; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/Barcode/AdapterInterface.php: -------------------------------------------------------------------------------- 1 | setLength(-1); 20 | $this->setCharacters('0123456789-$:/.+ABCDTN*E'); 21 | $this->useChecksum(false); 22 | } 23 | 24 | /** 25 | * Checks for allowed characters 26 | * @see Zend\Validator\Barcode.AbstractAdapter::checkChars() 27 | */ 28 | public function hasValidCharacters($value) 29 | { 30 | if (strpbrk($value, 'ABCD')) { 31 | $first = $value[0]; 32 | if (! strpbrk($first, 'ABCD')) { 33 | // Missing start char 34 | return false; 35 | } 36 | 37 | $last = substr($value, -1, 1); 38 | if (! strpbrk($last, 'ABCD')) { 39 | // Missing stop char 40 | return false; 41 | } 42 | 43 | $value = substr($value, 1, -1); 44 | } elseif (strpbrk($value, 'TN*E')) { 45 | $first = $value[0]; 46 | if (! strpbrk($first, 'TN*E')) { 47 | // Missing start char 48 | return false; 49 | } 50 | 51 | $last = substr($value, -1, 1); 52 | if (! strpbrk($last, 'TN*E')) { 53 | // Missing stop char 54 | return false; 55 | } 56 | 57 | $value = substr($value, 1, -1); 58 | } 59 | 60 | $chars = $this->getCharacters(); 61 | $this->setCharacters('0123456789-$:/.+'); 62 | $result = parent::hasValidCharacters($value); 63 | $this->setCharacters($chars); 64 | return $result; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Barcode/Code25.php: -------------------------------------------------------------------------------- 1 | setLength(-1); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('code25'); 22 | $this->useChecksum(false); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Barcode/Code25interleaved.php: -------------------------------------------------------------------------------- 1 | setLength('even'); 22 | $this->setCharacters('0123456789'); 23 | $this->setChecksum('code25'); 24 | $this->useChecksum(false); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Barcode/Code39.php: -------------------------------------------------------------------------------- 1 | 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, 19 | '7' => 7, '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 20 | 'E' => 14, 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 21 | 'L' => 21, 'M' => 22, 'N' => 23, 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 22 | 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34, 23 | 'Z' => 35, '-' => 36, '.' => 37, ' ' => 38, '$' => 39, '/' => 40, '+' => 41, 24 | '%' => 42, 25 | ]; 26 | 27 | /** 28 | * Constructor for this barcode adapter 29 | */ 30 | public function __construct() 31 | { 32 | $this->setLength(-1); 33 | $this->setCharacters('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -.$/+%'); 34 | $this->setChecksum('code39'); 35 | $this->useChecksum(false); 36 | } 37 | 38 | /** 39 | * Validates the checksum (Modulo 43) 40 | * 41 | * @param string $value The barcode to validate 42 | * @return bool 43 | */ 44 | protected function code39($value) 45 | { 46 | $checksum = substr($value, -1, 1); 47 | $value = str_split(substr($value, 0, -1)); 48 | $count = 0; 49 | foreach ($value as $char) { 50 | $count += $this->check[$char]; 51 | } 52 | 53 | $mod = $count % 43; 54 | if ($mod == $this->check[$checksum]) { 55 | return true; 56 | } 57 | 58 | return false; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Barcode/Code39ext.php: -------------------------------------------------------------------------------- 1 | setLength(-1); 20 | $this->setCharacters(128); 21 | $this->useChecksum(false); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Code93.php: -------------------------------------------------------------------------------- 1 | 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, 20 | '7' => 7, '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 21 | 'E' => 14, 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 22 | 'L' => 21, 'M' => 22, 'N' => 23, 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 23 | 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34, 24 | 'Z' => 35, '-' => 36, '.' => 37, ' ' => 38, '$' => 39, '/' => 40, '+' => 41, 25 | '%' => 42, '!' => 43, '"' => 44, '§' => 45, '&' => 46, 26 | ]; 27 | 28 | /** 29 | * Constructor for this barcode adapter 30 | */ 31 | public function __construct() 32 | { 33 | $this->setLength(-1); 34 | $this->setCharacters('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -.$/+%'); 35 | $this->setChecksum('code93'); 36 | $this->useChecksum(false); 37 | } 38 | 39 | /** 40 | * Validates the checksum (Modulo CK) 41 | * 42 | * @param string $value The barcode to validate 43 | * @return bool 44 | */ 45 | protected function code93($value) 46 | { 47 | $checksum = substr($value, -2, 2); 48 | $value = str_split(substr($value, 0, -2)); 49 | $count = 0; 50 | $length = count($value) % 20; 51 | foreach ($value as $char) { 52 | if ($length == 0) { 53 | $length = 20; 54 | } 55 | 56 | $count += $this->check[$char] * $length; 57 | --$length; 58 | } 59 | 60 | $check = array_search(($count % 47), $this->check); 61 | $value[] = $check; 62 | $count = 0; 63 | $length = count($value) % 15; 64 | foreach ($value as $char) { 65 | if ($length == 0) { 66 | $length = 15; 67 | } 68 | 69 | $count += $this->check[$char] * $length; 70 | --$length; 71 | } 72 | $check .= array_search(($count % 47), $this->check); 73 | 74 | if ($check == $checksum) { 75 | return true; 76 | } 77 | 78 | return false; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Barcode/Code93ext.php: -------------------------------------------------------------------------------- 1 | setLength(-1); 20 | $this->setCharacters(128); 21 | $this->useChecksum(false); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Ean12.php: -------------------------------------------------------------------------------- 1 | setLength(12); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Ean13.php: -------------------------------------------------------------------------------- 1 | setLength(13); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Ean14.php: -------------------------------------------------------------------------------- 1 | setLength(14); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Ean18.php: -------------------------------------------------------------------------------- 1 | setLength(18); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Ean2.php: -------------------------------------------------------------------------------- 1 | setLength(2); 20 | $this->setCharacters('0123456789'); 21 | $this->useChecksum(false); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Ean5.php: -------------------------------------------------------------------------------- 1 | setLength(5); 22 | $this->setCharacters('0123456789'); 23 | $this->useChecksum(false); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Barcode/Ean8.php: -------------------------------------------------------------------------------- 1 | setLength([7, 8]); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | 24 | /** 25 | * Overrides parent checkLength 26 | * 27 | * @param string $value Value 28 | * @return bool 29 | */ 30 | public function hasValidLength($value) 31 | { 32 | if (strlen($value) == 7) { 33 | $this->useChecksum(false); 34 | } else { 35 | $this->useChecksum(true); 36 | } 37 | 38 | return parent::hasValidLength($value); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Barcode/Gtin12.php: -------------------------------------------------------------------------------- 1 | setLength(12); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Gtin13.php: -------------------------------------------------------------------------------- 1 | setLength(13); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Gtin14.php: -------------------------------------------------------------------------------- 1 | setLength(14); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Identcode.php: -------------------------------------------------------------------------------- 1 | setLength(12); 38 | $this->setCharacters('0123456789'); 39 | $this->setChecksum('identcode'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Barcode/Intelligentmail.php: -------------------------------------------------------------------------------- 1 | setLength([20, 25, 29, 31]); 22 | $this->setCharacters('0123456789'); 23 | $this->useChecksum(false); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Barcode/Issn.php: -------------------------------------------------------------------------------- 1 | setLength([8, 13]); 20 | $this->setCharacters('0123456789X'); 21 | $this->setChecksum('gtin'); 22 | } 23 | 24 | /** 25 | * Allows X on length of 8 chars 26 | * 27 | * @param string $value The barcode to check for allowed characters 28 | * @return bool 29 | */ 30 | public function hasValidCharacters($value) 31 | { 32 | if (strlen($value) != 8) { 33 | if (strpos($value, 'X') !== false) { 34 | return false; 35 | } 36 | } 37 | 38 | return parent::hasValidCharacters($value); 39 | } 40 | 41 | /** 42 | * Validates the checksum 43 | * 44 | * @param string $value The barcode to check the checksum for 45 | * @return bool 46 | */ 47 | public function hasValidChecksum($value) 48 | { 49 | if (strlen($value) == 8) { 50 | $this->setChecksum('issn'); 51 | } else { 52 | $this->setChecksum('gtin'); 53 | } 54 | 55 | return parent::hasValidChecksum($value); 56 | } 57 | 58 | /** 59 | * Validates the checksum () 60 | * ISSN implementation (reversed mod11) 61 | * 62 | * @param string $value The barcode to validate 63 | * @return bool 64 | */ 65 | protected function issn($value) 66 | { 67 | $checksum = substr($value, -1, 1); 68 | $values = str_split(substr($value, 0, -1)); 69 | $check = 0; 70 | $multi = 8; 71 | foreach ($values as $token) { 72 | if ($token == 'X') { 73 | $token = 10; 74 | } 75 | 76 | $check += ($token * $multi); 77 | --$multi; 78 | } 79 | 80 | $check %= 11; 81 | $check = ($check === 0 ? 0 : (11 - $check)); 82 | if ($check == $checksum) { 83 | return true; 84 | } elseif (($check == 10) && ($checksum == 'X')) { 85 | return true; 86 | } 87 | 88 | return false; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Barcode/Itf14.php: -------------------------------------------------------------------------------- 1 | setLength(14); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Leitcode.php: -------------------------------------------------------------------------------- 1 | setLength(14); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('identcode'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Planet.php: -------------------------------------------------------------------------------- 1 | setLength([12, 14]); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('postnet'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Postnet.php: -------------------------------------------------------------------------------- 1 | setLength([6, 7, 10, 12]); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('postnet'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Royalmail.php: -------------------------------------------------------------------------------- 1 | 1, '1' => 1, '2' => 1, '3' => 1, '4' => 1, '5' => 1, 16 | '6' => 2, '7' => 2, '8' => 2, '9' => 2, 'A' => 2, 'B' => 2, 17 | 'C' => 3, 'D' => 3, 'E' => 3, 'F' => 3, 'G' => 3, 'H' => 3, 18 | 'I' => 4, 'J' => 4, 'K' => 4, 'L' => 4, 'M' => 4, 'N' => 4, 19 | 'O' => 5, 'P' => 5, 'Q' => 5, 'R' => 5, 'S' => 5, 'T' => 5, 20 | 'U' => 0, 'V' => 0, 'W' => 0, 'X' => 0, 'Y' => 0, 'Z' => 0, 21 | ]; 22 | 23 | protected $columns = [ 24 | '0' => 1, '1' => 2, '2' => 3, '3' => 4, '4' => 5, '5' => 0, 25 | '6' => 1, '7' => 2, '8' => 3, '9' => 4, 'A' => 5, 'B' => 0, 26 | 'C' => 1, 'D' => 2, 'E' => 3, 'F' => 4, 'G' => 5, 'H' => 0, 27 | 'I' => 1, 'J' => 2, 'K' => 3, 'L' => 4, 'M' => 5, 'N' => 0, 28 | 'O' => 1, 'P' => 2, 'Q' => 3, 'R' => 4, 'S' => 5, 'T' => 0, 29 | 'U' => 1, 'V' => 2, 'W' => 3, 'X' => 4, 'Y' => 5, 'Z' => 0, 30 | ]; 31 | 32 | /** 33 | * Constructor for this barcode adapter 34 | */ 35 | public function __construct() 36 | { 37 | $this->setLength(-1); 38 | $this->setCharacters('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'); 39 | $this->setChecksum('royalmail'); 40 | } 41 | 42 | /** 43 | * Validates the checksum () 44 | * 45 | * @param string $value The barcode to validate 46 | * @return bool 47 | */ 48 | protected function royalmail($value) 49 | { 50 | $checksum = substr($value, -1, 1); 51 | $values = str_split(substr($value, 0, -1)); 52 | $rowvalue = 0; 53 | $colvalue = 0; 54 | foreach ($values as $row) { 55 | $rowvalue += $this->rows[$row]; 56 | $colvalue += $this->columns[$row]; 57 | } 58 | 59 | $rowvalue %= 6; 60 | $colvalue %= 6; 61 | 62 | $rowchkvalue = array_keys($this->rows, $rowvalue); 63 | $colchkvalue = array_keys($this->columns, $colvalue); 64 | $intersect = array_intersect($rowchkvalue, $colchkvalue); 65 | $chkvalue = current($intersect); 66 | if ($chkvalue == $checksum) { 67 | return true; 68 | } 69 | 70 | return false; 71 | } 72 | 73 | /** 74 | * Allows start and stop tag within checked chars 75 | * 76 | * @param string $value The barcode to check for allowed characters 77 | * @return bool 78 | */ 79 | public function hasValidCharacters($value) 80 | { 81 | if ($value[0] == '(') { 82 | $value = substr($value, 1); 83 | 84 | if ($value[strlen($value) - 1] == ')') { 85 | $value = substr($value, 0, -1); 86 | } else { 87 | return false; 88 | } 89 | } 90 | 91 | return parent::hasValidCharacters($value); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Barcode/Sscc.php: -------------------------------------------------------------------------------- 1 | setLength(18); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Upca.php: -------------------------------------------------------------------------------- 1 | setLength(12); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Barcode/Upce.php: -------------------------------------------------------------------------------- 1 | setLength([6, 7, 8]); 20 | $this->setCharacters('0123456789'); 21 | $this->setChecksum('gtin'); 22 | } 23 | 24 | /** 25 | * Overrides parent checkLength 26 | * 27 | * @param string $value Value 28 | * @return bool 29 | */ 30 | public function hasValidLength($value) 31 | { 32 | if (strlen($value) != 8) { 33 | $this->useChecksum(false); 34 | } else { 35 | $this->useChecksum(true); 36 | } 37 | 38 | return parent::hasValidLength($value); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Bitwise.php: -------------------------------------------------------------------------------- 1 | "The input has no common bit set with '%control%'", 35 | self::NOT_AND_STRICT => "The input doesn't have the same bits set as '%control%'", 36 | self::NOT_XOR => "The input has common bit set with '%control%'", 37 | ]; 38 | 39 | /** 40 | * Additional variables available for validation failure messages 41 | * 42 | * @var array 43 | */ 44 | protected $messageVariables = [ 45 | 'control' => 'control', 46 | ]; 47 | 48 | /** 49 | * @var integer 50 | */ 51 | protected $operator; 52 | 53 | /** 54 | * @var boolean 55 | */ 56 | protected $strict = false; 57 | 58 | /** 59 | * Sets validator options 60 | * Accepts the following option keys: 61 | * 'control' => integer 62 | * 'operator' => 63 | * 'strict' => boolean 64 | * 65 | * @param array|Traversable $options 66 | */ 67 | public function __construct($options = null) 68 | { 69 | if ($options instanceof Traversable) { 70 | $options = iterator_to_array($options); 71 | } 72 | 73 | if (! is_array($options)) { 74 | $options = func_get_args(); 75 | 76 | $temp['control'] = array_shift($options); 77 | 78 | if (! empty($options)) { 79 | $temp['operator'] = array_shift($options); 80 | } 81 | 82 | if (! empty($options)) { 83 | $temp['strict'] = array_shift($options); 84 | } 85 | 86 | $options = $temp; 87 | } 88 | 89 | parent::__construct($options); 90 | } 91 | 92 | /** 93 | * Returns the control parameter. 94 | * 95 | * @return integer 96 | */ 97 | public function getControl() 98 | { 99 | return $this->control; 100 | } 101 | 102 | /** 103 | * Returns the operator parameter. 104 | * 105 | * @return string 106 | */ 107 | public function getOperator() 108 | { 109 | return $this->operator; 110 | } 111 | 112 | /** 113 | * Returns the strict parameter. 114 | * 115 | * @return boolean 116 | */ 117 | public function getStrict() 118 | { 119 | return $this->strict; 120 | } 121 | 122 | /** 123 | * Returns true if and only if $value is between min and max options, inclusively 124 | * if inclusive option is true. 125 | * 126 | * @param mixed $value 127 | * @return bool 128 | */ 129 | public function isValid($value) 130 | { 131 | $this->setValue($value); 132 | 133 | if (self::OP_AND === $this->operator) { 134 | if ($this->strict) { 135 | // All the bits set in value must be set in control 136 | $this->error(self::NOT_AND_STRICT); 137 | 138 | return (bool) (($this->control & $value) == $value); 139 | } else { 140 | // At least one of the bits must be common between value and control 141 | $this->error(self::NOT_AND); 142 | 143 | return (bool) ($this->control & $value); 144 | } 145 | } elseif (self::OP_XOR === $this->operator) { 146 | $this->error(self::NOT_XOR); 147 | 148 | return (bool) (($this->control ^ $value) === ($this->control | $value)); 149 | } 150 | 151 | return false; 152 | } 153 | 154 | /** 155 | * Sets the control parameter. 156 | * 157 | * @param integer $control 158 | * @return Bitwise 159 | */ 160 | public function setControl($control) 161 | { 162 | $this->control = (int) $control; 163 | 164 | return $this; 165 | } 166 | 167 | /** 168 | * Sets the operator parameter. 169 | * 170 | * @param string $operator 171 | * @return Bitwise 172 | */ 173 | public function setOperator($operator) 174 | { 175 | $this->operator = $operator; 176 | 177 | return $this; 178 | } 179 | 180 | /** 181 | * Sets the strict parameter. 182 | * 183 | * @param boolean $strict 184 | * @return Bitwise 185 | */ 186 | public function setStrict($strict) 187 | { 188 | $this->strict = (bool) $strict; 189 | 190 | return $this; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/Callback.php: -------------------------------------------------------------------------------- 1 | "The input is not valid", 31 | self::INVALID_CALLBACK => "An exception has been raised within the callback", 32 | ]; 33 | 34 | /** 35 | * Default options to set for the validator 36 | * 37 | * @var mixed 38 | */ 39 | protected $options = [ 40 | 'callback' => null, // Callback in a call_user_func format, string || array 41 | 'callbackOptions' => [], // Options for the callback 42 | ]; 43 | 44 | /** 45 | * Constructor 46 | * 47 | * @param array|callable $options 48 | */ 49 | public function __construct($options = null) 50 | { 51 | if (is_callable($options)) { 52 | $options = ['callback' => $options]; 53 | } 54 | 55 | parent::__construct($options); 56 | } 57 | 58 | /** 59 | * Returns the set callback 60 | * 61 | * @return mixed 62 | */ 63 | public function getCallback() 64 | { 65 | return $this->options['callback']; 66 | } 67 | 68 | /** 69 | * Sets the callback 70 | * 71 | * @param string|array|callable $callback 72 | * @return Callback Provides a fluent interface 73 | * @throws Exception\InvalidArgumentException 74 | */ 75 | public function setCallback($callback) 76 | { 77 | if (! is_callable($callback)) { 78 | throw new Exception\InvalidArgumentException('Invalid callback given'); 79 | } 80 | 81 | $this->options['callback'] = $callback; 82 | return $this; 83 | } 84 | 85 | /** 86 | * Returns the set options for the callback 87 | * 88 | * @return mixed 89 | */ 90 | public function getCallbackOptions() 91 | { 92 | return $this->options['callbackOptions']; 93 | } 94 | 95 | /** 96 | * Sets options for the callback 97 | * 98 | * @param mixed $options 99 | * @return Callback Provides a fluent interface 100 | */ 101 | public function setCallbackOptions($options) 102 | { 103 | $this->options['callbackOptions'] = (array) $options; 104 | return $this; 105 | } 106 | 107 | /** 108 | * Returns true if and only if the set callback returns 109 | * for the provided $value 110 | * 111 | * @param mixed $value 112 | * @param mixed $context Additional context to provide to the callback 113 | * @return bool 114 | * @throws Exception\InvalidArgumentException 115 | */ 116 | public function isValid($value, $context = null) 117 | { 118 | $this->setValue($value); 119 | 120 | $options = $this->getCallbackOptions(); 121 | $callback = $this->getCallback(); 122 | if (empty($callback)) { 123 | throw new Exception\InvalidArgumentException('No callback given'); 124 | } 125 | 126 | $args = [$value]; 127 | if (empty($options) && ! empty($context)) { 128 | $args[] = $context; 129 | } 130 | if (! empty($options) && empty($context)) { 131 | $args = array_merge($args, $options); 132 | } 133 | if (! empty($options) && ! empty($context)) { 134 | $args[] = $context; 135 | $args = array_merge($args, $options); 136 | } 137 | 138 | try { 139 | if (! call_user_func_array($callback, $args)) { 140 | $this->error(self::INVALID_VALUE); 141 | return false; 142 | } 143 | } catch (\Exception $e) { 144 | $this->error(self::INVALID_CALLBACK); 145 | return false; 146 | } 147 | 148 | return true; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/ConfigProvider.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 | 'ValidatorManager' => ValidatorPluginManager::class, 34 | ], 35 | 'factories' => [ 36 | ValidatorPluginManager::class => ValidatorPluginManagerFactory::class, 37 | ], 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Db/NoRecordExists.php: -------------------------------------------------------------------------------- 1 | adapter) { 25 | throw new Exception\RuntimeException('No database adapter present'); 26 | } 27 | 28 | $valid = true; 29 | $this->setValue($value); 30 | 31 | $result = $this->query($value); 32 | if ($result) { 33 | $valid = false; 34 | $this->error(self::ERROR_RECORD_FOUND); 35 | } 36 | 37 | return $valid; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Db/RecordExists.php: -------------------------------------------------------------------------------- 1 | adapter) { 25 | throw new Exception\RuntimeException('No database adapter present'); 26 | } 27 | 28 | $valid = true; 29 | $this->setValue($value); 30 | 31 | $result = $this->query($value); 32 | if (! $result) { 33 | $valid = false; 34 | $this->error(self::ERROR_NO_RECORD_FOUND); 35 | } 36 | 37 | return $valid; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Digits.php: -------------------------------------------------------------------------------- 1 | "The input must contain only digits", 34 | self::STRING_EMPTY => "The input is an empty string", 35 | self::INVALID => "Invalid type given. String, integer or float expected", 36 | ]; 37 | 38 | /** 39 | * Returns true if and only if $value only contains digit characters 40 | * 41 | * @param string $value 42 | * @return bool 43 | */ 44 | public function isValid($value) 45 | { 46 | if (! is_string($value) && ! is_int($value) && ! is_float($value)) { 47 | $this->error(self::INVALID); 48 | return false; 49 | } 50 | 51 | $this->setValue((string) $value); 52 | 53 | if ('' === $this->getValue()) { 54 | $this->error(self::STRING_EMPTY); 55 | return false; 56 | } 57 | 58 | if (null === static::$filter) { 59 | static::$filter = new DigitsFilter(); 60 | } 61 | 62 | if ($this->getValue() !== static::$filter->filter($this->getValue())) { 63 | $this->error(self::NOT_DIGITS); 64 | return false; 65 | } 66 | 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Exception/BadMethodCallException.php: -------------------------------------------------------------------------------- 1 | "Invalid type given", 27 | ]; 28 | 29 | /** 30 | * @var array 31 | */ 32 | protected $messageVariables = []; 33 | 34 | /** 35 | * @var string 36 | */ 37 | protected $valueDelimiter = ','; 38 | 39 | /** 40 | * @var ValidatorInterface 41 | */ 42 | protected $validator; 43 | 44 | /** 45 | * @var bool 46 | */ 47 | protected $breakOnFirstFailure = false; 48 | 49 | /** 50 | * Sets the delimiter string that the values will be split upon 51 | * 52 | * @param string $delimiter 53 | * @return Explode 54 | */ 55 | public function setValueDelimiter($delimiter) 56 | { 57 | $this->valueDelimiter = $delimiter; 58 | return $this; 59 | } 60 | 61 | /** 62 | * Returns the delimiter string that the values will be split upon 63 | * 64 | * @return string 65 | */ 66 | public function getValueDelimiter() 67 | { 68 | return $this->valueDelimiter; 69 | } 70 | 71 | /** 72 | * Set validator plugin manager 73 | * 74 | * @param ValidatorPluginManager $pluginManager 75 | */ 76 | public function setValidatorPluginManager(ValidatorPluginManager $pluginManager) 77 | { 78 | $this->pluginManager = $pluginManager; 79 | } 80 | 81 | /** 82 | * Get validator plugin manager 83 | * 84 | * @return ValidatorPluginManager 85 | */ 86 | public function getValidatorPluginManager() 87 | { 88 | if (! $this->pluginManager) { 89 | $this->setValidatorPluginManager(new ValidatorPluginManager(new ServiceManager)); 90 | } 91 | 92 | return $this->pluginManager; 93 | } 94 | 95 | /** 96 | * Sets the Validator for validating each value 97 | * 98 | * @param ValidatorInterface|array $validator 99 | * @throws Exception\RuntimeException 100 | * @return Explode 101 | */ 102 | public function setValidator($validator) 103 | { 104 | if (is_array($validator)) { 105 | if (! isset($validator['name'])) { 106 | throw new Exception\RuntimeException( 107 | 'Invalid validator specification provided; does not include "name" key' 108 | ); 109 | } 110 | $name = $validator['name']; 111 | $options = isset($validator['options']) ? $validator['options'] : []; 112 | $validator = $this->getValidatorPluginManager()->get($name, $options); 113 | } 114 | 115 | if (! $validator instanceof ValidatorInterface) { 116 | throw new Exception\RuntimeException( 117 | 'Invalid validator given' 118 | ); 119 | } 120 | 121 | $this->validator = $validator; 122 | return $this; 123 | } 124 | 125 | /** 126 | * Gets the Validator for validating each value 127 | * 128 | * @return ValidatorInterface 129 | */ 130 | public function getValidator() 131 | { 132 | return $this->validator; 133 | } 134 | 135 | /** 136 | * Set break on first failure setting 137 | * 138 | * @param bool $break 139 | * @return Explode 140 | */ 141 | public function setBreakOnFirstFailure($break) 142 | { 143 | $this->breakOnFirstFailure = (bool) $break; 144 | return $this; 145 | } 146 | 147 | /** 148 | * Get break on first failure setting 149 | * 150 | * @return bool 151 | */ 152 | public function isBreakOnFirstFailure() 153 | { 154 | return $this->breakOnFirstFailure; 155 | } 156 | 157 | /** 158 | * Defined by Zend\Validator\ValidatorInterface 159 | * 160 | * Returns true if all values validate true 161 | * 162 | * @param mixed $value 163 | * @param mixed $context Extra "context" to provide the composed validator 164 | * @return bool 165 | * @throws Exception\RuntimeException 166 | */ 167 | public function isValid($value, $context = null) 168 | { 169 | $this->setValue($value); 170 | 171 | if ($value instanceof Traversable) { 172 | $value = ArrayUtils::iteratorToArray($value); 173 | } 174 | 175 | if (is_array($value)) { 176 | $values = $value; 177 | } elseif (is_string($value)) { 178 | $delimiter = $this->getValueDelimiter(); 179 | // Skip explode if delimiter is null, 180 | // used when value is expected to be either an 181 | // array when multiple values and a string for 182 | // single values (ie. MultiCheckbox form behavior) 183 | $values = (null !== $delimiter) 184 | ? explode($this->valueDelimiter, $value) 185 | : [$value]; 186 | } else { 187 | $values = [$value]; 188 | } 189 | 190 | $validator = $this->getValidator(); 191 | 192 | if (! $validator) { 193 | throw new Exception\RuntimeException(sprintf( 194 | '%s expects a validator to be set; none given', 195 | __METHOD__ 196 | )); 197 | } 198 | 199 | foreach ($values as $value) { 200 | if (! $validator->isValid($value, $context)) { 201 | $this->abstractOptions['messages'][] = $validator->getMessages(); 202 | 203 | if ($this->isBreakOnFirstFailure()) { 204 | return false; 205 | } 206 | } 207 | } 208 | 209 | return ! $this->abstractOptions['messages']; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/File/Crc32.php: -------------------------------------------------------------------------------- 1 | "File does not match the given crc32 hashes", 33 | self::NOT_DETECTED => "A crc32 hash could not be evaluated for the given file", 34 | self::NOT_FOUND => "File is not readable or does not exist", 35 | ]; 36 | 37 | /** 38 | * Options for this validator 39 | * 40 | * @var string 41 | */ 42 | protected $options = [ 43 | 'algorithm' => 'crc32', 44 | 'hash' => null, 45 | ]; 46 | 47 | /** 48 | * Returns all set crc32 hashes 49 | * 50 | * @return array 51 | */ 52 | public function getCrc32() 53 | { 54 | return $this->getHash(); 55 | } 56 | 57 | /** 58 | * Sets the crc32 hash for one or multiple files 59 | * 60 | * @param string|array $options 61 | * @return self Provides a fluent interface 62 | */ 63 | public function setCrc32($options) 64 | { 65 | $this->setHash($options); 66 | return $this; 67 | } 68 | 69 | /** 70 | * Adds the crc32 hash for one or multiple files 71 | * 72 | * @param string|array $options 73 | * @return self Provides a fluent interface 74 | */ 75 | public function addCrc32($options) 76 | { 77 | $this->addHash($options); 78 | return $this; 79 | } 80 | 81 | /** 82 | * Returns true if and only if the given file confirms the set hash 83 | * 84 | * @param string|array $value Filename to check for hash 85 | * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 86 | * @return bool 87 | */ 88 | public function isValid($value, $file = null) 89 | { 90 | $fileInfo = $this->getFileInfo($value, $file); 91 | 92 | $this->setValue($fileInfo['filename']); 93 | 94 | // Is file readable ? 95 | if (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) { 96 | $this->error(self::NOT_FOUND); 97 | return false; 98 | } 99 | 100 | $hashes = array_unique(array_keys($this->getHash())); 101 | $filehash = hash_file('crc32', $fileInfo['file']); 102 | if ($filehash === false) { 103 | $this->error(self::NOT_DETECTED); 104 | return false; 105 | } 106 | 107 | foreach ($hashes as $hash) { 108 | if ($filehash === $hash) { 109 | return true; 110 | } 111 | } 112 | 113 | $this->error(self::DOES_NOT_MATCH); 114 | return false; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/File/ExcludeExtension.php: -------------------------------------------------------------------------------- 1 | "File has an incorrect extension", 33 | self::NOT_FOUND => "File is not readable or does not exist", 34 | ]; 35 | 36 | /** 37 | * Returns true if and only if the file extension of $value is not included in the 38 | * set extension list 39 | * 40 | * @param string|array $value Real file to check for extension 41 | * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 42 | * @return bool 43 | */ 44 | public function isValid($value, $file = null) 45 | { 46 | $fileInfo = $this->getFileInfo($value, $file); 47 | 48 | // Is file readable ? 49 | if (! $this->getAllowNonExistentFile() 50 | && (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) 51 | ) { 52 | if (preg_match('/nofile\.mo$/', $fileInfo['file'])) { 53 | } 54 | $this->error(self::NOT_FOUND); 55 | return false; 56 | } 57 | 58 | $this->setValue($fileInfo['filename']); 59 | 60 | $extension = substr($fileInfo['filename'], strrpos($fileInfo['filename'], '.') + 1); 61 | $extensions = $this->getExtension(); 62 | 63 | if ($this->getCase() && (! in_array($extension, $extensions))) { 64 | return true; 65 | } elseif (! $this->getCase()) { 66 | foreach ($extensions as $ext) { 67 | if (strtolower($ext) == strtolower($extension)) { 68 | if (preg_match('/nofile\.mo$/', $fileInfo['file'])) { 69 | } 70 | $this->error(self::FALSE_EXTENSION); 71 | return false; 72 | } 73 | } 74 | 75 | return true; 76 | } 77 | 78 | $this->error(self::FALSE_EXTENSION); 79 | return false; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/File/ExcludeMimeType.php: -------------------------------------------------------------------------------- 1 | "File has an incorrect mimetype of '%type%'", 31 | self::NOT_DETECTED => "The mimetype could not be detected from the file", 32 | self::NOT_READABLE => "File is not readable or does not exist", 33 | ]; 34 | 35 | /** 36 | * Returns true if the mimetype of the file does not matche the given ones. Also parts 37 | * of mimetypes can be checked. If you give for example "image" all image 38 | * mime types will not be accepted like "image/gif", "image/jpeg" and so on. 39 | * 40 | * @param string|array $value Real file to check for mimetype 41 | * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 42 | * @return bool 43 | */ 44 | public function isValid($value, $file = null) 45 | { 46 | $fileInfo = $this->getFileInfo($value, $file, true); 47 | 48 | $this->setValue($fileInfo['filename']); 49 | 50 | // Is file readable ? 51 | if (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) { 52 | $this->error(self::NOT_READABLE); 53 | return false; 54 | } 55 | 56 | $mimefile = $this->getMagicFile(); 57 | if (class_exists('finfo', false)) { 58 | if (! $this->isMagicFileDisabled() && (! empty($mimefile) && empty($this->finfo))) { 59 | $this->finfo = finfo_open(FILEINFO_MIME_TYPE, $mimefile); 60 | } 61 | 62 | if (empty($this->finfo)) { 63 | $this->finfo = finfo_open(FILEINFO_MIME_TYPE); 64 | } 65 | 66 | $this->type = null; 67 | if (! empty($this->finfo)) { 68 | $this->type = finfo_file($this->finfo, $fileInfo['file']); 69 | } 70 | } 71 | 72 | if (empty($this->type) && $this->getHeaderCheck()) { 73 | $this->type = $fileInfo['filetype']; 74 | } 75 | 76 | if (empty($this->type)) { 77 | $this->error(self::NOT_DETECTED); 78 | return false; 79 | } 80 | 81 | $mimetype = $this->getMimeType(true); 82 | if (in_array($this->type, $mimetype)) { 83 | $this->error(self::FALSE_TYPE); 84 | return false; 85 | } 86 | 87 | $types = explode('/', $this->type); 88 | $types = array_merge($types, explode('-', $this->type)); 89 | $types = array_merge($types, explode(';', $this->type)); 90 | foreach ($mimetype as $mime) { 91 | if (in_array($mime, $types)) { 92 | $this->error(self::FALSE_TYPE); 93 | return false; 94 | } 95 | } 96 | 97 | return true; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/File/Exists.php: -------------------------------------------------------------------------------- 1 | "File does not exist", 33 | ]; 34 | 35 | /** 36 | * Options for this validator 37 | * 38 | * @var array 39 | */ 40 | protected $options = [ 41 | 'directory' => null, // internal list of directories 42 | ]; 43 | 44 | /** 45 | * @var array Error message template variables 46 | */ 47 | protected $messageVariables = [ 48 | 'directory' => ['options' => 'directory'], 49 | ]; 50 | 51 | /** 52 | * Sets validator options 53 | * 54 | * @param string|array|\Traversable $options 55 | */ 56 | public function __construct($options = null) 57 | { 58 | if (is_string($options)) { 59 | $options = explode(',', $options); 60 | } 61 | 62 | if (is_array($options) && ! array_key_exists('directory', $options)) { 63 | $options = ['directory' => $options]; 64 | } 65 | 66 | parent::__construct($options); 67 | } 68 | 69 | /** 70 | * Returns the set file directories which are checked 71 | * 72 | * @param bool $asArray Returns the values as array; when false, a concatenated string is returned 73 | * @return string|null 74 | */ 75 | public function getDirectory($asArray = false) 76 | { 77 | $asArray = (bool) $asArray; 78 | $directory = $this->options['directory']; 79 | if ($asArray && isset($directory)) { 80 | $directory = explode(',', (string) $directory); 81 | } 82 | 83 | return $directory; 84 | } 85 | 86 | /** 87 | * Sets the file directory which will be checked 88 | * 89 | * @param string|array $directory The directories to validate 90 | * @return Extension Provides a fluent interface 91 | */ 92 | public function setDirectory($directory) 93 | { 94 | $this->options['directory'] = null; 95 | $this->addDirectory($directory); 96 | return $this; 97 | } 98 | 99 | /** 100 | * Adds the file directory which will be checked 101 | * 102 | * @param string|array $directory The directory to add for validation 103 | * @return Extension Provides a fluent interface 104 | * @throws Exception\InvalidArgumentException 105 | */ 106 | public function addDirectory($directory) 107 | { 108 | $directories = $this->getDirectory(true); 109 | if (! isset($directories)) { 110 | $directories = []; 111 | } 112 | 113 | if (is_string($directory)) { 114 | $directory = explode(',', $directory); 115 | } elseif (! is_array($directory)) { 116 | throw new Exception\InvalidArgumentException('Invalid options to validator provided'); 117 | } 118 | 119 | foreach ($directory as $content) { 120 | if (empty($content) || ! is_string($content)) { 121 | continue; 122 | } 123 | 124 | $directories[] = trim($content); 125 | } 126 | $directories = array_unique($directories); 127 | 128 | // Sanity check to ensure no empty values 129 | foreach ($directories as $key => $dir) { 130 | if (empty($dir)) { 131 | unset($directories[$key]); 132 | } 133 | } 134 | 135 | $this->options['directory'] = (! empty($directory)) 136 | ? implode(',', $directories) : null; 137 | 138 | return $this; 139 | } 140 | 141 | /** 142 | * Returns true if and only if the file already exists in the set directories 143 | * 144 | * @param string|array $value Real file to check for existence 145 | * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 146 | * @return bool 147 | */ 148 | public function isValid($value, $file = null) 149 | { 150 | $fileInfo = $this->getFileInfo($value, $file, false, true); 151 | 152 | $this->setValue($fileInfo['filename']); 153 | 154 | $check = false; 155 | $directories = $this->getDirectory(true); 156 | if (! isset($directories)) { 157 | $check = true; 158 | if (! file_exists($fileInfo['file'])) { 159 | $this->error(self::DOES_NOT_EXIST); 160 | return false; 161 | } 162 | } else { 163 | foreach ($directories as $directory) { 164 | if (! isset($directory) || '' === $directory) { 165 | continue; 166 | } 167 | 168 | $check = true; 169 | if (! file_exists($directory . DIRECTORY_SEPARATOR . $fileInfo['basename'])) { 170 | $this->error(self::DOES_NOT_EXIST); 171 | return false; 172 | } 173 | } 174 | } 175 | 176 | if (! $check) { 177 | $this->error(self::DOES_NOT_EXIST); 178 | return false; 179 | } 180 | 181 | return true; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/File/FileInformationTrait.php: -------------------------------------------------------------------------------- 1 | getLegacyFileInfo($file, $hasType, $hasBasename); 32 | } 33 | 34 | if (is_array($value)) { 35 | return $this->getSapiFileInfo($value, $hasType, $hasBasename); 36 | } 37 | 38 | if ($value instanceof UploadedFileInterface) { 39 | return $this->getPsr7FileInfo($value, $hasType, $hasBasename); 40 | } 41 | 42 | return $this->getFileBasedFileInfo($value, $hasType, $hasBasename); 43 | } 44 | 45 | /** 46 | * Generate file information array with legacy Zend_File_Transfer API 47 | * 48 | * @param array $file File data 49 | * @param bool $hasType Return with filetype 50 | * @param bool $hasBasename Basename is calculated from location path 51 | * @return array 52 | */ 53 | private function getLegacyFileInfo( 54 | array $file, 55 | $hasType = false, 56 | $hasBasename = false 57 | ) { 58 | $fileInfo = []; 59 | 60 | $fileInfo['filename'] = $file['name']; 61 | $fileInfo['file'] = $file['tmp_name']; 62 | 63 | if ($hasBasename) { 64 | $fileInfo['basename'] = basename($fileInfo['file']); 65 | } 66 | 67 | if ($hasType) { 68 | $fileInfo['filetype'] = $file['type']; 69 | } 70 | 71 | return $fileInfo; 72 | } 73 | 74 | /** 75 | * Generate file information array with SAPI 76 | * 77 | * @param array $file File data from SAPI 78 | * @param bool $hasType Return with filetype 79 | * @param bool $hasBasename Filename is calculated from location path 80 | * @return array 81 | */ 82 | private function getSapiFileInfo( 83 | array $file, 84 | $hasType = false, 85 | $hasBasename = false 86 | ) { 87 | if (! isset($file['tmp_name']) || ! isset($file['name'])) { 88 | throw new Exception\InvalidArgumentException( 89 | 'Value array must be in $_FILES format' 90 | ); 91 | } 92 | 93 | $fileInfo = []; 94 | 95 | $fileInfo['file'] = $file['tmp_name']; 96 | $fileInfo['filename'] = $file['name']; 97 | 98 | if ($hasBasename) { 99 | $fileInfo['basename'] = basename($fileInfo['file']); 100 | } 101 | 102 | if ($hasType) { 103 | $fileInfo['filetype'] = $file['type']; 104 | } 105 | 106 | return $fileInfo; 107 | } 108 | 109 | /** 110 | * Generate file information array with PSR-7 UploadedFileInterface 111 | * 112 | * @param UploadedFileInterface $file 113 | * @param bool $hasType Return with filetype 114 | * @param bool $hasBasename Filename is calculated from location path 115 | * @return array 116 | */ 117 | private function getPsr7FileInfo( 118 | UploadedFileInterface $file, 119 | $hasType = false, 120 | $hasBasename = false 121 | ) { 122 | $fileInfo = []; 123 | 124 | $fileInfo['file'] = $file->getStream()->getMetadata('uri'); 125 | $fileInfo['filename'] = $file->getClientFilename(); 126 | 127 | if ($hasBasename) { 128 | $fileInfo['basename'] = basename($fileInfo['file']); 129 | } 130 | 131 | if ($hasType) { 132 | $fileInfo['filetype'] = $file->getClientMediaType(); 133 | } 134 | 135 | return $fileInfo; 136 | } 137 | 138 | /** 139 | * Generate file information array with base method 140 | * 141 | * @param string $file File path 142 | * @param bool $hasType Return with filetype 143 | * @param bool $hasBasename Filename is calculated from location path 144 | * @return array 145 | */ 146 | private function getFileBasedFileInfo( 147 | $file, 148 | $hasType = false, 149 | $hasBasename = false 150 | ) { 151 | $fileInfo = []; 152 | 153 | $fileInfo['file'] = $file; 154 | $fileInfo['filename'] = basename($fileInfo['file']); 155 | 156 | if ($hasBasename) { 157 | $fileInfo['basename'] = basename($fileInfo['file']); 158 | } 159 | 160 | if ($hasType) { 161 | $fileInfo['filetype'] = null; 162 | } 163 | 164 | return $fileInfo; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/File/FilesSize.php: -------------------------------------------------------------------------------- 1 | "All files in sum should have a maximum size of '%max%' but '%size%' were detected", 35 | self::TOO_SMALL => "All files in sum should have a minimum size of '%min%' but '%size%' were detected", 36 | self::NOT_READABLE => "One or more files can not be read", 37 | ]; 38 | 39 | /** 40 | * Internal file array 41 | * 42 | * @var array 43 | */ 44 | protected $files; 45 | 46 | /** 47 | * Sets validator options 48 | * 49 | * Min limits the used disk space for all files, when used with max=null it is the maximum file size 50 | * It also accepts an array with the keys 'min' and 'max' 51 | * 52 | * @param int|array|Traversable $options Options for this validator 53 | * @throws \Zend\Validator\Exception\InvalidArgumentException 54 | */ 55 | public function __construct($options = null) 56 | { 57 | $this->files = []; 58 | $this->setSize(0); 59 | 60 | if ($options instanceof Traversable) { 61 | $options = ArrayUtils::iteratorToArray($options); 62 | } elseif (is_scalar($options)) { 63 | $options = ['max' => $options]; 64 | } elseif (! is_array($options)) { 65 | throw new Exception\InvalidArgumentException('Invalid options to validator provided'); 66 | } 67 | 68 | if (1 < func_num_args()) { 69 | $argv = func_get_args(); 70 | array_shift($argv); 71 | $options['max'] = array_shift($argv); 72 | if (! empty($argv)) { 73 | $options['useByteString'] = array_shift($argv); 74 | } 75 | } 76 | 77 | parent::__construct($options); 78 | } 79 | 80 | /** 81 | * Returns true if and only if the disk usage of all files is at least min and 82 | * not bigger than max (when max is not null). 83 | * 84 | * @param string|array $value Real file to check for size 85 | * @param array $file File data from \Zend\File\Transfer\Transfer 86 | * @return bool 87 | */ 88 | public function isValid($value, $file = null) 89 | { 90 | if (is_string($value)) { 91 | $value = [$value]; 92 | } elseif (is_array($value) && isset($value['tmp_name'])) { 93 | $value = [$value]; 94 | } 95 | 96 | $min = $this->getMin(true); 97 | $max = $this->getMax(true); 98 | $size = $this->getSize(); 99 | foreach ($value as $files) { 100 | if (is_array($files)) { 101 | if (! isset($files['tmp_name']) || ! isset($files['name'])) { 102 | throw new Exception\InvalidArgumentException( 103 | 'Value array must be in $_FILES format' 104 | ); 105 | } 106 | $file = $files; 107 | $files = $files['tmp_name']; 108 | } 109 | 110 | // Is file readable ? 111 | if (empty($files) || false === is_readable($files)) { 112 | $this->throwError($file, self::NOT_READABLE); 113 | continue; 114 | } 115 | 116 | if (! isset($this->files[$files])) { 117 | $this->files[$files] = $files; 118 | } else { 119 | // file already counted... do not count twice 120 | continue; 121 | } 122 | 123 | // limited to 2GB files 124 | ErrorHandler::start(); 125 | $size += filesize($files); 126 | ErrorHandler::stop(); 127 | $this->size = $size; 128 | if (($max !== null) && ($max < $size)) { 129 | if ($this->getByteString()) { 130 | $this->options['max'] = $this->toByteString($max); 131 | $this->size = $this->toByteString($size); 132 | $this->throwError($file, self::TOO_BIG); 133 | $this->options['max'] = $max; 134 | $this->size = $size; 135 | } else { 136 | $this->throwError($file, self::TOO_BIG); 137 | } 138 | } 139 | } 140 | 141 | // Check that aggregate files are >= minimum size 142 | if (($min !== null) && ($size < $min)) { 143 | if ($this->getByteString()) { 144 | $this->options['min'] = $this->toByteString($min); 145 | $this->size = $this->toByteString($size); 146 | $this->throwError($file, self::TOO_SMALL); 147 | $this->options['min'] = $min; 148 | $this->size = $size; 149 | } else { 150 | $this->throwError($file, self::TOO_SMALL); 151 | } 152 | } 153 | 154 | if ($this->getMessages()) { 155 | return false; 156 | } 157 | 158 | return true; 159 | } 160 | 161 | /** 162 | * Throws an error of the given type 163 | * 164 | * @param string $file 165 | * @param string $errorType 166 | * @return false 167 | */ 168 | protected function throwError($file, $errorType) 169 | { 170 | if ($file !== null) { 171 | if (is_array($file)) { 172 | if (array_key_exists('name', $file)) { 173 | $this->value = $file['name']; 174 | } 175 | } elseif (is_string($file)) { 176 | $this->value = $file; 177 | } 178 | } 179 | 180 | $this->error($errorType); 181 | return false; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/File/Hash.php: -------------------------------------------------------------------------------- 1 | "File does not match the given hashes", 35 | self::NOT_DETECTED => "A hash could not be evaluated for the given file", 36 | self::NOT_FOUND => "File is not readable or does not exist" 37 | ]; 38 | 39 | /** 40 | * Options for this validator 41 | * 42 | * @var string 43 | */ 44 | protected $options = [ 45 | 'algorithm' => 'crc32', 46 | 'hash' => null, 47 | ]; 48 | 49 | /** 50 | * Sets validator options 51 | * 52 | * @param string|array $options 53 | */ 54 | public function __construct($options = null) 55 | { 56 | if (is_scalar($options) || 57 | (is_array($options) && ! array_key_exists('hash', $options))) { 58 | $options = ['hash' => $options]; 59 | } 60 | 61 | if (1 < func_num_args()) { 62 | $options['algorithm'] = func_get_arg(1); 63 | } 64 | 65 | parent::__construct($options); 66 | } 67 | 68 | /** 69 | * Returns the set hash values as array, the hash as key and the algorithm the value 70 | * 71 | * @return array 72 | */ 73 | public function getHash() 74 | { 75 | return $this->options['hash']; 76 | } 77 | 78 | /** 79 | * Sets the hash for one or multiple files 80 | * 81 | * @param string|array $options 82 | * @return self Provides a fluent interface 83 | */ 84 | public function setHash($options) 85 | { 86 | $this->options['hash'] = null; 87 | $this->addHash($options); 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * Adds the hash for one or multiple files 94 | * 95 | * @param string|array $options 96 | * @throws Exception\InvalidArgumentException 97 | * @return self Provides a fluent interface 98 | */ 99 | public function addHash($options) 100 | { 101 | if (is_string($options)) { 102 | $options = [$options]; 103 | } elseif (! is_array($options)) { 104 | throw new Exception\InvalidArgumentException("False parameter given"); 105 | } 106 | 107 | $known = hash_algos(); 108 | if (! isset($options['algorithm'])) { 109 | $algorithm = $this->options['algorithm']; 110 | } else { 111 | $algorithm = $options['algorithm']; 112 | unset($options['algorithm']); 113 | } 114 | 115 | if (! in_array($algorithm, $known)) { 116 | throw new Exception\InvalidArgumentException("Unknown algorithm '{$algorithm}'"); 117 | } 118 | 119 | foreach ($options as $value) { 120 | if (! is_string($value)) { 121 | throw new Exception\InvalidArgumentException(sprintf( 122 | 'Hash must be a string, %s received', 123 | is_object($value) ? get_class($value) : gettype($value) 124 | )); 125 | } 126 | $this->options['hash'][$value] = $algorithm; 127 | } 128 | 129 | return $this; 130 | } 131 | 132 | /** 133 | * Returns true if and only if the given file confirms the set hash 134 | * 135 | * @param string|array $value File to check for hash 136 | * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 137 | * @return bool 138 | */ 139 | public function isValid($value, $file = null) 140 | { 141 | $fileInfo = $this->getFileInfo($value, $file); 142 | 143 | $this->setValue($fileInfo['filename']); 144 | 145 | // Is file readable ? 146 | if (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) { 147 | $this->error(self::NOT_FOUND); 148 | return false; 149 | } 150 | 151 | $algos = array_unique(array_values($this->getHash())); 152 | foreach ($algos as $algorithm) { 153 | $filehash = hash_file($algorithm, $fileInfo['file']); 154 | 155 | if ($filehash === false) { 156 | $this->error(self::NOT_DETECTED); 157 | return false; 158 | } 159 | 160 | if (isset($this->getHash()[$filehash]) && $this->getHash()[$filehash] === $algorithm) { 161 | return true; 162 | } 163 | } 164 | 165 | $this->error(self::DOES_NOT_MATCH); 166 | return false; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/File/IsCompressed.php: -------------------------------------------------------------------------------- 1 | "File is not compressed, '%type%' detected", 32 | self::NOT_DETECTED => "The mimetype could not be detected from the file", 33 | self::NOT_READABLE => "File is not readable or does not exist", 34 | ]; 35 | 36 | /** 37 | * Sets validator options 38 | * 39 | * @param string|array|Traversable $options 40 | */ 41 | public function __construct($options = []) 42 | { 43 | // http://hul.harvard.edu/ois/systems/wax/wax-public-help/mimetypes.htm 44 | $default = [ 45 | 'application/arj', 46 | 'application/gnutar', 47 | 'application/lha', 48 | 'application/lzx', 49 | 'application/vnd.ms-cab-compressed', 50 | 'application/x-ace-compressed', 51 | 'application/x-arc', 52 | 'application/x-archive', 53 | 'application/x-arj', 54 | 'application/x-bzip', 55 | 'application/x-bzip2', 56 | 'application/x-cab-compressed', 57 | 'application/x-compress', 58 | 'application/x-compressed', 59 | 'application/x-cpio', 60 | 'application/x-debian-package', 61 | 'application/x-eet', 62 | 'application/x-gzip', 63 | 'application/x-java-pack200', 64 | 'application/x-lha', 65 | 'application/x-lharc', 66 | 'application/x-lzh', 67 | 'application/x-lzma', 68 | 'application/x-lzx', 69 | 'application/x-rar', 70 | 'application/x-sit', 71 | 'application/x-stuffit', 72 | 'application/x-tar', 73 | 'application/zip', 74 | 'application/x-zip', 75 | 'application/zoo', 76 | 'multipart/x-gzip', 77 | ]; 78 | 79 | if ($options instanceof Traversable) { 80 | $options = ArrayUtils::iteratorToArray($options); 81 | } 82 | 83 | if ($options === null) { 84 | $options = []; 85 | } 86 | 87 | parent::__construct($options); 88 | 89 | if (! $this->getMimeType()) { 90 | $this->setMimeType($default); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/File/IsImage.php: -------------------------------------------------------------------------------- 1 | "File is no image, '%type%' detected", 32 | self::NOT_DETECTED => "The mimetype could not be detected from the file", 33 | self::NOT_READABLE => "File is not readable or does not exist", 34 | ]; 35 | 36 | /** 37 | * Sets validator options 38 | * 39 | * @param array|Traversable|string $options 40 | */ 41 | public function __construct($options = []) 42 | { 43 | // http://www.iana.org/assignments/media-types/media-types.xhtml#image 44 | $default = [ 45 | 'application/cdf', 46 | 'application/dicom', 47 | 'application/fractals', 48 | 'application/postscript', 49 | 'application/vnd.hp-hpgl', 50 | 'application/vnd.oasis.opendocument.graphics', 51 | 'application/x-cdf', 52 | 'application/x-cmu-raster', 53 | 'application/x-ima', 54 | 'application/x-inventor', 55 | 'application/x-koan', 56 | 'application/x-portable-anymap', 57 | 'application/x-world-x-3dmf', 58 | 'image/bmp', 59 | 'image/c', 60 | 'image/cgm', 61 | 'image/fif', 62 | 'image/gif', 63 | 'image/jpeg', 64 | 'image/jpm', 65 | 'image/jpx', 66 | 'image/jp2', 67 | 'image/naplps', 68 | 'image/pjpeg', 69 | 'image/png', 70 | 'image/svg', 71 | 'image/svg+xml', 72 | 'image/tiff', 73 | 'image/vnd.adobe.photoshop', 74 | 'image/vnd.djvu', 75 | 'image/vnd.fpx', 76 | 'image/vnd.net-fpx', 77 | 'image/webp', 78 | 'image/x-cmu-raster', 79 | 'image/x-cmx', 80 | 'image/x-coreldraw', 81 | 'image/x-cpi', 82 | 'image/x-emf', 83 | 'image/x-ico', 84 | 'image/x-icon', 85 | 'image/x-jg', 86 | 'image/x-ms-bmp', 87 | 'image/x-niff', 88 | 'image/x-pict', 89 | 'image/x-pcx', 90 | 'image/x-png', 91 | 'image/x-portable-anymap', 92 | 'image/x-portable-bitmap', 93 | 'image/x-portable-greymap', 94 | 'image/x-portable-pixmap', 95 | 'image/x-quicktime', 96 | 'image/x-rgb', 97 | 'image/x-tiff', 98 | 'image/x-unknown', 99 | 'image/x-windows-bmp', 100 | 'image/x-xpmi', 101 | ]; 102 | 103 | if ($options instanceof Traversable) { 104 | $options = ArrayUtils::iteratorToArray($options); 105 | } 106 | 107 | if ($options === null) { 108 | $options = []; 109 | } 110 | 111 | parent::__construct($options); 112 | 113 | if (! $this->getMimeType()) { 114 | $this->setMimeType($default); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/File/Md5.php: -------------------------------------------------------------------------------- 1 | "File does not match the given md5 hashes", 33 | self::NOT_DETECTED => "An md5 hash could not be evaluated for the given file", 34 | self::NOT_FOUND => "File is not readable or does not exist", 35 | ]; 36 | 37 | /** 38 | * Options for this validator 39 | * 40 | * @var string 41 | */ 42 | protected $options = [ 43 | 'algorithm' => 'md5', 44 | 'hash' => null, 45 | ]; 46 | 47 | /** 48 | * Returns all set md5 hashes 49 | * 50 | * @return array 51 | */ 52 | public function getMd5() 53 | { 54 | return $this->getHash(); 55 | } 56 | 57 | /** 58 | * Sets the md5 hash for one or multiple files 59 | * 60 | * @param string|array $options 61 | * @return Hash Provides a fluent interface 62 | */ 63 | public function setMd5($options) 64 | { 65 | $this->setHash($options); 66 | return $this; 67 | } 68 | 69 | /** 70 | * Adds the md5 hash for one or multiple files 71 | * 72 | * @param string|array $options 73 | * @return Hash Provides a fluent interface 74 | */ 75 | public function addMd5($options) 76 | { 77 | $this->addHash($options); 78 | return $this; 79 | } 80 | 81 | /** 82 | * Returns true if and only if the given file confirms the set hash 83 | * 84 | * @param string|array $value Filename to check for hash 85 | * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 86 | * @return bool 87 | */ 88 | public function isValid($value, $file = null) 89 | { 90 | $fileInfo = $this->getFileInfo($value, $file); 91 | 92 | $this->setValue($fileInfo['filename']); 93 | 94 | // Is file readable ? 95 | if (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) { 96 | $this->error(self::NOT_FOUND); 97 | return false; 98 | } 99 | 100 | $hashes = array_unique(array_keys($this->getHash())); 101 | $filehash = hash_file('md5', $fileInfo['file']); 102 | if ($filehash === false) { 103 | $this->error(self::NOT_DETECTED); 104 | return false; 105 | } 106 | 107 | foreach ($hashes as $hash) { 108 | if ($filehash === $hash) { 109 | return true; 110 | } 111 | } 112 | 113 | $this->error(self::DOES_NOT_MATCH); 114 | return false; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/File/NotExists.php: -------------------------------------------------------------------------------- 1 | "File exists", 32 | ]; 33 | 34 | /** 35 | * Returns true if and only if the file does not exist in the set destinations 36 | * 37 | * @param string|array $value Real file to check for existence 38 | * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 39 | * @return bool 40 | */ 41 | public function isValid($value, $file = null) 42 | { 43 | $fileInfo = $this->getFileInfo($value, $file, false, true); 44 | 45 | $this->setValue($fileInfo['filename']); 46 | 47 | $check = false; 48 | $directories = $this->getDirectory(true); 49 | if (! isset($directories)) { 50 | $check = true; 51 | if (file_exists($fileInfo['file'])) { 52 | $this->error(self::DOES_EXIST); 53 | return false; 54 | } 55 | } else { 56 | foreach ($directories as $directory) { 57 | if (! isset($directory) || '' === $directory) { 58 | continue; 59 | } 60 | 61 | $check = true; 62 | if (file_exists($directory . DIRECTORY_SEPARATOR . $fileInfo['basename'])) { 63 | $this->error(self::DOES_EXIST); 64 | return false; 65 | } 66 | } 67 | } 68 | 69 | if (! $check) { 70 | $this->error(self::DOES_EXIST); 71 | return false; 72 | } 73 | 74 | return true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/File/Sha1.php: -------------------------------------------------------------------------------- 1 | "File does not match the given sha1 hashes", 33 | self::NOT_DETECTED => "A sha1 hash could not be evaluated for the given file", 34 | self::NOT_FOUND => "File is not readable or does not exist", 35 | ]; 36 | 37 | /** 38 | * Options for this validator 39 | * 40 | * @var string 41 | */ 42 | protected $options = [ 43 | 'algorithm' => 'sha1', 44 | 'hash' => null, 45 | ]; 46 | 47 | /** 48 | * Returns all set sha1 hashes 49 | * 50 | * @return array 51 | */ 52 | public function getSha1() 53 | { 54 | return $this->getHash(); 55 | } 56 | 57 | /** 58 | * Sets the sha1 hash for one or multiple files 59 | * 60 | * @param string|array $options 61 | * @return Hash Provides a fluent interface 62 | */ 63 | public function setSha1($options) 64 | { 65 | $this->setHash($options); 66 | return $this; 67 | } 68 | 69 | /** 70 | * Adds the sha1 hash for one or multiple files 71 | * 72 | * @param string|array $options 73 | * @return Hash Provides a fluent interface 74 | */ 75 | public function addSha1($options) 76 | { 77 | $this->addHash($options); 78 | return $this; 79 | } 80 | 81 | /** 82 | * Returns true if and only if the given file confirms the set hash 83 | * 84 | * @param string $value|array Filename to check for hash 85 | * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 86 | * @return bool 87 | */ 88 | public function isValid($value, $file = null) 89 | { 90 | $fileInfo = $this->getFileInfo($value, $file); 91 | 92 | $this->setValue($fileInfo['filename']); 93 | 94 | // Is file readable ? 95 | if (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) { 96 | $this->error(self::NOT_FOUND); 97 | return false; 98 | } 99 | 100 | $hashes = array_unique(array_keys($this->getHash())); 101 | $filehash = hash_file('sha1', $fileInfo['file']); 102 | if ($filehash === false) { 103 | $this->error(self::NOT_DETECTED); 104 | return false; 105 | } 106 | 107 | foreach ($hashes as $hash) { 108 | if ($filehash === $hash) { 109 | return true; 110 | } 111 | } 112 | 113 | $this->error(self::DOES_NOT_MATCH); 114 | return false; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/File/UploadFile.php: -------------------------------------------------------------------------------- 1 | 'The uploaded file exceeds the upload_max_filesize directive in php.ini', 40 | self::FORM_SIZE => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was ' 41 | . 'specified in the HTML form', 42 | self::PARTIAL => 'The uploaded file was only partially uploaded', 43 | self::NO_FILE => 'No file was uploaded', 44 | self::NO_TMP_DIR => 'Missing a temporary folder', 45 | self::CANT_WRITE => 'Failed to write file to disk', 46 | self::EXTENSION => 'A PHP extension stopped the file upload', 47 | self::ATTACK => 'File was illegally uploaded. This could be a possible attack', 48 | self::FILE_NOT_FOUND => 'File was not found', 49 | self::UNKNOWN => 'Unknown error while uploading file', 50 | ]; 51 | 52 | /** 53 | * Returns true if and only if the file was uploaded without errors 54 | * 55 | * @param string|array|UploadedFileInterface $value File to check for upload errors 56 | * @return bool 57 | * @throws Exception\InvalidArgumentException 58 | */ 59 | public function isValid($value) 60 | { 61 | if (is_array($value)) { 62 | if (! isset($value['tmp_name']) || ! isset($value['name']) || ! isset($value['error'])) { 63 | throw new Exception\InvalidArgumentException( 64 | 'Value array must be in $_FILES format' 65 | ); 66 | } 67 | 68 | return $this->validateUploadedFile( 69 | $value['error'], 70 | $value['name'], 71 | $value['tmp_name'] 72 | ); 73 | } 74 | 75 | if ($value instanceof UploadedFileInterface) { 76 | return $this->validatePsr7UploadedFile($value); 77 | } 78 | 79 | if (is_string($value)) { 80 | return $this->validateUploadedFile(0, basename($value), $value); 81 | } 82 | 83 | $this->error(self::UNKNOWN); 84 | return false; 85 | } 86 | 87 | /** 88 | * @param int $error UPLOAD_ERR_* constant value 89 | * @return bool 90 | */ 91 | private function validateFileFromErrorCode($error) 92 | { 93 | switch ($error) { 94 | case UPLOAD_ERR_OK: 95 | return true; 96 | 97 | case UPLOAD_ERR_INI_SIZE: 98 | $this->error(self::INI_SIZE); 99 | return false; 100 | 101 | case UPLOAD_ERR_FORM_SIZE: 102 | $this->error(self::FORM_SIZE); 103 | return false; 104 | 105 | case UPLOAD_ERR_PARTIAL: 106 | $this->error(self::PARTIAL); 107 | return false; 108 | 109 | case UPLOAD_ERR_NO_FILE: 110 | $this->error(self::NO_FILE); 111 | return false; 112 | 113 | case UPLOAD_ERR_NO_TMP_DIR: 114 | $this->error(self::NO_TMP_DIR); 115 | return false; 116 | 117 | case UPLOAD_ERR_CANT_WRITE: 118 | $this->error(self::CANT_WRITE); 119 | return false; 120 | 121 | case UPLOAD_ERR_EXTENSION: 122 | $this->error(self::EXTENSION); 123 | return false; 124 | 125 | default: 126 | $this->error(self::UNKNOWN); 127 | return false; 128 | } 129 | } 130 | 131 | /** 132 | * @param int $error UPLOAD_ERR_* constant 133 | * @param string $filename 134 | * @param string $uploadedFile Name of uploaded file (gen tmp_name) 135 | * @return bool 136 | */ 137 | private function validateUploadedFile($error, $filename, $uploadedFile) 138 | { 139 | $this->setValue($filename); 140 | 141 | // Normal errors can be validated normally 142 | if ($error !== UPLOAD_ERR_OK) { 143 | return $this->validateFileFromErrorCode($error); 144 | } 145 | 146 | // Did we get no name? Is the file missing? 147 | if (empty($uploadedFile) || false === is_file($uploadedFile)) { 148 | $this->error(self::FILE_NOT_FOUND); 149 | return false; 150 | } 151 | 152 | // Do we have an invalid upload? 153 | if (! is_uploaded_file($uploadedFile)) { 154 | $this->error(self::ATTACK); 155 | return false; 156 | } 157 | 158 | return true; 159 | } 160 | 161 | /** 162 | * @return bool 163 | */ 164 | private function validatePsr7UploadedFile(UploadedFileInterface $uploadedFile) 165 | { 166 | $this->setValue($uploadedFile); 167 | return $this->validateFileFromErrorCode($uploadedFile->getError()); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/GpsPoint.php: -------------------------------------------------------------------------------- 1 | '%value% is out of Bounds.', 24 | 'gpsPointConvertError' => '%value% can not converted into a Decimal Degree Value.', 25 | 'gpsPointIncompleteCoordinate' => '%value% did not provided a complete Coordinate', 26 | ]; 27 | 28 | /** 29 | * Returns true if and only if $value meets the validation requirements 30 | * 31 | * If $value fails validation, then this method returns false, and 32 | * getMessages() will return an array of messages that explain why the 33 | * validation failed. 34 | * 35 | * @param mixed $value 36 | * @return bool 37 | * @throws Exception\RuntimeException If validation of $value is impossible 38 | */ 39 | public function isValid($value) 40 | { 41 | if (strpos($value, ',') === false) { 42 | $this->error(GpsPoint::INCOMPLETE_COORDINATE, $value); 43 | return false; 44 | } 45 | 46 | list($lat, $long) = explode(',', $value); 47 | 48 | if ($this->isValidCoordinate($lat, 90.0000) && $this->isValidCoordinate($long, 180.000)) { 49 | return true; 50 | } 51 | 52 | return false; 53 | } 54 | 55 | /** 56 | * @param string $value 57 | * @param $maxBoundary 58 | * @return bool 59 | */ 60 | private function isValidCoordinate($value, $maxBoundary) 61 | { 62 | $this->value = $value; 63 | 64 | $value = $this->removeWhiteSpace($value); 65 | if ($this->isDMSValue($value)) { 66 | $value = $this->convertValue($value); 67 | } else { 68 | $value = $this->removeDegreeSign($value); 69 | } 70 | 71 | if ($value === false || $value === null) { 72 | $this->error(self::CONVERT_ERROR); 73 | return false; 74 | } 75 | 76 | $doubleLatitude = (double)$value; 77 | 78 | if ($doubleLatitude <= $maxBoundary && $doubleLatitude >= $maxBoundary * -1) { 79 | return true; 80 | } 81 | 82 | $this->error(self::OUT_OF_BOUNDS); 83 | return false; 84 | } 85 | 86 | /** 87 | * Determines if the give value is a Degrees Minutes Second Definition 88 | * 89 | * @param $value 90 | * @return bool 91 | */ 92 | private function isDMSValue($value) 93 | { 94 | return preg_match('/([°\'"]+[NESW])/', $value) > 0; 95 | } 96 | 97 | 98 | /** 99 | * @param string $value 100 | * @return bool|string 101 | */ 102 | private function convertValue($value) 103 | { 104 | $matches = []; 105 | $result = preg_match_all('/(\d{1,3})°(\d{1,2})\'(\d{1,2}[\.\d]{0,6})"[NESW]/i', $value, $matches); 106 | 107 | if ($result === false || $result === 0) { 108 | return false; 109 | } 110 | 111 | return $matches[1][0] + $matches[2][0] / 60 + ((double)$matches[3][0]) / 3600; 112 | } 113 | 114 | /** 115 | * @param string $value 116 | * @return string 117 | */ 118 | private function removeWhiteSpace($value) 119 | { 120 | return preg_replace('/\s/', '', $value); 121 | } 122 | 123 | /** 124 | * @param string $value 125 | * @return string 126 | */ 127 | private function removeDegreeSign($value) 128 | { 129 | return str_replace('°', '', $value); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/GreaterThan.php: -------------------------------------------------------------------------------- 1 | "The input is not greater than '%min%'", 27 | self::NOT_GREATER_INCLUSIVE => "The input is not greater than or equal to '%min%'" 28 | ]; 29 | 30 | /** 31 | * @var array 32 | */ 33 | protected $messageVariables = [ 34 | 'min' => 'min' 35 | ]; 36 | 37 | /** 38 | * Minimum value 39 | * 40 | * @var mixed 41 | */ 42 | protected $min; 43 | 44 | /** 45 | * Whether to do inclusive comparisons, allowing equivalence to max 46 | * 47 | * If false, then strict comparisons are done, and the value may equal 48 | * the min option 49 | * 50 | * @var bool 51 | */ 52 | protected $inclusive; 53 | 54 | /** 55 | * Sets validator options 56 | * 57 | * @param array|Traversable $options 58 | * @throws Exception\InvalidArgumentException 59 | */ 60 | public function __construct($options = null) 61 | { 62 | if ($options instanceof Traversable) { 63 | $options = ArrayUtils::iteratorToArray($options); 64 | } 65 | if (! is_array($options)) { 66 | $options = func_get_args(); 67 | $temp['min'] = array_shift($options); 68 | 69 | if (! empty($options)) { 70 | $temp['inclusive'] = array_shift($options); 71 | } 72 | 73 | $options = $temp; 74 | } 75 | 76 | if (! array_key_exists('min', $options)) { 77 | throw new Exception\InvalidArgumentException("Missing option 'min'"); 78 | } 79 | 80 | if (! array_key_exists('inclusive', $options)) { 81 | $options['inclusive'] = false; 82 | } 83 | 84 | $this->setMin($options['min']) 85 | ->setInclusive($options['inclusive']); 86 | 87 | parent::__construct($options); 88 | } 89 | 90 | /** 91 | * Returns the min option 92 | * 93 | * @return mixed 94 | */ 95 | public function getMin() 96 | { 97 | return $this->min; 98 | } 99 | 100 | /** 101 | * Sets the min option 102 | * 103 | * @param mixed $min 104 | * @return GreaterThan Provides a fluent interface 105 | */ 106 | public function setMin($min) 107 | { 108 | $this->min = $min; 109 | return $this; 110 | } 111 | 112 | /** 113 | * Returns the inclusive option 114 | * 115 | * @return bool 116 | */ 117 | public function getInclusive() 118 | { 119 | return $this->inclusive; 120 | } 121 | 122 | /** 123 | * Sets the inclusive option 124 | * 125 | * @param bool $inclusive 126 | * @return GreaterThan Provides a fluent interface 127 | */ 128 | public function setInclusive($inclusive) 129 | { 130 | $this->inclusive = $inclusive; 131 | return $this; 132 | } 133 | 134 | /** 135 | * Returns true if and only if $value is greater than min option 136 | * 137 | * @param mixed $value 138 | * @return bool 139 | */ 140 | public function isValid($value) 141 | { 142 | $this->setValue($value); 143 | 144 | if ($this->inclusive) { 145 | if ($this->min > $value) { 146 | $this->error(self::NOT_GREATER_INCLUSIVE); 147 | return false; 148 | } 149 | } else { 150 | if ($this->min >= $value) { 151 | $this->error(self::NOT_GREATER); 152 | return false; 153 | } 154 | } 155 | 156 | return true; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/Hex.php: -------------------------------------------------------------------------------- 1 | "Invalid type given. String expected", 24 | self::NOT_HEX => "The input contains non-hexadecimal characters", 25 | ]; 26 | 27 | /** 28 | * Returns true if and only if $value contains only hexadecimal digit characters 29 | * 30 | * @param string $value 31 | * @return bool 32 | */ 33 | public function isValid($value) 34 | { 35 | if (! is_string($value) && ! is_int($value)) { 36 | $this->error(self::INVALID); 37 | return false; 38 | } 39 | 40 | $this->setValue($value); 41 | if (! ctype_xdigit((string) $value)) { 42 | $this->error(self::NOT_HEX); 43 | return false; 44 | } 45 | 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Identical.php: -------------------------------------------------------------------------------- 1 | "The two given tokens do not match", 31 | self::MISSING_TOKEN => 'No token was provided to match against', 32 | ]; 33 | 34 | /** 35 | * @var array 36 | */ 37 | protected $messageVariables = [ 38 | 'token' => 'tokenString' 39 | ]; 40 | 41 | /** 42 | * Original token against which to validate 43 | * @var string 44 | */ 45 | protected $tokenString; 46 | protected $token; 47 | protected $strict = true; 48 | protected $literal = false; 49 | 50 | /** 51 | * Sets validator options 52 | * 53 | * @param mixed $token 54 | */ 55 | public function __construct($token = null) 56 | { 57 | if ($token instanceof Traversable) { 58 | $token = ArrayUtils::iteratorToArray($token); 59 | } 60 | 61 | if (is_array($token) && array_key_exists('token', $token)) { 62 | if (array_key_exists('strict', $token)) { 63 | $this->setStrict($token['strict']); 64 | } 65 | 66 | if (array_key_exists('literal', $token)) { 67 | $this->setLiteral($token['literal']); 68 | } 69 | 70 | $this->setToken($token['token']); 71 | } elseif (null !== $token) { 72 | $this->setToken($token); 73 | } 74 | 75 | parent::__construct(is_array($token) ? $token : null); 76 | } 77 | 78 | /** 79 | * Retrieve token 80 | * 81 | * @return mixed 82 | */ 83 | public function getToken() 84 | { 85 | return $this->token; 86 | } 87 | 88 | /** 89 | * Set token against which to compare 90 | * 91 | * @param mixed $token 92 | * @return Identical 93 | */ 94 | public function setToken($token) 95 | { 96 | $this->tokenString = (is_array($token) ? var_export($token, true) : (string) $token); 97 | $this->token = $token; 98 | return $this; 99 | } 100 | 101 | /** 102 | * Returns the strict parameter 103 | * 104 | * @return bool 105 | */ 106 | public function getStrict() 107 | { 108 | return $this->strict; 109 | } 110 | 111 | /** 112 | * Sets the strict parameter 113 | * 114 | * @param bool $strict 115 | * @return Identical 116 | */ 117 | public function setStrict($strict) 118 | { 119 | $this->strict = (bool) $strict; 120 | return $this; 121 | } 122 | 123 | /** 124 | * Returns the literal parameter 125 | * 126 | * @return bool 127 | */ 128 | public function getLiteral() 129 | { 130 | return $this->literal; 131 | } 132 | 133 | /** 134 | * Sets the literal parameter 135 | * 136 | * @param bool $literal 137 | * @return Identical 138 | */ 139 | public function setLiteral($literal) 140 | { 141 | $this->literal = (bool) $literal; 142 | return $this; 143 | } 144 | 145 | /** 146 | * Returns true if and only if a token has been set and the provided value 147 | * matches that token. 148 | * 149 | * @param mixed $value 150 | * @param array|ArrayAccess $context 151 | * @throws Exception\InvalidArgumentException If context is not array or ArrayObject 152 | * @return bool 153 | */ 154 | public function isValid($value, $context = null) 155 | { 156 | $this->setValue($value); 157 | 158 | $token = $this->getToken(); 159 | 160 | if (! $this->getLiteral() && $context !== null) { 161 | if (! is_array($context) && ! ($context instanceof ArrayAccess)) { 162 | throw new Exception\InvalidArgumentException(sprintf( 163 | 'Context passed to %s must be array, ArrayObject or null; received "%s"', 164 | __METHOD__, 165 | is_object($context) ? get_class($context) : gettype($context) 166 | )); 167 | } 168 | 169 | if (is_array($token)) { 170 | while (is_array($token)) { 171 | $key = key($token); 172 | if (! isset($context[$key])) { 173 | break; 174 | } 175 | $context = $context[$key]; 176 | $token = $token[$key]; 177 | } 178 | } 179 | 180 | // if $token is an array it means the above loop didn't went all the way down to the leaf, 181 | // so the $token structure doesn't match the $context structure 182 | if (is_array($token) || ! isset($context[$token])) { 183 | $token = $this->getToken(); 184 | } else { 185 | $token = $context[$token]; 186 | } 187 | } 188 | 189 | if ($token === null) { 190 | $this->error(self::MISSING_TOKEN); 191 | return false; 192 | } 193 | 194 | $strict = $this->getStrict(); 195 | if (($strict && ($value !== $token)) || (! $strict && ($value != $token))) { 196 | $this->error(self::NOT_SAME); 197 | return false; 198 | } 199 | 200 | return true; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/IsCountable.php: -------------------------------------------------------------------------------- 1 | "The input must be an array or an instance of \\Countable", 41 | self::NOT_EQUALS => "The input count must equal '%count%'", 42 | self::GREATER_THAN => "The input count must be less than '%max%', inclusively", 43 | self::LESS_THAN => "The input count must be greater than '%min%', inclusively", 44 | ]; 45 | 46 | /** 47 | * Additional variables available for validation failure messages 48 | * 49 | * @var array 50 | */ 51 | protected $messageVariables = [ 52 | 'count' => ['options' => 'count'], 53 | 'min' => ['options' => 'min'], 54 | 'max' => ['options' => 'max'], 55 | ]; 56 | 57 | /** 58 | * Options for the between validator 59 | * 60 | * @var array 61 | */ 62 | protected $options = [ 63 | 'count' => null, 64 | 'min' => null, 65 | 'max' => null, 66 | ]; 67 | 68 | public function setOptions($options = []) 69 | { 70 | foreach (['count', 'min', 'max'] as $option) { 71 | if (! is_array($options) || ! isset($options[$option])) { 72 | continue; 73 | } 74 | 75 | $method = sprintf('set%s', ucfirst($option)); 76 | $this->$method($options[$option]); 77 | unset($options[$option]); 78 | } 79 | 80 | return parent::setOptions($options); 81 | } 82 | 83 | /** 84 | * Returns true if and only if $value is countable (and the count validates against optional values). 85 | * 86 | * @param iterable $value 87 | * @return bool 88 | */ 89 | public function isValid($value) 90 | { 91 | if (! (is_array($value) || $value instanceof Countable)) { 92 | $this->error(self::NOT_COUNTABLE); 93 | return false; 94 | } 95 | 96 | $count = count($value); 97 | 98 | if (is_numeric($this->getCount())) { 99 | if ($count != $this->getCount()) { 100 | $this->error(self::NOT_EQUALS); 101 | return false; 102 | } 103 | 104 | return true; 105 | } 106 | 107 | if (is_numeric($this->getMax()) && $count > $this->getMax()) { 108 | $this->error(self::GREATER_THAN); 109 | return false; 110 | } 111 | 112 | if (is_numeric($this->getMin()) && $count < $this->getMin()) { 113 | $this->error(self::LESS_THAN); 114 | return false; 115 | } 116 | 117 | return true; 118 | } 119 | 120 | /** 121 | * Returns the count option 122 | * 123 | * @return mixed 124 | */ 125 | public function getCount() 126 | { 127 | return $this->options['count']; 128 | } 129 | 130 | /** 131 | * Returns the min option 132 | * 133 | * @return mixed 134 | */ 135 | public function getMin() 136 | { 137 | return $this->options['min']; 138 | } 139 | 140 | /** 141 | * Returns the max option 142 | * 143 | * @return mixed 144 | */ 145 | public function getMax() 146 | { 147 | return $this->options['max']; 148 | } 149 | 150 | /** 151 | * @param mixed $value 152 | * @return void 153 | * @throws Exception\InvalidArgumentException if either a min or max option 154 | * was previously set. 155 | */ 156 | private function setCount($value) 157 | { 158 | if (isset($this->options['min']) || isset($this->options['max'])) { 159 | throw new Exception\InvalidArgumentException( 160 | 'Cannot set count; conflicts with either a min or max option previously set' 161 | ); 162 | } 163 | $this->options['count'] = $value; 164 | } 165 | 166 | /** 167 | * @param mixed $value 168 | * @return void 169 | * @throws Exception\InvalidArgumentException if either a count or max option 170 | * was previously set. 171 | */ 172 | private function setMin($value) 173 | { 174 | if (isset($this->options['count'])) { 175 | throw new Exception\InvalidArgumentException( 176 | 'Cannot set count; conflicts with either a count option previously set' 177 | ); 178 | } 179 | $this->options['min'] = $value; 180 | } 181 | 182 | /** 183 | * @param mixed $value 184 | * @return void 185 | * @throws Exception\InvalidArgumentException if either a count or min option 186 | * was previously set. 187 | */ 188 | private function setMax($value) 189 | { 190 | if (isset($this->options['count'])) { 191 | throw new Exception\InvalidArgumentException( 192 | 'Cannot set count; conflicts with either a count option previously set' 193 | ); 194 | } 195 | $this->options['max'] = $value; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/IsInstanceOf.php: -------------------------------------------------------------------------------- 1 | "The input is not an instance of '%className%'", 24 | ]; 25 | 26 | /** 27 | * Additional variables available for validation failure messages 28 | * 29 | * @var array 30 | */ 31 | protected $messageVariables = [ 32 | 'className' => 'className' 33 | ]; 34 | 35 | /** 36 | * Class name 37 | * 38 | * @var string 39 | */ 40 | protected $className; 41 | 42 | /** 43 | * Sets validator options 44 | * 45 | * @param array|Traversable $options 46 | * @throws Exception\InvalidArgumentException 47 | */ 48 | public function __construct($options = null) 49 | { 50 | if ($options instanceof Traversable) { 51 | $options = iterator_to_array($options); 52 | } 53 | 54 | // If argument is not an array, consider first argument as class name 55 | if (! is_array($options)) { 56 | $options = func_get_args(); 57 | 58 | $tmpOptions = []; 59 | $tmpOptions['className'] = array_shift($options); 60 | 61 | $options = $tmpOptions; 62 | } 63 | 64 | if (! array_key_exists('className', $options)) { 65 | throw new Exception\InvalidArgumentException('Missing option "className"'); 66 | } 67 | 68 | parent::__construct($options); 69 | } 70 | 71 | /** 72 | * Get class name 73 | * 74 | * @return string 75 | */ 76 | public function getClassName() 77 | { 78 | return $this->className; 79 | } 80 | 81 | /** 82 | * Set class name 83 | * 84 | * @param string $className 85 | * @return self 86 | */ 87 | public function setClassName($className) 88 | { 89 | $this->className = $className; 90 | return $this; 91 | } 92 | 93 | /** 94 | * Returns true if $value is instance of $this->className 95 | * 96 | * @param mixed $value 97 | * @return bool 98 | */ 99 | public function isValid($value) 100 | { 101 | if ($value instanceof $this->className) { 102 | return true; 103 | } 104 | $this->error(self::NOT_INSTANCE_OF); 105 | return false; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Isbn.php: -------------------------------------------------------------------------------- 1 | "Invalid type given. String or integer expected", 27 | self::NO_ISBN => "The input is not a valid ISBN number", 28 | ]; 29 | 30 | protected $options = [ 31 | 'type' => self::AUTO, // Allowed type 32 | 'separator' => '', // Separator character 33 | ]; 34 | 35 | /** 36 | * Detect input format. 37 | * 38 | * @return string 39 | */ 40 | protected function detectFormat() 41 | { 42 | // prepare separator and pattern list 43 | $sep = quotemeta($this->getSeparator()); 44 | $patterns = []; 45 | $lengths = []; 46 | $type = $this->getType(); 47 | 48 | // check for ISBN-10 49 | if ($type == self::ISBN10 || $type == self::AUTO) { 50 | if (empty($sep)) { 51 | $pattern = '/^[0-9]{9}[0-9X]{1}$/'; 52 | $length = 10; 53 | } else { 54 | $pattern = "/^[0-9]{1,7}[{$sep}]{1}[0-9]{1,7}[{$sep}]{1}[0-9]{1,7}[{$sep}]{1}[0-9X]{1}$/"; 55 | $length = 13; 56 | } 57 | 58 | $patterns[$pattern] = self::ISBN10; 59 | $lengths[$pattern] = $length; 60 | } 61 | 62 | // check for ISBN-13 63 | if ($type == self::ISBN13 || $type == self::AUTO) { 64 | if (empty($sep)) { 65 | $pattern = '/^[0-9]{13}$/'; 66 | $length = 13; 67 | } else { 68 | // @codingStandardsIgnoreStart 69 | $pattern = "/^[0-9]{1,9}[{$sep}]{1}[0-9]{1,5}[{$sep}]{1}[0-9]{1,9}[{$sep}]{1}[0-9]{1,9}[{$sep}]{1}[0-9]{1}$/"; 70 | // @codingStandardsIgnoreEnd 71 | $length = 17; 72 | } 73 | 74 | $patterns[$pattern] = self::ISBN13; 75 | $lengths[$pattern] = $length; 76 | } 77 | 78 | // check pattern list 79 | foreach ($patterns as $pattern => $type) { 80 | if ((strlen($this->getValue()) == $lengths[$pattern]) && preg_match($pattern, $this->getValue())) { 81 | return $type; 82 | } 83 | } 84 | 85 | return; 86 | } 87 | 88 | /** 89 | * Returns true if and only if $value is a valid ISBN. 90 | * 91 | * @param string $value 92 | * @return bool 93 | */ 94 | public function isValid($value) 95 | { 96 | if (! is_string($value) && ! is_int($value)) { 97 | $this->error(self::INVALID); 98 | return false; 99 | } 100 | 101 | $value = (string) $value; 102 | $this->setValue($value); 103 | 104 | switch ($this->detectFormat()) { 105 | case self::ISBN10: 106 | $isbn = new Isbn\Isbn10(); 107 | break; 108 | 109 | case self::ISBN13: 110 | $isbn = new Isbn\Isbn13(); 111 | break; 112 | 113 | default: 114 | $this->error(self::NO_ISBN); 115 | return false; 116 | } 117 | 118 | $value = str_replace($this->getSeparator(), '', $value); 119 | $checksum = $isbn->getChecksum($value); 120 | 121 | // validate 122 | if (substr($this->getValue(), -1) != $checksum) { 123 | $this->error(self::NO_ISBN); 124 | return false; 125 | } 126 | return true; 127 | } 128 | 129 | /** 130 | * Set separator characters. 131 | * 132 | * It is allowed only empty string, hyphen and space. 133 | * 134 | * @param string $separator 135 | * @throws Exception\InvalidArgumentException When $separator is not valid 136 | * @return Isbn Provides a fluent interface 137 | */ 138 | public function setSeparator($separator) 139 | { 140 | // check separator 141 | if (! in_array($separator, ['-', ' ', ''])) { 142 | throw new Exception\InvalidArgumentException('Invalid ISBN separator.'); 143 | } 144 | 145 | $this->options['separator'] = $separator; 146 | return $this; 147 | } 148 | 149 | /** 150 | * Get separator characters. 151 | * 152 | * @return string 153 | */ 154 | public function getSeparator() 155 | { 156 | return $this->options['separator']; 157 | } 158 | 159 | /** 160 | * Set allowed ISBN type. 161 | * 162 | * @param string $type 163 | * @throws Exception\InvalidArgumentException When $type is not valid 164 | * @return Isbn Provides a fluent interface 165 | */ 166 | public function setType($type) 167 | { 168 | // check type 169 | if (! in_array($type, [self::AUTO, self::ISBN10, self::ISBN13])) { 170 | throw new Exception\InvalidArgumentException('Invalid ISBN type'); 171 | } 172 | 173 | $this->options['type'] = $type; 174 | return $this; 175 | } 176 | 177 | /** 178 | * Get allowed ISBN type. 179 | * 180 | * @return string 181 | */ 182 | public function getType() 183 | { 184 | return $this->options['type']; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/Isbn/Isbn10.php: -------------------------------------------------------------------------------- 1 | sum($value); 21 | return $this->checksum($sum); 22 | } 23 | 24 | /** 25 | * Calculate the value sum. 26 | * 27 | * @param int|string $value 28 | * @return int 29 | */ 30 | private function sum($value) 31 | { 32 | $sum = 0; 33 | 34 | for ($i = 0; $i < 9; $i++) { 35 | $sum += (10 - $i) * $value[$i]; 36 | } 37 | 38 | return $sum; 39 | } 40 | 41 | /** 42 | * Calculate the checksum for the value's sum. 43 | * 44 | * @param int $sum 45 | * @return int|string 46 | */ 47 | private function checksum($sum) 48 | { 49 | $checksum = 11 - ($sum % 11); 50 | 51 | if ($checksum == 11) { 52 | return '0'; 53 | } 54 | 55 | if ($checksum == 10) { 56 | return 'X'; 57 | } 58 | 59 | return $checksum; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Isbn/Isbn13.php: -------------------------------------------------------------------------------- 1 | sum($value); 21 | return $this->checksum($sum); 22 | } 23 | 24 | /** 25 | * Calculate the value sum. 26 | * 27 | * @param int|string $value 28 | * @return int 29 | */ 30 | private function sum($value) 31 | { 32 | $sum = 0; 33 | 34 | for ($i = 0; $i < 12; $i++) { 35 | if ($i % 2 == 0) { 36 | $sum += $value[$i]; 37 | continue; 38 | } 39 | 40 | $sum += 3 * $value[$i]; 41 | } 42 | 43 | return $sum; 44 | } 45 | 46 | /** 47 | * Calculate the checksum for the value's sum. 48 | * 49 | * @param int $sum 50 | * @return int|string 51 | */ 52 | private function checksum($sum) 53 | { 54 | $checksum = 10 - ($sum % 10); 55 | 56 | if ($checksum == 10) { 57 | return '0'; 58 | } 59 | 60 | return $checksum; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/LessThan.php: -------------------------------------------------------------------------------- 1 | "The input is not less than '%max%'", 27 | self::NOT_LESS_INCLUSIVE => "The input is not less or equal than '%max%'" 28 | ]; 29 | 30 | /** 31 | * Additional variables available for validation failure messages 32 | * 33 | * @var array 34 | */ 35 | protected $messageVariables = [ 36 | 'max' => 'max' 37 | ]; 38 | 39 | /** 40 | * Maximum value 41 | * 42 | * @var mixed 43 | */ 44 | protected $max; 45 | 46 | /** 47 | * Whether to do inclusive comparisons, allowing equivalence to max 48 | * 49 | * If false, then strict comparisons are done, and the value may equal 50 | * the max option 51 | * 52 | * @var bool 53 | */ 54 | protected $inclusive; 55 | 56 | /** 57 | * Sets validator options 58 | * 59 | * @param array|Traversable $options 60 | * @throws Exception\InvalidArgumentException 61 | */ 62 | public function __construct($options = null) 63 | { 64 | if ($options instanceof Traversable) { 65 | $options = ArrayUtils::iteratorToArray($options); 66 | } 67 | if (! is_array($options)) { 68 | $options = func_get_args(); 69 | $temp['max'] = array_shift($options); 70 | 71 | if (! empty($options)) { 72 | $temp['inclusive'] = array_shift($options); 73 | } 74 | 75 | $options = $temp; 76 | } 77 | 78 | if (! array_key_exists('max', $options)) { 79 | throw new Exception\InvalidArgumentException("Missing option 'max'"); 80 | } 81 | 82 | if (! array_key_exists('inclusive', $options)) { 83 | $options['inclusive'] = false; 84 | } 85 | 86 | $this->setMax($options['max']) 87 | ->setInclusive($options['inclusive']); 88 | 89 | parent::__construct($options); 90 | } 91 | 92 | /** 93 | * Returns the max option 94 | * 95 | * @return mixed 96 | */ 97 | public function getMax() 98 | { 99 | return $this->max; 100 | } 101 | 102 | /** 103 | * Sets the max option 104 | * 105 | * @param mixed $max 106 | * @return LessThan Provides a fluent interface 107 | */ 108 | public function setMax($max) 109 | { 110 | $this->max = $max; 111 | return $this; 112 | } 113 | 114 | /** 115 | * Returns the inclusive option 116 | * 117 | * @return bool 118 | */ 119 | public function getInclusive() 120 | { 121 | return $this->inclusive; 122 | } 123 | 124 | /** 125 | * Sets the inclusive option 126 | * 127 | * @param bool $inclusive 128 | * @return LessThan Provides a fluent interface 129 | */ 130 | public function setInclusive($inclusive) 131 | { 132 | $this->inclusive = $inclusive; 133 | return $this; 134 | } 135 | 136 | /** 137 | * Returns true if and only if $value is less than max option, inclusively 138 | * when the inclusive option is true 139 | * 140 | * @param mixed $value 141 | * @return bool 142 | */ 143 | public function isValid($value) 144 | { 145 | $this->setValue($value); 146 | 147 | if ($this->inclusive) { 148 | if ($value > $this->max) { 149 | $this->error(self::NOT_LESS_INCLUSIVE); 150 | return false; 151 | } 152 | } else { 153 | if ($value >= $this->max) { 154 | $this->error(self::NOT_LESS); 155 | return false; 156 | } 157 | } 158 | 159 | return true; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/Module.php: -------------------------------------------------------------------------------- 1 | $provider->getDependencyConfig(), 21 | ]; 22 | } 23 | 24 | /** 25 | * Register a specification for the ValidatorManager 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 | 'ValidatorManager', 38 | 'validators', 39 | ValidatorProviderInterface::class, 40 | 'getValidatorConfig' 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Regex.php: -------------------------------------------------------------------------------- 1 | "Invalid type given. String, integer or float expected", 27 | self::NOT_MATCH => "The input does not match against pattern '%pattern%'", 28 | self::ERROROUS => "There was an internal error while using the pattern '%pattern%'", 29 | ]; 30 | 31 | /** 32 | * @var array 33 | */ 34 | protected $messageVariables = [ 35 | 'pattern' => 'pattern' 36 | ]; 37 | 38 | /** 39 | * Regular expression pattern 40 | * 41 | * @var string 42 | */ 43 | protected $pattern; 44 | 45 | /** 46 | * Sets validator options 47 | * 48 | * @param string|array|Traversable $pattern 49 | * @throws Exception\InvalidArgumentException On missing 'pattern' parameter 50 | */ 51 | public function __construct($pattern) 52 | { 53 | if (is_string($pattern)) { 54 | $this->setPattern($pattern); 55 | parent::__construct([]); 56 | return; 57 | } 58 | 59 | if ($pattern instanceof Traversable) { 60 | $pattern = ArrayUtils::iteratorToArray($pattern); 61 | } 62 | 63 | if (! is_array($pattern)) { 64 | throw new Exception\InvalidArgumentException('Invalid options provided to constructor'); 65 | } 66 | 67 | if (! array_key_exists('pattern', $pattern)) { 68 | throw new Exception\InvalidArgumentException("Missing option 'pattern'"); 69 | } 70 | 71 | $this->setPattern($pattern['pattern']); 72 | unset($pattern['pattern']); 73 | parent::__construct($pattern); 74 | } 75 | 76 | /** 77 | * Returns the pattern option 78 | * 79 | * @return string 80 | */ 81 | public function getPattern() 82 | { 83 | return $this->pattern; 84 | } 85 | 86 | /** 87 | * Sets the pattern option 88 | * 89 | * @param string $pattern 90 | * @throws Exception\InvalidArgumentException if there is a fatal error in pattern matching 91 | * @return Regex Provides a fluent interface 92 | */ 93 | public function setPattern($pattern) 94 | { 95 | ErrorHandler::start(); 96 | $this->pattern = (string) $pattern; 97 | $status = preg_match($this->pattern, "Test"); 98 | $error = ErrorHandler::stop(); 99 | 100 | if (false === $status) { 101 | throw new Exception\InvalidArgumentException( 102 | "Internal error parsing the pattern '{$this->pattern}'", 103 | 0, 104 | $error 105 | ); 106 | } 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * Returns true if and only if $value matches against the pattern option 113 | * 114 | * @param string $value 115 | * @return bool 116 | */ 117 | public function isValid($value) 118 | { 119 | if (! is_string($value) && ! is_int($value) && ! is_float($value)) { 120 | $this->error(self::INVALID); 121 | return false; 122 | } 123 | 124 | $this->setValue($value); 125 | 126 | ErrorHandler::start(); 127 | $status = preg_match($this->pattern, $value); 128 | ErrorHandler::stop(); 129 | if (false === $status) { 130 | $this->error(self::ERROROUS); 131 | return false; 132 | } 133 | 134 | if (! $status) { 135 | $this->error(self::NOT_MATCH); 136 | return false; 137 | } 138 | 139 | return true; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Sitemap/Changefreq.php: -------------------------------------------------------------------------------- 1 | value 16 | * 17 | * @link http://www.sitemaps.org/protocol.php Sitemaps XML format 18 | */ 19 | class Changefreq extends AbstractValidator 20 | { 21 | /** 22 | * Validation key for not valid 23 | * 24 | */ 25 | const NOT_VALID = 'sitemapChangefreqNotValid'; 26 | const INVALID = 'sitemapChangefreqInvalid'; 27 | 28 | /** 29 | * Validation failure message template definitions 30 | * 31 | * @var array 32 | */ 33 | protected $messageTemplates = [ 34 | self::NOT_VALID => "The input is not a valid sitemap changefreq", 35 | self::INVALID => "Invalid type given. String expected", 36 | ]; 37 | 38 | /** 39 | * Valid change frequencies 40 | * 41 | * @var array 42 | */ 43 | protected $changeFreqs = [ 44 | 'always', 'hourly', 'daily', 'weekly', 45 | 'monthly', 'yearly', 'never' 46 | ]; 47 | 48 | /** 49 | * Validates if a string is valid as a sitemap changefreq 50 | * 51 | * @link http://www.sitemaps.org/protocol.php#changefreqdef 52 | * 53 | * @param string $value value to validate 54 | * @return bool 55 | */ 56 | public function isValid($value) 57 | { 58 | if (! is_string($value)) { 59 | $this->error(self::INVALID); 60 | return false; 61 | } 62 | 63 | $this->setValue($value); 64 | if (! is_string($value)) { 65 | return false; 66 | } 67 | 68 | if (! in_array($value, $this->changeFreqs, true)) { 69 | $this->error(self::NOT_VALID); 70 | return false; 71 | } 72 | 73 | return true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Sitemap/Lastmod.php: -------------------------------------------------------------------------------- 1 | value 17 | * 18 | * @link http://www.sitemaps.org/protocol.php Sitemaps XML format 19 | */ 20 | class Lastmod extends AbstractValidator 21 | { 22 | /** 23 | * Regular expression to use when validating 24 | * 25 | */ 26 | // @codingStandardsIgnoreStart 27 | const LASTMOD_REGEX = '/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])(T([0-1][0-9]|2[0-3])(:[0-5][0-9])(:[0-5][0-9])?(\\+|-)([0-1][0-9]|2[0-3]):[0-5][0-9])?$/'; 28 | // @codingStandardsIgnoreEnd 29 | 30 | /** 31 | * Validation key for not valid 32 | * 33 | */ 34 | const NOT_VALID = 'sitemapLastmodNotValid'; 35 | const INVALID = 'sitemapLastmodInvalid'; 36 | 37 | /** 38 | * Validation failure message template definitions 39 | * 40 | * @var array 41 | */ 42 | protected $messageTemplates = [ 43 | self::NOT_VALID => "The input is not a valid sitemap lastmod", 44 | self::INVALID => "Invalid type given. String expected", 45 | ]; 46 | 47 | /** 48 | * Validates if a string is valid as a sitemap lastmod 49 | * 50 | * @link http://www.sitemaps.org/protocol.php#lastmoddef 51 | * 52 | * @param string $value value to validate 53 | * @return bool 54 | */ 55 | public function isValid($value) 56 | { 57 | if (! is_string($value)) { 58 | $this->error(self::INVALID); 59 | return false; 60 | } 61 | 62 | $this->setValue($value); 63 | ErrorHandler::start(); 64 | $result = preg_match(self::LASTMOD_REGEX, $value); 65 | ErrorHandler::stop(); 66 | if ($result != 1) { 67 | $this->error(self::NOT_VALID); 68 | return false; 69 | } 70 | 71 | return true; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Sitemap/Loc.php: -------------------------------------------------------------------------------- 1 | value 17 | * 18 | * @link http://www.sitemaps.org/protocol.php Sitemaps XML format 19 | * 20 | * @see Zend\Uri\Uri 21 | */ 22 | class Loc extends AbstractValidator 23 | { 24 | /** 25 | * Validation key for not valid 26 | * 27 | */ 28 | const NOT_VALID = 'sitemapLocNotValid'; 29 | const INVALID = 'sitemapLocInvalid'; 30 | 31 | /** 32 | * Validation failure message template definitions 33 | * 34 | * @var array 35 | */ 36 | protected $messageTemplates = [ 37 | self::NOT_VALID => "The input is not a valid sitemap location", 38 | self::INVALID => "Invalid type given. String expected", 39 | ]; 40 | 41 | /** 42 | * Validates if a string is valid as a sitemap location 43 | * 44 | * @link http://www.sitemaps.org/protocol.php#locdef 45 | * 46 | * @param string $value value to validate 47 | * @return bool 48 | */ 49 | public function isValid($value) 50 | { 51 | if (! is_string($value)) { 52 | $this->error(self::INVALID); 53 | return false; 54 | } 55 | 56 | $this->setValue($value); 57 | $uri = Uri\UriFactory::factory($value); 58 | if (! $uri->isValid()) { 59 | $this->error(self::NOT_VALID); 60 | return false; 61 | } 62 | 63 | return true; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Sitemap/Priority.php: -------------------------------------------------------------------------------- 1 | value 16 | * 17 | * @link http://www.sitemaps.org/protocol.php Sitemaps XML format 18 | */ 19 | class Priority extends AbstractValidator 20 | { 21 | /** 22 | * Validation key for not valid 23 | * 24 | */ 25 | const NOT_VALID = 'sitemapPriorityNotValid'; 26 | const INVALID = 'sitemapPriorityInvalid'; 27 | 28 | /** 29 | * Validation failure message template definitions 30 | * 31 | * @var array 32 | */ 33 | protected $messageTemplates = [ 34 | self::NOT_VALID => "The input is not a valid sitemap priority", 35 | self::INVALID => "Invalid type given. Numeric string, integer or float expected", 36 | ]; 37 | 38 | /** 39 | * Validates if a string is valid as a sitemap priority 40 | * 41 | * @link http://www.sitemaps.org/protocol.php#prioritydef 42 | * 43 | * @param string $value value to validate 44 | * @return bool 45 | */ 46 | public function isValid($value) 47 | { 48 | if (! is_numeric($value)) { 49 | $this->error(self::INVALID); 50 | return false; 51 | } 52 | 53 | $this->setValue($value); 54 | $value = (float) $value; 55 | if ($value < 0 || $value > 1) { 56 | $this->error(self::NOT_VALID); 57 | return false; 58 | } 59 | 60 | return true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/StaticValidator.php: -------------------------------------------------------------------------------- 1 | configure(['shared_by_default' => false]); 34 | } else { 35 | $plugins->setShareByDefault(false); 36 | } 37 | } 38 | static::$plugins = $plugins; 39 | } 40 | 41 | /** 42 | * Get plugin manager for locating validators 43 | * 44 | * @return ValidatorPluginManager 45 | */ 46 | public static function getPluginManager() 47 | { 48 | if (null === static::$plugins) { 49 | static::setPluginManager(new ValidatorPluginManager(new ServiceManager)); 50 | } 51 | return static::$plugins; 52 | } 53 | 54 | /** 55 | * @param mixed $value 56 | * @param string $classBaseName 57 | * @param array $options OPTIONAL associative array of options to pass as 58 | * the sole argument to the validator constructor. 59 | * @return bool 60 | * @throws Exception\InvalidArgumentException for an invalid $options argument. 61 | */ 62 | public static function execute($value, $classBaseName, array $options = []) 63 | { 64 | if ($options && array_values($options) === $options) { 65 | throw new Exception\InvalidArgumentException( 66 | 'Invalid options provided via $options argument; must be an associative array' 67 | ); 68 | } 69 | 70 | $plugins = static::getPluginManager(); 71 | 72 | $validator = $plugins->get($classBaseName, $options); 73 | return $validator->isValid($value); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Step.php: -------------------------------------------------------------------------------- 1 | "Invalid value given. Scalar expected", 24 | self::NOT_STEP => "The input is not a valid step" 25 | ]; 26 | 27 | /** 28 | * @var mixed 29 | */ 30 | protected $baseValue = 0; 31 | 32 | /** 33 | * @var mixed 34 | */ 35 | protected $step = 1; 36 | 37 | /** 38 | * Set default options for this instance 39 | * 40 | * @param array $options 41 | */ 42 | public function __construct($options = []) 43 | { 44 | if ($options instanceof Traversable) { 45 | $options = iterator_to_array($options); 46 | } elseif (! is_array($options)) { 47 | $options = func_get_args(); 48 | $temp['baseValue'] = array_shift($options); 49 | if (! empty($options)) { 50 | $temp['step'] = array_shift($options); 51 | } 52 | 53 | $options = $temp; 54 | } 55 | 56 | if (isset($options['baseValue'])) { 57 | $this->setBaseValue($options['baseValue']); 58 | } 59 | if (isset($options['step'])) { 60 | $this->setStep($options['step']); 61 | } 62 | 63 | parent::__construct($options); 64 | } 65 | 66 | /** 67 | * Sets the base value from which the step should be computed 68 | * 69 | * @param mixed $baseValue 70 | * @return Step 71 | */ 72 | public function setBaseValue($baseValue) 73 | { 74 | $this->baseValue = $baseValue; 75 | return $this; 76 | } 77 | 78 | /** 79 | * Returns the base value from which the step should be computed 80 | * 81 | * @return string 82 | */ 83 | public function getBaseValue() 84 | { 85 | return $this->baseValue; 86 | } 87 | 88 | /** 89 | * Sets the step value 90 | * 91 | * @param mixed $step 92 | * @return Step 93 | */ 94 | public function setStep($step) 95 | { 96 | $this->step = (float) $step; 97 | return $this; 98 | } 99 | 100 | /** 101 | * Returns the step value 102 | * 103 | * @return string 104 | */ 105 | public function getStep() 106 | { 107 | return $this->step; 108 | } 109 | 110 | /** 111 | * Returns true if $value is a scalar and a valid step value 112 | * 113 | * @param mixed $value 114 | * @return bool 115 | */ 116 | public function isValid($value) 117 | { 118 | if (! is_numeric($value)) { 119 | $this->error(self::INVALID); 120 | return false; 121 | } 122 | 123 | $this->setValue($value); 124 | 125 | $substract = $this->sub($value, $this->baseValue); 126 | 127 | $fmod = $this->fmod($substract, $this->step); 128 | 129 | if ($fmod !== 0.0 && $fmod !== $this->step) { 130 | $this->error(self::NOT_STEP); 131 | return false; 132 | } 133 | 134 | return true; 135 | } 136 | 137 | /** 138 | * replaces the internal fmod function which give wrong results on many cases 139 | * 140 | * @param float $x 141 | * @param float $y 142 | * @return float 143 | */ 144 | protected function fmod($x, $y) 145 | { 146 | if ($y == 0.0) { 147 | return 1.0; 148 | } 149 | 150 | //find the maximum precision from both input params to give accurate results 151 | $precision = $this->getPrecision($x) + $this->getPrecision($y); 152 | 153 | return round($x - $y * floor($x / $y), $precision); 154 | } 155 | 156 | /** 157 | * replaces the internal substraction operation which give wrong results on some cases 158 | * 159 | * @param float $x 160 | * @param float $y 161 | * @return float 162 | */ 163 | private function sub($x, $y) 164 | { 165 | $precision = $this->getPrecision($x) + $this->getPrecision($y); 166 | return round($x - $y, $precision); 167 | } 168 | 169 | /** 170 | * @param float $float 171 | * @return int 172 | */ 173 | private function getPrecision($float) 174 | { 175 | $segment = substr($float, strpos($float, '.') + 1); 176 | return $segment ? strlen($segment) : 0; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/Timezone.php: -------------------------------------------------------------------------------- 1 | 'location', 29 | self::ABBREVIATION => 'abbreviation', 30 | ]; 31 | 32 | /** 33 | * Default value for types; value = 3 34 | * 35 | * @var array 36 | */ 37 | protected $defaultType = [ 38 | self::LOCATION, 39 | self::ABBREVIATION, 40 | ]; 41 | 42 | /** 43 | * @var array 44 | */ 45 | protected $messageTemplates = [ 46 | self::INVALID => 'Invalid timezone given.', 47 | self::INVALID_TIMEZONE_LOCATION => 'Invalid timezone location given.', 48 | self::INVALID_TIMEZONE_ABBREVIATION => 'Invalid timezone abbreviation given.', 49 | ]; 50 | 51 | /** 52 | * Options for this validator 53 | * 54 | * @var array 55 | */ 56 | protected $options = []; 57 | 58 | /** 59 | * Constructor 60 | * 61 | * @param array|int $options OPTIONAL 62 | */ 63 | public function __construct($options = []) 64 | { 65 | $opts['type'] = $this->defaultType; 66 | 67 | if (is_array($options)) { 68 | if (array_key_exists('type', $options)) { 69 | $opts['type'] = $options['type']; 70 | } 71 | } elseif (! empty($options)) { 72 | $opts['type'] = $options; 73 | } 74 | 75 | // setType called by parent constructor then setOptions method 76 | parent::__construct($opts); 77 | } 78 | 79 | /** 80 | * Set the types 81 | * 82 | * @param int|array $type 83 | * 84 | * @throws Exception\InvalidArgumentException 85 | */ 86 | public function setType($type = null) 87 | { 88 | $type = $this->calculateTypeValue($type); 89 | 90 | if (! is_int($type) || ($type < 1) || ($type > self::ALL)) { 91 | throw new Exception\InvalidArgumentException(sprintf( 92 | 'Unknown type "%s" provided', 93 | (is_string($type) || is_int($type)) 94 | ? $type 95 | : (is_object($type) ? get_class($type) : gettype($type)) 96 | )); 97 | } 98 | 99 | $this->options['type'] = $type; 100 | } 101 | 102 | /** 103 | * Returns true if timezone location or timezone abbreviations is correct. 104 | * 105 | * @param mixed $value 106 | * @return bool 107 | */ 108 | public function isValid($value) 109 | { 110 | if ($value !== null && ! is_string($value)) { 111 | $this->error(self::INVALID); 112 | return false; 113 | } 114 | 115 | $type = $this->options['type']; 116 | $this->setValue($value); 117 | 118 | switch (true) { 119 | // Check in locations and abbreviations 120 | case (($type & self::LOCATION) && ($type & self::ABBREVIATION)): 121 | $abbrs = DateTimeZone::listAbbreviations(); 122 | $locations = DateTimeZone::listIdentifiers(); 123 | 124 | if (! array_key_exists($value, $abbrs) && ! in_array($value, $locations)) { 125 | $this->error(self::INVALID); 126 | return false; 127 | } 128 | break; 129 | 130 | // Check only in locations 131 | case ($type & self::LOCATION): 132 | $locations = DateTimeZone::listIdentifiers(); 133 | 134 | if (! in_array($value, $locations)) { 135 | $this->error(self::INVALID_TIMEZONE_LOCATION); 136 | return false; 137 | } 138 | break; 139 | 140 | // Check only in abbreviations 141 | case ($type & self::ABBREVIATION): 142 | $abbrs = DateTimeZone::listAbbreviations(); 143 | 144 | if (! array_key_exists($value, $abbrs)) { 145 | $this->error(self::INVALID_TIMEZONE_ABBREVIATION); 146 | return false; 147 | } 148 | break; 149 | } 150 | 151 | return true; 152 | } 153 | 154 | /** 155 | * @param array|int|string $type 156 | * 157 | * @return int 158 | */ 159 | protected function calculateTypeValue($type) 160 | { 161 | $types = (array) $type; 162 | $detected = 0; 163 | 164 | foreach ($types as $value) { 165 | if (is_int($value)) { 166 | $detected |= $value; 167 | } elseif (false !== ($position = array_search($value, $this->constants))) { 168 | $detected |= array_search($value, $this->constants); 169 | } 170 | } 171 | 172 | return $detected; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/Translator/TranslatorAwareInterface.php: -------------------------------------------------------------------------------- 1 | 30 | 'The provided password was found in previous breaches, please create another password', 31 | self::NOT_A_STRING => 'The provided password is not a string, please provide a correct password', 32 | ]; 33 | 34 | /** 35 | * @var ClientInterface 36 | */ 37 | private $httpClient; 38 | 39 | /** 40 | * @var RequestFactoryInterface 41 | */ 42 | private $makeHttpRequest; 43 | 44 | /** 45 | * @var ResponseFactoryInterface 46 | */ 47 | private $makeHttpResponse; 48 | 49 | /** 50 | * PasswordBreach constructor. 51 | */ 52 | public function __construct( 53 | ClientInterface $httpClient, 54 | RequestFactoryInterface $makeHttpRequest, 55 | ResponseFactoryInterface $makeHttpResponse 56 | ) { 57 | $this->httpClient = $httpClient; 58 | $this->makeHttpRequest = $makeHttpRequest; 59 | $this->makeHttpResponse = $makeHttpResponse; 60 | } 61 | 62 | /** 63 | * @inheritDoc 64 | */ 65 | public function isValid($value) 66 | { 67 | if (! is_string($value)) { 68 | $this->error(self::NOT_A_STRING); 69 | return false; 70 | } 71 | 72 | if ($this->isPwnedPassword($value)) { 73 | $this->error(self::PASSWORD_BREACHED); 74 | return false; 75 | } 76 | 77 | return true; 78 | } 79 | 80 | private function isPwnedPassword(string $password) : bool 81 | { 82 | $sha1Hash = $this->hashPassword($password); 83 | $rangeHash = $this->getRangeHash($sha1Hash); 84 | $hashList = $this->retrieveHashList($rangeHash); 85 | return $this->hashInResponse($sha1Hash, $hashList); 86 | } 87 | 88 | /** 89 | * We use a SHA1 hashed password for checking it against 90 | * the breached data set of HIBP. 91 | * 92 | * @param string $password 93 | * @return string 94 | */ 95 | private function hashPassword(string $password) : string 96 | { 97 | $hashedPassword = \sha1($password); 98 | return strtoupper($hashedPassword); 99 | } 100 | 101 | /** 102 | * Creates a hash range that will be send to HIBP API 103 | * applying K-Anonymity 104 | * 105 | * @param string $passwordHash 106 | * @return string 107 | * @see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-by-exclusively-supporting-anonymity/ 108 | */ 109 | private function getRangeHash(string $passwordHash) : string 110 | { 111 | return substr($passwordHash, self::HIBP_K_ANONYMITY_HASH_RANGE_BASE, self::HIBP_K_ANONYMITY_HASH_RANGE_LENGTH); 112 | } 113 | 114 | /** 115 | * Making a connection to the HIBP API to retrieve a 116 | * list of hashes that all have the same range as we 117 | * provided. 118 | * 119 | * @param string $passwordRange 120 | * @return string 121 | * @throws ClientExceptionInterface 122 | */ 123 | private function retrieveHashList(string $passwordRange) : string 124 | { 125 | $request = $this->makeHttpRequest->createRequest( 126 | 'GET', 127 | self::HIBP_API_URI . '/range/' . $passwordRange 128 | ); 129 | 130 | $response = $this->httpClient->sendRequest($request); 131 | return (string) $response->getBody(); 132 | } 133 | 134 | /** 135 | * Checks if the password is in the response from HIBP 136 | * 137 | * @param string $sha1Hash 138 | * @param string $resultStream 139 | * @return bool 140 | */ 141 | private function hashInResponse(string $sha1Hash, string $resultStream) : bool 142 | { 143 | $data = explode("\r\n", $resultStream); 144 | $hashes = array_filter($data, function ($value) use ($sha1Hash) { 145 | list($hash, $count) = explode(':', $value); 146 | if (0 === strcmp($hash, substr($sha1Hash, self::HIBP_K_ANONYMITY_HASH_RANGE_LENGTH))) { 147 | return true; 148 | } 149 | return false; 150 | }); 151 | if ([] === $hashes) { 152 | return false; 153 | } 154 | return true; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Uri.php: -------------------------------------------------------------------------------- 1 | "Invalid type given. String expected", 27 | self::NOT_URI => "The input does not appear to be a valid Uri", 28 | ]; 29 | 30 | /** 31 | * @var UriHandler 32 | */ 33 | protected $uriHandler; 34 | 35 | /** 36 | * @var bool 37 | */ 38 | protected $allowRelative = true; 39 | 40 | /** 41 | * @var bool 42 | */ 43 | protected $allowAbsolute = true; 44 | 45 | /** 46 | * Sets default option values for this instance 47 | * 48 | * @param array|Traversable $options 49 | */ 50 | public function __construct($options = []) 51 | { 52 | if ($options instanceof Traversable) { 53 | $options = iterator_to_array($options); 54 | } elseif (! is_array($options)) { 55 | $options = func_get_args(); 56 | $temp['uriHandler'] = array_shift($options); 57 | if (! empty($options)) { 58 | $temp['allowRelative'] = array_shift($options); 59 | } 60 | if (! empty($options)) { 61 | $temp['allowAbsolute'] = array_shift($options); 62 | } 63 | 64 | $options = $temp; 65 | } 66 | 67 | if (isset($options['uriHandler'])) { 68 | $this->setUriHandler($options['uriHandler']); 69 | } 70 | if (isset($options['allowRelative'])) { 71 | $this->setAllowRelative($options['allowRelative']); 72 | } 73 | if (isset($options['allowAbsolute'])) { 74 | $this->setAllowAbsolute($options['allowAbsolute']); 75 | } 76 | 77 | parent::__construct($options); 78 | } 79 | 80 | /** 81 | * @throws InvalidArgumentException 82 | * @return UriHandler 83 | */ 84 | public function getUriHandler() 85 | { 86 | if (null === $this->uriHandler) { 87 | // Lazy load the base Uri handler 88 | $this->uriHandler = new UriHandler(); 89 | } elseif (is_string($this->uriHandler) && class_exists($this->uriHandler)) { 90 | // Instantiate string Uri handler that references a class 91 | $this->uriHandler = new $this->uriHandler; 92 | } 93 | 94 | if (! $this->uriHandler instanceof UriHandler) { 95 | throw new InvalidArgumentException('URI handler is expected to be a Zend\Uri\Uri object'); 96 | } 97 | 98 | return $this->uriHandler; 99 | } 100 | 101 | /** 102 | * @param UriHandler $uriHandler 103 | * @throws InvalidArgumentException 104 | * @return Uri 105 | */ 106 | public function setUriHandler($uriHandler) 107 | { 108 | if (! is_subclass_of($uriHandler, 'Zend\Uri\Uri')) { 109 | throw new InvalidArgumentException('Expecting a subclass name or instance of Zend\Uri\Uri as $uriHandler'); 110 | } 111 | 112 | $this->uriHandler = $uriHandler; 113 | return $this; 114 | } 115 | 116 | /** 117 | * Returns the allowAbsolute option 118 | * 119 | * @return bool 120 | */ 121 | public function getAllowAbsolute() 122 | { 123 | return $this->allowAbsolute; 124 | } 125 | 126 | /** 127 | * Sets the allowAbsolute option 128 | * 129 | * @param bool $allowAbsolute 130 | * @return Uri 131 | */ 132 | public function setAllowAbsolute($allowAbsolute) 133 | { 134 | $this->allowAbsolute = (bool) $allowAbsolute; 135 | return $this; 136 | } 137 | 138 | /** 139 | * Returns the allowRelative option 140 | * 141 | * @return bool 142 | */ 143 | public function getAllowRelative() 144 | { 145 | return $this->allowRelative; 146 | } 147 | 148 | /** 149 | * Sets the allowRelative option 150 | * 151 | * @param bool $allowRelative 152 | * @return Uri 153 | */ 154 | public function setAllowRelative($allowRelative) 155 | { 156 | $this->allowRelative = (bool) $allowRelative; 157 | return $this; 158 | } 159 | 160 | /** 161 | * Returns true if and only if $value validates as a Uri 162 | * 163 | * @param string $value 164 | * @return bool 165 | */ 166 | public function isValid($value) 167 | { 168 | if (! is_string($value)) { 169 | $this->error(self::INVALID); 170 | return false; 171 | } 172 | 173 | $uriHandler = $this->getUriHandler(); 174 | try { 175 | $uriHandler->parse($value); 176 | if ($uriHandler->isValid()) { 177 | // It will either be a valid absolute or relative URI 178 | if (($this->allowRelative && $this->allowAbsolute) 179 | || ($this->allowAbsolute && $uriHandler->isAbsolute()) 180 | || ($this->allowRelative && $uriHandler->isValidRelative()) 181 | ) { 182 | return true; 183 | } 184 | } 185 | } catch (UriException $ex) { 186 | // Error parsing URI, it must be invalid 187 | } 188 | 189 | $this->error(self::NOT_URI); 190 | return false; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/Uuid.php: -------------------------------------------------------------------------------- 1 | 'Invalid type given; string expected', 28 | self::INVALID => 'Invalid UUID format', 29 | ]; 30 | 31 | /** 32 | * Returns true if and only if $value meets the validation requirements. 33 | * 34 | * If $value fails validation, then this method returns false, and 35 | * getMessages() will return an array of messages that explain why the 36 | * validation failed. 37 | * 38 | * @param mixed $value 39 | * 40 | * @return bool 41 | * 42 | * @throws Exception\RuntimeException If validation of $value is impossible 43 | */ 44 | public function isValid($value) 45 | { 46 | if (! is_string($value)) { 47 | $this->error(self::NOT_STRING); 48 | return false; 49 | } 50 | 51 | $this->setValue($value); 52 | 53 | if (empty($value) 54 | || $value !== '00000000-0000-0000-0000-000000000000' 55 | && ! preg_match(self::REGEX_UUID, $value) 56 | ) { 57 | $this->error(self::INVALID); 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/ValidatorInterface.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 validators configuration, nothing more to do 47 | if (! isset($config['validators']) || ! is_array($config['validators'])) { 48 | return $pluginManager; 49 | } 50 | 51 | // Wire service configuration for validators 52 | (new Config($config['validators']))->configureServiceManager($pluginManager); 53 | 54 | return $pluginManager; 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | * 60 | * @return ValidatorPluginManager 61 | */ 62 | public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null) 63 | { 64 | return $this($container, $requestedName ?: ValidatorPluginManager::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/ValidatorProviderInterface.php: -------------------------------------------------------------------------------- 1 |