【PHP】例外処理(try-catch-finally) - 例外発生時の対応処理

【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行目の除算は実行されません。

例外発生後はcatchfinallyと続き、例外処理を抜けて、23行目の出力が実行されます。

このように例外処理によって例外発生時に、プログラムを終了せずに正常な処理へと復帰することが可能です。

複数の例外

Exceptionクラスを継承した独自の例外を定義することで、処理の状況に応じて例外の種類を分けることができます。

tryにおいて複数種類の例外が発生する可能性がある場合、各例外に対応するcatchを定義することが可能です。

例えば、上記サンプルの入力値について次の仕様を追加します。

ルール 例外 対応
値は数値のみ。 NoNumberException エラーメッセージを出力する。
割る数($y)は0以外とする。 DivByZeroException エラーメッセージを出力する。
値は-10001000とする。 NumberOutOfRangeException 値が-1000未満の場合は-10001000より大きい場合は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


このように例外は種類に応じて、クラスを分けて例外処理を変えることが可能です。