PHP 異常処理

例外(Exception)は、指定されたエラーが発生した時にスクリプトの正常なフローを変更するために使用されます。

例外とは何ですか?

PHP 5は新しいオブジェクト指向のエラーハンドリング方法を提供しています。

例外ハンドリングは、指定されたエラー(例外)が発生した時にスクリプトの正常なフローを変更するために使用されます。この状況を「例外」と言います。

例外がトリガーされた場合、通常以下のようなことが起こります:

  • 現在のコード状態が保存されます
  • コードの実行が予め定義された例外ハンドラ関数に切り替わります
  • 状況によっては、保存されたコード状態から再開し、スクリプトの実行を終了したり、コードの別の場所からスクリプトを続行することができます。

異なるエラーハンドリング方法を示します:

  • 例外の基本的な使用方法
  • カスタムの例外ハンドラを作成する
  • 複数の異常
  • 異常を再投げ
  • トップレベルの例外ハンドラを設定する

例外の基本的な使用方法

例外がスローされた場合、その後のコードは実行されません。PHPは適合する「catch」コードブロックを探します。

例外がキャッチされず、set_exception_handler()を使用して適切な処理が行われなければ、深刻なエラー(致命的なエラー)が発生し、「未捕獲の例外」エラーメッセージが表示されます。

例外をスローし、それをキャッチしないことを試みましょう:

<?php
//例外を持つ関数を作成
function checkNum($number)
 {
 if($number>1)
  {
  throw new Exception("Value must be 1 or below");
  }
 return true;
 }
//例外をトリガー
checkNum(2);
?>

上記のコードはこのようなエラーを取得します:

致命的なエラー: 未捕獲の例外 'Exception' 
メッセージ「値は1または以下でなければなりません」で C:\webfolder\test.php:6 
スタックトレース: #0 C:\webfolder\test.php(12): 
checkNum(28) #1 {main} が C:\webfolder\test.php の 6行目でスローされました

Try, throw, catch

上記の例で発生するエラーを避けるために、適切なコードを例外処理に作成する必要があります。

正しいハンドラは以下を含むべきです:

  1. try - 異常の関数を使用する場合は「try」コードブロック内に位置するべきです。異常が発生しなければ、コードは通常通りに続行しますが、異常が発生した場合、例外がスローされます。
  2. Throw - ここで例外をトリガーする方法が規定されています。それぞれの「throw」には少なくとも1つの「catch」が対応する必要があります。
  3. Catch - 「catch」コードブロックが例外をキャッチし、例外情報を含むオブジェクトを作成します。

例外をトリガーしてみましょう:

<?php
//例外をスローできる関数を作成
function checkNum($number)
 {
 if($number>1)
  {
  throw new Exception("Value must be 1 or below");
  }
 return true;
 }
//「try」コードブロック内で例外をトリガー
try
 {
 checkNum(2);
 //例外がスローされた場合、このテキストは表示されません
 echo 'If you see this, the number is 1 or below';
 }
//例外をキャッチ
catch(Exception $e)
 {
 echo 'Message: ' .$e->getMessage();
 }
?>

上記のコードはこのようなエラーを取得します:

メッセージ: 値は1または以下でなければなりません

例説明:

上記のコードは例外をスローし、それをキャッチしました:

  1. checkNum()関数を作成します。この関数は数字が1を超えているかどうかを検出し、その場合には例外をスローします。
  2. "try"コードブロック内でcheckNum()関数を呼び出します。
  3. checkNum()関数内の例外がスローされます。
  4. "catch"コードブロックがこの例外を受け取り、例外情報を含むオブジェクト($e)を作成します。
  5. このexceptionオブジェクトから$e->getMessage()を呼び出すことで、その例外からのエラーメッセージを出力します。

ただし、「それぞれのthrowにはcatchが対応する必要がある」という原則に従うために、欠落したエラーを処理するためのトップレベルの例外処理プログラムを設定することができます。

カスタムのExceptionクラスの作成

カスタムの例外処理プログラムの作成は非常に簡単です。私たちは特別なクラスを作成し、PHPで例外が発生した場合にその関数を呼び出すことができます。このクラスはexceptionクラスの拡張である必要があります。

このカスタムのexceptionクラスはPHPのexceptionクラスのすべての属性を継承しており、カスタムの関数を追加できます。

私たちはexceptionクラスの作成を開始します:

<?php
class customException extends Exception
 {
 public function errorMessage()
  {
  //エラーメッセージ
  $errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
  ': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
  return $errorMsg;
  }
 }
$email = "someone@example...com";
try
 {
 //check if 
 if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE)
  {
  //メールアドレスが無効の場合に例外を投げ出す
  throw new customException($email);
  }
 }
catch(customException $e)
 {
 //カスタムメッセージを表示
 echo $e->errorMessage();
 }
?>

この新しいクラスは古い exception クラスのコピーであり、errorMessage() 関数が追加されています。古いクラスのコピーであるため、属性とメソッドを継承しており、exception クラスのメソッド、例えば getLine() 、getFile() 、getMessage() を使用できます。

例説明:

上記のコードは異常を投げて、カスタム exception クラスを通じてキャッチします:

  1. customException() クラスは旧の exception クラスの拡張として作成されました。これにより、旧クラスのすべての属性とメソッドが継承されます。
  2. errorMessage() 関数を作成します。メールアドレスが無効である場合、この関数はエラーメッセージを返します。
  3. $email 変数を無効なメールアドレス文字列に設定します。
  4. "try" コードブロックを実行し、メールアドレスが無効であるため、異常が投げられます。
  5. "catch" コードブロックは異常をキャッチし、エラーメッセージを表示します。

複数の異常

一つのスクリプトに複数の異常を使用して、様々な状況を検出することができます。

複数の if..else コードブロック、または switch コードブロック、または複数の異常をネストすることができます。これらの異常は異なる exception クラスを使用し、異なるエラーメッセージを返します:

<?php
class customException extends Exception
{
public function errorMessage()
{
//エラーメッセージ
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
$email = "someone@example.com";
try
 {
 //check if 
 if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE)
  {
  //メールアドレスが無効の場合に例外を投げ出す
  throw new customException($email);
  }
 //メールアドレスに「example」をチェック
 if(strpos($email, "example") !== FALSE)
  {
  throw new Exception("$email is an example e-mail");
  }
 }
catch(customException $e)
 {
 echo $e->errorMessage();
 }
catch(Exception $e)
 {
 echo $e->getMessage();
 }
?>

例説明:

上記のコードは二つの条件をテストし、どちらの条件も成立しない場合に異常を投げます:

  1. customException() クラスは旧の exception クラスの拡張として作成されました。これにより、旧クラスのすべての属性とメソッドが継承されます。
  2. errorMessage() 関数を作成しました。メールアドレスが無効の場合、この関数はエラーメッセージを返します。
  3. "try" コードブロックを実行し、最初の条件下では異常は投げられません。
  4. e-mailが"example"という文字列を含むため、二つ目の条件が異常をトリガーします。
  5. "catch" コードブロックは異常をキャッチし、適切なエラーメッセージを表示します。

customExceptionがキャッチされず、base exceptionだけがキャッチされた場合、その場所で異常を処理します。

異常を再投げ

異常が投げられる場合、標準的な方法とは異なる方法で処理したいかもしれません。"catch" コードブロックで再び異常を投げることができます。

スクリプトはユーザーにシステムエラーを隠すべきです。システムエラーはプログラマーにとって重要かもしれませんが、ユーザーにとっては興味がないかもしれません。ユーザーがより使いやすくするために、ユーザーに対してよりフレンドリーなメッセージを持つ例外を再投げ出すことができます:

<?php
class customException extends Exception
 {
 public function errorMessage()
  {
  //エラーメッセージ
  $errorMsg = $this->getMessage().' は有効な E-Mail アドレスではありません。';
  return $errorMsg;
  }
 }
$email = "someone@example.com";
try
 {
 try
  {
  //メールアドレスに「example」をチェック
  if(strpos($email, "example") !== FALSE)
   {
   //メールアドレスが無効の場合に例外を投げ出す
   throw new Exception($email);
   }
  }
 catch(Exception $e)
  {
  //例外を再投げ出す
  throw new customException($email);
  }
 }
catch(customException $e)
 {
 //カスタムメッセージを表示
 echo $e->errorMessage();
 }
?>

例説明:

上記のコードは、メールアドレスに「example」文字列が含まれているかどうかを検出します。含まれていれば、例外を再投げ出します:

  1. customException() クラスは旧の exception クラスの拡張として作成されました。これにより、旧クラスのすべての属性とメソッドが継承されます。
  2. errorMessage() 関数を作成しました。メールアドレスが無効の場合、この関数はエラーメッセージを返します。
  3. 変数 $email に有効なメールアドレスを設定しましたが、文字列「example」が含まれています。
  4. 「try」コードブロックには別の「try」コードブロックが含まれており、例外を再投げ出すことができます。
  5. メールアドレスに「example」文字列が含まれているため、例外がトリガーされました。
  6. 「catch」がこの例外をキャッチし、「customException」を再投げ出しました。
  7. 「customException」がキャッチされ、エラーメッセージが表示されました。

現在の "try" コードブロック内で例外がキャッチされていない場合、より高いレベルで catch コードブロックを探します。

トップレベル例外処理器(Top Level Exception Handler)の設定

set_exception_handler() 関数は、すべての未キャッチの例外を処理するユーザー定義関数を設定します。

<?php
function myException($exception)
{
echo "<b>Exception:</b> " , $exception->getMessage();
}
set_exception_handler('myException');
throw new Exception('Uncaught Exception occurred');
?>

上記のコードの出力は、以下のようになるべきです:

Exception: Uncaught Exception occurred

上記のコードでは、"catch" コードブロックが存在せず、代わりにトップレベルの例外処理プログラムがトリガーされます。すべての未キャッチの例外をキャッチするためにこの関数を使用する必要があります。

例外の規則

  • 例外処理が必要なコードは、潜在的な例外をキャッチするために try コードブロック内に置かれるべきです。
  • それぞれの try または throw コードブロックには、少なくとも1つの対応する catch コードブロックが必要です。
  • 複数の catch コードブロックを使用して、異なる種類の例外をキャッチすることができます。
  • catch コードブロック内で再び例外を投げることができます(re-thrown)。

簡而言之:如果投げられた例外があれば、それをキャッチする必要があります。