MIME Type Reference — PHP source
A searchable map of common file extensions to their MIME types - text, image, audio, video, application, font, and multipart. Look up by extension or by type, filter by category, and copy the exact Content-Type string for any format.
This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.
<?php
/**
* MIME / Content-Type reference — pure, deterministic data + lookups.
*
* Language: PHP (8.0+, uses `str_contains`, arrow functions, named args).
* CosmoDev polyglot showcase port of the `mime-types` tool, ported from
* src/lib/mime-types.ts. Display source — part of CosmoDev's polyglot tool
* pages.
*
* A curated set of common file extensions mapped to their standard MIME types,
* grouped by the seven top-level media types (RFC 2046): text, image, audio,
* video, application, font, and multipart. Each entry's `type` always equals
* the top-level segment of its own `mime`, so the category filter and the
* Content-Type never disagree.
*/
declare(strict_types=1);
namespace CosmoDev\MimeTypes;
/**
* Curated extension ↔ MIME reference table and the pure lookups over it.
*
* Static API: the table is immutable constant data, so every method is static
* and stateless — no need to instantiate.
*/
final class MimeTypes
{
/** Display order of the categories (used by the UI filter). */
public const MIME_CATEGORIES = [
'text',
'image',
'audio',
'video',
'application',
'font',
'multipart',
];
/**
* The curated extension ↔ MIME table. One row is one (extension, type)
* pair, so an extension with several MIME types (e.g. `.js`) appears more
* than once, and a MIME with several extensions (e.g. `image/jpeg`)
* appears once per extension. Values follow IANA / MDN conventions.
*
* Each row is an associative array shaped like the TypeScript `MimeEntry`:
* ['ext' => string, 'mime' => string, 'type' => string, 'name' => string]
*/
public const MIME = [
// ── text ────────────────────────────────────────────────────────────
['ext' => 'txt', 'mime' => 'text/plain', 'type' => 'text', 'name' => 'Plain text'],
['ext' => 'html', 'mime' => 'text/html', 'type' => 'text', 'name' => 'HTML document'],
['ext' => 'htm', 'mime' => 'text/html', 'type' => 'text', 'name' => 'HTML document'],
['ext' => 'xhtml', 'mime' => 'application/xhtml+xml', 'type' => 'application', 'name' => 'XHTML'],
['ext' => 'css', 'mime' => 'text/css', 'type' => 'text', 'name' => 'CSS stylesheet'],
['ext' => 'csv', 'mime' => 'text/csv', 'type' => 'text', 'name' => 'Comma-separated values'],
['ext' => 'xml', 'mime' => 'application/xml', 'type' => 'application', 'name' => 'XML document'],
['ext' => 'js', 'mime' => 'text/javascript', 'type' => 'text', 'name' => 'JavaScript'],
['ext' => 'js', 'mime' => 'application/javascript', 'type' => 'application', 'name' => 'JavaScript (legacy)'],
['ext' => 'mjs', 'mime' => 'text/javascript', 'type' => 'text', 'name' => 'JavaScript module'],
['ext' => 'json', 'mime' => 'application/json', 'type' => 'application', 'name' => 'JSON'],
['ext' => 'md', 'mime' => 'text/markdown', 'type' => 'text', 'name' => 'Markdown'],
['ext' => 'yaml', 'mime' => 'text/yaml', 'type' => 'text', 'name' => 'YAML'],
['ext' => 'yml', 'mime' => 'text/yaml', 'type' => 'text', 'name' => 'YAML'],
['ext' => 'ics', 'mime' => 'text/calendar', 'type' => 'text', 'name' => 'iCalendar'],
['ext' => 'rtf', 'mime' => 'application/rtf', 'type' => 'application', 'name' => 'Rich text'],
['ext' => 'tsv', 'mime' => 'text/tab-separated-values', 'type' => 'text', 'name' => 'Tab-separated values'],
// ── image ───────────────────────────────────────────────────────────
['ext' => 'jpg', 'mime' => 'image/jpeg', 'type' => 'image', 'name' => 'JPEG image'],
['ext' => 'jpeg', 'mime' => 'image/jpeg', 'type' => 'image', 'name' => 'JPEG image'],
['ext' => 'png', 'mime' => 'image/png', 'type' => 'image', 'name' => 'PNG image'],
['ext' => 'gif', 'mime' => 'image/gif', 'type' => 'image', 'name' => 'GIF image'],
['ext' => 'webp', 'mime' => 'image/webp', 'type' => 'image', 'name' => 'WebP image'],
['ext' => 'avif', 'mime' => 'image/avif', 'type' => 'image', 'name' => 'AVIF image'],
['ext' => 'svg', 'mime' => 'image/svg+xml', 'type' => 'image', 'name' => 'SVG image'],
['ext' => 'bmp', 'mime' => 'image/bmp', 'type' => 'image', 'name' => 'Bitmap image'],
['ext' => 'ico', 'mime' => 'image/x-icon', 'type' => 'image', 'name' => 'Icon'],
['ext' => 'tif', 'mime' => 'image/tiff', 'type' => 'image', 'name' => 'TIFF image'],
['ext' => 'tiff', 'mime' => 'image/tiff', 'type' => 'image', 'name' => 'TIFF image'],
['ext' => 'heic', 'mime' => 'image/heic', 'type' => 'image', 'name' => 'HEIC image'],
// ── audio ───────────────────────────────────────────────────────────
['ext' => 'mp3', 'mime' => 'audio/mpeg', 'type' => 'audio', 'name' => 'MP3 audio'],
['ext' => 'wav', 'mime' => 'audio/wav', 'type' => 'audio', 'name' => 'WAV audio'],
['ext' => 'ogg', 'mime' => 'audio/ogg', 'type' => 'audio', 'name' => 'Ogg audio'],
['ext' => 'oga', 'mime' => 'audio/ogg', 'type' => 'audio', 'name' => 'Ogg audio'],
['ext' => 'flac', 'mime' => 'audio/flac', 'type' => 'audio', 'name' => 'FLAC audio'],
['ext' => 'aac', 'mime' => 'audio/aac', 'type' => 'audio', 'name' => 'AAC audio'],
['ext' => 'm4a', 'mime' => 'audio/mp4', 'type' => 'audio', 'name' => 'M4A audio'],
['ext' => 'weba', 'mime' => 'audio/webm', 'type' => 'audio', 'name' => 'WebM audio'],
['ext' => 'mid', 'mime' => 'audio/midi', 'type' => 'audio', 'name' => 'MIDI'],
['ext' => 'midi', 'mime' => 'audio/midi', 'type' => 'audio', 'name' => 'MIDI'],
// ── video ───────────────────────────────────────────────────────────
['ext' => 'mp4', 'mime' => 'video/mp4', 'type' => 'video', 'name' => 'MP4 video'],
['ext' => 'webm', 'mime' => 'video/webm', 'type' => 'video', 'name' => 'WebM video'],
['ext' => 'avi', 'mime' => 'video/x-msvideo', 'type' => 'video', 'name' => 'AVI video'],
['ext' => 'mov', 'mime' => 'video/quicktime', 'type' => 'video', 'name' => 'QuickTime video'],
['ext' => 'mkv', 'mime' => 'video/x-matroska', 'type' => 'video', 'name' => 'Matroska video'],
['ext' => 'mpeg', 'mime' => 'video/mpeg', 'type' => 'video', 'name' => 'MPEG video'],
['ext' => 'mpg', 'mime' => 'video/mpeg', 'type' => 'video', 'name' => 'MPEG video'],
['ext' => 'ogv', 'mime' => 'video/ogg', 'type' => 'video', 'name' => 'Ogg video'],
['ext' => 'm4v', 'mime' => 'video/mp4', 'type' => 'video', 'name' => 'M4V video'],
// ── application ─────────────────────────────────────────────────────
['ext' => 'pdf', 'mime' => 'application/pdf', 'type' => 'application', 'name' => 'PDF document'],
['ext' => 'zip', 'mime' => 'application/zip', 'type' => 'application', 'name' => 'ZIP archive'],
['ext' => 'gz', 'mime' => 'application/gzip', 'type' => 'application', 'name' => 'Gzip archive'],
['ext' => 'tar', 'mime' => 'application/x-tar', 'type' => 'application', 'name' => 'Tar archive'],
['ext' => 'rar', 'mime' => 'application/vnd.rar', 'type' => 'application', 'name' => 'RAR archive'],
['ext' => '7z', 'mime' => 'application/x-7z-compressed', 'type' => 'application', 'name' => '7-Zip archive'],
['ext' => 'bin', 'mime' => 'application/octet-stream', 'type' => 'application', 'name' => 'Binary data'],
['ext' => 'exe', 'mime' => 'application/x-msdownload', 'type' => 'application', 'name' => 'Windows executable'],
['ext' => 'jar', 'mime' => 'application/java-archive', 'type' => 'application', 'name' => 'Java archive'],
['ext' => 'wasm', 'mime' => 'application/wasm', 'type' => 'application', 'name' => 'WebAssembly'],
['ext' => 'doc', 'mime' => 'application/msword', 'type' => 'application', 'name' => 'Word document'],
['ext' => 'docx', 'mime' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'type' => 'application', 'name' => 'Word document (OOXML)'],
['ext' => 'xls', 'mime' => 'application/vnd.ms-excel', 'type' => 'application', 'name' => 'Excel spreadsheet'],
['ext' => 'xlsx', 'mime' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'type' => 'application', 'name' => 'Excel spreadsheet (OOXML)'],
['ext' => 'ppt', 'mime' => 'application/vnd.ms-powerpoint', 'type' => 'application', 'name' => 'PowerPoint'],
['ext' => 'pptx', 'mime' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'type' => 'application', 'name' => 'PowerPoint (OOXML)'],
// ── font ────────────────────────────────────────────────────────────
['ext' => 'woff', 'mime' => 'font/woff', 'type' => 'font', 'name' => 'WOFF font'],
['ext' => 'woff2', 'mime' => 'font/woff2', 'type' => 'font', 'name' => 'WOFF2 font'],
['ext' => 'ttf', 'mime' => 'font/ttf', 'type' => 'font', 'name' => 'TrueType font'],
['ext' => 'otf', 'mime' => 'font/otf', 'type' => 'font', 'name' => 'OpenType font'],
['ext' => 'eot', 'mime' => 'application/vnd.ms-fontobject', 'type' => 'application', 'name' => 'Embedded OpenType'],
// ── multipart ───────────────────────────────────────────────────────
['ext' => 'mht', 'mime' => 'multipart/related', 'type' => 'multipart', 'name' => 'MHTML web archive'],
['ext' => 'mhtml', 'mime' => 'multipart/related', 'type' => 'multipart', 'name' => 'MHTML web archive'],
];
/**
* Lowercase an extension and drop a leading dot so `.JSON` and `json` match.
* `ltrim($s, '.')` strips one or more leading dots, matching the TypeScript
* `/^\.+/` regex.
*/
public static function normalizeExt(string $ext): string
{
return ltrim(strtolower(trim($ext)), '.');
}
/**
* Find every entry whose extension matches. The dot is optional and matching
* is case-insensitive, so `.js`, `JS`, and `js` are equivalent. Returns
* `[]` for an unknown/empty extension.
*
* @return list<array{ext: string, mime: string, type: string, name: string}>
*/
public static function lookupByExt(string $ext): array
{
$e = self::normalizeExt($ext);
if ($e === '') {
return [];
}
// array_values reindexes to a 0-based list so the result matches the
// contiguous array shape callers expect from the TypeScript port.
return array_values(array_filter(
self::MIME,
static fn (array $m): bool => $m['ext'] === $e,
));
}
/**
* Find every entry whose MIME type matches, case-insensitively (also
* tolerating surrounding whitespace). One MIME commonly maps to several
* extensions — e.g. `image/jpeg` → `jpg`, `jpeg` — so all are returned.
* `[]` for unknown/empty.
*
* @return list<array{ext: string, mime: string, type: string, name: string}>
*/
public static function lookupByMime(string $mime): array
{
$m = trim(strtolower($mime));
if ($m === '') {
return [];
}
return array_values(array_filter(
self::MIME,
static fn (array $e): bool => strtolower($e['mime']) === $m,
));
}
/**
* Filter the full table by category and/or a free-text query. Omitted
* options are ignored; an empty query returns the category-filtered set
* unchanged. An unknown category value yields nothing (it is not silently
* coerced).
*
* `array_key_exists` (not `isset`) is used for `type` so that a `null` value
* is treated as a real constraint that matches nothing, mirroring the
* TypeScript `!== undefined` semantics exactly.
*
* @param array{type?: string|null, query?: string|null} $opts
* @return list<array{ext: string, mime: string, type: string, name: string}>
*/
public static function filter(array $opts = []): array
{
$query = array_key_exists('query', $opts) ? (string) $opts['query'] : '';
$q = trim(strtolower($query));
return array_values(array_filter(
self::MIME,
static function (array $e) use ($opts, $q): bool {
// Present-but-null type is a real (impossible) constraint → no match,
// matching TypeScript's `e.type !== opts.type` against null.
if (array_key_exists('type', $opts) && $e['type'] !== $opts['type']) {
return false;
}
if ($q !== '') {
return str_contains(strtolower($e['ext']), $q)
|| str_contains(strtolower($e['mime']), $q)
|| str_contains(strtolower($e['name']), $q);
}
return true;
},
));
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →