はじめに
PHPのファイル操作でたびたび登場する mime_content_type() 関数。
一見すると「拡張子で判定しているのでは?」と思いがちですが、実際はファイルの中身(バイナリ)からMIMEタイプを推定しています。
そのため、image.gif にリネームしたJPEGファイルを読み込んでも、結果は "image/jpeg" になります。
この挙動の理由を、実際のコードとマジックナンバーの仕組みを交えて解説します。
目次
📘 mime_content_type() の基本仕様
mime_content_type(resource|string $filename): string|false指定したファイルの MIME Content-Type を返します。
たとえば JPEG 画像なら "image/jpeg", PNG なら "image/png" が返ります。
🧪 実験:JPEGファイルを「image.gif」にリネームして判定
<?php
declare(strict_types=1);
error_reporting(-1);
var_dump(mime_content_type(__DIR__ . '/image.gif'));この image.gif は実際には JPEG ファイルです。
実行結果は次のとおり:
string(10) "image/jpeg"👀 結果は「image/jpeg」!
⚙️ なぜ拡張子を無視して正しく判定できるのか?
mime_content_type() は内部的に ファイルの先頭バイト(マジックナンバー) を読み取り、そこからファイル種別を推定します。
🧩 マジックナンバーの仕組み(図解)
+------------------------------------------------------+
| ファイルの先頭部分(バイナリデータ) |
+------------------------------------------------------+
| FF D8 FF E0 ... → JPEG (image/jpeg) |
| 89 50 4E 47 ... → PNG (image/png) |
| 47 49 46 38 ... → GIF (image/gif) |
+------------------------------------------------------+拡張子をいくら変えても、中身のマジックナンバーがJPEGであれば 「image/jpeg」 と判定されます。
💡 より強力な代替:finfo_file()
同じ仕組みをより正確に扱えるのが finfo ファミリの関数です。
$finfo = finfo_open(FILEINFO_MIME_TYPE);
echo finfo_file($finfo, __DIR__ . '/image.gif'); // image/jpeg
finfo_close($finfo);こちらのほうが、文字コード情報なども取得でき、上位互換的な使い方ができます。
❌ よくある誤解
| 誤解 | 正しい理解 |
|---|---|
| mime_content_type() は拡張子を見て判断している | ファイルの中身を見て判断している |
| ファイル名を変えれば MIME タイプも変わる | 中身が変わらなければ MIME タイプは変わらない |
✅ まとめ
| 項目 | 内容 |
|---|---|
| 関数 | mime_content_type() |
| 判定基準 | ファイルの中身(マジックナンバー) |
| 拡張子変更の影響 | なし |
実行結果 "image/jpeg" | 正しい挙動! |
🧠 試験ポイントメモ
mime_content_type()は fileinfo拡張に依存している- 拡張子ではなく中身で判定する
finfo_file()が後継として推奨されている
