アプリ本体のベースとなるAppBaseクラス

MVCフレームワーク

アプリケーションの本体となるクラスを作成し、このクラスでリクエストされたURLに対するルーティングの実行、データベースのアクセスなど、コントローラーでアクションを実行するための前処理を中心に各メソッドを定義します。
MVCフレームワークを利用するアプリケーションの本体部分を作成し、アプリを作成するときはこのクラスを継承して独自の機能を定義していくのです。

プロパティとコンストラクターを用意する

AppBaseクラスを定義するにあたり、各プロパティを用意します。これらのプロパティでConnectModelクラスやRequest、Response、Session、Routerの各クラスのオブジェクトやサインイン時のコントローラーとアクションの組み合わせ、エラー表示のフラグを保持します。
そこで、コンストラクターでは以下の処理を定義します。

  • initialize()メソッドで初期化を実行。
  • dbConnect()メソッドでデータベース接続を実行。
  • setDisplayErrors()メソッドでエラー表示の有無の設定を実行。
<?php
abstract class AppBase{
    protected $_request;
    protected $_response;
    protected $_session;
    protected $_connectModel;
    protected $_router;
    protected $_signinControlAction = array();
    protected $_displayErrors = false;
    const CONTROLLER = 'controller';//コントローラークラス名のベース部分
    const VIEWDIR = '/views';//viewsディレクトリ
    const MODELDIR = '/models';//modelsディレクトリ
    const WEBDIR = '/mvc_htdocs';//ドキュメントルートディレクトリ
    const CONTROLLERDIR = '/controllers'//controllersディレクトリ

    //コンストラクター
    public function __construct($dispErr){
        $this->setDisplayErrors($dispErr);
        $this->initialize();
        $this->dbConnect();
    }
}

?>

アプリケーションの基本処理を定義する

initialize()メソッド

Request、Response、Session、ConnectModel、Routerクラスのインスタンス化を行い、情報をプロパティに格納します。

protected function initialize(){
    //ConnectModelクラスのインスタンスを格納
    $this->connectModel = new ConnectModel();
    //AppBaseクラスのgetRouteDef()メソッドの戻り値を引数にRouterクラスのインスタンスを格納
    $this->_router = new Router($this->getRouteDef());
    //それぞれのクラスのインスタンスを格納
    $this->_request = new Request();
    $this->_response = new Response();
    $this->_session = new Session();
}

getRouteDef()メソッド

先ほどinitialize()メソッドの中に記述した、Routerクラスをインスタンス化する際の引数にしようしたgetRouteDef()メソッドを抽象メソッドを記述します。

abstract protected function getRouteDef();

displayErrors()メソッドでエラー表示設定

エラー表示フラグがtrueの場合はすべてのエラーを出力するよう定義します。

ここでini_set()関数を使います。以下ini_setの説明については公式ドキュメントを引用します。

指定した設定オプションの値を設定します。 設定オプションは、スクリプトの実行中は新しい値を保持し、 スクリプト終了時に元の値へ戻されます。

ini_set(string $option, string|int|float|bool|null $value):

$valueにオプションの新しい値を指定し、$optionに設定します。
全てのオプションがini_set()を使用して変更することが可能なわけではありません。有効なオプションの完全な一覧は付録を参照ください。

ここではdisplay_errorsオプションを使用して、ini_set()関数でエラーをHTML出力の一部として画面に出力をするか決めます。

//エラーを出力する場合
ini_set('display_errors', 1);
//エラーを出力しない場合
ini_set('display_errors', 0);

また、error_reportingですべてのエラーを表示するようにします。

//すべてのエラーを表示する
ini_set('error_reporting', E_ALL);

では、setDisplayErrors()メソッドを定義しましょう。

protected function setDisplayErrors($dispErr){
    //$dispErrの値で表示するかしないか決定する
    if($dispErr){
        $this->_displayErrors = true;
        ini_set('_displayErrors', 1);
        ini_set('error_reporting', E_ALL);
    } else {
        $this->_displayErrors = false;
        ini_set('display_errors', 0);
    }
}

isDisplayErrors()メソッドでエラー表示の有無を返す

public function isDisplayErrors(){
    return $this->_displayErrors;
}

run()メソッドでリクエストに応答してレスポンスを送信

run()メソッドで以下の処理を行います。ルーティング情報の取得はtry-catchブロックで処理して、例外処理も定義します。

  • ①tryブロックの中でルーティング情報の取得
  • ②1つ目のcatchブロックの中でFileNotFoundException例外の処理
  • ③2つ目のcatchブロックの中でAuthenticateException例外の処理
  • ④Responseクラスのsend()メソッドを実行してレスポンス情報を送信
public function run(){
    try{
        //リクエストされたパス情報が存在するか調べ、マッチした場合はその結果を変数に格納する
        //Router::getRouteParams()メソッドでURL末尾のパス情報をルーティング用の情報に加工
        //Request::getPath()メソッドでURLに含まれるパス情報を取得
        $parameters = $this->_router->getRouteParams($this->_request->getPath());
        //上で取得したルーティング情報が存在しない場合は例外を投げる
        if($parameters === false){
            //メッセージを投げる
            throw new FileNotFoundException('NO ROUTE' . $this->_request->getPath());
        }
        //配列parametersのそれぞれの値を格納
        $controller = $parameters['controller'];
        $action = $parameters['action'];
        $this->getContent($controller, $action, $parameters);
    //FileNotFoundExceptionの例外処理
    } catch(FileNotFoundException $e) {
        //例外オブジェクトを引数にしてrender404Page()を呼び出す
        $this->dispErrPage($e);
    } catch(AuthenticateException $e){
        // list命令で$controllerと$actionに代入
        list($controller, $action) = $this->_signinAction;//$_signinActionはアプリ側で定義
        //ログイン画面に遷移
        $this->getContent($controller, $action);
    }

    //Responseクラスのsend()メソッドでレスポンス情報を送信する
    $this->_response->send();
}

getContent()メソッドでコンテンツをレスポンス情報にセットする

getContent()メソッドでは以下の処理を行います。

  • ①コントローラーのクラス名を生成してインスタンス化する
  • ②getControllerObj()の戻り値がfalseであれば例外処理
  • ③アクションを実行してコンテンツ取得してコンテンツをレスポンス情報にセット

ルーティング情報からコントローラークラスを特定し、インスタンス化します。
先ほど作成したrun()メソッドでアクションを実行し、ResponseクラスのsetContent()メソッドでコンテンツをレスポンス情報にセットするまでを行います。

①コントローラーのクラス名を生成してインスタンス化

まずコントローラー名からクラス名を生成します。

//ucfirst()で最初の文字を大文字にする
$controllerClass = ucfirst($controllerName) . self::CONTROLLER;

コントローラーのクラス名を引数にしてgetCOntrollerObj()を呼び出します。このメソッドは後程定義しますが対象のコントローラークラスをインスタンス化して返す処理を行います。

//戻り値のコントローラーサブクラスのインスタンスを$controllerに格納
$controller = $this->getControllerObj($controllerClass);

②getControllerObj()の戻り値がfalseであれば例外処理

if($controller === false){
    throw new FileNotFoundException($controllerClass . ' NOT FOUND.');
}

③アクションを実行してコンテンツ取得してコンテンツをレスポンス情報にセット

すべてのコントローラークラスのスーパークラスであるControllerクラスのdispatchメソッドを実行して、
アクション名とルーティング情報を受け取ることで指定されたアクションを実行し、コンテンツのデータを戻り値として返します。

//$contentにアクション名とルーティング情報を格納
$content = $controller->dispatch($action, $parameters);

ResposeクラスのsetContent()メソッドに取得したコンテンツを渡してコンテンツがResponseクラスのプロパティにセットされます。
呼び出し元のrun()メソッドの最後の処理ではResponseクラスのsend()メソッドを実行してるのでここでプロパティにセットしたコンテンツが画面に表示されるという流れになります。

//コンテンツをレスポンス情報にセット
$this->_response->setContent($content);

getContent()まとめ

getContent()メソッドをまとめたのが以下のコードです。

public function getContent(){
    //ucfirst()で最初の文字を大文字にする
    $controllerClass = ucfirst($controllerName) . self::CONTROLLER;
    //戻り値のコントローラーサブクラスのインスタンスを$controllerに格納
    $controller = $this->getControllerObj($controllerClass);
    //FileNotFoundException例外を投げる
    if($controller === false){
        throw new FileNotFoundException($controllerClass . ' NOT FOUND.');
    }
    //$contentにアクション名とルーティング情報を格納
    $content = $controller->dispatch($action, $parameters);
    //コンテンツをレスポンス情報にセット
    $this->_response->setContent($content);
}

getControllerObj()メソッドでコントローラークラスのインスタンス化

インスタンス化するメソッドを定義し、以下の処理を行います。

  • ①コントローラークラスのファイルパスを取得し、読み込む
  • ②コントローラークラスをインスタンス化

①コントローラークラスのファイルパスを取得し、読み込む

コントローラークラスが読み込まれていない場合の処理として、コントローラークラスが格納されているディレクトリへのパスに、生成済みの名前を連結します。パスを取得する際はgetControllerDirectory()メソッドを取得します。

if(!class_exists($controllerClass)){
    $controllerFile = $this->getControllerDirectory() . '/' . $controllerClass . '.php';
}

上の処理で取得したコントローラークラスを読み込んで、読み込んだクラスが定義済みでなかったらfalseを返します。

if(is_readable($controllerFile)){
    //ファイルが存在すれば読み込む
    require_once $controllerFile;
    if(!class_exists($controllerClass)){
        //定義済みでない場合はfalse
        return false;
    }                
} else {
    //コントローラークラスのファイルが存在しない場合はfalseを返す
    return false;
}

コントローラクラスをインスタンス化

AppBaseクラスのインスタンスを受け取るパラメーターがあるので引数をthisとして、読み込まれている定義済みの$controllerClassを使ってインスタンス化します。

$controller = new $controllerClass($this);

getControllerObj()メソッドのまとめ

protected function getControllerObj($controllerClass){
    if(!class_exists($controllerClass)){
        $controllerFile = $this->getControllerDirectory() . '/' . $controllerClass . '.php';

        if(is_readable($controllerFile)){
            //ファイルが存在すれば読み込む
            require_once $controllerFile;
            if(!class_exists($controllerClass)){
                //定義済みでない場合はfalse
                return false;
            }                
        } else {
            //コントローラークラスのファイルが存在しない場合はfalseを返す
            return false;
        }
    }
    $controller = new $controllerClass($this);
    return $controller;
}

dispErrPage()メソッドでエラー画面を作成

ここではHTTP404エラーが発生した際に、以下の処理を行います。

  • ステータスコードとステータスメッセージの設定
  • エラーメッセージを設定
  • エラーメッセージを登録
    protected function dispErrPage($e){
        //ResponseクラスのsetStatusCode()メソッドを呼び出してコードとメッセージをヘッダーに登録
        $this->_response->setStatusCode(404, 'FILE NOT FOUND.');
        //isDisplayErrors()がtrueであれば例外メッセージを$errMsgへ格納、falseであれば'FILE NOT FOUND'を
        $errMsg = $this->isDisplayErrors() ? $e->getMessage(): 'FILE NOT FOUND.';
        $errMsg = htmlspecialchars($errMsg, ENT_QUOTES, 'UTF-8');
        $html = "
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8' />
<title>HTTP 404 Error</title>
</head>
<body>
{$errMsg}
</body>
</html>
";
        $this->_response->setContent($html);
    }

getRootDef()を抽象メソッドで用意

アプリケーションのルートディレクトリを取得するメソッドを、オーバーライド前提として抽象メソッドで定義します。

abstract public function getRootDef();

dbConnect()メソッドでデータベース接続を行う

これは実装はアプリ側で行うので実装は空で定義します。

protected function dbConnect(){}

Requestクラスなどのインスタンスを返すメソッド

initialize()メソッドでは、Requestクラス、Responseクラス、Sessionクラス、ConnectModelクラスのインスタンスを生成してプロパティに格納する処理を行ったので、アプリ側で取得できるようにゲッターを作成します。

public function getRequest(){
    return $this->__request;
}

public function getResponse(){
    return $this->_response;
}

public function getSession(){
    return $this->_session;
}

public function getConnectModel(){
    return $this->_connectModel;
}

ディレクトリのパス情報を取得するメソッドをまとめて定義

フレームワークとアプリを含むプロジェクト自体のルートディレクトリを取得できるメソッドを定義しておいて、このメソッドから得られたパス情報を基にして、それぞれのメソッドで対象のディレクトリのパスを返します。
中身をアプリ側で設定できるように抽象メソッドで定義しておきます。

abstract public function getRootDir();

モデル、ビュー、コントローラー用のフォルダのパスを返すメソッドを定義します。

public function getViewDir(){
    return $this->getRootDir() . self::VIEWDIR;
}

public function getModelDir(){
    return $this->getRootDir() . self::MODELDIR;
}

public function getWebDir(){
    return $this->getRootDir() . self::WEBDIR;
}

public function getControllerDir(){
    return $this->getRootDir() . self::CONTROLLERDIR;
}

AppBaseクラスのコードまとめ

今までの処理をまとめたコードは以下の通りです。

<?php
abstract class AppBase{
    protected $_request;
    protected $_response;
    protected $_session;
    protected $_connectModel;
    protected $_router;
    protected $_signinControlAction = array();
    protected $_displayErrors = false;
    const CONTROLLER = 'controller';//コントローラークラス名のベース部分
    const VIEWDIR = '/views';//viewsディレクトリ
    const MODELDIR = '/models';//modelsディレクトリ
    const WEBDIR = '/mvc_htdocs';//ドキュメントルートディレクトリ
    const CONTROLLERDIR = '/controllers'//controllersディレクトリ

    //コンストラクター
    public function __construct($dispErr){
        $this->setDisplayErrors($dispErr);
        $this->initialize();
        $this->dbConnect();
    }

    protected function initialize(){
        //ConnectModelクラスのインスタンスを格納
        $this->connectModel = new ConnectModel();
        //AppBaseクラスのgetRouteDef()メソッドの戻り値を引数にRouterクラスのインスタンスを格納
        $this->_router = new Router($this->getRouteDef());
        //それぞれのクラスのインスタンスを格納
        $this->_request = new Request();
        $this->_response = new Response();
        $this->_session = new Session();
    }

    abstract protected function getRouteDef();
    
    protected function dbConnect(){}

    protected function setDisplayErrors($dispErr){
        //$dispErrの値で表示するかしないか決定する
        if($dispErr){
            $this->_displayErrors = true;
            ini_set('_displayErrors', 1);
            ini_set('error_reporting', E_ALL);
        } else {
            $this->_displayErrors = false;
            ini_set('display_errors', 0);
        }
    }

    public function isDisplayErrors(){
        return $this->_displayErrors;
    }

    public function run(){
        try{
            //リクエストされたパス情報が存在するか調べ、マッチした場合はその結果を変数に格納する
            //Router::getRouteParams()メソッドでURL末尾のパス情報をルーティング用の情報に加工
            //Request::getPath()メソッドでURLに含まれるパス情報を取得
            $parameters = $this->_router->getRouteParams($this->_request->getPath());
            //上で取得したルーティング情報が存在しない場合は例外を投げる
            if($parameters === false){
                //メッセージを投げる
                throw new FileNotFoundException('NO ROUTE' . $this->_request->getPath());
            }
            //配列parametersのそれぞれの値を格納
            $controller = $parameters['controller'];
            $action = $parameters['action'];
            $this->getContent($controller, $action, $parameters);
        //FileNotFoundExceptionの例外処理
        } catch(FileNotFoundException $e) {
            //例外オブジェクトを引数にしてrender404Page()を呼び出す
            $this->dispErrPage($e);
        } catch(AuthenticateException $e){
            // list命令で$controllerと$actionに代入
            list($controller, $action) = $this->_signinAction;//$_signinActionはアプリ側で定義
            //ログイン画面に遷移
            $this->getContent($controller, $action);
        }
    
        //Responseクラスのsend()メソッドでレスポンス情報を送信する
        $this->_response->send();
    }
    
    public function getContent(){
        //ucfirst()で最初の文字を大文字にする
        $controllerClass = ucfirst($controllerName) . self::CONTROLLER;
        //戻り値のコントローラーサブクラスのインスタンスを$controllerに格納
        $controller = $this->getControllerObj($controllerClass);
        //FileNotFoundException例外を投げる
        if($controller === false){
            throw new FileNotFoundException($controllerClass . ' NOT FOUND.');
        }
        //$contentにアクション名とルーティング情報を格納
        $content = $controller->dispatch($action, $parameters);
        //コンテンツをレスポンス情報にセット
        $this->_response->setContent($content);
    }
    
    protected function getControllerObj($controllerClass){
        if(!class_exists($controllerClass)){
            $controllerFile = $this->getControllerDirectory() . '/' . $controllerClass . '.php';

            if(is_readable($controllerFile)){
                //ファイルが存在すれば読み込む
                require_once $controllerFile;
                if(!class_exists($controllerClass)){
                    //定義済みでない場合はfalse
                    return false;
                }                
            } else {
                //コントローラークラスのファイルが存在しない場合はfalseを返す
                return false;
            }
        }
        $controller = new $controllerClass($this);
        return $controller;
    }
    
    protected function dispErrPage($e){
        //ResponseクラスのsetStatusCode()メソッドを呼び出してコードとメッセージをヘッダーに登録
        $this->_response->setStatusCode(404, 'FILE NOT FOUND.');
        //isDisplayErrors()がtrueであれば例外メッセージを$errMsgへ格納、falseであれば'FILE NOT FOUND'を
        $errMsg = $this->isDisplayErrors() ? $e->getMessage(): 'FILE NOT FOUND.';
        $errMsg = htmlspecialchars($errMsg, ENT_QUOTES, 'UTF-8');
        $html = "
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8' />
<title>HTTP 404 Error</title>
</head>
<body>
{$errMsg}
</body>
</html>
";
        $this->_response->setContent($html);
    }

    public function getRequest(){
        return $this->__request;
    }

    public function getResponse(){
        return $this->_response;
    }

    public function getSession(){
        return $this->_session;
    }

    public function getConnectModel(){
        return $this->_connectModel;
    }

    abstract public function getRootDir();

    public function getViewDir(){
        return $this->getRootDir() . self::VIEWDIR;
    }

    public function getModelDir(){
        return $this->getRootDir() . self::MODELDIR;
    }

    public function getWebDir(){
        return $this->getRootDir() . self::WEBDIR;
    }

    public function getControllerDir(){
        return $this->getRootDir() . self::CONTROLLERDIR;
    }


}

?>

コメント

タイトルとURLをコピーしました