ORJavaScriptが有効ではありません。なに?「例外処理」というのは、「例外」が投げられた場合の処理の事です。わけが解かりませんね。「例外」って何なのか?大雑把に言うと「エラー(問題)」の事です。通常は、エラーが発生したら「エラーですねちゃんちゃん」とそこで処理を中断するか何事も無かったかのようにスルーしてしまう所を、「例外処理」では、実行中にエラーが発生した場合の処理を記述しておいて、エラーが発生した場合でも処理の中断はせず、発生したエラーを「例外」として“投げ”、記述しておいた“エラー発生時の処理”を実行します。この“エラー発生時の処理”が「例外処理」であり、これを実現するのが「例外処理機能」です。この説明で理解できたら天才ですね(何)。要するに、何か問題が発生した場合に“エラーですから!切腹!”するのではなく、“エラーみたいですよ”と報告し、その報告を受け取って何かしらの処置をするのが「例外処理」です。ここでいう“報告”が「例外」で、“報告する行為”を“「例外」を投げる”といいます。tryブロック、throw文、catchブロック「例外処理」は、エラーが起こりそうな個所を「トライ」して、トライ中にエラーが発生したら「例外」を「スロウ(投げる)」してトライを中断し、投げられた「例外」を「キャッチ」して「エラー時用処理」を実行する、というものです(汗)。以下のサンプルと結果をご覧下さい。******Warning:「substr()」関数の引数が不正です。$e)」の後のブロック内の処理は実行されませんでした。「try」の後のブロック内では、意図的にエラーを発生させました。エラーが発生しましたが、特に何も起こりません。今回のサンプルは実は全く無意味なサンプルです。何が無意味かというと、エラーが起こっても「例外」を「スロウ(投げる)」していないので、「トライ」は全部実行され、「例外処理」も実行されていません。「例外処理」は、「例外」が「スロウ(投げる)」されてはじめて実行されるのです。上のサンプルでは「例外」を「スロウ(投げる)」していないので、特に何も起こらなかったのです。次に、「例外」を「スロウ(投げる)」して「例外処理」を実行するサンプルを示します。以下のサンプルと結果をご覧下さい。thrown.");」なる一文が追加されました。$e)」ブロックの処理が実行されました。$e)」によって「キャッチ」し、キャッチブロック内の「例外処理」が実行されたのです(汗)。thrown."」という文字列を渡して生成しているのが解かります。「throw文」によって生成したオブジェクトを“何処かに投げている”のがイメージできると思います。投げられたオブジェクトの運命や如何に。$e)」によってキャッチされるんですが、よく見ると引数がオブジェクトの場合にクラス名を指定する関数定義のような格好をしています。実はそれと同じ様なもので「変数$e」は必ず「Exception」クラスのインスタンスが渡される事を想定した「catch」の引数なのです。「throw文」によって投げられた「Exception」クラスのオブジェクトは「変数$e」に引数として代入され、キャッチブロック内で使用します。$e)」の部分及びキャッチブロックは関数定義のように見え、そう捉える事も出来ますが、関数定義とは相違点があります。引数のクラス名の指定を省略出来ません。あと、「変数$e」のスコープは関数の様にローカルスコープではなく、キャッチブロック外でも有効です。「catch」の引数は同じスコープにある同名の変数と被らない様に注意する必要がありますが、「変数$e」を例外処理用の変数と決めて記述していけば問題ないでしょう。あと、「try」も「catch」も関数定義と同様に、後に続く処理が短文の場合であっても、処理部の「{}(波括弧)」を省略する事が出来ません。thrown.")」によって、「Exception」クラスのインスタンスを生成しましたが、「Exception」クラスって何なのか?これが「例外」なんです。覚えるしかありません(オイ)。「Exception」クラスは、「Directory」クラスや「stdClass」クラスの様に、PHPで利用する為に、PHP側で予め定義されているクラスです。この定義済みの「Exception」クラスがどのような形態をしているのか、下記のスクリプトを実行してみて見ましょう。thrown.");?></pre>可視性の関係で、「Exception」クラスの派生クラス「MyException」クラスを作りました。この結果は以下のようになります。}}4つの「プロテクテッド」なプロパティと2つの「プライベート」なプロパティ、計6つのプロパティと、8つのメソッドを持っているのが解かります。詳細については置いといて、前のサンプルで「$e->getMessage()」をコールしたら、インスタンスの生成時に渡した文字列を得る事が出来ました。「変数$e」を文字列として出力した所、インスタンス生成時に渡した文字列を含めた様々な情報を得る事が出来ました。こんな感じで、「スロウ」されて「キャッチ」した「例外オブジェクト」を使いながら、「例外処理」を行います。いうものに対して疑念を持ち始めている事かと思います。JavaScriptでは(「Java」ではありません。自分は「Java」を使った事がありませんで、あくまで「JavaScript」です)、「トライ」ブロック内でエラーが発生すると、自動的に各種の「例外」を投げてくれたんですが、PHPには(今の所?)そのような機能は無いようです(一部の新しいビルトイン関数では「例外」を投げるものもある)。PHPでは、そのような機能を自分で実装しなくてはなりません。クラス」を定義し、スクリプト内の、問題の発生が予想される場所に「例外処理」を記述し、問題があれば「トライ」ブロック内で発生した問題用に用意しておいた「例外オブジェクト」を投げます。問題発生時に「例外」を投げさせる前のサンプルでは、「トライ」ブロック内で直接「例外」を投げていましたが、これではただ単に投げるだけで、全く無意味です。問題があったときに投げるようにしないと意味がありません。いう事で、“問題があった時に「例外」を投げる”サンプルと結果を以下に。Exception('「0」で割る事は出来ません。Exception('値が不正です。"\n";?></pre>文字化けを考慮してここまで日本語を使わないで来ましたが、これにも限界を感じたので(オイ)日本語を使ってしまいました。視認性の問題もあり、今更という気もしますが、これからは日本語を使うようにします。以降のサンプルを実行する際は文字化けしないように各自修正するなりして下さい。使うまでもない事が「例外処理」で実現できました。いうより「例外処理」を使わない方がシンプルに書けそうです。そんな事はいいとして、「throw文」で投げる事が出来るのは、「Exception」クラスのインスタンスだけではありません。「Exception」クラスの派生クラスのインスタンスであれば、「例外」として投げる事が出来ます。この場合は、「catch」の引数に指定するクラス名を、投げられる「例外オブジェクト」のクラス名にする必要があります。逆に、「Exception」クラスと無関係なクラスのインスタンスを投げたり、また投げられた「例外オブジェクト」のクラス名が指定されないと、「E_ERROR」エラーとなります。「try」の後の「catch」は複数設置する事が出来ます。この場合、最初の「catch」から順に、投げられた「例外オブジェクト」のクラス名と引数に指定したクラス名がマッチするかチェックしていき、最初にマッチした所の「キャッチ」ブロックの処理が実行されます。以下のサンプルと結果をご覧下さい。error:「例外オブジェクト」が「Exception」クラスと無関係です。「Exception」クラスの派生クラス「MyException1」クラスと「MyException2」クラス、「MyException1」クラスの派生クラス「MyException3」クラス、「MyException2」クラスの派生クラス「MyException4」クラスと、「Exception」クラスと無関係なクラス「NotException」クラスを定義して、それぞれを「例外」として投げました。$e)」に、「NotException」クラスのインスタンスは「Exception」クラスと無関係なので「例外」とみなされずに「E_ERROR」エラーを発しました。この様な結果になった理由は、関数定義時の、オブジェクト引数のクラス名指定の場合と同じ考え方です。$e)」にキャッチされたのです。これを踏まえて、割算をするサンプルを書き直してみます。'「0」で割る事は出来ません。"\n";?></pre>5値が不正です。0「0」で割る事は出来ません。$num2){}」がシンプルになりましたが、その代わりクラスが4つも定義されました。「Exception」クラスを継承した「DivisionException」クラスを定義しました。これは“割算における「例外」”です。次に「DivisionException」クラスを継承した「DivisionIsZeroException」クラスと「DivisionWrongValueException」クラスを定義しました。それぞれ、“分母が「0」であるという「例外」”と“値が不正であるという「例外」”で、問題発生時に投げられます。“割算”は数値演算なので、数値以外の値で演算する事は出来ません。数学的に「0」で割る事は出来ません。数値以外で演算しようとしたり、「0」で割ろうとする事はどちらも割算的に想定外なことで、要するに割算的に「例外」なのです。各クラスで「プロパティ$message」に初期値を設定しています。この「プロパティ$message」は、「例外オブジェクト」の生成時にコンストラクタへ渡された場合に、渡された文字列がセットされる所です。「Exception」クラスでの初期値は空文字列ですが、「DivisionIsZeroException」クラスと「DivisionWrongValueException」クラスはそれぞれ固有の「例外」なので、「例外オブジェクト」を生成する再にわざわざメッセージを渡すよりも最初から決定しておいた方が楽だし自然です。「Division」クラスは、割算を行う為のクラスです。このクラスでは、コンストラクタに渡された2つの値をチェックし、問題があればそれ用の「例外」を投げ、問題がなければ(「例外」が投げられていなければ)演算結果をプロパティに保存します。$e)」にキャッチされて)「$e->getMessage()」の返り値を返すというものになりました。不正な値で割算をしようとした時に自動的に「例外」が投げられればこんなに面倒にはならない、というより割算の値のチェック程度の事に「例外処理」を使う必要は無かったですね。ハァ?そんなこんなで(どんなこんなで)、「例外処理」を使うかどうかはケースバイケースです。使った方が良さそうな時に使いましょう。「例外処理」のネスト「例外処理」はネストする事が出来ます。ネストすると、投げた「例外」をキャッチ出来る「キャッチャー?」が同レベルに無い場合は、上位レベルの「キャッチャー?」を探します。「キャッチ」ブロック内からも「例外」を投げられるようになり、まず上位レベルの「キャッチャー?」を探します。以下のサンプルと結果をご覧下さい。error:「例外:MyException2」がキャッチされませんでした。$e)」でキャッチされました。$e)」でキャッチされました。MyException2;」で「例外」を投げますが、既に最上位レベルなのでキャッチされずに、「E_ERROR」エラーとなります。「例外スロウ」のキャンセル「例外オブジェクト」の生成時に、コンストラクタ内で別の「例外オブジェクト」を生成して投げるとどうなるでしょうか。以下のサンプルと結果をご覧下さい。error:「例外オブジェクト」が「Exception」クラスと無関係です。MyException;」で「例外オブジェクト」を生成して投げようとしています。NotException;」で「偽例外オブジェクト」を生成して投げようとしています。結果は、「偽例外オブジェクト」を投げないで下さいと「E_ERROR」エラーになりました。要するに、最初の「スロウ」はキャンセルされて、その後の「スロウ」が実行された格好になりますが、実際は、最初の「throw文」が実行される前に、コンストラクタ内の「throw文」が実行されて投げられたってだけの事です。「例外オブジェクト」のメソッドデフォルトの(「Exception」クラスの)「例外オブジェクト」は、コンストラクタ等を含めて8つのメソッドを備えています。これらのメソッドがどういうものなのか見てみます。以下のサンプルと結果をご覧下さい。{main}メソッドの機能をまとめると、下表の様になります。「getTraceAsString()」メソッドと似た値を返すコンストラクタは2つの引数を持っていて、第一引数には「メッセージ」を、第二引数には「コード」を指定でき、それぞれ省略できます。ちなみに、間違った指定をすると以下の様なアラートと共に「E_ERROR」エラーを発します。「例外オブジェクト」が持つ各プロパティは下表の通りです。不明(「Exception」クラス内部で使用するので意識する必要なし)「Exception」クラスは上表の様なプロパティとメソッドを備えています。「Exception」クラスの派生クラスを作る場合は、メソッドやプロパティの追加、コンストラクタ等特殊メソッドのオーバーライドが出来ますが、コンストラクタ等特殊メソッド以外のメソッドは訳あって(「final修飾子」で宣言されているため)オーバーライドする事が出来ません。いう事は、「プライベート」なプロパティの再定義は意味をなしません。派生クラスでは「プロテクテッド」なプロパティを「プロテクテッド」なプロパティとして再定義出来ます。例外ハンドラ投げられた「例外オブジェクト」が最終的に何処にもキャッチされないと、通常は「E_ERROR」エラーを発しますが、「例外ハンドラ」というものをセットしておくと、キャッチされなかった「例外オブジェクト」を引数に、この「例外ハンドラ」がコールされます。「例外ハンドラ」を有効にするには、まず“「例外ハンドラ」用のユーザ定義関数”を定義し、この関数名を引数に「set_exception_handler()」関数をコールします。これによって指定された名前の関数は「例外ハンドラ」として機能するようになります。「例外ハンドラ」は何度でも設定でき、また「restore_exception_handler()」関数によって1つ前の状態に復旧できます。「例外ハンドラ」がコールされると、その時点で処理は終了してしまいます。例外ハンドラ)投げられた例外がキャッチされなかった場合にコールされる“例外ハンドラ”を設定します。「例外ハンドラ」には、文字列で例外ハンドラ名を指定するか、第一要素が例外ハンドラをメソッドに持つオブジェクトもしくはクラス名で、第二要素がその例外ハンドラメソッド名である配列を指定します。指定された「例外ハンドラ」をセットし、セット直前の例外ハンドラ名を返します。未設定の場合は空文字列を返し、エラー時には「FALSE」を返します。restore_exception_handler(void)例外ハンドラをひとつ前の設定に復旧します。以下のサンプルと結果をご覧下さい。#「キャッチャー?」が不在な所から「例外」を投げる。#「例外ハンドラ」が有効だと、こんな所からも「例外」を投げられる。block.「例外ハンドラ」用に、関数と、メソッドを含むクラスを定義しました。その後、「set_exception_handler()」関数で4回セットし、「restore_exception_handler()」関数で2つ前の設定に戻しました。「トライ」ブロック外で「例外オブジェクト」を投げ、これでは何処にもキャッチされないので、「例外ハンドラ」が「キャッチャー」の代わりにコールされます。2つ前の設定はクラスのメソッドバージョンなので、こちらが「例外ハンドラ」としてコールされ、そこで処理は終了しました。定義済みの「例外」PHPで「例外処理」を使うときは、投げる「例外オブジェクト」のクラスと投げる機構を自分で用意しなくてはなりませんでしたが、一部の新めのビルトイン関数の中には、エラー時に「例外オブジェクト」を投げてくれるものもあります。投げられる「例外オブジェクト」のクラスは、その関数用の、定義済みクラスです。“定義済みの「例外」”にはどのようなものがあるのか、下記のスクリプトを実行して見てみます。}?></pre>ReflectionExceptioncom_exceptionSQLiteExceptionDOMException定義済みのクラス名の中から、「Exception」クラスを継承しているクラス名を抽出しました。今の所(PHP5.0.3)この4つが“定義済みの「例外」”として定義されています。XML関数)」用に定義された「例外」で、エラー時にこの「例外オブジェクト」を投げます。以下のサンプルと結果をご覧下さい。Error」だという「例外」が投げられました。投げられた「例外オブジェクト」がキャッチされなければ、単に「E_ERROR」エラーとなってスクリプトは終了します。