例外(exceptions)

目次

PHP は、他のプログラミング言語に似た例外モデルを持っています。 PHP 内で例外がスローされ (throw され)、それが 捕捉され (catch され) ます。発生した例外を 捕捉するには、コードを try ブロックで囲みます。 各 try ブロックには、対応する catch ブロックあるいは finally ブロックが存在する必要があります。

例外がスローされ、現在の関数スコープに catch ブロックがなかった場合、 その例外は、マッチする catch ブロックが見つかるまで関数のコールスタックを "遡って" いきます。 その途中で見つかった全ての finally ブロックが実行されます。 グローバルスコープに遡るまで全てのコールスタックを探しても、 マッチする catch ブロックが見つからない場合は、 グローバルな例外ハンドラが設定されていない限り fatal error となり、 プログラムが終了します。

スローされるオブジェクトは、 Throwable のインスタンスでなければなりません。 それ以外のオブジェクトをスローしようとすると PHP の fatal error が発生します。

PHP 8.0.0 以降では、throw キーワードは式として扱えるようになり、 様々なコンテクストで使えるようになりました。 これより前のバージョンでは、throw は文であり、 それが現れる行でだけでしか使えませんでした。

catch

catch ブロックは、スローされた例外にどのように反応するかを定義します。 catch ブロックは、扱えるひとつ以上の例外またはエラー型を定義します。 そして、オプションで例外を代入できる変数も定義できます。 (PHP 8.0.0 より前のバージョンでは、この変数定義は必須でした) スローされた例外またはエラーにマッチする最初の catch ブロックが、そのオブジェクトを処理します。

さまざまな型の例外を捕捉するために 複数の catch ブロックを使用することができます。 通常の実行時 (try ブロック内で例外がスローされなかった 場合) は、catch ブロック内は処理されず、それ以降から処理が続けられます。 catch ブロックの中から例外を throw する (あるいは throw しなおす) こともできます。 throw し直さない場合、その catch ブロックの後から処理が続けられます。

例外がスローされた場合、その命令に続くコードは実行されず、 PHP は最初にマッチする catch ブロックを探します。 例外が捕捉されない場合、PHP は "Uncaught Exception ..." というメッセージとともに 致命的なエラー(fatal error)を発生させます。 ただし、set_exception_handler() でハンドラが 定義されている場合を除きます。

PHP 7.1.0 以降では、catch ブロック で 複数の例外を パイプ文字 (|) を使って指定できるようになりました。 これは、異なるクラス階層からの例外を同時に扱う必要がある場合に有用です。

PHP 8.0.0 以降では、キャッチされた例外に対応する変数はオプションになりました。 指定されない場合、catch ブロックは実行されるものの、 スローされたオブジェクトへアクセスすることは出来ません。

finally

catch ブロックの後、または catch ブロックの代わりに、 finally ブロックも指定できます。 finally ブロックの中に書いたコードは、 try および catch ブロックの後で、 かつ通常のコードの実行が再開される前に常に実行されます。 例外がスローされたかどうかは関係ありません。

finally ブロックと return 文の間には注意すべき相互作用があります。 return 文が trycatch ブロックの内部に存在した場合でも、 finally ブロックは実行されます。 さらに、return 文は出現した時に評価されますが、 結果は finally ブロックが実行された後に返されます。 さらに、finally ブロックにも return 文が存在した場合は、 finally ブロックから値が返されます。

グローバルな例外ハンドラ

例外がグローバルスコープにまで遡った場合、 設定されていれば、グローバルな例外ハンドラがそれをキャッチすることができます。 他の catch ブロックが呼び出されなかった場合に、 catch の代わりに呼び出される関数を set_exception_handler() 関数で設定できます。 その効果は、プログラム全体を try-catch ブロックで囲むことと同じです。

注意

注意:

PHP の内部関数の多くは エラー報告(error_reporting) を使っており、例外を使っているのは新しい オブジェクト指向 の拡張モジュールのみです。 しかし、ErrorException を使えば簡単にエラーを例外に変換することができます。 この変換テクニックが使えるのは、致命的でないエラーに限ります。

例1 エラーを例外に変換する

<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new
ErrorException($message, 0, $severity, $filename, $lineno);
}

set_error_handler('exceptions_error_handler');
?>
ヒント

Standard PHP Library (SPL) には 組み込みの例外 が数多く用意されています。

例2 例外をスローする

<?php
function inverse($x) {
if (!
$x) {
throw new
Exception('ゼロによる除算。');
}
return
1/$x;
}

try {
echo
inverse(5) . "\n";
echo
inverse(0) . "\n";
} catch (
Exception $e) {
echo
'捕捉した例外: ', $e->getMessage(), "\n";
}

// 実行は継続される
echo "Hello World\n";
?>

上の例の出力は以下となります。

 0.2 捕捉した例外: ゼロによる除算。 Hello World 

例3 例外処理での finally ブロック

<?php
function inverse($x) {
if (!
$x) {
throw new
Exception('ゼロによる除算。');
}
return
1/$x;
}

try {
echo
inverse(5) . "\n";
} catch (
Exception $e) {
echo
'捕捉した例外: ', $e->getMessage(), "\n";
} finally {
echo
"First finally.\n";
}

try {
echo
inverse(0) . "\n";
} catch (
Exception $e) {
echo
'捕捉した例外: ', $e->getMessage(), "\n";
} finally {
echo
"Second finally.\n";
}

// 実行は継続される
echo "Hello World\n";
?>

上の例の出力は以下となります。

 0.2 First finally. 捕捉した例外: ゼロによる除算。 Second finally. Hello World 

例4 finally ブロックと return の相互作用

<?php

function test() {
try {
throw new
Exception('foo');
} catch (
Exception $e) {
return
'catch';
} finally {
return
'finally';
}
}

echo
test();
?>

上の例の出力は以下となります。

 finally 

例5 ネストした例外

<?php

class MyException extends Exception { }

class
Test {
public function
testing() {
try {
try {
throw new
MyException('foo!');
} catch (
MyException $e) {
// 改めてスロー
throw $e;
}
} catch (
Exception $e) {
var_dump($e->getMessage());
}
}
}

$foo = new Test;
$foo->testing();

?>

上の例の出力は以下となります。

 string(4) "foo!" 

例6 複数の例外ハンドリングをひとつの catch で行う

<?php

class MyException extends Exception { }

class
MyOtherException extends Exception { }

class
Test {
public function
testing() {
try {
throw new
MyException();
} catch (
MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}

$foo = new Test;
$foo->testing();

?>

上の例の出力は以下となります。

 string(11) "MyException" 

例7 キャッチする時に変数を省略する

PHP 8.0.0 以降でのみ許されます

<?php

function test() {
throw new
SpecificException('Oopsie');
}

try {
test();
} catch (
SpecificException) {
print
"A SpecificException was thrown, but we don't care about the details.";
}
?>

例8 throw を 式として扱う

PHP 8.0.0 以降でのみ許されます

<?php

class SpecificException extends Exception {}

function
test() {
do_something_risky() or throw new Exception('It did not work');
}

try {
test();
} catch (
Exception $e) {
print
$e->getMessage();
}
?>
To Top