はじめに
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()
が後継として推奨されている