例外(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; 文が &try; や &catch; ブロックの内部に存在した場合でも、
&finally; ブロックは実行されます。
さらに、&return; 文は出現した時に評価されますが、
結果は &finally; ブロックが実行された後に返されます。
さらに、&finally; ブロックにも &return; 文が存在した場合は、
&finally; ブロックから値が返されます。
グローバルな例外ハンドラ
例外がグローバルスコープにまで遡った場合、
設定されていれば、グローバルな例外ハンドラがそれをキャッチすることができます。
他の &catch; ブロックが呼び出されなかった場合に、
&catch; の代わりに呼び出される関数を
set_exception_handler 関数で設定できます。
その効果は、プログラム全体を &try;-&catch; ブロックで囲むことと同じです。
&reftitle.notes;
PHP の内部関数の多くは
エラー報告(error_reporting)
を使っており、例外を使っているのは新しい
オブジェクト指向
の拡張モジュールのみです。
しかし、ErrorException
を使えば簡単にエラーを例外に変換することができます。
この変換テクニックが使えるのは、致命的でないエラーに限ります。
エラーを例外に変換する
]]>
Standard PHP Library (SPL)
には 組み込みの例外 が数多く用意されています。
&reftitle.examples;
例外をスローする
getMessage(), "\n";
}
// 実行は継続される
echo "Hello World\n";
?>
]]>
&example.outputs;
例外処理での &finally; ブロック
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";
?>
]]>
&example.outputs;
&finally; ブロックと &return; の相互作用
]]>
&example.outputs;
ネストした例外
getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
]]>
&example.outputs;
複数の例外ハンドリングをひとつの catch で行う
testing();
?>
]]>
&example.outputs;
キャッチする時に変数を省略する
PHP 8.0.0 以降でのみ許されます
]]>
throw を 式として扱う
PHP 8.0.0 以降でのみ許されます
getMessage();
}
?>
]]>
例外を拡張する
組み込みの Exception クラスを拡張することで、例外クラスをユーザーが
定義することが可能です。以下のメンバーおよびプロパティは、
組み込みの Exception クラスから派生した子クラスの中でアクセス可能です。
組み込みの例外クラス
]]>
クラスが、組み込みの Exception クラスを拡張し、
コンストラクタを再定義した場合、
全ての利用可能なデータが正しく代入されることを保証するために
parent::__construct() もコールすることが強く推奨されます。
__toString() メソッドは、
オブジェクトが文字列として表された際に独自の出力を行うために
上書きすることができます。
例外を複製することはできません。Exception を clone しようとすると
致命的な E_ERROR エラーが発生します。
例外クラスを拡張する
code}]: {$this->message}\n";
}
public function customFunction() {
echo "A custom function for this type of exception\n";
}
}
/**
* 例外をテストするためのクラスを作成
*/
class TestException
{
public $var;
const THROW_NONE = 0;
const THROW_CUSTOM = 1;
const THROW_DEFAULT = 2;
function __construct($avalue = self::THROW_NONE) {
switch ($avalue) {
case self::THROW_CUSTOM:
// カスタム例外をスローする
throw new MyException('1 is an invalid parameter', 5);
break;
case self::THROW_DEFAULT:
// デフォルトの例外をスローする
throw new Exception('2 is not allowed as a parameter', 6);
break;
default:
// 例外が発生しなかった。オブジェクトが生成される
$this->var = $avalue;
break;
}
}
}
// 例1
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (MyException $e) { // ここでキャッチされる
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (Exception $e) { // ここはスキップされる
echo "Caught Default Exception\n", $e;
}
// 実行を継続する
var_dump($o); // Null
echo "\n\n";
// 例2
try {
$o = new TestException(TestException::THROW_DEFAULT);
} catch (MyException $e) { // この型にはマッチしない
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (Exception $e) { // ここでキャッチされる
echo "Caught Default Exception\n", $e;
}
// 実行を継続する
var_dump($o); // Null
echo "\n\n";
// 例3
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (Exception $e) { // ここでキャッチされる
echo "Default Exception caught\n", $e;
}
// 実行を継続する
var_dump($o); // Null
echo "\n\n";
// 例4
try {
$o = new TestException();
} catch (Exception $e) { // スキップされる、例外は発生しない
echo "Default Exception caught\n", $e;
}
// 実行を継続する
var_dump($o); // TestException
echo "\n\n";
?>
]]>