├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json └── src ├── ClassFileLocator.php ├── Exception ├── BadMethodCallException.php ├── ExceptionInterface.php ├── InvalidArgumentException.php └── RuntimeException.php ├── PhpClassFile.php └── Transfer ├── Adapter ├── AbstractAdapter.php ├── FilterPluginManager.php ├── Http.php └── ValidatorPluginManager.php ├── Exception ├── BadMethodCallException.php ├── ExceptionInterface.php ├── InvalidArgumentException.php ├── PhpEnvironmentException.php └── RuntimeException.php └── Transfer.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file, in reverse chronological order by release. 4 | 5 | ## 2.8.4 - TBD 6 | 7 | ### Added 8 | 9 | - Nothing. 10 | 11 | ### Changed 12 | 13 | - Nothing. 14 | 15 | ### Deprecated 16 | 17 | - Nothing. 18 | 19 | ### Removed 20 | 21 | - Nothing. 22 | 23 | ### Fixed 24 | 25 | - Nothing. 26 | 27 | ## 2.8.3 - 2019-02-06 28 | 29 | ### Added 30 | 31 | - Nothing. 32 | 33 | ### Changed 34 | 35 | - Nothing. 36 | 37 | ### Deprecated 38 | 39 | - Nothing. 40 | 41 | ### Removed 42 | 43 | - Nothing. 44 | 45 | ### Fixed 46 | 47 | - [#46](https://github.com/zendframework/zend-file/pull/46) fixes a unit test assertion for `Zend\File\Transfer\Adapter\Http`. 48 | 49 | ## 2.8.2 - 2018-10-15 50 | 51 | ### Added 52 | 53 | - Nothing. 54 | 55 | ### Changed 56 | 57 | - Nothing. 58 | 59 | ### Deprecated 60 | 61 | - Nothing. 62 | 63 | ### Removed 64 | 65 | - Nothing. 66 | 67 | ### Fixed 68 | 69 | - [#45](https://github.com/zendframework/zend-file/pull/45) fixes compatibility 70 | issue with php 7.3 with deprecated continue keyword usage in switch statements 71 | 72 | ## 2.8.1 - 2018-05-01 73 | 74 | ### Added 75 | 76 | - Nothing. 77 | 78 | ### Changed 79 | 80 | - Nothing. 81 | 82 | ### Deprecated 83 | 84 | - Nothing. 85 | 86 | ### Removed 87 | 88 | - Nothing. 89 | 90 | ### Fixed 91 | 92 | - [#44](https://github.com/zendframework/zend-file/pull/44) fixes an issue where 93 | ClassFileLocator would skip the file (otherwise valid class file) containing a 94 | `use function` declaration. 95 | 96 | ## 2.8.0 - 2018-04-25 97 | 98 | ### Added 99 | 100 | - [#43](https://github.com/zendframework/zend-file/pull/43) adds support for PHP 7.1 and 7.2. 101 | 102 | ### Changed 103 | 104 | - Nothing. 105 | 106 | ### Deprecated 107 | 108 | - Nothing. 109 | 110 | ### Removed 111 | 112 | - [#43](https://github.com/zendframework/zend-file/pull/43) removes support for PHP 5.5. 113 | 114 | - [#43](https://github.com/zendframework/zend-file/pull/43) removes support for HHVM. 115 | 116 | ### Fixed 117 | 118 | - [#41](https://github.com/zendframework/zend-file/pull/41) fixes an issue in PHP 7.1 and up with false-positive detection of classes, 119 | interfaces, and traits when class methods are named after these keywords. 120 | 121 | ## 2.7.1 - 2017-01-11 122 | 123 | ### Added 124 | 125 | - Nothing. 126 | 127 | ### Deprecated 128 | 129 | - Nothing. 130 | 131 | ### Removed 132 | 133 | - Nothing. 134 | 135 | ### Fixed 136 | 137 | - [#34](https://github.com/zendframework/zend-file/pull/34) ensures that 138 | anonymous classes are ignored by the `ClassFileLocator` when identifying files 139 | with class declarations. 140 | 141 | ## 2.7.0 - 2016-04-28 142 | 143 | ### Added 144 | 145 | - [#25](https://github.com/zendframework/zend-file/pull/25) adds and publishes 146 | documentation to https://zendframework.github.io/zend-file/ 147 | 148 | ### Deprecated 149 | 150 | - [#25](https://github.com/zendframework/zend-file/pull/25) deprecates the 151 | `Zend\File\Transfer` subcomponent. Its functionality is now split between each 152 | of: 153 | - zend-filter, for moving uploaded files to their final location, renaming 154 | them, and potentially transforming them. 155 | - zend-validator, for validating upload succes, file type, hash, etc. 156 | - zend-progressbar, for managing upload status. 157 | 158 | ### Removed 159 | 160 | - Nothing. 161 | 162 | ### Fixed 163 | 164 | - Nothing. 165 | 166 | ## 2.6.1 - 2016-03-02 167 | 168 | ### Added 169 | 170 | - Nothing. 171 | 172 | ### Deprecated 173 | 174 | - Nothing. 175 | 176 | ### Removed 177 | 178 | - Nothing. 179 | 180 | ### Fixed 181 | 182 | - [#21](https://github.com/zendframework/zend-file/pull/21) updates the codebase 183 | to re-enable tests against zend-progressbar, and fixes static calls inside 184 | `Zend\File\Transfer\Adapter\Http::getProgress` for testing APC and/or 185 | uploadprogress availability to ensure they work correctly. 186 | 187 | ## 2.6.0 - 2016-02-17 188 | 189 | ### Added 190 | 191 | - Nothing. 192 | 193 | ### Deprecated 194 | 195 | - Nothing. 196 | 197 | ### Removed 198 | 199 | - Nothing. 200 | 201 | ### Fixed 202 | 203 | - [#18](https://github.com/zendframework/zend-file/pull/18) updates the codebase 204 | to be forwards compatible with zend-servicemanager and zend-stdlib v3. 205 | 206 | ## 2.5.2 - 2016-02-16 207 | 208 | ### Added 209 | 210 | - Nothing. 211 | 212 | ### Deprecated 213 | 214 | - Nothing. 215 | 216 | ### Removed 217 | 218 | - Nothing. 219 | 220 | ### Fixed 221 | 222 | - [#13](https://github.com/zendframework/zend-file/pull/13) fixes the behavior 223 | of the `Zend\File\Transfer` component when multiple uploads using the same 224 | client name are provided, and no filename filtering is performed; the code now 225 | ensures that unique names are used in such situations. 226 | - [#14](https://github.com/zendframework/zend-file/pull/14) updates the 227 | `FilterPluginManager` to work with the updated zend-filter 2.6.0 changes, 228 | fixing an issue where the zend-file filters were replacing (instead of 229 | merging) with those in the parent zend-filter implementation. 230 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2018, 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-file 2 | 3 | > ## Repository abandoned 2019-12-31 4 | > 5 | > This repository has moved to [laminas/laminas-file](https://github.com/laminas/laminas-file). 6 | 7 | [![Build Status](https://secure.travis-ci.org/zendframework/zend-file.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-file) 8 | [![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-file/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-file?branch=master) 9 | 10 | zend-file provides a `ClassFileLocator` for locating PHP files containing 11 | classes, abstract classes, interfaces, and traits in a specified tree. 12 | 13 | - File issues at https://github.com/zendframework/zend-file/issues 14 | - Documentation is at https://docs.zendframework.com/zend-file/ 15 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zendframework/zend-file", 3 | "description": "Locate PHP classfiles", 4 | "license": "BSD-3-Clause", 5 | "keywords": [ 6 | "zf", 7 | "zendframework", 8 | "file" 9 | ], 10 | "support": { 11 | "docs": "https://docs.zendframework.com/zend-file/", 12 | "issues": "https://github.com/zendframework/zend-file/issues", 13 | "source": "https://github.com/zendframework/zend-file", 14 | "rss": "https://github.com/zendframework/zend-file/releases.atom", 15 | "chat": "https://zendframework-slack.herokuapp.com", 16 | "forum": "https://discourse.zendframework.com/c/questions/components" 17 | }, 18 | "require": { 19 | "php": "^5.6 || ^7.0", 20 | "zendframework/zend-stdlib": "^2.7.7 || ^3.1" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", 24 | "zendframework/zend-coding-standard": "~1.0.0", 25 | "zendframework/zend-filter": "^2.7.2", 26 | "zendframework/zend-i18n": "^2.7.4", 27 | "zendframework/zend-progressbar": "^2.5.2", 28 | "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", 29 | "zendframework/zend-session": "^2.8", 30 | "zendframework/zend-validator": "^2.10.1" 31 | }, 32 | "suggest": { 33 | "zendframework/zend-filter": "Zend\\Filter component", 34 | "zendframework/zend-i18n": "Zend\\I18n component", 35 | "zendframework/zend-validator": "Zend\\Validator component" 36 | }, 37 | "autoload": { 38 | "psr-4": { 39 | "Zend\\File\\": "src/" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "ZendTest\\File\\": "test/" 45 | } 46 | }, 47 | "config": { 48 | "sort-packages": true 49 | }, 50 | "extra": { 51 | "branch-alias": { 52 | "dev-master": "2.8.x-dev", 53 | "dev-develop": "2.9.x-dev" 54 | } 55 | }, 56 | "scripts": { 57 | "check": [ 58 | "@cs-check", 59 | "@test" 60 | ], 61 | "cs-check": "phpcs", 62 | "cs-fix": "phpcbf", 63 | "test": "phpunit --colors=always", 64 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ClassFileLocator.php: -------------------------------------------------------------------------------- 1 | setInfoClass(PhpClassFile::class); 54 | } 55 | 56 | /** 57 | * Filter for files containing PHP classes, interfaces, or abstracts 58 | * 59 | * @return bool 60 | */ 61 | public function accept() 62 | { 63 | $file = $this->getInnerIterator()->current(); 64 | // If we somehow have something other than an SplFileInfo object, just 65 | // return false 66 | if (! $file instanceof SplFileInfo) { 67 | return false; 68 | } 69 | 70 | // If we have a directory, it's not a file, so return false 71 | if (! $file->isFile()) { 72 | return false; 73 | } 74 | 75 | // If not a PHP file, skip 76 | if ($file->getBasename('.php') == $file->getBasename()) { 77 | return false; 78 | } 79 | 80 | $contents = file_get_contents($file->getRealPath()); 81 | $tokens = token_get_all($contents); 82 | $count = count($tokens); 83 | $inFunctionDeclaration = false; 84 | for ($i = 0; $i < $count; $i++) { 85 | $token = $tokens[$i]; 86 | 87 | // single character token found; skip 88 | if (! is_array($token)) { 89 | // If we were in a function declaration, and we encounter an 90 | // opening paren, reset the $inFunctionDeclaration flag. 91 | if ('(' === $token) { 92 | $inFunctionDeclaration = false; 93 | } 94 | 95 | $i++; 96 | continue; 97 | } 98 | 99 | switch ($token[0]) { 100 | case T_NAMESPACE: 101 | // Namespace found; grab it for later 102 | $namespace = ''; 103 | for ($i++; $i < $count; $i++) { 104 | $token = $tokens[$i]; 105 | if (is_string($token)) { 106 | if (';' === $token) { 107 | $saveNamespace = false; 108 | break; 109 | } 110 | if ('{' === $token) { 111 | $saveNamespace = true; 112 | break; 113 | } 114 | continue; 115 | } 116 | list($type, $content, $line) = $token; 117 | switch ($type) { 118 | case T_STRING: 119 | case T_NS_SEPARATOR: 120 | $namespace .= $content; 121 | break; 122 | } 123 | } 124 | if ($saveNamespace) { 125 | $savedNamespace = $namespace; 126 | } 127 | break; 128 | case T_FUNCTION: 129 | // `use function` should not enter function context 130 | if ($i < 2 || ! is_array($tokens[$i - 2]) || $tokens[$i - 2][0] !== T_USE) { 131 | $inFunctionDeclaration = true; 132 | } 133 | break; 134 | case T_TRAIT: 135 | case T_CLASS: 136 | // ignore T_CLASS after T_DOUBLE_COLON to allow PHP >=5.5 FQCN scalar resolution 137 | if ($i > 0 && is_array($tokens[$i - 1]) && $tokens[$i - 1][0] === T_DOUBLE_COLON) { 138 | break; 139 | } 140 | 141 | // Ignore if we are within a function declaration; 142 | // functions are allowed to be named after keywords 143 | // such as class, interface, and trait. 144 | if ($inFunctionDeclaration) { 145 | break; 146 | } 147 | 148 | // ignore anonymous classes on PHP 7.1 and greater 149 | if ($i >= 2 150 | && \is_array($tokens[$i - 1]) 151 | && T_WHITESPACE === $tokens[$i - 1][0] 152 | && \is_array($tokens[$i - 2]) 153 | && T_NEW === $tokens[$i - 2][0] 154 | ) { 155 | break; 156 | } 157 | 158 | // no break 159 | case T_INTERFACE: 160 | // Abstract class, class, interface or trait found 161 | 162 | // Ignore if we are within a function declaration; 163 | // functions are allowed to be named after keywords 164 | // such as class, interface, and trait. 165 | if ($inFunctionDeclaration) { 166 | break; 167 | } 168 | 169 | // Get the classname 170 | for ($i++; $i < $count; $i++) { 171 | $token = $tokens[$i]; 172 | if (is_string($token)) { 173 | continue; 174 | } 175 | list($type, $content, $line) = $token; 176 | if (T_STRING == $type) { 177 | // If a classname was found, set it in the object, and 178 | // return boolean true (found) 179 | if (! isset($namespace) || null === $namespace) { 180 | if (isset($saveNamespace) && $saveNamespace) { 181 | $namespace = $savedNamespace; 182 | } else { 183 | $namespace = null; 184 | } 185 | } 186 | $class = (null === $namespace) ? $content : $namespace . '\\' . $content; 187 | $file->addClass($class); 188 | if ($namespace) { 189 | $file->addNamespace($namespace); 190 | } 191 | $namespace = null; 192 | break; 193 | } 194 | } 195 | break; 196 | default: 197 | break; 198 | } 199 | } 200 | $classes = $file->getClasses(); 201 | if (! empty($classes)) { 202 | return true; 203 | } 204 | // No class-type tokens found; return false 205 | return false; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/Exception/BadMethodCallException.php: -------------------------------------------------------------------------------- 1 | classes; 37 | } 38 | 39 | /** 40 | * Get namespaces 41 | * 42 | * @return array 43 | */ 44 | public function getNamespaces() 45 | { 46 | return $this->namespaces; 47 | } 48 | 49 | /** 50 | * Add class 51 | * 52 | * @param string $class 53 | * @return self 54 | */ 55 | public function addClass($class) 56 | { 57 | $this->classes[] = $class; 58 | return $this; 59 | } 60 | 61 | /** 62 | * Add namespace 63 | * 64 | * @param string $namespace 65 | * @return self 66 | */ 67 | public function addNamespace($namespace) 68 | { 69 | if (in_array($namespace, $this->namespaces)) { 70 | return $this; 71 | } 72 | $this->namespaces[] = $namespace; 73 | return $this; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Transfer/Adapter/AbstractAdapter.php: -------------------------------------------------------------------------------- 1 | array( - Form is the name within the form or, if not set the filename 111 | * name, - Original name of this file 112 | * type, - Mime type of this file 113 | * size, - Filesize in bytes 114 | * tmp_name, - Internally temporary filename for uploaded files 115 | * error, - Error which has occurred 116 | * destination, - New destination for this file 117 | * validators, - Set validator names for this file 118 | * files - Set file names for this file 119 | * )) 120 | * 121 | * @var array 122 | */ 123 | protected $files = []; 124 | 125 | /** 126 | * TMP directory 127 | * @var string 128 | */ 129 | protected $tmpDir; 130 | 131 | /** 132 | * Available options for file transfers 133 | */ 134 | protected $options = [ 135 | 'ignoreNoFile' => false, 136 | 'useByteString' => true, 137 | 'magicFile' => null, 138 | 'detectInfos' => true, 139 | ]; 140 | 141 | /** 142 | * Send file 143 | * 144 | * @param mixed $options 145 | * @return bool 146 | */ 147 | abstract public function send($options = null); 148 | 149 | /** 150 | * Receive file 151 | * 152 | * @param mixed $options 153 | * @return bool 154 | */ 155 | abstract public function receive($options = null); 156 | 157 | /** 158 | * Is file sent? 159 | * 160 | * @param array|string|null $files 161 | * @return bool 162 | */ 163 | abstract public function isSent($files = null); 164 | 165 | /** 166 | * Is file received? 167 | * 168 | * @param array|string|null $files 169 | * @return bool 170 | */ 171 | abstract public function isReceived($files = null); 172 | 173 | /** 174 | * Has a file been uploaded ? 175 | * 176 | * @param array|string|null $files 177 | * @return bool 178 | */ 179 | abstract public function isUploaded($files = null); 180 | 181 | /** 182 | * Has the file been filtered ? 183 | * 184 | * @param array|string|null $files 185 | * @return bool 186 | */ 187 | abstract public function isFiltered($files = null); 188 | 189 | /** 190 | * Adds one or more files 191 | * 192 | * @param string|array $file File to add 193 | * @param string|array $validator Validators to use for this file, must be set before 194 | * @param string|array $filter Filters to use for this file, must be set before 195 | * @return AbstractAdapter 196 | * @throws Exception Not implemented 197 | */ 198 | //abstract public function addFile($file, $validator = null, $filter = null); 199 | 200 | /** 201 | * Returns all set types 202 | * 203 | * @return array List of set types 204 | * @throws Exception Not implemented 205 | */ 206 | //abstract public function getType(); 207 | 208 | /** 209 | * Adds one or more type of files 210 | * 211 | * @param string|array $type Type of files to add 212 | * @param string|array $validator Validators to use for this file, must be set before 213 | * @param string|array $filter Filters to use for this file, must be set before 214 | * @return AbstractAdapter 215 | * @throws Exception Not implemented 216 | */ 217 | //abstract public function addType($type, $validator = null, $filter = null); 218 | 219 | /** 220 | * Returns all set files 221 | * 222 | * @return array List of set files 223 | */ 224 | //abstract public function getFile(); 225 | 226 | /** 227 | * Set the filter plugin manager instance 228 | * 229 | * @param FilterPluginManager $filterManager 230 | * @return AbstractAdapter 231 | */ 232 | public function setFilterManager(FilterPluginManager $filterManager) 233 | { 234 | $this->filterManager = $filterManager; 235 | return $this; 236 | } 237 | 238 | /** 239 | * Get the filter plugin manager instance 240 | * 241 | * @return FilterPluginManager 242 | */ 243 | public function getFilterManager() 244 | { 245 | if (! $this->filterManager instanceof FilterPluginManager) { 246 | $this->setFilterManager(new FilterPluginManager(new ServiceManager())); 247 | } 248 | return $this->filterManager; 249 | } 250 | 251 | /** 252 | * Set the validator plugin manager instance 253 | * 254 | * @param ValidatorPluginManager $validatorManager 255 | * @return AbstractAdapter 256 | */ 257 | public function setValidatorManager(ValidatorPluginManager $validatorManager) 258 | { 259 | $this->validatorManager = $validatorManager; 260 | return $this; 261 | } 262 | 263 | /** 264 | * Get the validator plugin manager instance 265 | * 266 | * @return ValidatorPluginManager 267 | */ 268 | public function getValidatorManager() 269 | { 270 | if (! $this->validatorManager instanceof ValidatorPluginManager) { 271 | if ($this->isServiceManagerV3()) { 272 | $this->setValidatorManager(new ValidatorPluginManager(new ServiceManager())); 273 | } else { 274 | $this->setValidatorManager(new ValidatorPluginManager()); 275 | } 276 | } 277 | return $this->validatorManager; 278 | } 279 | 280 | /** 281 | * Adds a new validator for this class 282 | * 283 | * @param string|Validator\ValidatorInterface $validator Type of validator to add 284 | * @param bool $breakChainOnFailure If the validation chain should stop a failure 285 | * @param string|array $options Options to set for the validator 286 | * @param string|array $files Files to limit this validator to 287 | * @return AbstractAdapter 288 | * @throws Exception\InvalidArgumentException for invalid type 289 | */ 290 | public function addValidator($validator, $breakChainOnFailure = false, $options = null, $files = null) 291 | { 292 | if (is_string($validator)) { 293 | $options = (null !== $options && is_scalar($options)) ? [$options] : $options; 294 | $validator = $this->getValidatorManager()->get($validator, $options); 295 | if (is_array($options) && isset($options['messages'])) { 296 | if (is_array($options['messages'])) { 297 | $validator->setMessages($options['messages']); 298 | } elseif (is_string($options['messages'])) { 299 | $validator->setMessage($options['messages']); 300 | } 301 | 302 | unset($options['messages']); 303 | } 304 | } 305 | 306 | if (! $validator instanceof Validator\ValidatorInterface) { 307 | throw new Exception\InvalidArgumentException( 308 | 'Invalid validator provided to addValidator; ' . 309 | 'must be string or Zend\Validator\ValidatorInterface' 310 | ); 311 | } 312 | 313 | $name = get_class($validator); 314 | 315 | $this->validators[$name] = $validator; 316 | $this->break[$name] = $breakChainOnFailure; 317 | $files = $this->getFiles($files, true, true); 318 | foreach ($files as $file) { 319 | if ($name == 'NotEmpty') { 320 | $temp = $this->files[$file]['validators']; 321 | $this->files[$file]['validators'] = [$name]; 322 | $this->files[$file]['validators'] += $temp; 323 | } else { 324 | $this->files[$file]['validators'][] = $name; 325 | } 326 | 327 | $this->files[$file]['validated'] = false; 328 | } 329 | 330 | return $this; 331 | } 332 | 333 | /** 334 | * Add Multiple validators at once 335 | * 336 | * @param array $validators 337 | * @param string|array $files 338 | * @return AbstractAdapter 339 | * @throws Exception\InvalidArgumentException for invalid type 340 | */ 341 | public function addValidators(array $validators, $files = null) 342 | { 343 | foreach ($validators as $name => $validatorInfo) { 344 | if ($validatorInfo instanceof Validator\ValidatorInterface) { 345 | $this->addValidator($validatorInfo, null, null, $files); 346 | } elseif (is_string($validatorInfo)) { 347 | if (! is_int($name)) { 348 | $this->addValidator($name, null, $validatorInfo, $files); 349 | } else { 350 | $this->addValidator($validatorInfo, null, null, $files); 351 | } 352 | } elseif (is_array($validatorInfo)) { 353 | $argc = count($validatorInfo); 354 | $breakChainOnFailure = false; 355 | $options = []; 356 | if (isset($validatorInfo['validator'])) { 357 | $validator = $validatorInfo['validator']; 358 | if (isset($validatorInfo['breakChainOnFailure'])) { 359 | $breakChainOnFailure = $validatorInfo['breakChainOnFailure']; 360 | } 361 | 362 | if (isset($validatorInfo['options'])) { 363 | $options = $validatorInfo['options']; 364 | } 365 | 366 | $this->addValidator($validator, $breakChainOnFailure, $options, $files); 367 | } else { 368 | if (is_string($name)) { 369 | $validator = $name; 370 | $options = $validatorInfo; 371 | $this->addValidator($validator, $breakChainOnFailure, $options, $files); 372 | } else { 373 | $file = $files; 374 | switch (true) { 375 | case (0 == $argc): 376 | break; 377 | case (1 <= $argc): 378 | $validator = array_shift($validatorInfo); 379 | // fall-through 380 | case (2 <= $argc): 381 | $breakChainOnFailure = array_shift($validatorInfo); 382 | // fall-through 383 | case (3 <= $argc): 384 | $options = array_shift($validatorInfo); 385 | // fall-through 386 | case (4 <= $argc): 387 | if (! empty($validatorInfo)) { 388 | $file = array_shift($validatorInfo); 389 | } 390 | // fall-through 391 | default: 392 | $this->addValidator($validator, $breakChainOnFailure, $options, $file); 393 | break; 394 | } 395 | } 396 | } 397 | } else { 398 | throw new Exception\InvalidArgumentException('Invalid validator passed to addValidators()'); 399 | } 400 | } 401 | 402 | return $this; 403 | } 404 | 405 | /** 406 | * Sets a validator for the class, erasing all previous set 407 | * 408 | * @param array $validators Validators to set 409 | * @param string|array $files Files to limit this validator to 410 | * @return AbstractAdapter 411 | */ 412 | public function setValidators(array $validators, $files = null) 413 | { 414 | $this->clearValidators(); 415 | return $this->addValidators($validators, $files); 416 | } 417 | 418 | /** 419 | * Determine if a given validator has already been registered 420 | * 421 | * @param string $name 422 | * @return bool 423 | */ 424 | public function hasValidator($name) 425 | { 426 | return (false !== $this->getValidatorIdentifier($name)); 427 | } 428 | 429 | /** 430 | * Retrieve individual validator 431 | * 432 | * @param string $name 433 | * @return Validator\ValidatorInterface|null 434 | */ 435 | public function getValidator($name) 436 | { 437 | if (false === ($identifier = $this->getValidatorIdentifier($name))) { 438 | return; 439 | } 440 | return $this->validators[$identifier]; 441 | } 442 | 443 | /** 444 | * Returns all set validators 445 | * 446 | * @param string|array $files (Optional) Returns the validator for this files 447 | * @return null|array List of set validators 448 | */ 449 | public function getValidators($files = null) 450 | { 451 | if ($files === null) { 452 | return $this->validators; 453 | } 454 | 455 | $files = $this->getFiles($files, true, true); 456 | $validators = []; 457 | foreach ($files as $file) { 458 | if (! empty($this->files[$file]['validators'])) { 459 | $validators += $this->files[$file]['validators']; 460 | } 461 | } 462 | 463 | $validators = array_unique($validators); 464 | $result = []; 465 | foreach ($validators as $validator) { 466 | $result[$validator] = $this->validators[$validator]; 467 | } 468 | 469 | return $result; 470 | } 471 | 472 | /** 473 | * Remove an individual validator 474 | * 475 | * @param string $name 476 | * @return AbstractAdapter 477 | */ 478 | public function removeValidator($name) 479 | { 480 | if (false === ($key = $this->getValidatorIdentifier($name))) { 481 | return $this; 482 | } 483 | 484 | unset($this->validators[$key]); 485 | foreach (array_keys($this->files) as $file) { 486 | if (empty($this->files[$file]['validators'])) { 487 | continue; 488 | } 489 | 490 | $index = array_search($key, $this->files[$file]['validators']); 491 | if ($index === false) { 492 | continue; 493 | } 494 | 495 | unset($this->files[$file]['validators'][$index]); 496 | $this->files[$file]['validated'] = false; 497 | } 498 | 499 | return $this; 500 | } 501 | 502 | /** 503 | * Remove all validators 504 | * 505 | * @return AbstractAdapter 506 | */ 507 | public function clearValidators() 508 | { 509 | $this->validators = []; 510 | foreach (array_keys($this->files) as $file) { 511 | $this->files[$file]['validators'] = []; 512 | $this->files[$file]['validated'] = false; 513 | } 514 | 515 | return $this; 516 | } 517 | 518 | /** 519 | * Sets Options for adapters 520 | * 521 | * @param array $options Options to set 522 | * @param array $files (Optional) Files to set the options for 523 | * @return AbstractAdapter 524 | */ 525 | public function setOptions($options = [], $files = null) 526 | { 527 | $file = $this->getFiles($files, false, true); 528 | 529 | if (is_array($options)) { 530 | if (empty($file)) { 531 | $this->options = array_merge($this->options, $options); 532 | } 533 | 534 | foreach ($options as $name => $value) { 535 | foreach ($file as $key => $content) { 536 | switch ($name) { 537 | case 'magicFile': 538 | $this->files[$key]['options'][$name] = (string) $value; 539 | break; 540 | 541 | case 'ignoreNoFile': 542 | case 'useByteString': 543 | case 'detectInfos': 544 | $this->files[$key]['options'][$name] = (bool) $value; 545 | break; 546 | 547 | default: 548 | break; 549 | } 550 | } 551 | } 552 | } 553 | 554 | return $this; 555 | } 556 | 557 | /** 558 | * Returns set options for adapters or files 559 | * 560 | * @param array $files (Optional) Files to return the options for 561 | * @return array Options for given files 562 | */ 563 | public function getOptions($files = null) 564 | { 565 | $file = $this->getFiles($files, false, true); 566 | 567 | foreach ($file as $key => $content) { 568 | if (isset($this->files[$key]['options'])) { 569 | $options[$key] = $this->files[$key]['options']; 570 | } else { 571 | $options[$key] = []; 572 | } 573 | } 574 | 575 | return $options; 576 | } 577 | 578 | /** 579 | * Checks if the files are valid 580 | * 581 | * @param string|array $files (Optional) Files to check 582 | * @return bool True if all checks are valid 583 | */ 584 | public function isValid($files = null) 585 | { 586 | $check = $this->getFiles($files, false, true); 587 | if (empty($check)) { 588 | return false; 589 | } 590 | 591 | $translator = $this->getTranslator(); 592 | $this->messages = []; 593 | $break = false; 594 | foreach ($check as $content) { 595 | if (array_key_exists('validators', $content) && 596 | in_array(Validator\File\Count::class, $content['validators'])) { 597 | $validator = $this->validators[Validator\File\Count::class]; 598 | $count = $content; 599 | if (empty($content['tmp_name'])) { 600 | continue; 601 | } 602 | 603 | if (array_key_exists('destination', $content)) { 604 | $checkit = $content['destination']; 605 | } else { 606 | $checkit = dirname($content['tmp_name']); 607 | } 608 | 609 | $checkit .= DIRECTORY_SEPARATOR . $content['name']; 610 | $validator->addFile($checkit); 611 | } 612 | } 613 | 614 | if (isset($count)) { 615 | if (! $validator->isValid($count['tmp_name'], $count)) { 616 | $this->messages += $validator->getMessages(); 617 | } 618 | } 619 | 620 | foreach ($check as $key => $content) { 621 | $fileerrors = []; 622 | if (array_key_exists('validators', $content) && $content['validated']) { 623 | continue; 624 | } 625 | 626 | if (array_key_exists('validators', $content)) { 627 | foreach ($content['validators'] as $class) { 628 | $validator = $this->validators[$class]; 629 | if (method_exists($validator, 'setTranslator')) { 630 | $validator->setTranslator($translator); 631 | } 632 | 633 | if (($class === 'Zend\Validator\File\Upload') && (empty($content['tmp_name']))) { 634 | $tocheck = $key; 635 | } else { 636 | $tocheck = $content['tmp_name']; 637 | } 638 | 639 | if (! $validator->isValid($tocheck, $content)) { 640 | $fileerrors += $validator->getMessages(); 641 | } 642 | 643 | if (! empty($content['options']['ignoreNoFile']) && (isset($fileerrors['fileUploadErrorNoFile']))) { 644 | unset($fileerrors['fileUploadErrorNoFile']); 645 | break; 646 | } 647 | 648 | if (($class === 'Zend\Validator\File\Upload') && (count($fileerrors) > 0)) { 649 | break; 650 | } 651 | 652 | if (($this->break[$class]) && (count($fileerrors) > 0)) { 653 | $break = true; 654 | break; 655 | } 656 | } 657 | } 658 | 659 | if (count($fileerrors) > 0) { 660 | $this->files[$key]['validated'] = false; 661 | } else { 662 | $this->files[$key]['validated'] = true; 663 | } 664 | 665 | $this->messages += $fileerrors; 666 | if ($break) { 667 | break; 668 | } 669 | } 670 | 671 | if (count($this->messages) > 0) { 672 | return false; 673 | } 674 | 675 | return true; 676 | } 677 | 678 | /** 679 | * Returns found validation messages 680 | * 681 | * @return array 682 | */ 683 | public function getMessages() 684 | { 685 | return $this->messages; 686 | } 687 | 688 | /** 689 | * Retrieve error codes 690 | * 691 | * @return array 692 | */ 693 | public function getErrors() 694 | { 695 | return array_keys($this->messages); 696 | } 697 | 698 | /** 699 | * Are there errors registered? 700 | * 701 | * @return bool 702 | */ 703 | public function hasErrors() 704 | { 705 | return (! empty($this->messages)); 706 | } 707 | 708 | /** 709 | * Adds a new filter for this class 710 | * 711 | * @param string|Filter\FilterInterface $filter Type of filter to add 712 | * @param string|array $options Options to set for the filter 713 | * @param string|array $files Files to limit this filter to 714 | * @return AbstractAdapter 715 | * @throws Exception\InvalidArgumentException for invalid type 716 | */ 717 | public function addFilter($filter, $options = null, $files = null) 718 | { 719 | if (is_string($filter)) { 720 | $options = (null !== $options && is_scalar($options)) ? [$options] : $options; 721 | $filter = $this->getFilterManager()->get($filter, $options); 722 | } 723 | 724 | if (! $filter instanceof Filter\FilterInterface) { 725 | throw new Exception\InvalidArgumentException('Invalid filter specified'); 726 | } 727 | 728 | $class = get_class($filter); 729 | $this->filters[$class] = $filter; 730 | $files = $this->getFiles($files, true, true); 731 | foreach ($files as $file) { 732 | $this->files[$file]['filters'][] = $class; 733 | } 734 | 735 | return $this; 736 | } 737 | 738 | /** 739 | * Add Multiple filters at once 740 | * 741 | * @param array $filters 742 | * @param string|array $files 743 | * @return AbstractAdapter 744 | */ 745 | public function addFilters(array $filters, $files = null) 746 | { 747 | foreach ($filters as $key => $spec) { 748 | if ($spec instanceof Filter\FilterInterface) { 749 | $this->addFilter($spec, null, $files); 750 | continue; 751 | } 752 | 753 | if (is_string($key)) { 754 | $this->addFilter($key, $spec, $files); 755 | continue; 756 | } 757 | 758 | if (is_int($key)) { 759 | if (is_string($spec)) { 760 | $this->addFilter($spec, null, $files); 761 | continue; 762 | } 763 | 764 | if (is_array($spec)) { 765 | if (! array_key_exists('filter', $spec)) { 766 | continue; 767 | } 768 | 769 | $filter = $spec['filter']; 770 | unset($spec['filter']); 771 | $this->addFilter($filter, $spec, $files); 772 | continue; 773 | } 774 | 775 | continue; 776 | } 777 | } 778 | 779 | return $this; 780 | } 781 | 782 | /** 783 | * Sets a filter for the class, erasing all previous set 784 | * 785 | * @param array $filters Filter to set 786 | * @param string|array $files Files to limit this filter to 787 | * @return Filter\AbstractFilter 788 | */ 789 | public function setFilters(array $filters, $files = null) 790 | { 791 | $this->clearFilters(); 792 | return $this->addFilters($filters, $files); 793 | } 794 | 795 | /** 796 | * Determine if a given filter has already been registered 797 | * 798 | * @param string $name 799 | * @return bool 800 | */ 801 | public function hasFilter($name) 802 | { 803 | return (false !== $this->getFilterIdentifier($name)); 804 | } 805 | 806 | /** 807 | * Retrieve individual filter 808 | * 809 | * @param string $name 810 | * @return Filter\FilterInterface|null 811 | */ 812 | public function getFilter($name) 813 | { 814 | if (false === ($identifier = $this->getFilterIdentifier($name))) { 815 | return; 816 | } 817 | 818 | return $this->filters[$identifier]; 819 | } 820 | 821 | /** 822 | * Returns all set filters 823 | * 824 | * @param string|array $files (Optional) Returns the filter for this files 825 | * @return array List of set filters 826 | * @throws Exception\RuntimeException When file not found 827 | */ 828 | public function getFilters($files = null) 829 | { 830 | if ($files === null) { 831 | return $this->filters; 832 | } 833 | 834 | $files = $this->getFiles($files, true, true); 835 | $filters = []; 836 | foreach ($files as $file) { 837 | if (! empty($this->files[$file]['filters'])) { 838 | $filters += $this->files[$file]['filters']; 839 | } 840 | } 841 | 842 | $filters = array_unique($filters); 843 | $result = []; 844 | foreach ($filters as $filter) { 845 | $result[] = $this->filters[$filter]; 846 | } 847 | 848 | return $result; 849 | } 850 | 851 | /** 852 | * Remove an individual filter 853 | * 854 | * @param string $name 855 | * @return AbstractAdapter 856 | */ 857 | public function removeFilter($name) 858 | { 859 | if (false === ($key = $this->getFilterIdentifier($name))) { 860 | return $this; 861 | } 862 | 863 | unset($this->filters[$key]); 864 | foreach (array_keys($this->files) as $file) { 865 | if (empty($this->files[$file]['filters'])) { 866 | continue; 867 | } 868 | 869 | $index = array_search($key, $this->files[$file]['filters']); 870 | if ($index === false) { 871 | continue; 872 | } 873 | 874 | unset($this->files[$file]['filters'][$index]); 875 | } 876 | return $this; 877 | } 878 | 879 | /** 880 | * Remove all filters 881 | * 882 | * @return AbstractAdapter 883 | */ 884 | public function clearFilters() 885 | { 886 | $this->filters = []; 887 | foreach (array_keys($this->files) as $file) { 888 | $this->files[$file]['filters'] = []; 889 | } 890 | 891 | return $this; 892 | } 893 | 894 | /** 895 | * Retrieves the filename of transferred files. 896 | * 897 | * @param string $file (Optional) Element to return the filename for 898 | * @param bool $path (Optional) Should the path also be returned ? 899 | * @return string|array 900 | */ 901 | public function getFileName($file = null, $path = true) 902 | { 903 | $files = $this->getFiles($file, true, true); 904 | $result = []; 905 | $directory = ""; 906 | foreach ($files as $file) { 907 | if (empty($this->files[$file]['name'])) { 908 | continue; 909 | } 910 | 911 | if ($path === true) { 912 | $directory = $this->getDestination($file) . DIRECTORY_SEPARATOR; 913 | } 914 | 915 | $result[$file] = $directory . $this->files[$file]['name']; 916 | } 917 | 918 | if (count($result) == 1) { 919 | return current($result); 920 | } 921 | 922 | return $result; 923 | } 924 | 925 | /** 926 | * Retrieve additional internal file information for files 927 | * 928 | * @param string $file (Optional) File to get information for 929 | * @return array 930 | */ 931 | public function getFileInfo($file = null) 932 | { 933 | return $this->getFiles($file); 934 | } 935 | 936 | /** 937 | * Sets a new destination for the given files 938 | * 939 | * @deprecated Will be changed to be a filter!!! 940 | * @param string $destination New destination directory 941 | * @param string|array $files Files to set the new destination for 942 | * @return AbstractAdapter 943 | * @throws Exception\InvalidArgumentException when the given destination is not a directory or does not exist 944 | */ 945 | public function setDestination($destination, $files = null) 946 | { 947 | $orig = $files; 948 | $destination = rtrim($destination, "/\\"); 949 | if (! is_dir($destination)) { 950 | throw new Exception\InvalidArgumentException('The given destination is not a directory or does not exist'); 951 | } 952 | 953 | if (! is_writable($destination)) { 954 | throw new Exception\InvalidArgumentException('The given destination is not writeable'); 955 | } 956 | 957 | if ($files === null) { 958 | foreach ($this->files as $file => $content) { 959 | $this->files[$file]['destination'] = $destination; 960 | } 961 | } else { 962 | $files = $this->getFiles($files, true, true); 963 | if (empty($files) and is_string($orig)) { 964 | $this->files[$orig]['destination'] = $destination; 965 | } 966 | 967 | foreach ($files as $file) { 968 | $this->files[$file]['destination'] = $destination; 969 | } 970 | } 971 | 972 | return $this; 973 | } 974 | 975 | /** 976 | * Retrieve destination directory value 977 | * 978 | * @param null|string|array $files 979 | * @throws Exception\InvalidArgumentException 980 | * @return null|string|array 981 | */ 982 | public function getDestination($files = null) 983 | { 984 | $orig = $files; 985 | $files = $this->getFiles($files, false, true); 986 | $destinations = []; 987 | if (empty($files) and is_string($orig)) { 988 | if (isset($this->files[$orig]['destination'])) { 989 | $destinations[$orig] = $this->files[$orig]['destination']; 990 | } else { 991 | throw new Exception\InvalidArgumentException( 992 | sprintf('The file transfer adapter can not find "%s"', $orig) 993 | ); 994 | } 995 | } 996 | 997 | foreach ($files as $key => $content) { 998 | if (isset($this->files[$key]['destination'])) { 999 | $destinations[$key] = $this->files[$key]['destination']; 1000 | } else { 1001 | $tmpdir = $this->getTmpDir(); 1002 | $this->setDestination($tmpdir, $key); 1003 | $destinations[$key] = $tmpdir; 1004 | } 1005 | } 1006 | 1007 | if (empty($destinations)) { 1008 | $destinations = $this->getTmpDir(); 1009 | } elseif (count($destinations) == 1) { 1010 | $destinations = current($destinations); 1011 | } 1012 | 1013 | return $destinations; 1014 | } 1015 | 1016 | /** 1017 | * Sets translator to use in helper 1018 | * 1019 | * @param Translator $translator [optional] translator. 1020 | * Default is null, which sets no translator. 1021 | * @param string $textDomain [optional] text domain 1022 | * Default is null, which skips setTranslatorTextDomain 1023 | * @return AbstractAdapter 1024 | */ 1025 | public function setTranslator(Translator $translator = null, $textDomain = null) 1026 | { 1027 | $this->translator = $translator; 1028 | if (null !== $textDomain) { 1029 | $this->setTranslatorTextDomain($textDomain); 1030 | } 1031 | return $this; 1032 | } 1033 | 1034 | /** 1035 | * Retrieve localization translator object 1036 | * 1037 | * @return Translator|null 1038 | */ 1039 | public function getTranslator() 1040 | { 1041 | if ($this->isTranslatorEnabled()) { 1042 | return; 1043 | } 1044 | 1045 | return $this->translator; 1046 | } 1047 | 1048 | /** 1049 | * Checks if the helper has a translator 1050 | * 1051 | * @return bool 1052 | */ 1053 | public function hasTranslator() 1054 | { 1055 | return (bool) $this->getTranslator(); 1056 | } 1057 | 1058 | /** 1059 | * Indicate whether or not translation should be enabled 1060 | * 1061 | * @param bool $flag 1062 | * @return AbstractAdapter 1063 | */ 1064 | public function setTranslatorEnabled($flag = true) 1065 | { 1066 | $this->translatorEnabled = (bool) $flag; 1067 | return $this; 1068 | } 1069 | 1070 | /** 1071 | * Is translation enabled? 1072 | * 1073 | * @return bool 1074 | */ 1075 | public function isTranslatorEnabled() 1076 | { 1077 | return $this->translatorEnabled; 1078 | } 1079 | 1080 | /** 1081 | * Set translation text domain 1082 | * 1083 | * @param string $textDomain 1084 | * @return AbstractAdapter 1085 | */ 1086 | public function setTranslatorTextDomain($textDomain = 'default') 1087 | { 1088 | $this->translatorTextDomain = $textDomain; 1089 | return $this; 1090 | } 1091 | 1092 | /** 1093 | * Return the translation text domain 1094 | * 1095 | * @return string 1096 | */ 1097 | public function getTranslatorTextDomain() 1098 | { 1099 | return $this->translatorTextDomain; 1100 | } 1101 | 1102 | /** 1103 | * Returns the hash for a given file 1104 | * 1105 | * @param string $hash Hash algorithm to use 1106 | * @param string|array $files Files to return the hash for 1107 | * @return string|array Hashstring 1108 | * @throws Exception\InvalidArgumentException On unknown hash algorithm 1109 | */ 1110 | public function getHash($hash = 'crc32', $files = null) 1111 | { 1112 | if (! in_array($hash, hash_algos())) { 1113 | throw new Exception\InvalidArgumentException('Unknown hash algorithm'); 1114 | } 1115 | 1116 | $files = $this->getFiles($files); 1117 | $result = []; 1118 | foreach ($files as $key => $value) { 1119 | if (file_exists($value['name'])) { 1120 | $result[$key] = hash_file($hash, $value['name']); 1121 | } elseif (file_exists($value['tmp_name'])) { 1122 | $result[$key] = hash_file($hash, $value['tmp_name']); 1123 | } elseif (empty($value['options']['ignoreNoFile'])) { 1124 | throw new Exception\InvalidArgumentException("The file '{$value['name']}' does not exist"); 1125 | } 1126 | } 1127 | 1128 | if (count($result) == 1) { 1129 | return current($result); 1130 | } 1131 | 1132 | return $result; 1133 | } 1134 | 1135 | /** 1136 | * Returns the real filesize of the file 1137 | * 1138 | * @param string|array $files Files to get the filesize from 1139 | * @return string|array Filesize 1140 | * @throws Exception\InvalidArgumentException When the file does not exist 1141 | */ 1142 | public function getFileSize($files = null) 1143 | { 1144 | $files = $this->getFiles($files); 1145 | $result = []; 1146 | foreach ($files as $key => $value) { 1147 | if (file_exists($value['name']) || file_exists($value['tmp_name'])) { 1148 | if ($value['options']['useByteString']) { 1149 | $result[$key] = static::toByteString($value['size']); 1150 | } else { 1151 | $result[$key] = $value['size']; 1152 | } 1153 | } elseif (empty($value['options']['ignoreNoFile'])) { 1154 | throw new Exception\InvalidArgumentException("The file '{$value['name']}' does not exist"); 1155 | } else { 1156 | continue; 1157 | } 1158 | } 1159 | 1160 | if (count($result) == 1) { 1161 | return current($result); 1162 | } 1163 | 1164 | return $result; 1165 | } 1166 | 1167 | /** 1168 | * Internal method to detect the size of a file 1169 | * 1170 | * @param array $value File infos 1171 | * @return string Filesize of given file 1172 | */ 1173 | protected function detectFileSize($value) 1174 | { 1175 | if (file_exists($value['name'])) { 1176 | $filename = $value['name']; 1177 | } elseif (file_exists($value['tmp_name'])) { 1178 | $filename = $value['tmp_name']; 1179 | } else { 1180 | return; 1181 | } 1182 | 1183 | ErrorHandler::start(); 1184 | $filesize = filesize($filename); 1185 | $return = ErrorHandler::stop(); 1186 | if ($return instanceof ErrorException) { 1187 | $filesize = 0; 1188 | } 1189 | 1190 | return sprintf("%u", $filesize); 1191 | } 1192 | 1193 | /** 1194 | * Returns the real mimetype of the file 1195 | * Uses fileinfo, when not available mime_magic and as last fallback a manual given mimetype 1196 | * 1197 | * @param string|array $files Files to get the mimetype from 1198 | * @return string|array MimeType 1199 | * @throws Exception\InvalidArgumentException When the file does not exist 1200 | */ 1201 | public function getMimeType($files = null) 1202 | { 1203 | $files = $this->getFiles($files); 1204 | $result = []; 1205 | foreach ($files as $key => $value) { 1206 | if (file_exists($value['name']) || file_exists($value['tmp_name'])) { 1207 | $result[$key] = $value['type']; 1208 | } elseif (empty($value['options']['ignoreNoFile'])) { 1209 | throw new Exception\InvalidArgumentException("the file '{$value['name']}' does not exist"); 1210 | } else { 1211 | continue; 1212 | } 1213 | } 1214 | 1215 | if (count($result) == 1) { 1216 | return current($result); 1217 | } 1218 | 1219 | return $result; 1220 | } 1221 | 1222 | /** 1223 | * Internal method to detect the mime type of a file 1224 | * 1225 | * @param array $value File infos 1226 | * @return string Mimetype of given file 1227 | */ 1228 | protected function detectMimeType($value) 1229 | { 1230 | if (file_exists($value['name'])) { 1231 | $file = $value['name']; 1232 | } elseif (file_exists($value['tmp_name'])) { 1233 | $file = $value['tmp_name']; 1234 | } else { 1235 | return; 1236 | } 1237 | 1238 | if (class_exists('finfo', false)) { 1239 | if (! empty($value['options']['magicFile'])) { 1240 | ErrorHandler::start(); 1241 | $mime = finfo_open(FILEINFO_MIME_TYPE, $value['options']['magicFile']); 1242 | ErrorHandler::stop(); 1243 | } 1244 | 1245 | if (empty($mime)) { 1246 | ErrorHandler::start(); 1247 | $mime = finfo_open(FILEINFO_MIME_TYPE); 1248 | ErrorHandler::stop(); 1249 | } 1250 | 1251 | if (! empty($mime)) { 1252 | $result = finfo_file($mime, $file); 1253 | } 1254 | 1255 | unset($mime); 1256 | } 1257 | 1258 | if (empty($result) && (function_exists('mime_content_type') 1259 | && ini_get('mime_magic.magicfile'))) { 1260 | $result = mime_content_type($file); 1261 | } 1262 | 1263 | if (empty($result)) { 1264 | $result = 'application/octet-stream'; 1265 | } 1266 | 1267 | return $result; 1268 | } 1269 | 1270 | /** 1271 | * Returns the formatted size 1272 | * 1273 | * @param int $size 1274 | * @return string 1275 | */ 1276 | protected static function toByteString($size) 1277 | { 1278 | $sizes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 1279 | for ($i = 0; $size >= 1024 && $i < 9; $i++) { 1280 | $size /= 1024; 1281 | } 1282 | 1283 | return round($size, 2) . $sizes[$i]; 1284 | } 1285 | 1286 | /** 1287 | * Internal function to filter all given files 1288 | * 1289 | * @param string|array $files (Optional) Files to check 1290 | * @return bool False on error 1291 | */ 1292 | protected function filter($files = null) 1293 | { 1294 | $check = $this->getFiles($files); 1295 | foreach ($check as $name => $content) { 1296 | if (array_key_exists('filters', $content)) { 1297 | foreach ($content['filters'] as $class) { 1298 | $filter = $this->filters[$class]; 1299 | try { 1300 | $result = $filter->filter($this->getFileName($name)); 1301 | 1302 | $this->files[$name]['destination'] = dirname($result); 1303 | $this->files[$name]['name'] = basename($result); 1304 | } catch (FilterException\ExceptionInterface $e) { 1305 | $this->messages += [$e->getMessage()]; 1306 | } 1307 | } 1308 | } 1309 | } 1310 | 1311 | if (count($this->messages) > 0) { 1312 | return false; 1313 | } 1314 | 1315 | return true; 1316 | } 1317 | 1318 | /** 1319 | * Determine system TMP directory and detect if we have read access 1320 | * 1321 | * @return string 1322 | * @throws Exception\RuntimeException if unable to determine directory 1323 | */ 1324 | protected function getTmpDir() 1325 | { 1326 | if (null === $this->tmpDir) { 1327 | $tmpdir = []; 1328 | if (function_exists('sys_get_temp_dir')) { 1329 | $tmpdir[] = sys_get_temp_dir(); 1330 | } 1331 | 1332 | if (! empty($_ENV['TMP'])) { 1333 | $tmpdir[] = realpath($_ENV['TMP']); 1334 | } 1335 | 1336 | if (! empty($_ENV['TMPDIR'])) { 1337 | $tmpdir[] = realpath($_ENV['TMPDIR']); 1338 | } 1339 | 1340 | if (! empty($_ENV['TEMP'])) { 1341 | $tmpdir[] = realpath($_ENV['TEMP']); 1342 | } 1343 | 1344 | $upload = ini_get('upload_tmp_dir'); 1345 | if ($upload) { 1346 | $tmpdir[] = realpath($upload); 1347 | } 1348 | 1349 | foreach ($tmpdir as $directory) { 1350 | if ($this->isPathWriteable($directory)) { 1351 | $this->tmpDir = $directory; 1352 | } 1353 | } 1354 | 1355 | if (empty($this->tmpDir)) { 1356 | // Attemp to detect by creating a temporary file 1357 | $tempFile = tempnam(md5(uniqid(rand(), true)), ''); 1358 | if ($tempFile) { 1359 | $this->tmpDir = realpath(dirname($tempFile)); 1360 | unlink($tempFile); 1361 | } else { 1362 | throw new Exception\RuntimeException('Could not determine a temporary directory'); 1363 | } 1364 | } 1365 | 1366 | $this->tmpDir = rtrim($this->tmpDir, "/\\"); 1367 | } 1368 | return $this->tmpDir; 1369 | } 1370 | 1371 | /** 1372 | * Tries to detect if we can read and write to the given path 1373 | * 1374 | * @param string $path 1375 | * @return bool 1376 | */ 1377 | protected function isPathWriteable($path) 1378 | { 1379 | $tempFile = rtrim($path, "/\\"); 1380 | $tempFile .= '/' . 'test.1'; 1381 | 1382 | ErrorHandler::start(); 1383 | $result = file_put_contents($tempFile, 'TEST'); 1384 | ErrorHandler::stop(); 1385 | 1386 | if ($result == false) { 1387 | return false; 1388 | } 1389 | 1390 | ErrorHandler::start(); 1391 | $result = unlink($tempFile); 1392 | ErrorHandler::stop(); 1393 | 1394 | if ($result == false) { 1395 | return false; 1396 | } 1397 | 1398 | return true; 1399 | } 1400 | 1401 | /** 1402 | * Returns found files based on internal file array and given files 1403 | * 1404 | * @param string|array $files (Optional) Files to return 1405 | * @param bool $names (Optional) Returns only names on true, else complete info 1406 | * @param bool $noexception (Optional) Allows throwing an exception, otherwise returns an empty array 1407 | * @return array Found files 1408 | * @throws Exception\RuntimeException On false filename 1409 | */ 1410 | protected function getFiles($files, $names = false, $noexception = false) 1411 | { 1412 | $check = []; 1413 | 1414 | if (is_string($files)) { 1415 | $files = [$files]; 1416 | } 1417 | 1418 | if (is_array($files)) { 1419 | foreach ($files as $find) { 1420 | $found = []; 1421 | foreach ($this->files as $file => $content) { 1422 | if (! isset($content['name'])) { 1423 | continue; 1424 | } 1425 | 1426 | if (($content['name'] === $find) && isset($content['multifiles'])) { 1427 | foreach ($content['multifiles'] as $multifile) { 1428 | $found[] = $multifile; 1429 | } 1430 | break; 1431 | } 1432 | 1433 | if ($file === $find) { 1434 | $found[] = $file; 1435 | break; 1436 | } 1437 | 1438 | if ($content['name'] === $find) { 1439 | $found[] = $file; 1440 | break; 1441 | } 1442 | } 1443 | 1444 | if (empty($found)) { 1445 | if ($noexception !== false) { 1446 | return []; 1447 | } 1448 | 1449 | throw new Exception\RuntimeException(sprintf('The file transfer adapter can not find "%s"', $find)); 1450 | } 1451 | 1452 | foreach ($found as $checked) { 1453 | $check[$checked] = $this->files[$checked]; 1454 | } 1455 | } 1456 | } 1457 | 1458 | if ($files === null) { 1459 | $check = $this->files; 1460 | $keys = array_keys($check); 1461 | foreach ($keys as $key) { 1462 | if (isset($check[$key]['multifiles'])) { 1463 | unset($check[$key]); 1464 | } 1465 | } 1466 | } 1467 | 1468 | if ($names) { 1469 | $check = array_keys($check); 1470 | } 1471 | 1472 | return $check; 1473 | } 1474 | 1475 | /** 1476 | * Retrieve internal identifier for a named validator 1477 | * 1478 | * @param string $name 1479 | * @return string 1480 | */ 1481 | protected function getValidatorIdentifier($name) 1482 | { 1483 | if (array_key_exists($name, $this->validators)) { 1484 | return $name; 1485 | } 1486 | 1487 | foreach (array_keys($this->validators) as $test) { 1488 | if (preg_match('/' . preg_quote($name) . '$/i', $test)) { 1489 | return $test; 1490 | } 1491 | } 1492 | 1493 | return false; 1494 | } 1495 | 1496 | /** 1497 | * Retrieve internal identifier for a named filter 1498 | * 1499 | * @param string $name 1500 | * @return string 1501 | */ 1502 | protected function getFilterIdentifier($name) 1503 | { 1504 | if (array_key_exists($name, $this->filters)) { 1505 | return $name; 1506 | } 1507 | 1508 | foreach (array_keys($this->filters) as $test) { 1509 | if (preg_match('/' . preg_quote($name) . '$/i', $test)) { 1510 | return $test; 1511 | } 1512 | } 1513 | 1514 | return false; 1515 | } 1516 | 1517 | /** 1518 | * Is the service manager component v3? 1519 | * 1520 | * This is needed until zend-validator is updated, to ensure we instantiate 1521 | * the validator plugin manager properly. 1522 | * 1523 | * @return bool 1524 | */ 1525 | private function isServiceManagerV3() 1526 | { 1527 | $r = new ReflectionClass(ServiceManager::class); 1528 | return ! $r->hasProperty('invokableClasses'); 1529 | } 1530 | } 1531 | -------------------------------------------------------------------------------- /src/Transfer/Adapter/FilterPluginManager.php: -------------------------------------------------------------------------------- 1 | File\Decrypt::class, 27 | 'Decrypt' => File\Decrypt::class, 28 | 'encrypt' => File\Encrypt::class, 29 | 'Encrypt' => File\Encrypt::class, 30 | 'lowercase' => File\LowerCase::class, 31 | 'lowerCase' => File\LowerCase::class, 32 | 'LowerCase' => File\LowerCase::class, 33 | 'rename' => File\Rename::class, 34 | 'Rename' => File\Rename::class, 35 | 'uppercase' => File\UpperCase::class, 36 | 'upperCase' => File\UpperCase::class, 37 | 'UpperCase' => File\UpperCase::class, 38 | ]; 39 | 40 | /** 41 | * Constructor 42 | * 43 | * Merges default aliases pertinent to this plugin manager with those 44 | * defined in the parent filter plugin manager. 45 | * 46 | * @param null|\Zend\ServiceManager\ConfigInterface|\Interop\Container\ContainerInterface $configOrContainerInstance 47 | * @param array $v3config If $configOrContainerInstance is a container, this 48 | * value will be passed to the parent constructor. 49 | */ 50 | public function __construct($configOrContainerInstance = null, array $v3config = []) 51 | { 52 | $this->aliases = array_merge($this->defaultFileFilterAliases, $this->aliases); 53 | parent::__construct($configOrContainerInstance, $v3config); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Transfer/Adapter/Http.php: -------------------------------------------------------------------------------- 1 | setOptions($options); 41 | $this->prepareFiles(); 42 | $this->addValidator('Upload', false, $this->files); 43 | } 44 | 45 | /** 46 | * Sets a validator for the class, erasing all previous set 47 | * 48 | * @param array $validators Validator to set 49 | * @param string|array $files Files to limit this validator to 50 | * @return AbstractAdapter 51 | */ 52 | public function setValidators(array $validators, $files = null) 53 | { 54 | $this->clearValidators(); 55 | return $this->addValidators($validators, $files); 56 | } 57 | 58 | /** 59 | * Remove an individual validator 60 | * 61 | * @param string $name 62 | * @return AbstractAdapter 63 | */ 64 | public function removeValidator($name) 65 | { 66 | if ($name == 'Upload') { 67 | return $this; 68 | } 69 | 70 | return parent::removeValidator($name); 71 | } 72 | 73 | /** 74 | * Clear the validators 75 | * 76 | * @return AbstractAdapter 77 | */ 78 | public function clearValidators() 79 | { 80 | parent::clearValidators(); 81 | $this->addValidator('Upload', false, $this->files); 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Send the file to the client (Download) 88 | * 89 | * @param string|array $options Options for the file(s) to send 90 | * @return void 91 | * @throws Exception\BadMethodCallException Not implemented 92 | */ 93 | public function send($options = null) 94 | { 95 | throw new Exception\BadMethodCallException('Method not implemented'); 96 | } 97 | 98 | /** 99 | * Checks if the files are valid 100 | * 101 | * @param string|array $files (Optional) Files to check 102 | * @return bool True if all checks are valid 103 | */ 104 | public function isValid($files = null) 105 | { 106 | // Workaround for WebServer not conforming HTTP and omitting CONTENT_LENGTH 107 | $content = 0; 108 | if (isset($_SERVER['CONTENT_LENGTH'])) { 109 | $content = $_SERVER['CONTENT_LENGTH']; 110 | } elseif (! empty($_POST)) { 111 | $content = serialize($_POST); 112 | } 113 | 114 | // Workaround for a PHP error returning empty $_FILES when form data exceeds php settings 115 | if (empty($this->files) && ($content > 0)) { 116 | if (is_array($files)) { 117 | $files = current($files); 118 | } 119 | 120 | $temp = [$files => [ 121 | 'name' => $files, 122 | 'error' => 1]]; 123 | $validator = $this->validators[Validator\File\Upload::class]; 124 | $validator->setTranslator($this->getTranslator()) 125 | ->setFiles($temp) 126 | ->isValid($files, null); 127 | $this->messages += $validator->getMessages(); 128 | return false; 129 | } 130 | 131 | return parent::isValid($files); 132 | } 133 | 134 | /** 135 | * Receive the file from the client (Upload) 136 | * 137 | * @param string|array $files (Optional) Files to receive 138 | * @return bool 139 | */ 140 | public function receive($files = null) 141 | { 142 | if (! $this->isValid($files)) { 143 | return false; 144 | } 145 | 146 | $check = $this->getFiles($files); 147 | foreach ($check as $file => $content) { 148 | if (! $content['received']) { 149 | $directory = ''; 150 | $destination = $this->getDestination($file); 151 | if ($destination !== null) { 152 | $directory = $destination . DIRECTORY_SEPARATOR; 153 | } 154 | 155 | $filename = $directory . $content['name']; 156 | $rename = $this->getFilter('Rename'); 157 | if ($rename !== null) { 158 | $tmp = $rename->getNewName($content['tmp_name']); 159 | if ($tmp != $content['tmp_name']) { 160 | $filename = $tmp; 161 | } 162 | 163 | if (dirname($filename) == '.') { 164 | $filename = $directory . $filename; 165 | } 166 | 167 | $key = array_search(get_class($rename), $this->files[$file]['filters']); 168 | unset($this->files[$file]['filters'][$key]); 169 | } 170 | 171 | // Should never return false when it's tested by the upload validator 172 | if (! move_uploaded_file($content['tmp_name'], $filename)) { 173 | if ($content['options']['ignoreNoFile']) { 174 | $this->files[$file]['received'] = true; 175 | $this->files[$file]['filtered'] = true; 176 | continue; 177 | } 178 | 179 | $this->files[$file]['received'] = false; 180 | return false; 181 | } 182 | 183 | if ($rename !== null) { 184 | $this->files[$file]['destination'] = dirname($filename); 185 | $this->files[$file]['name'] = basename($filename); 186 | } 187 | 188 | $this->files[$file]['tmp_name'] = $filename; 189 | $this->files[$file]['received'] = true; 190 | } 191 | 192 | if (! $content['filtered']) { 193 | if (! $this->filter($file)) { 194 | $this->files[$file]['filtered'] = false; 195 | return false; 196 | } 197 | 198 | $this->files[$file]['filtered'] = true; 199 | } 200 | } 201 | 202 | return true; 203 | } 204 | 205 | /** 206 | * Checks if the file was already sent 207 | * 208 | * @param string|array $files Files to check 209 | * @return bool 210 | * @throws Exception\BadMethodCallException Not implemented 211 | */ 212 | public function isSent($files = null) 213 | { 214 | throw new Exception\BadMethodCallException('Method not implemented'); 215 | } 216 | 217 | /** 218 | * Checks if the file was already received 219 | * 220 | * @param string|array $files (Optional) Files to check 221 | * @return bool 222 | */ 223 | public function isReceived($files = null) 224 | { 225 | $files = $this->getFiles($files, false, true); 226 | if (empty($files)) { 227 | return false; 228 | } 229 | 230 | foreach ($files as $content) { 231 | if ($content['received'] !== true) { 232 | return false; 233 | } 234 | } 235 | 236 | return true; 237 | } 238 | 239 | /** 240 | * Checks if the file was already filtered 241 | * 242 | * @param string|array $files (Optional) Files to check 243 | * @return bool 244 | */ 245 | public function isFiltered($files = null) 246 | { 247 | $files = $this->getFiles($files, false, true); 248 | if (empty($files)) { 249 | return false; 250 | } 251 | 252 | foreach ($files as $content) { 253 | if ($content['filtered'] !== true) { 254 | return false; 255 | } 256 | } 257 | 258 | return true; 259 | } 260 | 261 | /** 262 | * Has a file been uploaded ? 263 | * 264 | * @param array|string|null $files 265 | * @return bool 266 | */ 267 | public function isUploaded($files = null) 268 | { 269 | $files = $this->getFiles($files, false, true); 270 | if (empty($files)) { 271 | return false; 272 | } 273 | 274 | foreach ($files as $file) { 275 | if (empty($file['name'])) { 276 | return false; 277 | } 278 | } 279 | 280 | return true; 281 | } 282 | 283 | /** 284 | * Returns the actual progress of file up-/downloads 285 | * 286 | * @param string|array $id The upload to get the progress for 287 | * @return array|null 288 | * @throws Exception\PhpEnvironmentException whether APC nor UploadProgress extension installed 289 | * @throws Exception\RuntimeException 290 | */ 291 | public static function getProgress($id = null) 292 | { 293 | if (! self::isApcAvailable() && ! self::isUploadProgressAvailable()) { 294 | throw new Exception\PhpEnvironmentException('Neither APC nor UploadProgress extension installed'); 295 | } 296 | 297 | $session = 'Zend\File\Transfer\Adapter\Http\ProgressBar'; 298 | $status = [ 299 | 'total' => 0, 300 | 'current' => 0, 301 | 'rate' => 0, 302 | 'message' => '', 303 | 'done' => false 304 | ]; 305 | 306 | if (is_array($id)) { 307 | if (isset($id['progress'])) { 308 | $adapter = $id['progress']; 309 | } 310 | 311 | if (isset($id['session'])) { 312 | $session = $id['session']; 313 | } 314 | 315 | if (isset($id['id'])) { 316 | $id = $id['id']; 317 | } else { 318 | unset($id); 319 | } 320 | } 321 | 322 | if (! empty($id) && (($id instanceof Adapter\AbstractAdapter) || ($id instanceof ProgressBar\ProgressBar))) { 323 | $adapter = $id; 324 | unset($id); 325 | } 326 | 327 | if (empty($id)) { 328 | if (! isset($_GET['progress_key'])) { 329 | $status['message'] = 'No upload in progress'; 330 | $status['done'] = true; 331 | } else { 332 | $id = $_GET['progress_key']; 333 | } 334 | } 335 | 336 | if (! empty($id)) { 337 | if (self::isApcAvailable()) { 338 | $call = call_user_func(static::$callbackApc, ini_get('apc.rfc1867_prefix') . $id); 339 | if (is_array($call)) { 340 | $status = $call + $status; 341 | } 342 | } elseif (self::isUploadProgressAvailable()) { 343 | $call = call_user_func(static::$callbackUploadProgress, $id); 344 | if (is_array($call)) { 345 | $status = $call + $status; 346 | $status['total'] = isset($status['bytes_total']) ? $status['bytes_total'] : $status['total']; 347 | $status['current'] = isset($status['bytes_uploaded']) 348 | ? $status['bytes_uploaded'] 349 | : $status['current']; 350 | $status['rate'] = isset($status['speed_average']) ? $status['speed_average'] : $status['rate']; 351 | if ($status['total'] == $status['current']) { 352 | $status['done'] = true; 353 | } 354 | } 355 | } 356 | 357 | if (! is_array($call)) { 358 | $status['done'] = true; 359 | $status['message'] = 'Failure while retrieving the upload progress'; 360 | } elseif (! empty($status['cancel_upload'])) { 361 | $status['done'] = true; 362 | $status['message'] = 'The upload has been canceled'; 363 | } else { 364 | $status['message'] = static::toByteString($status['current']) . " - " 365 | . static::toByteString($status['total']); 366 | } 367 | 368 | $status['id'] = $id; 369 | } 370 | 371 | if (isset($adapter) && isset($status['id'])) { 372 | if ($adapter instanceof Adapter\AbstractAdapter) { 373 | $adapter = new ProgressBar\ProgressBar($adapter, 0, $status['total'], $session); 374 | } 375 | 376 | if (! ($adapter instanceof ProgressBar\ProgressBar)) { 377 | throw new Exception\RuntimeException('Unknown Adapter given'); 378 | } 379 | 380 | if ($status['done']) { 381 | $adapter->finish(); 382 | } else { 383 | $adapter->update($status['current'], $status['message']); 384 | } 385 | 386 | $status['progress'] = $adapter; 387 | } 388 | 389 | return $status; 390 | } 391 | 392 | /** 393 | * Checks the APC extension for progress information 394 | * 395 | * @return bool 396 | */ 397 | public static function isApcAvailable() 398 | { 399 | return (bool) ini_get('apc.enabled') && (bool) ini_get('apc.rfc1867') && is_callable(static::$callbackApc); 400 | } 401 | 402 | /** 403 | * Checks the UploadProgress extension for progress information 404 | * 405 | * @return bool 406 | */ 407 | public static function isUploadProgressAvailable() 408 | { 409 | return is_callable(static::$callbackUploadProgress); 410 | } 411 | 412 | /** 413 | * Prepare the $_FILES array to match the internal syntax of one file per entry 414 | * 415 | * @return Http 416 | */ 417 | protected function prepareFiles() 418 | { 419 | $this->files = []; 420 | foreach ($_FILES as $form => $content) { 421 | if (is_array($content['name'])) { 422 | foreach ($content as $param => $file) { 423 | foreach ($file as $number => $target) { 424 | $this->files[$form . '_' . $number . '_'][$param] = $target; 425 | $this->files[$form]['multifiles'][$number] = $form . '_' . $number . '_'; 426 | } 427 | } 428 | 429 | $this->files[$form]['name'] = $form; 430 | foreach ($this->files[$form]['multifiles'] as $key => $value) { 431 | if ($this->files[$value]['tmp_name'] !== '') { 432 | $this->files[$value]['name'] = basename($this->files[$value]['tmp_name']) 433 | . '_' . $this->files[$value]['name']; 434 | } 435 | $this->files[$value]['options'] = $this->options; 436 | $this->files[$value]['validated'] = false; 437 | $this->files[$value]['received'] = false; 438 | $this->files[$value]['filtered'] = false; 439 | 440 | $mimetype = $this->detectMimeType($this->files[$value]); 441 | $this->files[$value]['type'] = $mimetype; 442 | 443 | $filesize = $this->detectFileSize($this->files[$value]); 444 | $this->files[$value]['size'] = $filesize; 445 | 446 | if ($this->options['detectInfos']) { 447 | $_FILES[$form]['type'][$key] = $mimetype; 448 | $_FILES[$form]['size'][$key] = $filesize; 449 | } 450 | } 451 | } else { 452 | $this->files[$form] = $content; 453 | if ($this->files[$form]['tmp_name'] !== '') { 454 | $this->files[$form]['name'] = basename($this->files[$form]['tmp_name']) 455 | . '_' . $this->files[$form]['name']; 456 | } 457 | $this->files[$form]['options'] = $this->options; 458 | $this->files[$form]['validated'] = false; 459 | $this->files[$form]['received'] = false; 460 | $this->files[$form]['filtered'] = false; 461 | 462 | $mimetype = $this->detectMimeType($this->files[$form]); 463 | $this->files[$form]['type'] = $mimetype; 464 | 465 | $filesize = $this->detectFileSize($this->files[$form]); 466 | $this->files[$form]['size'] = $filesize; 467 | 468 | if ($this->options['detectInfos']) { 469 | $_FILES[$form]['type'] = $mimetype; 470 | $_FILES[$form]['size'] = $filesize; 471 | } 472 | } 473 | } 474 | 475 | return $this; 476 | } 477 | } 478 | -------------------------------------------------------------------------------- /src/Transfer/Adapter/ValidatorPluginManager.php: -------------------------------------------------------------------------------- 1 | File\Count::class, 22 | 'Count' => File\Count::class, 23 | 'crc32' => File\Crc32::class, 24 | 'Crc32' => File\Crc32::class, 25 | 'CRC32' => File\Crc32::class, 26 | 'excludeextension' => File\ExcludeExtension::class, 27 | 'excludeExtension' => File\ExcludeExtension::class, 28 | 'ExcludeExtension' => File\ExcludeExtension::class, 29 | 'excludemimetype' => File\ExcludeMimeType::class, 30 | 'excludeMimeType' => File\ExcludeMimeType::class, 31 | 'ExcludeMimeType' => File\ExcludeMimeType::class, 32 | 'exists' => File\Exists::class, 33 | 'Exists' => File\Exists::class, 34 | 'extension' => File\Extension::class, 35 | 'Extension' => File\Extension::class, 36 | 'filessize' => File\FilesSize::class, 37 | 'filesSize' => File\FilesSize::class, 38 | 'FilesSize' => File\FilesSize::class, 39 | 'hash' => File\Hash::class, 40 | 'Hash' => File\Hash::class, 41 | 'imagesize' => File\ImageSize::class, 42 | 'imageSize' => File\ImageSize::class, 43 | 'ImageSize' => File\ImageSize::class, 44 | 'iscompressed' => File\IsCompressed::class, 45 | 'isCompressed' => File\IsCompressed::class, 46 | 'IsCompressed' => File\IsCompressed::class, 47 | 'isimage' => File\IsImage::class, 48 | 'isImage' => File\IsImage::class, 49 | 'IsImage' => File\IsImage::class, 50 | 'md5' => File\Md5::class, 51 | 'Md5' => File\Md5::class, 52 | 'MD5' => File\Md5::class, 53 | 'mimetype' => File\MimeType::class, 54 | 'mimeType' => File\MimeType::class, 55 | 'MimeType' => File\MimeType::class, 56 | 'notexists' => File\NotExists::class, 57 | 'notExists' => File\NotExists::class, 58 | 'NotExists' => File\NotExists::class, 59 | 'sha1' => File\Sha1::class, 60 | 'Sha1' => File\Sha1::class, 61 | 'SHA1' => File\Sha1::class, 62 | 'size' => File\Size::class, 63 | 'Size' => File\Size::class, 64 | 'upload' => File\Upload::class, 65 | 'Upload' => File\Upload::class, 66 | 'wordcount' => File\WordCount::class, 67 | 'wordCount' => File\WordCount::class, 68 | 'WordCount' => File\WordCount::class, 69 | ]; 70 | 71 | /** 72 | * Constructor 73 | * 74 | * Merges default aliases pertinent to this plugin manager with those 75 | * defined in the parent filter plugin manager. 76 | * 77 | * @param null|\Zend\ServiceManager\ConfigInterface|\Interop\Container\ContainerInterface $configOrContainerInstance 78 | * @param array $v3config If $configOrContainerInstance is a container, this 79 | * value will be passed to the parent constructor. 80 | */ 81 | public function __construct($configOrContainerInstance = null, array $v3config = []) 82 | { 83 | $this->aliases = array_merge($this->defaultFileValidationAliases, $this->aliases); 84 | parent::__construct($configOrContainerInstance, $v3config); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Transfer/Exception/BadMethodCallException.php: -------------------------------------------------------------------------------- 1 | setAdapter($adapter, $direction, $options); 37 | } 38 | 39 | /** 40 | * Sets a new adapter 41 | * 42 | * @param string $adapter Adapter to use 43 | * @param bool $direction OPTIONAL False means Download, true means upload 44 | * @param array $options OPTIONAL Options to set for this adapter 45 | * @return Transfer 46 | * @throws Exception\InvalidArgumentException 47 | */ 48 | public function setAdapter($adapter, $direction = false, $options = []) 49 | { 50 | if (! is_string($adapter)) { 51 | throw new Exception\InvalidArgumentException('Adapter must be a string'); 52 | } 53 | 54 | if ($adapter[0] != '\\') { 55 | $adapter = '\Zend\File\Transfer\Adapter\\' . ucfirst($adapter); 56 | } 57 | 58 | $direction = (int) $direction; 59 | $this->adapter[$direction] = new $adapter($options); 60 | if (! $this->adapter[$direction] instanceof Adapter\AbstractAdapter) { 61 | throw new Exception\InvalidArgumentException( 62 | 'Adapter ' . $adapter . ' does not extend Zend\File\Transfer\Adapter\AbstractAdapter' 63 | ); 64 | } 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * Returns all set adapters 71 | * 72 | * @param bool $direction On null, all directions are returned 73 | * On false, download direction is returned 74 | * On true, upload direction is returned 75 | * @return array|Adapter\AbstractAdapter 76 | */ 77 | public function getAdapter($direction = null) 78 | { 79 | if ($direction === null) { 80 | return $this->adapter; 81 | } 82 | 83 | $direction = (int) $direction; 84 | return $this->adapter[$direction]; 85 | } 86 | 87 | /** 88 | * Calls all methods from the adapter 89 | * 90 | * @param string $method Method to call 91 | * @param array $options Options for this method 92 | * @throws Exception\BadMethodCallException if unknown method 93 | * @return mixed 94 | */ 95 | public function __call($method, array $options) 96 | { 97 | if (array_key_exists('direction', $options)) { 98 | $direction = (int) $options['direction']; 99 | } else { 100 | $direction = 0; 101 | } 102 | 103 | if (method_exists($this->adapter[$direction], $method)) { 104 | return call_user_func_array([$this->adapter[$direction], $method], $options); 105 | } 106 | 107 | throw new Exception\BadMethodCallException("Unknown method '" . $method . "' called!"); 108 | } 109 | } 110 | --------------------------------------------------------------------------------