【PHP】例外処理(try-catch-finally) - 例外発生時の対応処理
PHPには例外処理と呼ぶ仕組みがあります。
例外処理を使うことで、例外に対応して処理を実行することが可能です。
ここでは、例外処理について解説します。
検証環境
例外処理
例外処理は“例外に対応した処理”のことです。
例外の発生に応じて、任意の処理を実行することができます。
例外処理を使うことで、例外発生時にプログラムを中断せずに、正常な処理へと復帰することが可能です。
基本構文
try{
処理
} catch ( Exception $e ) {
例外発生時の処理
} finally {
必ず実行する処理
}
例外処理はtry-catch-finally文を使用します。
try
try
のブロック内に通常実行したい処理を記述します。
このブロック内で例外が発生した場合、以降の処理を中断してcatch
の処理に移行します。
また、そのことを『例外のキャッチ』と呼びます。
catch
catch
のブロック内は例外が発生した場合に実行する処理を記述します。
引数のように例外オブジェクトを受け取って扱うことが可能です。
finally
finally
のブロック内は例外の発生可否に関わらず必ず実行する処理を記述します。
必須ではないため、try-catchのみの例外処理コードでも問題ありません。
サンプル
2値の除算結果を出力するサンプルを以下に示します。
<?php
echo '[ please input $x ] ';
$x = trim(fgets(STDIN));
echo '[ please input $y ] ';
$y = trim(fgets(STDIN));
try {
if( $y == 0 ) {
throw new Exception('Dividing by zero is not possible.');
}
echo $x . ' / ' . $y . ' = ' . ( $x / $y ) . "\n";
} catch( Exception $e ) {
echo $e->getMessage() . "\n";
} finally {
echo "Dividing Exit\n";
}
echo "Program Exit\n";
?>
2値は$x
と$y
に標準入力で受け取り、$x
を$y
で除算します。
除算はゼロで割ることができないため、11〜13行目で$y
の値が0
の場合、例外を発生するようにしました。
そして、例外が発生した時に例外処理ができるようtry-catch-finally文を適用しています。
$y
の値が0
ではないケースの実行結果は次のようになります。
$ php sample.php
[ please input $x ] 7
[ please input $y ] 5
7 / 5 = 1.4
Dividing Exit
Program Exit
正常に除算処理を終了しました。
例外が発生しないため、catch
は実行されません。
次に$y
の値が0
のケースの実行結果は次のようになります。
$ php sample.php
[ please input $x ] 7
[ please input $y ] 0
___ih_hl_start
Dividing by zero is not possible.
___ih_hl_end
Dividing Exit
Program Exit
例外が発生し、try
の処理は中断されるため、15行目の除算は実行されません。
例外発生後はcatch
→finally
と続き、例外処理を抜けて、23行目の出力が実行されます。
このように例外処理によって例外発生時に、プログラムを終了せずに正常な処理へと復帰することが可能です。
複数の例外
Exception
クラスを継承した独自の例外を定義することで、処理の状況に応じて例外の種類を分けることができます。
try
において複数種類の例外が発生する可能性がある場合、各例外に対応するcatch
を定義することが可能です。
例えば、上記サンプルの入力値について次の仕様を追加します。
ルール | 例外 | 対応 |
---|---|---|
値は数値のみ。 | NoNumberException |
エラーメッセージを出力する。 |
割る数($y )は0 以外とする。 |
DivByZeroException |
エラーメッセージを出力する。 |
値は-1000 〜1000 とする。 |
NumberOutOfRangeException |
値が-1000 未満の場合は-1000 、1000 より大きい場合は1000 とする。 |
この仕様を適用すると次のコードになります。
<?php
class NoNumberException extends Exception {}
class DivByZeroException extends Exception {}
class NumberOutOfRangeException extends Exception {}
echo '[ please input $x ] ';
$x = trim(fgets(STDIN));
echo '[ please input $y ] ';
$y = trim(fgets(STDIN));
try {
if( !is_numeric($x) || !is_numeric($y) ) {
throw new NoNumberException('Please input number.');
}
if( $y == 0 ) {
throw new DivByZeroException('Dividing by zero is not possible.');
}
if( !( -1000 <= $x && $x <= 1000 ) || !( -1000 <= $y && $y <= 1000 ) ) {
throw new NumberOutOfRangeException('Please enter a number between -1000 and 1000.');
}
echo $x . ' / ' . $y . ' = ' . ( $x / $y ) . "\n";
} catch( NoNumberException $e ) {
echo $e->getMessage() . "\n";
} catch( DivByZeroException $e ) {
echo $e->getMessage() . "\n";
} catch( NumberOutOfRangeException $e ) {
if( $x < -1000 ) $x = -1000;
else if ( 1000 < $x ) $x = 1000;
if( $y < -1000 ) $y = -1000;
else if ( 1000 < $y ) $y = 1000;
echo $x . ' / ' . $y . ' = ' . ( $x / $y ) . "\n";
} finally {
echo "Dividing Exit\n";
}
echo "Program Exit\n";
?>
3〜5行目は各例外クラスの定義です。
try
の除算処理(27行目)の前で値をチェックしています。
※ is_numeric
関数はPHPの標準関数で、引数の値が数値または数値形式の文字列であるかを判定します。
そして、29〜46行目のようにcatch
を繋げて記述して、各例外クラスのオブジェクトに対応する例外処理を定義できます。
try
で発生した例外は先に定義されたcatch
から順に、例外オブジェクトのクラスとcatch
の例外クラスが“一致またはキャストが可能(スーパークラス)であるか判定”し、マッチした場合にその処理を実行します。
各例外が発生するケースの実行結果は次の通りです。
NoNumberException
$ php sample.php
[ please input $x ] abcde
[ please input $y ] 8
Please input number.
Dividing Exit
Program Exit
DivByZeroException
$ php sample.php
[ please input $x ] 7
[ please input $y ] 0
Dividing by zero is not possible.
Dividing Exit
Program Exit
NumberOutOfRangeException
$ php sample.php
[ please input $x ] 9999
[ please input $y ] 10
1000 / 10 = 100
Dividing Exit
Program Exit
このように例外は種類に応じて、クラスを分けて例外処理を変えることが可能です。