2013年12月17日火曜日

RIsolutoのコードを読む その2 | Risoluto@アドベント・ぼっち・カレンダー:12日目

ABC(*1)の12日目。共通テーマは私が開発している「Risouto」についてです。



ユーザコードがコールされるまで


さて、本日は前回の続きと言うことで、Risolutoのコアコードとも言うべき「risoluto/lib/vendor/Risoluto/Core.php」のコードを見ていくことにします。このコードは前回の「index.php」から直接コールされます。

というわけで、重要そうな部分をピックアップしていくわけですが、まずはこの部分から。

/**
     * Perform()
     *
     * 指定されたアプリケーションを呼び出す
     *
     * @access    public
     *
     * @param     void なし
     *
     * @return    void    なし
     */
    public function Perform()
    {
        //------------------------------------------------------//
        // アプリケーションクラスをロードし実行する
        //------------------------------------------------------//
        // クラスインスタンスを生成し、実行する
        try {
            // 呼び出すクラスを決定する
            $call = $this->FindCallClass();

            // インスタンスの生成
            $targetInstance = new $call['load'];

            // イニシャライズメソッドをコール
            if (method_exists($targetInstance, 'Init')) {
                $targetInstance->Init($call['param']);
            } else {
                // メソッドが存在しなければ例外をThrow
                throw new Exception($this->coreError('notfound_init'));
            }

            // HTTPのメソッドに応じて適切なコントローラをコール
            switch ($_SERVER['REQUEST_METHOD']) {
                // GETの場合
                case 'GET':
                    if (method_exists($targetInstance, 'PlayGet')) {
                        $targetInstance->PlayGet();
                    } elseif (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;

                // POSTの場合
                case 'POST':
                    if (method_exists($targetInstance, 'PlayPost')) {
                        $targetInstance->PlayPost();
                    } elseif (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;

                // PUTの場合
                case 'PUT':
                    if (method_exists($targetInstance, 'PlayPut')) {
                        $targetInstance->PlayPut();
                    } elseif (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;

                // DELETEの場合
                case 'DELETE':
                    if (method_exists($targetInstance, 'PlayDelete')) {
                        $targetInstance->PlayDelete();
                    } elseif (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;

                // OPTIONの場合
                case 'OPTION':
                    if (method_exists($targetInstance, 'PlayOption')) {
                        $targetInstance->PlayOption();
                    } elseif (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;

                // HEADの場合
                case 'HEAD':
                    if (method_exists($targetInstance, 'PlayHead')) {
                        $targetInstance->PlayHead();
                    } elseif (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;

                // TRACEの場合
                case 'TRACE':
                    if (method_exists($targetInstance, 'PlayTrace')) {
                        $targetInstance->PlayTrace();
                    } elseif (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;

                // CONNECTの場合
                case 'CONNECT':
                    if (method_exists($targetInstance, 'PlayConnect')) {
                        $targetInstance->PlayConnect();
                    } elseif (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;

                // デフォルトの場合
                default:
                    if (method_exists($targetInstance, 'Play')) {
                        $targetInstance->Play();
                    } else {
                        // メソッドが存在しなければ例外をThrow
                        throw new Exception($this->coreError('notfound_play'));
                    }
                    break;
            }
        } catch (Exception $e) {
            // エラーハンドリングメソッドをコール
            if (method_exists($targetInstance, 'Error')) {
                $targetInstance->Error($e);
            } else {
                // メソッドが存在しなければ強制終了
                die($this->coreError('notfound_error'));
            }
        } finally {
            // クリーニングメソッドをコール
            if (method_exists($targetInstance, 'Clean')) {
                $targetInstance->Clean();
            } else {
                // メソッドが存在しなければ強制終了
                die($this->coreError('notfound_clean'));
            }
        }
    }

やってることはとてもシンプルで、呼び出すクラスファイル(すなわちユーザアプリのコード)を決定して、そのインスタンスを生成して、「決められた順序」でそのインスタンス中のメソッドをコールする。その「決められた順序」でメソッドのコールができない場合はエラーとする。つまりはそれだけなのです。

この「決められた順序」っていうのは、

  1. Init()
  2. Pray()
  3. Clean()

の順番というのが基本となり、各メソッドで例外が発生した場合は「Error()」というメソッドがコールされる……という流れになります。

で、ちょっとややこしいのだけれども、「Pray()」メソッドはHTTPのメソッドに応じて複数定義することができたりするわけで。例えば、POSTメソッドでアクセスされた場合は「PrayPost()」というメソッドが「Pray()」の代わりにコールされます。

必須メソッドを整理するとこんな感じになります。このうち「Pray()」の代わりに「Pray{HTTPのメソッド名}()」というルールのメソッドを定義しておくと、そのHTTPメソッドでアプリケーションがアクセスされた場合にそちらが呼び出されます。

Init()
初期化処理を書く。コール時にRisoluto形式で指定されたパラメタが渡されてくる。
Pray()
主処理を書く。
Error()
エラー処理を書く。このメソッドが例外をThrowしてはならない。
Clean()
後処理を書く。

で、どのクラスインスタンスを呼び出すかをどう決めているか……というと、「Core::FindCallClass()」ってメソッドが良きようにしてくれるのです。ついでにいえば、このCoreクラスでエラー(*2)が発生した場合は、例外をThrowします。例外発生時のエラーメッセージテキストは「Core::CoreError()」というメソッドで生成する……というのが大まかな流れ。

まあ、そのあたりはまた明日。


*1:アドベント・ぼっち・カレンダーの意
*2:必須メソッドが定義されていない場合

0 件のコメント:

コメントを投稿