こんにちは、かつコーチです。
前回は「ファイルが見つかりません」エラーの原因と対処法を扱いました。
今回は、日本語を扱うPHP開発でほぼ全員がぶつかる「文字化け」について解説します。
画面に「縺薙s縺ォ縺。縺ッ」のような意味不明な文字列が表示されて、思わずページを閉じたくなった経験がある人も多いのではないでしょうか。
文字化けは原因さえ分かれば、実は対処自体はシンプルです。
順番に見ていきましょう。
文字化けが起きる仕組み
「文字コード」とは
文字コードとは、コンピュータの中で文字をどんな数値(バイト列)として扱うかを決めたルールのことです。
同じ「あ」という文字でも、文字コードの種類によって内部的には全く違う数値として保存されています。
代表的な文字コードには、次のようなものがあります。
| 文字コード | 特徴 |
|---|---|
| UTF-8 | 現在の標準。日本語含め世界中の文字を扱える |
| Shift_JIS | 昔のWindows環境で主流だった日本語用コード |
| EUC-JP | UNIX系環境で使われていた日本語用コード |
文字化けが起きる理由
文字化けは、「保存・送信されたときの文字コード」と「読み込む・表示するときに想定している文字コード」が食い違うことで発生します。
たとえるなら、モールス信号で送られたメッセージを、モールス信号のルールを知らない人がそのまま英字として読もうとしているようなイメージです。
同じ文字コードのルールで「書く」「読む」を統一しない限り、正しく変換されません。
文字化けの典型パターン
パターン1:HTMLの文字コード指定が抜けている
一番シンプルな原因は、ブラウザに「このページはこの文字コードで書かれています」と伝え忘れているケースです。
<?php
// ❌ Before:文字コードの指定がない
echo "<html>";
echo "<head><title>サンプル</title></head>";
echo "<body>こんにちは、かつコーチです。</body>";
echo "</html>";
ブラウザは文字コードの指定がないと、推測で表示しようとして失敗することがあります。
<?php
// ✅ After:meta タグで文字コードを明示する
echo "<html>";
echo "<head>";
echo '<meta charset="UTF-8">';
echo "<title>サンプル</title>";
echo "</head>";
echo "<body>こんにちは、かつコーチです。</body>";
echo "</html>";
<meta charset="UTF-8">を<head>内の先頭付近に入れておくことで、ブラウザに文字コードを正しく伝えられます。
パターン2:ファイル自体の保存形式が違う
エディタでPHPファイルを保存するとき、文字コードの選択を誤ると文字化けします。
私がまだ駆け出しだった頃、Windows用の古いテキストエディタで保存したPHPファイルを本番サーバーにアップロードしたら、日本語部分だけが「�」の羅列になって表示された、という経験があります。
原因は、そのエディタのデフォルト保存形式がShift_JISになっていたことでした。
エディタの保存文字コードは必ずUTF-8(BOMなし)に統一する、これが今も習慣として身についています。
VSCodeなど主要なエディタは、右下や設定画面で保存時の文字コードを確認・変更できます。
データベース接続時の文字コード設定
PDOでの文字コード指定を忘れる
データベースとやり取りするときも、文字コードのズレによる文字化けが起きやすいポイントです。
<?php
// ❌ Before:接続時に文字コードを指定していない
$pdo = new PDO(
"mysql:host=localhost;dbname=my_app",
"root",
"password"
);
$stmt = $pdo->query("SELECT name FROM users WHERE id = 1");
$user = $stmt->fetch();
echo $user["name"];
// データベース内の日本語データが文字化けして表示されることがある
かつコーチが実際につまずいたケース
これは私が初めてPDOでMySQLに接続したときに実際に遭遇したエラーです。
登録したはずの「かつコーチ」というユーザー名が、画面には「??????」と表示されてしまいました。
DBのテーブル自体の文字コード設定は正しくutf8mb4にしていたのに、なぜか化けるという状況で、半日ほど原因を探し回りました。
結局、PHPからMySQLへの「接続時」の文字コード指定が抜けていたことが原因でした。
テーブルの文字コードが正しくても、接続時にPHP側とMySQL側でやり取りする文字コードを明示していないと、意図しない変換が起きてしまうのです。
<?php
// ✅ After:DSNに charset=utf8mb4 を指定する
$pdo = new PDO(
"mysql:host=localhost;dbname=my_app;charset=utf8mb4",
"root",
"password"
);
$stmt = $pdo->query("SELECT name FROM users WHERE id = 1");
$user = $stmt->fetch();
echo $user["name"];
// 出力: かつコーチ
DSN(接続情報の文字列)にcharset=utf8mb4を追加するだけで、この文字化けはあっさり解決しました。
以来、PDOで接続するときはcharset=utf8mb4をセットで書くことを徹底しています。
mb_convert_encodingで文字コードを変換する
基本的な使い方
mb_convert_encoding()は、文字列の文字コードを別の文字コードに変換してくれる関数です。
外部システムから受け取ったデータや、古いShift_JISのファイルを扱うときに活躍します。
<?php
// Shift_JISで書かれた文字列をUTF-8に変換する
$sjisText = mb_convert_encoding("こんにちは", "SJIS", "UTF-8");
// SJISの文字列をUTF-8に戻す
$utf8Text = mb_convert_encoding($sjisText, "UTF-8", "SJIS");
echo $utf8Text;
// 出力: こんにちは
第1引数が変換したい文字列、第2引数が変換後の文字コード、第3引数が変換前の文字コードです。
文字コードが分からないファイルを扱うとき
CSVファイルなど、文字コードが不明なファイルを取り込むときは、mb_detect_encoding()と組み合わせると安全です。
<?php
// ✅ ファイルの文字コードを自動判定してからUTF-8に統一する
$rawText = file_get_contents(__DIR__ . "/import_data.csv");
$detectedEncoding = mb_detect_encoding(
$rawText,
["UTF-8", "SJIS", "EUC-JP"],
true
);
$utf8Text = mb_convert_encoding($rawText, "UTF-8", $detectedEncoding);
echo $utf8Text;
mb_detect_encoding()の第2引数に、候補となる文字コードを配列で渡しておくと、その中から最も近いものを判定してくれます。
取引先から送られてくるCSVがShift_JISで書かれている、というのは今でもよくある実務あるあるなので、このパターンは覚えておいて損はありません。
まとめ
この記事のポイント
- 文字化けは「書き込み時」と「読み込み時」の文字コードが食い違うことで起きる
- HTML出力時は
<meta charset="UTF-8">、エディタの保存時はUTF-8に統一する - PDOでDB接続する際は、DSNに
charset=utf8mb4を必ず指定する - 文字コード不明なデータには
mb_detect_encoding()+mb_convert_encoding()で統一する - 「テーブルの文字コードは合っているのに文字化けする」場合は接続時の設定を疑う
次に読むべき記事
文字化けが解決できたら、次はPHP開発に欠かせないツール「Composer」について理解を深めていきましょう。
→ 次の記事:Composerとは?PHPのパッケージ管理を理解する