何が起きているのか
PHPは1行も実行する前に、スクリプト全体を上から下まで読み込んで構文チェックを行います。セミコロンの欠落、閉じていない括弧、余分な文字があると、その時点で処理を止めてParse errorを投げます。スクリプトは一切実行されません。
エラーはこのような形式で表示されます:
Parse error: syntax error, unexpected token "echo", expecting "," or ";" in /var/www/html/index.php on line 15
PHPが言いたいのは「前のステートメントを閉じるためにカンマかセミコロンを期待していたのに、echoが見つかった」ということです。重要なポイント:実際の問題はほとんどの場合、報告された行にはありません。たいていはその1行上、つまり何かが閉じられていない行が原因です。
デバッグの手順
1. 報告された行の1行前を確認する
報告された15行目の1つ上、14行目を見てください。十中八九、そこにセミコロンが欠けています。
<?php
// 14行目 — 末尾にセミコロンがない
$message = "Hello, world"
echo $message; // 15行目 — PHPはここでつまずく
セミコロンを追加すれば解決です:
$message = "Hello, world";
echo $message;
2. ターミナルからPHP構文チェックを実行する
推測に頼る必要はありません。php -l(lint)を使えば、スクリプトを実際に実行せずに正確なエラーを確認できます:
php -l /var/www/html/index.php
問題がない場合:
No syntax errors detected in /var/www/html/index.php
エラーがある場合:
Parse error: syntax error, unexpected token "echo", expecting "," or ";" in /var/www/html/index.php on line 15
これをワークフローに組み込んで、ブラウザでテストする前に毎回実行する習慣をつけましょう。
3. 開発中はエラー表示を有効にする
真っ白なページが表示されていますか?PHPがエラーを隠しています。スクリプトの先頭に一時的にこの3行を追加してください:
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
php.iniで設定する場合:
display_errors = On
error_reporting = E_ALL
php.iniを編集したら、サーバーを再起動してください:
sudo systemctl restart php8.2-fpm
# または
sudo systemctl restart apache2
よくある原因と修正方法
セミコロンの欠落
このエラーの大半の原因です。PHPのすべてのステートメントは;で終わります — 例外はありません。
// 誤り
$name = "Alice"
echo $name;
// 修正後
$name = "Alice";
echo $name;
引用符の不一致または閉じ忘れ
引用符を開いて閉じ忘れると、PHPは対応する引用符が見つかるまで読み続けます — 改行を含めて間にあるすべてを取り込んでしまいます。
// 誤り — シングルクォートが開かれたまま閉じていない
$sql = 'SELECT * FROM users WHERE id = 1;
echo $sql;
// 修正後
$sql = 'SELECT * FROM users WHERE id = 1';
echo $sql;
括弧の不一致または閉じ忘れ
// 誤り — 閉じ括弧が欠けている
if ($x > 0 {
echo "positive";
}
// 修正後
if ($x > 0) {
echo "positive";
}
ヒアドキュメントの構文エラー
ヒアドキュメントには厳格なフォーマット規則があります。PHP 7.3より古いバージョンでは、閉じる識別子は0列目に置く必要があります — 先頭のスペースも末尾のスペースも不可、ラベルとセミコロンだけです。
// PHP < 7.3 では誤り — 閉じタグがインデントされている
$text = <<<EOT
Some text
EOT;
// すべてのPHPバージョンで正しい — 閉じタグは0列目
$text = <<<EOT
Some text
EOT;
// PHP 7.3以降では閉じタグの一貫したインデントも許可される
$text = <<<EOT
Some text
EOT;
PHP 7サーバーでPHP 8の構文を使用している
名前付き引数、match式、nullsafeオペレーター(?->)はすべてPHP 8.0以上が必要です。PHP 7でそのコードを実行すると、即座にパースエラーが発生します。
# 実際に動作しているバージョンを確認する
php --version
# 名前付き引数 — PHP 8.0以上のみ
array_slice(array: $arr, offset: 0, length: 3); // PHP 7.xでは失敗する
PHP 8にアップグレードするか、影響を受けるコードを位置引数を使用するように書き直してください。
BOMまたは不可視文字
Wordドキュメントからペーストしたファイルや、UTF-8 BOMエンコードで保存されたファイルには、<?phpの前に不可視バイトが含まれていることがあります。PHPはそれを認識して混乱し、コードに達する前に終了してしまいます。
# BOMを確認する
file /var/www/html/index.php
# sedで除去する
sed -i '1s/^\xEF\xBB\xBF//' /var/www/html/index.php
修正を確認する
変更を加えたら、まずlintを実行してからブラウザでテストしてください。lintのステップをスキップしないでください。
php -l /var/www/html/index.php
その後、スクリプトを直接実行するかHTTP経由でアクセスします:
php /var/www/html/index.php
# または
curl -I http://localhost/index.php
Nginx + PHP-FPMを使用していますか?FPMエラーログも確認してください — ブラウザに表示されないエラーがここに出ることがあります:
sudo tail -f /var/log/php8.2-fpm.log
得られた教訓
- **報告された行が壊れている行であることはほとんどありません。**その1〜2行上を見てください。閉じていない式はそこから始まっており、PHPが警告する場所ではありません。
- **
php -lを頻繁に実行してください。**エディターの保存アクションやpre-commitフックに組み込みましょう。構文エラーをサーバーに到達させるべきではありません。 - **本番環境ではdisplay_errorsをオフにしてください。**パースエラーはファイルパスや内部コード構造を露出させます。サーバー側でログに記録し、ユーザーには絶対に表示しないでください。
- **リアルタイムlinterをインストールしてください。**VS CodeのPHP IntelephenseやPhpStormの組み込みインスペクターは、入力中に構文エラーを検出します — セミコロンを忘れたことを実行時まで待って気づく必要がなくなります。

