PHP 入門

PHP エラーの種類とレベル

PHP スクリプトは実行プロセスにおいて、実行を中断させない軽微な警告から、スクリプトを突然終了させる深刻な致命的エラーまで、さまざまな問題に遭遇する可能性があります。これらの異なるエラータイプを理解することは、問題の診断(デバッグ)と堅牢なアプリケーションの構築において極めて重要な「基本スキル」です。PHP はエラーをいくつかの異なるタイプに分類しており、それぞれのタイプが問題の深刻度と発生源を表しています。

1. PHP エラーの種類

PHP は主に、エラーの深刻度およびスクリプトの実行継続を許可するかどうかに基づいて分類を行っています。これらのタイプは PHP において「定義済み定数」として表され、エラーレポートやエラーハンドリングのメカニズムを構成する上で不可欠な要素です。

1.1 通知 (Notices)

通知(Notices)は、最も深刻度が低いエラータイプです。これらは開発やテストの期間中によく発見される軽微な問題を示しており、スクリプトの実行を中断させることはありません。通知は通常、特定の境界ケース(エッジケース)で予期しない挙動を招く可能性のある潜在的な問題や、より良い記述方法があることを示唆しています。

特徴:

  • スクリプトの実行は継続される。
  • 通常、未定義の変数や配列インデックスへのアクセスに関連する。
  • ベストプラクティスとしては、致命的ではないにせよ、すべての通知を解決すること。これらは潜在的なロジックの欠陥を露呈させる可能性があるため。

例 1:未定義の変数
宣言や代入が行われていない変数にアクセスすると、E_NOTICE が発生します。

<?php
// このスクリプトは挨拶を出力しようとしていますが、'name' が定義されていません。
echo "Hello, " . $name . "!"; 
// 出力結果: Notice: Undefined variable $name in /path/to/script.php on line X
// Hello, !

この例では、$name が事前に宣言されずに使用されています。PHP は実行を継続し、$name を空の文字列(empty string)として扱いますが、開発者に警告するために通知を出します。

例 2:未定義の配列インデックス
存在しないインデックスを使用して配列(アレイ)要素にアクセスしようとした場合も、通知が生成されます。

<?php
$data = ['item1' => 'Value A', 'item2' => 'Value B']; 

// 存在するキーにアクセス
echo $data['item1'] . "\n"; // 出力: Value A 

// 存在しないキーにアクセス
echo $data['item3'] . "\n";
// 出力結果: Notice: Undefined index: item3 in /path/to/script.php on line X
//

ここでは $data['item3'] にアクセスしていますが、item3$data アレイ内のキーではありません。スクリプトは動作し続けますが、ロジックやデータ処理にミスがある可能性を示しています。

1.2 警告 (Warnings)

警告(Warnings)は通知よりも深刻ですが、通知と同様にスクリプトの実行を停止させることはありません。警告は通知よりも重大な問題が存在することを示し、通常、スクリプトが期待通りに動作することを保証するために注意を払う必要があることを暗示しています。スクリプトは継続されますが、その後の出力や操作が正しくない可能性があります。

特徴:

  • スクリプトの実行は継続される。
  • 通常、存在しないファイルのインクルードや、関数(ファンクション)呼び出し時に誤った数の引数(パラメータ)を渡した場合に関連する。
  • 警告は、検出が困難なバグや予測不可能な挙動を招くため、常に解決すべきである。

例 1:存在しないファイルのインクルードinclude()require() を使用して存在しないファイルをロードしようとすると、E_WARNINGinclude の場合)が発生します。

<?php
// 存在しないファイルをインクルードしようとする
include 'non_existent_file.php';
// 出力結果: Warning: include(non_existent_file.php): Failed to open stream: No such file or directory in /path/to/script.php on line X
// Warning: include(): Failed opening 'non_existent_file.php' for inclusion (include_path='.:/usr/share/php') in /path/to/script.php on line X 

echo "スクリプトは警告後も実行を継続します。\n";
// 出力: スクリプトは警告後も実行を継続します。

スクリプトは non_existent_file.php のインクルードを試みて失敗し、2つの警告を出しましたが、その後の echo ステートメントは実行されました。

例 2:無効な関数の引数
無効な引数が提供された場合、一部のビルトイン関数は警告を出します(深刻度によっては、例外をスローしたり致命的エラーになる関数もあります)。

<?php
// strlen() 関数にアレイを渡す(本来はストリングを期待している)
$myArray = [1, 2, 3];
echo strlen($myArray);
// 出力結果: Warning: strlen() expects parameter 1 to be string, array given in /path/to/script.php on line X

echo "スクリプト完了。\n";
// 出力: スクリプト完了。

ここで strlen() は文字列(ストリング)を期待していますが、アレイが渡されました。PHP は警告を出し、この関数は nullfalse を返す可能性がありますが、スクリプトは次の行まで実行されます。

1.3 解析エラー / 構文エラー (Parse Errors / Syntax Errors)

解析エラー(パースエラー、構文エラーとも呼ばれる)は、開発プロセスにおいて最も一般的かつ致命的なエラーの一つです。PHP パーサーがスクリプト内で無効な構文を検出したときに発生します。これらのエラーはスクリプトのコンパイルと実行を阻止します。

特徴:

  • スクリプトは一切実行を開始しない。パーサーがコードを理解できないため。
  • 常に致命的(フェイタル)。
  • 通常、セミコロンの欠落、閉じられていない括弧、中括弧、または誤ったキーワードの使用によって引き起こされる。
  • PHP は、構文エラーが最初に検出されたファイルと行番号を明示するメッセージを提供。

例 1:セミコロンの欠落
最も一般的な解析エラーの一つは、ステートメント(文)の末尾にセミコロンを忘れることです。

<?php
echo "Hello, World!" // ここにセミコロンが欠落
echo "この行のコードは決して実行されません。";
// 出力結果: Parse error: syntax error, unexpected 'echo' (T_ECHO) in /path/to/script.php on line X

セミコロンがないため、スクリプトは最初の echo ステートメントで即座に終了し、2番目の echo は処理されません。エラーメッセージは、パーサーが echo キーワードの前にセミコロンを期待していたことを示します。

例 2:閉じられていない中括弧
閉じられていない中括弧 {} や括弧 () も、解析エラーの一般的な原因です。

<?php
if (true) {
    echo "if ブロックの内部。";
// ここで閉じの中括弧 } が欠落
echo "これは if ブロックの外部。";
// 出力結果: Parse error: syntax error, unexpected end of file in /path/to/script.php on line X

PHP は if ステートメントに対応する閉じの中括弧を期待します。ファイルの末尾に達しても見つからない場合、「予期しないファイルの終端 (unexpected end of file)」解析エラーを報告します。

1.4 致命的エラー (Fatal Errors)

致命的エラー(フェイタルエラー)は、スクリプトを即座に終了させる深刻なエラーです。警告や通知とは異なり、致命的エラーはスクリプトの継続が不可能であることを意味します。PHP が回復不可能な問題に遭遇した場合(例えば、存在しないクラスのインスタンス化や、未定義の関数の呼び出し)に発生します。

特徴:

  • スクリプトの実行が即座に停止する。
  • それ以上の実行が不可能、あるいは無意味であることを示す極めて深刻な問題。
  • 通常、メモリ問題、未定義の関数/クラス、または存在しないファイルに対する require() の呼び出しに関連する。

例 1:未定義の関数の呼び出し
定義されていない関数を呼び出そうとすると、致命的エラーが発生します。

<?php
// 存在しない関数 'my_non_existent_function' を呼び出そうとする
my_non_existent_function();
// 出力結果: Fatal error: Uncaught Error: Call to undefined function my_non_existent_function() in /path/to/script.php on line X 

echo "この行のコードは決して実行されません。";

関数の呼び出しを試みた時点でスクリプトは即座に停止し、echo ステートメントは実行されません。

例 2:存在しないクラスのインスタンス化
定義されていないクラスからオブジェクトを作成しようとした場合も、致命的エラーとなります。

<?php
// 存在しないクラス 'NonExistentClass' からオブジェクトを作成
$obj = new NonExistentClass();
// 出力結果: Fatal error: Uncaught Error: Class "NonExistentClass" not found in /path/to/script.php on line X 

echo "この行のコードは決して実行されません。";

例 3:require() による存在しないファイルの読み込み

include() は存在しないファイルに対して警告を出すだけですが、require() は致命的エラーを引き起こします。

<?php
// require() を使用して存在しないファイルをロードする
require 'another_non_existent_file.php';
// 出力結果: Fatal error: require(): Failed opening required 'another_non_existent_file.php' (include_path='.:/usr/share/php') in /path/to/script.php on line X 

echo "スクリプトはここまで到達しません。\n";

require ステートメントはスクリプトの実行に不可欠(必須)であることを意味します。ファイルが見つからない場合、回復不可能なエラーとみなされ、スクリプトが終了します。

1.5 キャッチ可能な致命的エラー (Recoverable Fatal Errors)

PHP 5 で導入された「キャッチ可能な致命的エラー(Recoverable Fatal Errors)」は、通常は致命的ですが、カスタムエラーハンドラー(エラー処理プログラム)が定義されていれば、キャッチして処理できるエラーです。これにより、特定のタイプの致命的エラー(特にタイプ宣言に関連するもの)に対して、より優雅な終了処理やログ記録を実行できます。

特徴:

  • 通常、スクリプトの実行は停止する。
  • カスタムエラーハンドラーが登録されていれば、回復を試みることが可能。
  • 通常、厳格なタイプ宣言を強制している関数に対して無効な引数を渡した場合に関連する。

例:引数の型宣言の不一致

<?php declare(strict_types=1); 

function sum(int $a, int $b): int {
    return $a + $b;
} 

// これは通常、TypeError(キャッチ可能な致命的エラーの一種)を引き起こす
echo sum(5, 'hello');
// 出力結果: Fatal error: Uncaught TypeError: sum(): Argument #2 ($b) must be of type int, string given, called in /path/to/script.php on line X and defined in /path/to/script.php:3

カスタムエラーハンドラーがない場合、このスクリプトは TypeError で終了します。カスタムエラーハンドラーについては今後のレッスンで詳しく学習します。

1.6 棄用エラー (Deprecated Errors)

棄用エラー(デプレケーテッドエラー)は、コード内で使用されている特定の機能、関数、または定数が「時代遅れ」であり、将来の PHP バージョンで削除される可能性があることを示します。これらは通常致命的ではなく、スクリプトの継続を許可します。

特徴:

  • スクリプトの実行は継続される。
  • コードを更新し、より新しくサポートされている代替案を使用するよう開発者に促す。

例:非推奨の関数の使用(想定シナリオ)

<?php
// 関数 'old_style_connect' が非推奨になったと仮定
function old_style_connect($host) {
    // ここに非推奨の機能
    return "Connected to " . $host . " using old method.";
} 

echo old_style_connect("localhost");
// 出力例: Deprecated: Function old_style_connect() is deprecated in /path/to/script.php on line X
// Connected to localhost using old method.

2. エラーレベル定数

PHP は、異なるエラーレベルを表すいくつかの定数を定義しています。これらの定数は、error_reporting()ini_set() などの関数と組み合わせて使用され、PHP がどのタイプのエラーを報告すべきかを設定します。

  • E_ERROR: 致命的なランタイムエラー。回復不可能なエラーを示す。
  • E_WARNING: ランタイムの警告。非致命的なエラー。
  • E_PARSE: コンパイル時の解析エラー。構文エラー。
  • E_NOTICE: ランタイムの通知。クリティカルではない問題。
  • E_CORE_ERROR: PHP の初期起動時に発生する致命的エラー。
  • E_CORE_WARNING: PHP の初期起動時に発生する警告。
  • E_COMPILE_ERROR: 致命的なコンパイル時エラー。
  • E_COMPILE_WARNING: コンパイル時の警告。
  • E_USER_ERROR: ユーザー生成のエラーメッセージ(trigger_error() で発生)。
  • E_USER_WARNING: ユーザー生成の警告メッセージ。
  • E_USER_NOTICE: ユーザー生成の通知メッセージ。
  • E_STRICT: ランタイム通知。コードの相互運用性と前方互換性を高めるためのもの。
  • E_RECOVERABLE_ERROR: キャッチ可能な致命的エラー。
  • E_DEPRECATED: 非推奨の機能に対するランタイム通知。
  • E_USER_DEPRECATED: ユーザー生成の非推奨メッセージ。
  • E_ALL: すべてのエラーと警告(モダンな PHP では、すべてのタイプを含む)。

これらの定数は、ビット演算子(例:E_ALL & ~E_NOTICE)を使用して組み合わせることができ、報告すべきエラーのタイプを正確に指定できます。

3. 総合実戦演習

理解を深めるために、意図的に異なるタイプのエラーを発生させ、それらの挙動を観察するスクリプトを作成してみましょう。

<?php
// --- パート 1: 通知 (Notice) ---
// 目的: 未定義変数へのアクセスによる E_NOTICE を実演。
echo "--- パート 1: 通知の例 ---\n";
$favoriteFood = "pizza";
// 本来は $favoriteFood を使いたかったが、スペルミスで $favouriteFood と記述
echo "私のお気に入りの食べ物は: " . $favouriteFood . "\n\n"; // E_NOTICE を誘発

// --- パート 2: 警告 (Warning) ---
// 目的: 存在しないファイルのインクルードによる E_WARNING を実演。
echo "--- パート 2: 警告の例 ---\n";
// 'config.php' はこのディレクトリに存在しない。
// include() は警告を出すが、スクリプトの継続を許可する。
include 'non_existent_config.php';
echo "このコードは 'include' 警告の後に実行されます。\n\n";

// --- パート 3: キャッチ可能な致命的エラー (TypeError) ---
// 目的: 厳格な型の不一致による TypeError を実演。
echo "--- パート 3: TypeError の例 ---\n";
function addNumbers(int $num1, int $num2): int {
    return $num1 + $num2;
} 

try {
    // 第2引数にインテジャーではなくストリングを渡す
    echo "10 と '5' の和は: " . addNumbers(10, '5') . "\n";
} catch (TypeError $e) {
    // この catch ブロックで TypeError をキャッチ
    echo "エラーをキャッチしました: " . $e->getMessage() . "\n";
}
echo "TypeError がキャッチされたため、この行は実行されます。\n\n";

// --- パート 4: 解析エラー (Syntax Error) ---
// 目的: 意図的に構文エラーを起こし、スクリプトの停止を実演。
// 注意: この部分のコメントを外すと、スクリプト自体の実行が阻止されます!
/*
echo "--- パート 4: 解析エラーの例 ---\n";
if (true) {
    echo "ブロックの内部。";
// ここで閉じの中括弧が欠落
echo "解析エラーのため、この行に到達することはありません。";
*/

// --- パート 5: 致命的エラー (Fatal Error) ---
// 目的: 回復不可能な致命的エラーを実演。
echo "--- パート 5: 致命的エラー(未定義関数)の例 ---\n";
// 存在しない関数を呼び出す
nonExistentFunctionCall();
echo "致命的エラーのため、この行は【決して】実行されません。\n";
?>