PHPのデバッグの方法について

採用はこちら

PHPのデバッグは、ただエラー文を見るだけではなく、「どこで」「なぜ」「どんな値で」処理が失敗したのかを順番に特定していく作業です。

特にPHPは、設定や実行環境によってはエラーが画面に表示されなかったり、実際の原因が別のファイルや別の関数にあったりすることがよくあります。

そのため、デバッグでは場当たり的にコードを書き換えるのではなく、エラー表示・ログ・変数確認・処理の流れの観察を組み合わせて進めることが重要です。

ここでは、初心者にも分かりやすく、実務でも通用する形でPHPのデバッグ方法を整理して説明します。

目次

PHPのデバッグとは何か

PHPのデバッグでは、主に次の4つを確認します。

  1. エラーが起きているか
  2. どこで起きているか
  3. その時の変数や配列の中身がどうなっているか
  4. なぜその状態になったのか

たとえば、画面が真っ白になった場合でも、実際には内部で次のような問題が起きていることがあります。

  • 構文エラー
  • 存在しない関数やメソッドの呼び出し
  • 未定義変数や未定義配列キーの参照
  • DB接続エラー
  • 例外未処理
  • 条件分岐や型比較のミス
  • 読み込むファイルのパス間違い

つまりデバッグとは、コードを眺めて勘で直すことではなく、PHPが実際にどう動いたかを確認することです。

まず最初にやるべき設定

PHPのデバッグで最初に重要なのは、エラーが見える状態を作ることです。

開発環境ではエラー表示を有効にする

開発中は、次の設定を使うことが多いです。

<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

それぞれの役割は次の通りです。

  • display_errors
    実行時エラーを画面に表示する設定
  • display_startup_errors
    PHP起動時のエラーも表示する設定
  • error_reporting(E_ALL)
    どの種類のエラーを報告対象にするかを設定するもの

ここで注意したいのは、error_reporting(E_ALL) は「表示する」設定ではなく、「対象にする」設定だという点です。

画面表示は display_errors、ログ出力は log_errors が関係します。

補足

この3行は便利ですが、同じファイルの構文エラーそのものには効かない場合があります。

構文エラーはPHPがそのコードを実行する前に止まることがあるため、その場合は php.ini やサーバーログの確認が必要です。

php.iniでも確認しておきたい設定

PHPの動作は php.ini の影響を受けます。

開発環境では、次のような設定がよく使われます。

display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On

見るべきポイントは次の4つです。

  • エラーを画面に表示するか
  • エラーをログに保存するか
  • どのレベルのエラーまで拾うか
  • 実際にその php.ini が読み込まれているか

設定変更後は、Apache や PHP-FPM の再起動が必要になることがあります。

本番環境では画面にエラーを出さない

本番環境では、利用者にエラー内容をそのまま見せてはいけません。

ファイルパスや内部構造などの情報が漏れる可能性があるためです。

本番では、基本的に次の方針にします。

  • display_errors = Off
  • log_errors = On
  • エラーはログに記録する
  • 画面には一般的なメッセージだけ表示する

try {
    // 何らかの処理
} catch (Throwable $e) {
    error_log($e->getMessage());
    echo 'システムエラーが発生しました。';
}

ここで Throwable を使っているのは、Exception だけでなく Error 系も含めて扱えるようにするためです。

もっとも基本的なデバッグ方法

var_dump() で値と型を確認する

デバッグの基本は var_dump() です。

$name = "Taro";
var_dump($name);

出力例

string(4) "Taro"

var_dump() は値だけでなく、型や長さまで表示してくれるため、PHPのデバッグでは非常に重要です。

たとえば次のように使います。

$age = "20";
var_dump($age == 20);   // true
var_dump($age === 20);  // false

これで、見た目は同じでも "20" は文字列、20 は整数だと分かります。

print_r() で配列やオブジェクトを見やすく確認する

print_r() は、配列やオブジェクトの中身を人間が読みやすい形で確認したい時に便利です。

$user = [
    'name' => 'Yamada',
    'age' => 30
];

print_r($user);

出力例

Array
(
    [name] => Yamada
    [age] => 30
)

使い分けとしては、

  • 型まで詳しく確認したい → var_dump()
  • 配列やオブジェクトをざっと見たい → print_r()

と考えると分かりやすいです。

HTMLの中では <pre> を付けると見やすい

ブラウザ上では改行やインデントが崩れることがあるので、次のようにすると見やすくなります。

echo '<pre>';
print_r($data);
echo '</pre>';

または

echo '<pre>';
var_dump($data);
echo '</pre>';

echo で処理の流れを確認する

デバッグでは、処理がどこまで進んだかを見るのも有効です。

echo 'ここまで来た1';
echo 'ここまで来た2';

条件分岐の確認にも使えます。

if ($user) {
    echo 'ifに入った';
} else {
    echo 'elseに入った';
}

単純ですが、どこで止まっているかを把握するにはかなり役立ちます。

exitdie() で途中停止する

怪しい場所で処理を止めると、原因の切り分けがしやすくなります。

echo '<pre>';
var_dump($data);
echo '</pre>';
exit;

これにより、

  • その時点の変数の状態
  • そこまでは正常に進んでいるか
  • その先で問題が起きるのか

を確認できます。

エラーログを活用する

画面に出せない場合や、Ajax・API・CLI処理では error_log() が便利です。

error_log('ここまで来た');
error_log(print_r($data, true));

print_r($data, true) のように第2引数を true にすると、表示せずに文字列として取得できます。

$user = ['name' => 'Tanaka', 'age' => 28];
error_log(print_r($user, true));

ログ出力は、画面を汚さずに状況を追いたい時に特に有効です。

PHPのエラーの種類を理解する

エラーメッセージは種類ごとに意味が違います。

Parse error

構文エラーです。

コードの書き方自体に問題があります。

if ($a > 10) {
    echo "ok"
}

この場合はセミコロンがないためエラーになります。

よくある原因は次の通りです。

  • セミコロンの付け忘れ
  • 括弧や波括弧の閉じ忘れ
  • クォートの閉じ忘れ
  • 配列のカンマ抜け

なお、構文エラーは表示された行の少し前に原因があることも多いです。

Fatal error

致命的なエラーです。

処理が継続できず停止します。

たとえば、

  • 存在しない関数の呼び出し
  • 存在しないメソッドの呼び出し
  • 型エラー
  • 必須引数不足

などです。

testFunction();

testFunction() が定義されていなければ、致命的なエラーになります。

Warning

警告です。

処理が継続する場合もありますが、放置すべきではありません。

include 'missing.php';

ファイルが存在しない場合は警告になります。

Warning は「まだ動いているから大丈夫」と思われがちですが、その後の処理が壊れていることも多いです。

Notice / Warning に関する注意

古いPHP記事では「未定義変数は Notice」と説明されることが多いですが、PHP 8以降では未定義変数は Warning として扱われます

そのため、古い解説を読むときはPHPのバージョン差に注意が必要です。

条件分岐のデバッグ

条件分岐のミスは非常に多いです。

PHPは型変換が起きやすいため、見た目だけで判断するとバグを見逃します。

$status = "0";

if ($status) {
    echo "ON";
} else {
    echo "OFF";
}

このようなケースでは、値だけでなく真偽値への変換も確認すると分かりやすいです。

var_dump($status);
var_dump((bool)$status);

また、比較演算子の違いも重要です。

  • == は型変換あり
  • === は型も含めて比較
var_dump(0 == "0");    // true
var_dump(0 === "0");   // false

実務では、できるだけ === を使う方が安全です。

配列のデバッグ

PHPでは配列まわりのミスが非常に多く発生します。

キーの存在確認

if (isset($user['name'])) {
    echo $user['name'];
}

または

if (array_key_exists('name', $user)) {
    echo $user['name'];
}

違いは次の通りです。

  • isset() は値が null だと false
  • array_key_exists() はキーが存在するかどうかを見る

配列全体を確認する

echo '<pre>';
print_r($user);
echo '</pre>';

たとえば本当は username なのに user_name と書いていた、というようなミスはよくあります。

フォーム送信のデバッグ

フォーム処理では、まず $_POST$_GET の中身を見るのが基本です。

echo '<pre>';
print_r($_POST);
echo '</pre>';

これで次の点を確認できます。

  • 値が本当に送られているか
  • name 属性が正しいか
  • 空送信になっていないか
  • method="post" / method="get" が想定通りか

たとえばHTML側が

<input type="text" name="username">

なのに、PHP側で

$_POST['user_name']

としていれば値は取得できません。

セッション・クッキーのデバッグ

ログイン処理や一時保存データでは、セッション確認が重要です。

session_start();
var_dump($_SESSION);

クッキー確認も役立ちます。

var_dump($_COOKIE);

特に、

  • セッション開始前に参照していないか
  • クッキー名が違っていないか
  • 有効期限切れになっていないか

はよくある確認ポイントです。

ファイル読み込みのデバッグ

includerequire のエラーもよくあります。

var_dump($path);
var_dump(file_exists($path));

これで、

  • パスが本当に想定通りか
  • 相対パスと絶対パスがズレていないか
  • 実行ディレクトリの違いで失敗していないか

を確認できます。

データベースのデバッグ

DBまわりでは、例外を有効にしておくことが重要です。

PDOで例外モードを使う

$pdo = new PDO($dsn, $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

これを設定しておくと、SQL失敗時に原因を追いやすくなります。

try-catch で囲む

try {
    $pdo = new PDO($dsn, $user, $pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $stmt = $pdo->query("SELECT * FROM users");
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    echo '<pre>';
    print_r($rows);
    echo '</pre>';

} catch (Throwable $e) {
    echo 'DBエラー: ' . $e->getMessage();
}

これで、

  • 接続エラー
  • SQL文法ミス
  • テーブル名やカラム名のミス

などを把握しやすくなります。

SQLと値を確認する

プリペアドステートメントでは、SQL自体ではなくバインド値のミスが原因になることも多いです。

$sql = "SELECT * FROM users WHERE id = :id";
$id = 5;

var_dump($sql, $id);

例外処理は Exception だけでなく Throwable も意識する

PHP 7以降では、Exception だけでなく Error 系も重要です。

そのため、広く捕まえたい場合は Throwable を使う方が安全です。

try {
    $result = riskyFunction();
    var_dump($result);
} catch (Throwable $e) {
    echo 'エラー: ' . $e->getMessage();
}

ただし、何でもかんでも握りつぶすのではなく、

  • 開発中は原因確認のために表示
  • 本番ではログ出力して利用者には一般的な文言を表示

という使い分けが重要です。

Xdebugを使った本格的なデバッグ

より本格的にデバッグするなら、Xdebugが非常に強力です。

var_dump() だけでは追いにくい複雑な不具合でも、かなり調査しやすくなります。

Xdebugを使うと、次のことができます。

  • ブレークポイントを置く
  • 1行ずつ実行する
  • その場で変数の中身を見る
  • 関数の呼び出し順を追う
  • スタックトレースを確認する

ブレークポイント

「この行で一時停止する」という目印です。

たとえばログイン処理の途中で止めて、

  • $_POST に何が入っているか
  • DBの取得結果はどうか
  • 条件分岐がどちらに入るか

をその場で確認できます。

ステップ実行

処理を少しずつ進める方法です。

  • Step Over: 次の行へ進む
  • Step Into: 関数の中に入る
  • Step Out: 現在の関数から抜ける

複数ファイルをまたぐ処理や、フレームワーク内部の動き確認に役立ちます。

VS Codeでの基本的なXdebug設定例

VS CodeでPHPデバッグを行う場合、一般的には次の流れです。

  1. PHPをインストール
  2. Xdebugを有効化
  3. VS CodeにPHP Debug拡張を入れる
  4. launch.json を設定する
  5. ブレークポイントを置く
  6. ブラウザやCLIから実行する

launch.json の例

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9003
        }
    ]
}

Xdebug設定例

zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_port=9003
xdebug.client_host=127.0.0.1

ただし、xdebug.start_with_request=yes は毎回デバッグ接続を試みる設定です。

手軽に最初の動作確認をするには便利ですが、常に最適とは限りません。

必要なときだけ有効にしたい場合は、環境によっては trigger を使う構成のほうが扱いやすいことがあります。

スタックトレースを見る習慣をつける

エラーが出た時は、エラー行だけでなくスタックトレースも重要です。

スタックトレースを見ると、どの関数がどの順番で呼ばれて、最終的にそのエラーに至ったかが分かります。

たとえば、

  • index.php
  • UserController.php
  • UserService.php
  • UserRepository.php

という順で呼ばれてエラーになっているなら、表面的には index.php で止まって見えても、本当の原因は UserRepository.php にあるかもしれません。

デバッグをうまく進める考え方

PHPのデバッグは、テクニック以上に切り分け方が重要です。

「動かない」ではなく「どこまでは動くか」を見る

悪い進め方は、いきなり全体を書き換えることです。

それよりも、次のように考える方が効率的です。

  • どこまでは正常に動いているか
  • どの条件でだけ失敗するか
  • 値が変になったのはどこか

一度にたくさん直さない

SQLもif文もHTMLもまとめて直すと、どれが原因だったのか分からなくなります。

デバッグ中は、1回に1か所ずつ修正するのが基本です。

再現条件を固定する

「たまに発生するバグ」は特に難しいです。

その場合は次をできるだけ固定します。

  • 入力値
  • URLパラメータ
  • ログイン状態
  • セッション状態
  • DBデータ
  • 実行手順

同じ条件で毎回再現できる状態を作ると、原因を追いやすくなります。

初心者が見落としやすいポイント

エラーが出ない

原因としては、

  • display_errors がOff
  • ログを見ていない
  • 構文エラーで手前で止まっている
  • 実際には別のPHP設定が読み込まれている

などがあります。

var_dump() の置き場所が悪い

怪しい処理の後ろではなく、怪しい場所の手前に置かないと意味がありません。

PHPの問題だと思ったらHTML側だった

実際には、

  • name 属性のミス
  • method の違い
  • JavaScriptによる送信制御
  • CSSで見えていないだけ

ということもよくあります。

@ でエラーを隠している

PHPには @ 演算子でエラーを抑制する書き方がありますが、デバッグ時には原因が見えなくなるため避けるべきです。

@$value = $_GET['id'];

このような書き方は、調査を難しくします。

CLIとWebでは見え方が違うことがある

PHPは、ブラウザ経由で動かす場合とCLIで動かす場合で、エラーの見え方やログ出力先が異なることがあります。

たとえば、

  • CLIではエラーが見える
  • Webでは白画面になる
  • ログの保存場所が違う

といったケースがあります。

「同じPHPなのに結果が違う」と感じたら、実行環境の違いを疑うのも大切です。

最初に覚えておきたいデバッグ用コード

よく使う基本形をまとめると次の通りです。

エラー表示

ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

値の確認

var_dump($value);

配列・オブジェクトの確認

echo '<pre>';
print_r($data);
echo '</pre>';

途中停止

exit;

ログ出力

error_log(print_r($data, true));

例外確認

try {
    // 処理
} catch (Throwable $e) {
    echo $e->getMessage();
}

まとめ

PHPのデバッグで大切なのは、勘でコードを書き換えることではなく、事実を1つずつ確認することです。

特に重要なのは次の点です。

  • 開発環境ではエラー表示を有効にする
  • 画面表示だけでなくログも見る
  • var_dump()print_r() を使い分ける
  • exit で範囲を絞る
  • 型、条件分岐、配列、POST、SESSION、SQLを順番に確認する
  • 例外処理では Throwable も意識する
  • 複雑な不具合ではXdebugを使う
  • 一度に多くを直さず、1つずつ切り分ける

PHPは柔軟な言語なので、そのぶん曖昧さがバグにつながりやすいです。

だからこそ、デバッグでは「勘」よりも「観察」が重要になります。

実務では、次の流れを意識するとかなり安定します。

エラーを見えるようにする → 値を確認する → 範囲を絞る → 原因を特定する → 必要最小限の修正を行う

以上、PHPのデバッグの方法についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次