PHP は、他のプログラミング言語に似た例外モデルを有しています。
PHP 内で例外が投げられ (throw
され)、それが
捕捉され (catch
され) ます。発生した例外を
捕捉するには、コードを try
ブロックで囲みます。
各 try
ブロックには、対応する catch
ブロックあるいは finally
ブロックが存在する必要があります。
例外がスローされ、現在の関数スコープに catch
ブロックがなかった場合、
その例外は、マッチする catch
ブロックが見つかるまで関数のコールスタックを "遡って" いきます。
その途中で見つかった全ての finally
ブロックが実行されます。
グローバルスコープに遡るまで全てのコールスタックを探しても、
マッチする catch
ブロックが見つからない場合は、
グローバルな例外ハンドラが設定されていない限り Fatal Error となり、
プログラムが終了します。
スローされるオブジェクトは、Exception クラスあるいは Exception のサブクラスのインスタンスでなければなりません。 それ以外のオブジェクトをスローしようとすると 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
ブロックと return
文の間には注意すべき相互作用があります。
return
文が try
や catch
ブロックの内部に存在した場合でも、
finally
ブロックは実行されます。
さらに、return
文は出現した時に評価されますが、
結果は finally
ブロックが実行された後に返されます。
さらに、finally
ブロックにも return
文が存在した場合は、
finally
ブロックから値が返されます。
finally
catch
ブロックの後に
finally
ブロックも指定できます。
finally
ブロックの中に書いたコードは、
try
および catch
ブロックの後で常に実行されます。
例外がスローされたかどうかは関係ありません。
グローバルな例外ハンドラ
例外がグローバルスコープにまで遡った場合、
設定されていれば、グローバルな例外ハンドラがそれをキャッチすることができます。
他の catch
ブロックが呼び出されなかった場合に、
catch
の代わりに呼び出される関数を
set_exception_handler() 関数で設定できます。
その効果は、プログラム全体を try
-catch
ブロックで囲むことと同じです。
注意:
PHP の内部関数の多くは エラー報告 を使っており、例外を使っているのは新しい オブジェクト指向 の拡張モジュールのみです。 しかし、ErrorException を使えば簡単にエラーを例外に変換することができます。 この変換テクニックが使えるのは、致命的でないエラーに限ります。
例3 エラーを例外に変換する
<?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) には組み込みの例外が数多く用意されています。
例4 例外を投げるには
<?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
例5 例外処理での 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
<?php
function test() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
return 'finally';
}
}
echo test();
?>
上の例の出力は以下となります。
finally
例7 ネストした例外
<?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!"
例8 複数の例外ハンドリングをひとつの 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"
例9 キャッチする時に変数を省略する
PHP 8.0.0 以降でのみ許されます
<?php
class SpecificException extends Exception {}
function test() {
throw new SpecificException('Oopsie');
}
try {
test();
} catch (SpecificException) {
print "A SpecificException was thrown, but we don't care about the details.";
}
?>
例10 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();
}
?>