diff --git a/composer.json b/composer.json index 69d461d..d5e7c27 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ } ], "require": { - "php": "^7.1" + "php": "^7.1", + "psr/http-message": "^1.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.3", diff --git a/composer.lock b/composer.lock index 594b280..e8c7055 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,59 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8caea8ec6313d5a0b1057c54c7782356", - "packages": [], + "content-hash": "7310d28d306d123fbb958435959d94d4", + "packages": [ + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + } + ], "packages-dev": [ { "name": "bamarni/composer-bin-plugin", diff --git a/lib/MimeType.php b/lib/MimeType.php index 19009d2..5788395 100644 --- a/lib/MimeType.php +++ b/lib/MimeType.php @@ -38,20 +38,20 @@ namespace MensBeam\Mime; */ class MimeType { protected const TYPE_PATTERN = <<<'PATTERN' - <^ + /^ [\t\r\n ]* # optional leading whitespace - ([^/]+) # type - / # type/subtype delimiter + ([^\/]+) # type + \/ # type-subtype delimiter ([^;]+) # subtype (possibly with trailing whitespace) (;.*)? # optional parameters, to be parsed separately [\t\r\n ]* # optional trailing whitespace - $>sx + $/sx PATTERN; protected const PARAM_PATTERN = <<<'PATTERN' - < + / [;\t\r\n ]* # parameter delimiter and leading whitespace, all optional ([^=;]*) # parameter name; may be empty - (?:= # parameter name/value delimiter + (?:= # parameter name-value delimiter ( "(?:\\"|[^"])*(?:"|$)[^;]* # quoted parameter value and optional garbage |[^;]* # unquoted parameter value (possibly with trailing whitespace) @@ -59,13 +59,52 @@ PATTERN; )? ;? # optional trailing parameter delimiter [\t\r\n ]* # optional trailing whitespace - >sx + /sx PATTERN; - protected const TOKEN_PATTERN = '<^[A-Za-z0-9!#$%&\'*+\-\.\^_`|~]+$>s'; - protected const BARE_VALUE_PATTERN = '<^[\t\x{20}-\x{7E}\x{80}-\x{FF}]+$>su'; - protected const QUOTED_VALUE_PATTERN = '<^"((?:\\\"|[\t !\x{23}-\x{7E}\x{80}-\x{FF}])*)(?:"|$)>su'; - protected const ESCAPE_PATTERN = '<\\\(.)>s'; + protected const TOKEN_PATTERN = '/^[A-Za-z0-9!#$%&\'*+\-\.\^_`|~]+$/s'; + protected const BARE_VALUE_PATTERN = '/^[\t\x{20}-\x{7E}\x{80}-\x{FF}]+$/su'; + protected const QUOTED_VALUE_PATTERN = '/^"((?:\\\"|[\t !\x{23}-\x{7E}\x{80}-\x{FF}])*)(?:"|$)/su'; + protected const ESCAPE_PATTERN = '/\\\(.)/s'; protected const CHAR_MAP = [0x80 => "\u{80}","\u{81}","\u{82}","\u{83}","\u{84}","\u{85}","\u{86}","\u{87}","\u{88}","\u{89}","\u{8a}","\u{8b}","\u{8c}","\u{8d}","\u{8e}","\u{8f}","\u{90}","\u{91}","\u{92}","\u{93}","\u{94}","\u{95}","\u{96}","\u{97}","\u{98}","\u{99}","\u{9a}","\u{9b}","\u{9c}","\u{9d}","\u{9e}","\u{9f}","\u{a0}","\u{a1}","\u{a2}","\u{a3}","\u{a4}","\u{a5}","\u{a6}","\u{a7}","\u{a8}","\u{a9}","\u{aa}","\u{ab}","\u{ac}","\u{ad}","\u{ae}","\u{af}","\u{b0}","\u{b1}","\u{b2}","\u{b3}","\u{b4}","\u{b5}","\u{b6}","\u{b7}","\u{b8}","\u{b9}","\u{ba}","\u{bb}","\u{bc}","\u{bd}","\u{be}","\u{bf}","\u{c0}","\u{c1}","\u{c2}","\u{c3}","\u{c4}","\u{c5}","\u{c6}","\u{c7}","\u{c8}","\u{c9}","\u{ca}","\u{cb}","\u{cc}","\u{cd}","\u{ce}","\u{cf}","\u{d0}","\u{d1}","\u{d2}","\u{d3}","\u{d4}","\u{d5}","\u{d6}","\u{d7}","\u{d8}","\u{d9}","\u{da}","\u{db}","\u{dc}","\u{dd}","\u{de}","\u{df}","\u{e0}","\u{e1}","\u{e2}","\u{e3}","\u{e4}","\u{e5}","\u{e6}","\u{e7}","\u{e8}","\u{e9}","\u{ea}","\u{eb}","\u{ec}","\u{ed}","\u{ee}","\u{ef}","\u{f0}","\u{f1}","\u{f2}","\u{f3}","\u{f4}","\u{f5}","\u{f6}","\u{f7}","\u{f8}","\u{f9}","\u{fa}","\u{fb}","\u{fc}","\u{fd}","\u{fe}","\u{ff}"]; + protected const SNIFF_PATTERNS_IMAGE = [ + '/^\x{00}\x{00}[\x{01}\x{02}]\x{00}/s' => "image/x-icon", + '/^BM/s' => "image/bmp", + '/^GIF8[79]a/s' => "image/gif", + '/^RIFF.{4}WEBPVP/s' => "image/webp", + '/^\x{89}PNG\r\n\x{1A}\n/s' => "image/png", + '/^\x{FF}\x{D8}\x{FF}/s' => "imaged/jpeg", + ]; + protected const SNIFF_PATTERNS_AUDIOVIDEO = [ + '/^\.snd/s' => "audio/basic", + '/^FORM.{4}AIFF/s' => "audio/aiff", + '/^ID3/s' => "audi/mpeg", + '/^OggS\x{00}/s' => "application/ogg", + '/^MThd\x{00}{3}\x{06}/s' => "audio/midi", + '/^RIFF.{4}AVI /s' => "video/avi", + '/^RIFF.{4}WAVE/s' => "audio/wave", + ]; + protected const SNIFF_PATTERNS_FONT = [ + '/^.{34}LP/s' => "application/vnd.ms-fontobject", + '/^\x{00}\x{01}\x{00}{2}/s' => "font/ttf", + '/^OTTO/s' => "font/otf", + '/^ttcf/s' => "font/collection", + '/^wOFF/s' => "font/woff", + '/^wOF2/s' => "font/woff2", + ]; + protected const SNIFF_PATTERNS_ARCHIVE = [ + '/^\x{1F}\x{8B}\x{08}/s' => "application/x-gzip", + '/^PK\x{03}\x{04}/s' => "application/zip", + '/^Rar \x{1A}\x{07}\x{00}/s' => "application/x-rar-compressed", + ]; + protected const SNIFF_PATTERNS_UNKNWON_SCRIPTABLE = [ + '/^\s*<(?:!DOCTYPE HTML|HTML|HEAD|SCRIPT|IFRAME|H1|DIV|FONT|TABLE|A|B|STYLE|TITLE|BODY|BR|P|!--)[ >]/si' => "text/html", + '/^\s*<\?xml/s' => "text/xml", + '/^%PDF-/s' => "application/pdf", + ]; + protected const SNIFF_PATTERN_UNKNWON_SAFE = [ + '/^%!PS-Adobe-/s' => "application/postscript", + '/^(?:(?:\x{FE}\x{FF}|\x{FF}\x{FE})..|\x{EF}\x{BB}\x{BF}.)/s' => "text/plain", + ]; protected $type = ""; protected $subtype = ""; @@ -265,4 +304,52 @@ PATTERN; } return null; } + + public static function interpretHttpMessage(\Psr\Http\Message\MessageInterface $msg, bool $sniff = true): ?self { + $checkForApacheBug = false; + // Use the last Content-Type header-field + $type = array_pop($msg->getHeader("Content-Type")); + if (!is_null($type)) { + if ($msg instanceof \Psr\Http\Message\ResponseInterface) { + $checkForApacheBug = (bool) preg_match("<^text/plain(?:; charset=(?:UTF-8|(?:ISO|iso)-8859-1))?$>", $type); + } + $type = static::decode($type); + } + # stub + return null; + } + + public static function sniffImage(string $resource): ?self { + foreach (self::SNIFF_PATTERNS_IMAGE as $pattern => $type) { + if (preg_match($pattern, $resource)) { + return static::parse($type); + } + } + return null; + } + + public static function sniffAudioVideo(string $resource): ?self { + foreach (self::SNIFF_PATTERNS_AUDIOVIDEO as $pattern => $type) { + if (preg_match($pattern, $resource)) { + return static::parse($type); + } + } + return static::sniffMp4($resource) ?? static::sniffWebm($resource) ?? static::sniffMp3($resource); + } + + protected static function sniffMp4(string $d): ?self { + if (strlen($d) < 12) { + return null; + } + $boxSize = hexdec(bin2hex(substr($d, 0, 4))); + return null; + } + + protected static function sniffWebm(string $d): ?self { + return null; + } + + protected static function sniffMp3(string $d): ?self { + return null; + } } diff --git a/vendor-bin/phpunit/composer.lock b/vendor-bin/phpunit/composer.lock index a3e47d0..014a604 100644 --- a/vendor-bin/phpunit/composer.lock +++ b/vendor-bin/phpunit/composer.lock @@ -493,16 +493,16 @@ }, { "name": "phpunit/php-file-iterator", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "354d4a5faa7449a377a18b94a2026ca3415e3d7a" + "reference": "4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/354d4a5faa7449a377a18b94a2026ca3415e3d7a", - "reference": "354d4a5faa7449a377a18b94a2026ca3415e3d7a", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4", + "reference": "4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4", "shasum": "" }, "require": { @@ -539,7 +539,7 @@ "filesystem", "iterator" ], - "time": "2020-02-07T06:05:22+00:00" + "time": "2020-04-18T05:02:12+00:00" }, { "name": "phpunit/php-invoker", @@ -642,16 +642,16 @@ }, { "name": "phpunit/php-timer", - "version": "3.0.0", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "4118013a4d0f97356eae8e7fb2f6c6472575d1df" + "reference": "35e732ff964693cb91ef4690f1f6016833b68f9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/4118013a4d0f97356eae8e7fb2f6c6472575d1df", - "reference": "4118013a4d0f97356eae8e7fb2f6c6472575d1df", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/35e732ff964693cb91ef4690f1f6016833b68f9d", + "reference": "35e732ff964693cb91ef4690f1f6016833b68f9d", "shasum": "" }, "require": { @@ -663,7 +663,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -687,7 +687,7 @@ "keywords": [ "timer" ], - "time": "2020-02-07T06:08:11+00:00" + "time": "2020-04-17T15:08:36+00:00" }, { "name": "phpunit/php-token-stream", @@ -1662,16 +1662,16 @@ }, { "name": "webmozart/assert", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", + "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6", + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6", "shasum": "" }, "require": { @@ -1679,7 +1679,7 @@ "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "vimeo/psalm": "<3.6.0" + "vimeo/psalm": "<3.9.1" }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" @@ -1706,7 +1706,7 @@ "check", "validate" ], - "time": "2020-02-14T12:15:55+00:00" + "time": "2020-04-18T12:12:48+00:00" } ], "aliases": [],