├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json └── src ├── DataSource.php ├── Mimetypes.php └── UploadedFile.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | None 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/thephpleague/uploads). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ phpunit 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Josh Lockhart 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Uploads 2 | 3 | Receive, validate, and distribute uploaded files. 4 | 5 | ## Install 6 | 7 | Via Composer 8 | 9 | ``` bash 10 | $ composer require league/uploads 11 | ``` 12 | 13 | ## Usage 14 | 15 | TBD 16 | 17 | ## Testing 18 | 19 | ``` bash 20 | $ phpunit 21 | ``` 22 | 23 | ## Contributing 24 | 25 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 26 | 27 | ## Security 28 | 29 | If you discover any security related issues, please email hello@joshlockhart.com instead of using the issue tracker. 30 | 31 | ## Credits 32 | 33 | - [Josh Lockhart](https://github.com/codeguy) 34 | - [All Contributors](../../contributors) 35 | 36 | ## License 37 | 38 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "league/uploads", 3 | "description": "Receive, validate, and distribute file uploads", 4 | "keywords": [ 5 | "league", 6 | "uploads" 7 | ], 8 | "homepage": "https://github.com/thephpleague/uploads", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Josh Lockhart", 13 | "email": "hello@joshlockhart.com", 14 | "homepage": "https://github.com/codeguy", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php" : ">=5.3.0", 20 | "league/flysystem": "~0.3" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit" : "4.*", 24 | "scrutinizer/ocular": "~1.1" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "League\\Uploads\\": "src" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "League\\Uploads\\Test\\": "tests" 34 | } 35 | }, 36 | "extra": { 37 | "branch-alias": { 38 | "dev-master": "1.0-dev" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/DataSource.php: -------------------------------------------------------------------------------- 1 | [ 17 | * 0 => [ 18 | * 'name' => 'foo.txt', 19 | * 'tmp_name' => 'sdfsfdfsdd', 20 | * 'type' => 'text/plain', 21 | * 'size' => 300, 22 | * 'error' => UPLOAD_ERR_OK 23 | * ] 24 | * ] 25 | * ] 26 | * 27 | * @var array 28 | */ 29 | protected $data = []; 30 | 31 | /** 32 | * Create new data source 33 | * 34 | * @param array $data Associative array that matches the `$_FILES` superglobal. 35 | */ 36 | public function __construct(array $data) 37 | { 38 | $this->data = $this->normalize($data); 39 | } 40 | 41 | /** 42 | * Normalize `$_FILES` array 43 | * 44 | * @param array $origFiles Original `$_FILES` array 45 | * @return array 46 | */ 47 | protected function normalize(array $origFiles) 48 | { 49 | $newFiles = []; 50 | foreach ($origFiles as $fieldName => $fieldValue) { 51 | foreach ($fieldValue as $paramName => $paramValue) { 52 | foreach ((array)$paramValue as $index => $value) { 53 | $newFiles[$fieldName][$index][$paramName] = $value; 54 | } 55 | } 56 | } 57 | 58 | return $newFiles; 59 | } 60 | 61 | /************************************************************************** 62 | * Accessors 63 | *************************************************************************/ 64 | 65 | public function getData() 66 | { 67 | return $this->data; 68 | } 69 | 70 | /************************************************************************** 71 | * ArrayAccess interface 72 | *************************************************************************/ 73 | 74 | public function offsetExists($offset) 75 | { 76 | return isset($this->data[$offset]); 77 | } 78 | 79 | public function offsetGet($offset) 80 | { 81 | return $this->offsetExists($offset) ? $this->data[$offset] : null; 82 | } 83 | 84 | public function offsetSet($offset, $value) 85 | { 86 | $this->data[$offset] = $value; 87 | } 88 | 89 | public function offsetUnset($offset) 90 | { 91 | unset($this->data[$offset]); 92 | } 93 | 94 | /************************************************************************** 95 | * IteratorAggregate interface 96 | *************************************************************************/ 97 | 98 | public function getIterator() 99 | { 100 | return new \ArrayIterator($this->data); 101 | } 102 | 103 | /************************************************************************** 104 | * Countable interface 105 | *************************************************************************/ 106 | 107 | public function count() 108 | { 109 | return count($this->data); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Mimetypes.php: -------------------------------------------------------------------------------- 1 | 'aab', 18 | 'application/x-authorware-map' => 'aam', 19 | 'application/x-authorware-seg' => 'aas', 20 | 'text/vnd.abc' => 'abc', 21 | 'video/animaflex' => 'afl', 22 | 'application/x-aim' => 'aim', 23 | 'text/x-audiosoft-intra' => 'aip', 24 | 'application/x-navi-animation' => 'ani', 25 | 'application/x-nokia-9000-communicator-add-on-software' => 'aos', 26 | 'application/mime' => 'aps', 27 | 'application/arj' => 'arj', 28 | 'image/x-jg' => 'art', 29 | 'text/asp' => 'asp', 30 | 'application/x-mplayer2' => 'asx', 31 | 'video/x-ms-asf-plugin' => 'asx', 32 | 'audio/x-au' => 'au', 33 | 'application/x-troff-msvideo' => 'avi', 34 | 'video/avi' => 'avi', 35 | 'video/msvideo' => 'avi', 36 | 'video/x-msvideo' => 'avi', 37 | 'video/avs-video' => 'avs', 38 | 'application/x-bcpio' => 'bcpio', 39 | 'application/mac-binary' => 'bin', 40 | 'application/macbinary' => 'bin', 41 | 'application/x-binary' => 'bin', 42 | 'application/x-macbinary' => 'bin', 43 | 'image/x-windows-bmp' => 'bmp', 44 | 'application/x-bzip' => 'bz', 45 | 'application/vnd.ms-pki.seccat' => 'cat', 46 | 'application/clariscad' => 'ccad', 47 | 'application/x-cocoa' => 'cco', 48 | 'application/cdf' => 'cdf', 49 | 'application/x-cdf' => 'cdf', 50 | 'application/java' => 'class', 51 | 'application/java-byte-code' => 'class', 52 | 'application/x-java-class' => 'class', 53 | 'application/x-cpio' => 'cpio', 54 | 'application/mac-compactpro' => 'cpt', 55 | 'application/x-compactpro' => 'cpt', 56 | 'application/x-cpt' => 'cpt', 57 | 'application/pkcs-crl' => 'crl', 58 | 'application/pkix-crl' => 'crl', 59 | 'application/x-x509-user-cert' => 'crt', 60 | 'application/x-csh' => 'csh', 61 | 'text/x-script.csh' => 'csh', 62 | 'application/x-pointplus' => 'css', 63 | 'text/css' => 'css', 64 | 'application/x-deepv' => 'deepv', 65 | 'video/dl' => 'dl', 66 | 'video/x-dl' => 'dl', 67 | 'application/commonground' => 'dp', 68 | 'application/drafting' => 'drw', 69 | 'application/x-dvi' => 'dvi', 70 | 'drawing/x-dwf (old)' => 'dwf', 71 | 'model/vnd.dwf' => 'dwf', 72 | 'application/acad' => 'dwg', 73 | 'application/dxf' => 'dxf', 74 | 'text/x-script.elisp' => 'el', 75 | 'application/x-bytecode.elisp (compiled elisp)' => 'elc', 76 | 'application/x-elc' => 'elc', 77 | 'application/x-esrehber' => 'es', 78 | 'text/x-setext' => 'etx', 79 | 'application/envoy' => 'evy', 80 | 'application/vnd.fdf' => 'fdf', 81 | 'application/fractals' => 'fif', 82 | 'image/fif' => 'fif', 83 | 'video/fli' => 'fli', 84 | 'video/x-fli' => 'fli', 85 | 'text/vnd.fmi.flexstor' => 'flx', 86 | 'video/x-atomic3d-feature' => 'fmf', 87 | 'image/vnd.fpx' => 'fpx', 88 | 'image/vnd.net-fpx' => 'fpx', 89 | 'application/freeloader' => 'frl', 90 | 'image/g3fax' => 'g3', 91 | 'image/gif' => 'gif', 92 | 'video/gl' => 'gl', 93 | 'video/x-gl' => 'gl', 94 | 'application/x-gsp' => 'gsp', 95 | 'application/x-gss' => 'gss', 96 | 'application/x-gtar' => 'gtar', 97 | 'multipart/x-gzip' => 'gzip', 98 | 'application/x-hdf' => 'hdf', 99 | 'text/x-script' => 'hlb', 100 | 'application/hlp' => 'hlp', 101 | 'application/x-winhelp' => 'hlp', 102 | 'application/binhex' => 'hqx', 103 | 'application/binhex4' => 'hqx', 104 | 'application/mac-binhex' => 'hqx', 105 | 'application/mac-binhex40' => 'hqx', 106 | 'application/x-binhex40' => 'hqx', 107 | 'application/x-mac-binhex40' => 'hqx', 108 | 'application/hta' => 'hta', 109 | 'text/x-component' => 'htc', 110 | 'text/webviewhtml' => 'htt', 111 | 'x-conference/x-cooltalk' => 'ice ', 112 | 'image/x-icon' => 'ico', 113 | 'application/x-ima' => 'ima', 114 | 'application/x-httpd-imap' => 'imap', 115 | 'application/inf' => 'inf ', 116 | 'application/x-internett-signup' => 'ins', 117 | 'application/x-ip2' => 'ip ', 118 | 'video/x-isvideo' => 'isu', 119 | 'audio/it' => 'it', 120 | 'application/x-inventor' => 'iv', 121 | 'i-world/i-vrml' => 'ivr', 122 | 'application/x-livescreen' => 'ivy', 123 | 'audio/x-jam' => 'jam ', 124 | 'application/x-java-commerce' => 'jcm ', 125 | 'image/x-jps' => 'jps', 126 | 'application/x-javascript' => 'js ', 127 | 'image/jutvision' => 'jut', 128 | 'music/x-karaoke' => 'kar', 129 | 'application/x-ksh' => 'ksh', 130 | 'text/x-script.ksh' => 'ksh', 131 | 'audio/x-liveaudio' => 'lam', 132 | 'application/lha' => 'lha', 133 | 'application/x-lha' => 'lha', 134 | 'application/x-lisp' => 'lsp ', 135 | 'text/x-script.lisp' => 'lsp ', 136 | 'text/x-la-asf' => 'lsx', 137 | 'application/x-lzh' => 'lzh', 138 | 'application/lzx' => 'lzx', 139 | 'application/x-lzx' => 'lzx', 140 | 'text/x-m' => 'm', 141 | 'audio/x-mpequrl' => 'm3u ', 142 | 'application/x-troff-man' => 'man', 143 | 'application/x-navimap' => 'map', 144 | 'application/mbedlet' => 'mbd', 145 | 'application/x-magic-cap-package-1.0' => 'mc$', 146 | 'application/mcad' => 'mcd', 147 | 'application/x-mathcad' => 'mcd', 148 | 'image/vasa' => 'mcf', 149 | 'text/mcf' => 'mcf', 150 | 'application/netmc' => 'mcp', 151 | 'application/x-troff-me' => 'me ', 152 | 'application/x-frame' => 'mif', 153 | 'application/x-mif' => 'mif', 154 | 'www/mime' => 'mime ', 155 | 'audio/x-vnd.audioexplosion.mjuicemediafile' => 'mjf', 156 | 'video/x-motion-jpeg' => 'mjpg ', 157 | 'application/x-meme' => 'mm', 158 | 'audio/mod' => 'mod', 159 | 'audio/x-mod' => 'mod', 160 | 'audio/x-mpeg' => 'mp2', 161 | 'video/x-mpeq2a' => 'mp2', 162 | 'audio/mpeg3' => 'mp3', 163 | 'audio/x-mpeg-3' => 'mp3', 164 | 'application/vnd.ms-project' => 'mpp', 165 | 'application/marc' => 'mrc', 166 | 'application/x-troff-ms' => 'ms', 167 | 'application/x-vnd.audioexplosion.mzz' => 'mzz', 168 | 'application/vnd.nokia.configuration-message' => 'ncm', 169 | 'application/x-mix-transfer' => 'nix', 170 | 'application/x-conference' => 'nsc', 171 | 'application/x-navidoc' => 'nvd', 172 | 'application/oda' => 'oda', 173 | 'application/x-omc' => 'omc', 174 | 'application/x-omcdatamaker' => 'omcd', 175 | 'application/x-omcregerator' => 'omcr', 176 | 'text/x-pascal' => 'p', 177 | 'application/pkcs10' => 'p10', 178 | 'application/x-pkcs10' => 'p10', 179 | 'application/pkcs-12' => 'p12', 180 | 'application/x-pkcs12' => 'p12', 181 | 'application/x-pkcs7-signature' => 'p7a', 182 | 'application/x-pkcs7-certreqresp' => 'p7r', 183 | 'application/pkcs7-signature' => 'p7s', 184 | 'text/pascal' => 'pas', 185 | 'image/x-portable-bitmap' => 'pbm ', 186 | 'application/vnd.hp-pcl' => 'pcl', 187 | 'application/x-pcl' => 'pcl', 188 | 'image/x-pict' => 'pct', 189 | 'image/x-pcx' => 'pcx', 190 | 'application/pdf' => 'pdf', 191 | 'audio/make.my.funk' => 'pfunk', 192 | 'image/x-portable-graymap' => 'pgm', 193 | 'image/x-portable-greymap' => 'pgm', 194 | 'application/x-newton-compatible-pkg' => 'pkg', 195 | 'application/vnd.ms-pki.pko' => 'pko', 196 | 'text/x-script.perl' => 'pl', 197 | 'application/x-pixclscript' => 'plx', 198 | 'text/x-script.perl-module' => 'pm', 199 | 'application/x-portable-anymap' => 'pnm', 200 | 'image/x-portable-anymap' => 'pnm', 201 | 'model/x-pov' => 'pov', 202 | 'image/x-portable-pixmap' => 'ppm', 203 | 'application/powerpoint' => 'ppt', 204 | 'application/x-mspowerpoint' => 'ppt', 205 | 'application/x-freelance' => 'pre', 206 | 'paleovu/x-pv' => 'pvu', 207 | 'text/x-script.phyton' => 'py ', 208 | 'applicaiton/x-bytecode.python' => 'pyc ', 209 | 'audio/vnd.qcelp' => 'qcp ', 210 | 'video/x-qtc' => 'qtc', 211 | 'audio/x-realaudio' => 'ra', 212 | 'application/x-cmu-raster' => 'ras', 213 | 'image/x-cmu-raster' => 'ras', 214 | 'text/x-script.rexx' => 'rexx ', 215 | 'image/vnd.rn-realflash' => 'rf', 216 | 'image/x-rgb' => 'rgb ', 217 | 'application/vnd.rn-realmedia' => 'rm', 218 | 'audio/mid' => 'rmi', 219 | 'application/ringing-tones' => 'rng', 220 | 'application/vnd.nokia.ringing-tone' => 'rng', 221 | 'application/vnd.rn-realplayer' => 'rnx ', 222 | 'image/vnd.rn-realpix' => 'rp ', 223 | 'text/vnd.rn-realtext' => 'rt', 224 | 'application/x-rtf' => 'rtf', 225 | 'video/vnd.rn-realvideo' => 'rv', 226 | 'audio/s3m' => 's3m ', 227 | 'application/x-lotusscreencam' => 'scm', 228 | 'text/x-script.guile' => 'scm', 229 | 'text/x-script.scheme' => 'scm', 230 | 'video/x-scm' => 'scm', 231 | 'application/sdp' => 'sdp ', 232 | 'application/x-sdp' => 'sdp ', 233 | 'application/sounder' => 'sdr', 234 | 'application/sea' => 'sea', 235 | 'application/x-sea' => 'sea', 236 | 'application/set' => 'set', 237 | 'application/x-sh' => 'sh', 238 | 'text/x-script.sh' => 'sh', 239 | 'audio/x-psid' => 'sid', 240 | 'application/x-sit' => 'sit', 241 | 'application/x-stuffit' => 'sit', 242 | 'application/x-seelogo' => 'sl ', 243 | 'audio/x-adpcm' => 'snd', 244 | 'application/solids' => 'sol', 245 | 'application/x-pkcs7-certificates' => 'spc ', 246 | 'application/futuresplash' => 'spl', 247 | 'application/streamingmedia' => 'ssm ', 248 | 'application/vnd.ms-pki.certstore' => 'sst', 249 | 'application/sla' => 'stl', 250 | 'application/vnd.ms-pki.stl' => 'stl', 251 | 'application/x-navistyle' => 'stl', 252 | 'application/x-sv4cpio' => 'sv4cpio', 253 | 'application/x-sv4crc' => 'sv4crc', 254 | 'x-world/x-svr' => 'svr', 255 | 'application/x-shockwave-flash' => 'swf', 256 | 'application/x-tar' => 'tar', 257 | 'application/toolbook' => 'tbk', 258 | 'application/x-tcl' => 'tcl', 259 | 'text/x-script.tcl' => 'tcl', 260 | 'text/x-script.tcsh' => 'tcsh', 261 | 'application/x-tex' => 'tex', 262 | 'application/plain' => 'text', 263 | 'application/gnutar' => 'tgz', 264 | 'audio/tsp-audio' => 'tsi', 265 | 'application/dsptype' => 'tsp', 266 | 'audio/tsplayer' => 'tsp', 267 | 'text/tab-separated-values' => 'tsv', 268 | 'text/x-uil' => 'uil', 269 | 'application/i-deas' => 'unv', 270 | 'application/x-ustar' => 'ustar', 271 | 'multipart/x-ustar' => 'ustar', 272 | 'application/x-cdlink' => 'vcd', 273 | 'text/x-vcalendar' => 'vcs', 274 | 'application/vda' => 'vda', 275 | 'video/vdo' => 'vdo', 276 | 'application/groupwise' => 'vew ', 277 | 'application/vocaltec-media-desc' => 'vmd ', 278 | 'application/vocaltec-media-file' => 'vmf', 279 | 'audio/voc' => 'voc', 280 | 'audio/x-voc' => 'voc', 281 | 'video/vosaic' => 'vos', 282 | 'audio/voxware' => 'vox', 283 | 'audio/x-twinvq' => 'vqf', 284 | 'application/x-vrml' => 'vrml', 285 | 'x-world/x-vrt' => 'vrt', 286 | 'application/wordperfect6.1' => 'w61', 287 | 'audio/wav' => 'wav', 288 | 'audio/x-wav' => 'wav', 289 | 'application/x-qpro' => 'wb1', 290 | 'image/vnd.wap.wbmp' => 'wbmp', 291 | 'application/vnd.xara' => 'web', 292 | 'application/x-123' => 'wk1', 293 | 'windows/metafile' => 'wmf', 294 | 'text/vnd.wap.wml' => 'wml', 295 | 'application/vnd.wap.wmlc' => 'wmlc ', 296 | 'text/vnd.wap.wmlscript' => 'wmls', 297 | 'application/vnd.wap.wmlscriptc' => 'wmlsc ', 298 | 'application/x-wpwin' => 'wpd', 299 | 'application/x-lotus' => 'wq1', 300 | 'application/mswrite' => 'wri', 301 | 'application/x-wri' => 'wri', 302 | 'text/scriplet' => 'wsc', 303 | 'application/x-wintalk' => 'wtk ', 304 | 'image/x-xbitmap' => 'xbm', 305 | 'image/x-xbm' => 'xbm', 306 | 'image/xbm' => 'xbm', 307 | 'video/x-amt-demorun' => 'xdr', 308 | 'xgl/drawing' => 'xgz', 309 | 'image/vnd.xiff' => 'xif', 310 | 'audio/xm' => 'xm', 311 | 'application/xml' => 'xml', 312 | 'text/xml' => 'xml', 313 | 'xgl/movie' => 'xmz', 314 | 'application/x-vnd.ls-xpix' => 'xpix', 315 | 'image/xpm' => 'xpm', 316 | 'video/x-amt-showrun' => 'xsr', 317 | 'image/x-xwd' => 'xwd', 318 | 'image/x-xwindowdump' => 'xwd', 319 | 'application/x-compress' => 'z', 320 | 'application/x-zip-compressed' => 'zip', 321 | 'application/zip' => 'zip', 322 | 'multipart/x-zip' => 'zip', 323 | 'text/x-script.zsh' => 'zsh' 324 | ]; 325 | 326 | /** 327 | * Get extension for mimetype 328 | * 329 | * @param string $mimetype A mimetype 330 | * 331 | * @return null|string 332 | */ 333 | public static function getExtensionForMimetype($mimetype) 334 | { 335 | return isset(static::$extensions[$mimetype]) ? static::$extensions[$mimetype] : null; 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/UploadedFile.php: -------------------------------------------------------------------------------- 1 | isUploadedFile($path)) { 57 | throw new \InvalidArgumentException('File path is not a valid uploaded file'); 58 | } 59 | $this->originalName = $originalName; 60 | 61 | parent::__construct($path); 62 | } 63 | 64 | /** 65 | * Create new uploaded file from `$_FILES` superglobal data 66 | * 67 | * @param string $name The uploaded file's key in the `$_FILES` superglobal 68 | * 69 | * @return self 70 | */ 71 | public static function createFromGlobals($name) 72 | { 73 | if (static::$dataSource === null) { 74 | static::$dataSource = new DataSource($_FILES); 75 | } 76 | 77 | if (!isset(static::$dataSource[$name])) { 78 | throw new \InvalidArgumentException('File does not exist in datasource.'); 79 | } 80 | 81 | if (static::$dataSource[$name]['error'] !== UPLOAD_ERR_OK) { 82 | throw new \RuntimeException('File upload error', static::$dataSource[$name]['error']); 83 | } 84 | 85 | return new static(static::$dataSource[$name]['tmp_name'], static::$dataSource[$name]['name']); 86 | } 87 | 88 | /** 89 | * Set uploaded file data source 90 | * 91 | * @param DataSource $source 92 | */ 93 | public static function setDataSource(DataSource $source) 94 | { 95 | static::$dataSource = $source; 96 | } 97 | 98 | /************************************************************************** 99 | * Helpers 100 | *************************************************************************/ 101 | 102 | /** 103 | * Is path a valid uploaded file? 104 | * 105 | * We separate this test into its own method so we can easily 106 | * stub this method in unit tests. 107 | * 108 | * @param string $path Local filesystem file path 109 | * 110 | * @return bool 111 | */ 112 | public function isUploadedFile($path) 113 | { 114 | return is_uploaded_file($path); 115 | } 116 | 117 | /************************************************************************** 118 | * Properties 119 | *************************************************************************/ 120 | 121 | /** 122 | * Get file extension 123 | * 124 | * @return string 125 | */ 126 | public function getExtension() 127 | { 128 | if ($this->extension === null) { 129 | $hit = Mimetypes::getExtensionForMimetype($this->getMimetype()); 130 | $this->extension = $hit ? $hit : parent::getExtension(); 131 | } 132 | 133 | return $this->extension; 134 | } 135 | 136 | /** 137 | * Get file mimetype 138 | * 139 | * @return string 140 | */ 141 | public function getMimetype() 142 | { 143 | if ($this->mimetype === null) { 144 | // NOTE: We can probably be smarter about this 145 | $finfo = new finfo(FILEINFO_MIME_TYPE); 146 | $this->mimetype = $finfo->file($this->getRealPath()); 147 | finfo_close($finfo); 148 | } 149 | 150 | return $this->mimetype; 151 | } 152 | 153 | /** 154 | * Get human readable file size in a given unit 155 | * 156 | * @param string $unit The desired unit 157 | * @param int $decimals The number of desired decimals 158 | * 159 | * @return float 160 | */ 161 | public function getHumanSize($unit = 'B', $decimals = 2) 162 | { 163 | $sz = 'BKMGTP'; 164 | $bytes = $this->getSize(); 165 | $factor = floor((strlen($bytes) - 1) / 3); 166 | 167 | return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor]; 168 | } 169 | 170 | /** 171 | * Get file name 172 | * 173 | * @return string 174 | */ 175 | public function getName() 176 | { 177 | if ($this->name === null) { 178 | $this->name = $this->getBasename('.' . $this->getExtension()); 179 | } 180 | 181 | return $this->name; 182 | } 183 | 184 | /** 185 | * Set new file name 186 | * 187 | * @param string $name File name without extension 188 | * 189 | * @throws \InvalidArgumentException If arguent is not a string 190 | * @throws \InvalidArgumentException If argument is not a valid file name 191 | */ 192 | public function setName($name) 193 | { 194 | if (!is_string($name)) { 195 | throw new \InvalidArgumentException('File name must be a string'); 196 | } 197 | $filename = pathinfo($name, PATHINFO_FILENAME); 198 | if (empty($filename)) { 199 | throw new \InvalidArgumentException('File name is invalid'); 200 | } 201 | $this->name = $filename; 202 | } 203 | 204 | /** 205 | * Return original file name provided by HTTP client 206 | * 207 | * @return null|string 208 | */ 209 | public function getOriginalName() 210 | { 211 | return $this->originalName; 212 | } 213 | } 214 | --------------------------------------------------------------------------------