├── LICENSE ├── composer.json └── lib └── blobfolio └── mimes └── mimes.php /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blobfolio\/blob-mimes", 3 | "description": "A comprehensive MIME and file extension tool for PHP.", 4 | "keywords": [ 5 | "mimes", 6 | "files", 7 | "extensions", 8 | "suffix" 9 | ], 10 | "version": "8.0.0", 11 | "type": "library", 12 | "homepage": "https:\/\/github.com\/Blobfolio\/blob-mimes", 13 | "license": "WTFPL", 14 | "authors": [ 15 | { 16 | "name": "Josh Stoik", 17 | "email": "josh@blobfolio.com", 18 | "homepage": "https:\/\/blobfolio.com" 19 | } 20 | ], 21 | "require": { 22 | "php": ">= 7.0", 23 | "ext-filter": "*", 24 | "ext-dom": "*", 25 | "ext-mbstring": "*", 26 | "ext-json": "*", 27 | "ext-bcmath": "*", 28 | "ext-hash": "*", 29 | 30 | "blobfolio\/blob-common": "*" 31 | }, 32 | "require-dev": { 33 | "phpunit\/phpunit": "5.7.*" 34 | }, 35 | "config": { 36 | "vendor-dir": "lib\/vendor", 37 | "preferred-install": "dist" 38 | }, 39 | "minimum-stability": "dev", 40 | "autoload": { 41 | "psr-4": { 42 | "blobfolio\\mimes\\":"lib\/blobfolio\/mimes" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/blobfolio/mimes/mimes.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | 12 | namespace blobfolio\mimes; 13 | 14 | use blobfolio\common\data as c_data; 15 | use blobfolio\common\ref\cast as r_cast; 16 | use blobfolio\common\ref\file as r_file; 17 | use blobfolio\common\ref\sanitize as r_sanitize; 18 | use blobfolio\common\sanitize as v_sanitize; 19 | 20 | class mimes { 21 | const MIME_DEFAULT = 'application/octet-stream'; 22 | const MIME_EMPTY = 'inode/x-empty'; 23 | 24 | 25 | 26 | // ----------------------------------------------------------------- 27 | // Public Data Access 28 | // ----------------------------------------------------------------- 29 | 30 | /** 31 | * Get All MIMEs 32 | * 33 | * Return the entire parsed MIME database. 34 | * 35 | * @return array MIME data. 36 | */ 37 | public static function get_mimes() { 38 | return data::BY_MIME; 39 | } 40 | 41 | /** 42 | * Get One MIME 43 | * 44 | * Return information about a single MIME type. 45 | * 46 | * @param string $mime MIME type. 47 | * @return array MIME data. 48 | */ 49 | public static function get_mime(string $mime='') { 50 | r_sanitize::mime($mime); 51 | 52 | // Try the real MIME first. 53 | if (isset(data::BY_MIME[$mime])) { 54 | return data::BY_MIME[$mime]; 55 | } 56 | 57 | // Try aliases. 58 | $loose = \array_diff(static::get_loose_mimes($mime), array($mime)); 59 | foreach ($loose as $v) { 60 | if (isset(data::BY_MIME[$v])) { 61 | return data::BY_MIME[$v]; 62 | } 63 | } 64 | 65 | return false; 66 | } 67 | 68 | /** 69 | * Get All Extensions 70 | * 71 | * Return the entire file extension database. 72 | * 73 | * @return array Extension data. 74 | */ 75 | public static function get_extensions() { 76 | return data::BY_EXT; 77 | } 78 | 79 | /** 80 | * Get One Extension 81 | * 82 | * Return information about a single file extension. 83 | * 84 | * @param string $ext File extension. 85 | * @return array Extension data. 86 | */ 87 | public static function get_extension(string $ext='') { 88 | r_sanitize::file_extension($ext); 89 | return data::BY_EXT[$ext] ?? false; 90 | } 91 | 92 | /** 93 | * Verify a MIME/ext pairing 94 | * 95 | * @param string $ext File extension. 96 | * @param string $mime MIME type. 97 | * @param bool $soft Soft pass not-found. 98 | * @return bool True. 99 | */ 100 | public static function check_ext_and_mime(string $ext='', string $mime='', bool $soft=true) { 101 | // Check the extension. 102 | r_sanitize::file_extension($ext); 103 | if (! $ext) { 104 | return false; 105 | } 106 | 107 | // Check the MIME. 108 | r_sanitize::mime($mime); 109 | if ( 110 | ! $mime || 111 | (static::MIME_EMPTY === $mime) || 112 | ($soft && (static::MIME_DEFAULT === $mime)) 113 | ) { 114 | return true; 115 | } 116 | 117 | // Soft pass on extension fail. 118 | if (false === ($ext = static::get_extension($ext))) { 119 | return $soft; 120 | } 121 | 122 | // Loose mime check. 123 | $real = $ext['mime']; 124 | $found = \array_intersect($real, static::get_loose_mimes($mime)); 125 | 126 | // If we found something, hurray! 127 | return \count($found) > 0; 128 | } 129 | 130 | /** 131 | * Get Loose MIMEs 132 | * 133 | * Build a list of test MIMEs with x- and vnd. prefixes. These do 134 | * not necessarily exist; this method is called prior to such 135 | * checks. 136 | * 137 | * @param string $mime MIME. 138 | * @return array MIMEs. 139 | */ 140 | public static function get_loose_mimes(string $mime) { 141 | r_sanitize::mime($mime); 142 | 143 | $out = array(); 144 | 145 | if (! $mime) { 146 | return $out; 147 | } 148 | 149 | $out[] = $mime; 150 | if ((static::MIME_EMPTY === $mime) || (static::MIME_DEFAULT === $mime)) { 151 | return $out; 152 | } 153 | 154 | // Weird Microsoft MIME. 155 | if (0 === \strpos($mime, 'application/cdfv2')) { 156 | $out[] = 'application/vnd.ms-office'; 157 | } 158 | 159 | // Split it up. 160 | list($type, $subtype) = \explode('/', $mime); 161 | if ($type && $subtype) { 162 | $subtype = \preg_replace('/^(x\-|vnd.)/', '', $subtype); 163 | $out[] = "$type/x-$subtype"; 164 | $out[] = "$type/vnd.$subtype"; 165 | $out[] = "$type/$subtype"; 166 | } 167 | 168 | // Sort our results, preferring non-prefixed types. 169 | $out = \array_unique($out); 170 | \usort($out, function($a, $b) { 171 | $a_key = (! \preg_match('#/(x\-|vnd\.)#', $a) ? '0_' : '1_') . $a; 172 | $b_key = (! \preg_match('#/(x\-|vnd\.)#', $b) ? '0_' : '1_') . $b; 173 | 174 | return $a_key < $b_key ? -1 : 1; 175 | }); 176 | 177 | return $out; 178 | } 179 | 180 | /** 181 | * Get File Info 182 | * 183 | * This function is a sexier version of finfo_open(). 184 | * 185 | * @param string $path File path or name. 186 | * @param string $nice Nice file name (for e.g. tmp uploads). 187 | * @return array File data. 188 | */ 189 | public static function finfo($path='', $nice=null) { 190 | r_cast::string($path, true); 191 | if (! \is_null($nice)) { 192 | r_cast::string($nice, true); 193 | } 194 | 195 | $out = array( 196 | 'dirname'=>'', 197 | 'basename'=>'', 198 | 'extension'=>'', 199 | 'filename'=>'', 200 | 'path'=>'', 201 | 'mime'=>static::MIME_DEFAULT, 202 | 'suggested_filename'=>array(), 203 | ); 204 | 205 | // Path might just be an extension. 206 | if ( 207 | (false === \strpos($path, '.')) && 208 | (false === \strpos($path, '/')) && 209 | (false === \strpos($path, '\\')) 210 | ) { 211 | $out['extension'] = v_sanitize::file_extension($path); 212 | if (false !== ($ext = static::get_extension($path))) { 213 | $out['mime'] = $ext['mime'][0]; 214 | } 215 | 216 | return $out; 217 | } 218 | 219 | // Path is something path-like. 220 | r_file::path($path, false); 221 | $out['path'] = $path; 222 | $out = c_data::parse_args(\pathinfo($path), $out); 223 | 224 | if (! \is_null($nice)) { 225 | $pathinfo = \pathinfo($nice); 226 | $out['filename'] = $pathinfo['filename'] ?? ''; 227 | $out['extension'] = $pathinfo['extension'] ?? ''; 228 | } 229 | 230 | r_sanitize::file_extension($out['extension']); 231 | 232 | // Pull the mimes from the extension. 233 | if (false !== ($ext = static::get_extension($out['extension']))) { 234 | $out['mime'] = $ext['mime'][0]; 235 | } 236 | 237 | // Try to read the magic mime, if possible. 238 | try { 239 | // Find the real path, if possible. 240 | if (false !== ($path = @\realpath($path))) { 241 | $out['path'] = $path; 242 | $out['dirname'] = \dirname($path); 243 | } 244 | 245 | // Lookup magic mime, if possible. 246 | if ( 247 | (false !== $path) && 248 | \function_exists('finfo_file') && 249 | \defined('FILEINFO_MIME_TYPE') && 250 | @\is_file($path) && 251 | @\filesize($path) > 0 252 | ) { 253 | $finfo = \finfo_open(\FILEINFO_MIME_TYPE); 254 | $magic_mime = v_sanitize::mime(\finfo_file($finfo, $path)); 255 | \finfo_close($finfo); 256 | 257 | // SVGs can be misidentified by fileinfo if they are 258 | // missing the XML tag and/or DOCTYPE declarations. Most 259 | // other applications don't have that problem, so let's 260 | // override fileinfo if the file starts with an opening 261 | // SVG tag. 262 | if ( 263 | ('svg' === $out['extension']) && 264 | ('image/svg+xml' !== $magic_mime) 265 | ) { 266 | $tmp = @\file_get_contents($path); 267 | if ( 268 | \is_string($tmp) && 269 | (false !== \strpos(\strtolower($tmp), '