前回(URLルーティングを作る – part one : PEAR::Net_URL_Mapper)および前々回(URLルーティングを作る – part zero : mod_rewrite)の内容を応用して、URLをルーティングしてビジネスロジックを実行するアクションクラスを実装してみる。

【router.php】

<?php
/**
*  Kwappa Framework URLルータクラス
*
*  すべてのリクエストを集めてURLルーティングを行う。
*/
//==============================================================================
// constants
//==============================================================================
define("KWAPPA_ACTION_CLASS_DIR", "/path/to/action_class/") ;
define("KWAPPA_ACTION_DEFAULT_NAME", "index") ;
//==============================================================================
// includes
//==============================================================================
require_once "Net/URL/Mapper.php" ;
//==============================================================================
// class
//==============================================================================
class KwappaUrlRouter
{
    /**
     *  コンストラクタ : インスタンス生成を禁止する
     */
    private function __construct()
    {
        // do nothing.
    }
    /**
     *  URLをルーティングしてアクションクラスを走らせる
     */
    public static function routeUrl()
    {
        // pathからスクリプト名を除去する
        $path = str_replace($_SERVER['SCRIPT_NAME'], "", $_SERVER['REQUEST_URI']) ;
        // 自前でアクションクラスをマッピングする
        $path = trim($path, "/") ;
        $path = str_replace("/?", "?", $path) ;
        list($controller_name, $action_name, $url_param) = explode("/", $path, 3) ;
        if (!$controller_name)
        {
            die("controller name is not found.") ;
        }
        if (!$action_name)
        {
            $action_name = KWAPPA_ACTION_DEFAULT_NAME ;
        }
        // Directory Traversal対策
        if (!preg_match("/^\w+$/", $controller_name) ||
            !preg_match("/^\w+$/", $action_name))
        {
            die("invalid controller or action name. [{$controller_name}]/[{$action_name}]") ;
        }
        // アクションクラスをrequireして処理を委譲
        $action_class = KWAPPA_ACTION_CLASS_DIR . "/{$controller_name}/{$action_name}.php" ;
        if (!file_exists($action_class))
        {
            die("controller [{$action_class}] is not found.") ;
        }
        require_once $action_class ;
        // アクションクラスを生成して走らせる
        $kwappa = new KwappaAction($action_name, $url_param) ;
        $kwappa->swim() ;
    }
}
KwappaUrlRouter::routeUrl() ;
?>

[CONTROLLER_NAME]/index.php】

<?php
class KwappaAction
{
    private $param ;
    // マッピングするパラメータ
    private static $URL_MAPPING_PARAMS  = "/:action/:mechanism/:hero_name/:hero_id/" ;
    // デフォルト値の配列
    private static $URL_MAPPING_DEFAULT = array(
        // [TIPS]パラメータの数をきっちり縛るならデフォルト値がないほうが使いやすい
//       'mechanism' => 'yatter_one',
//       'hero_name' => 'gan_chan',
//       'hero_id'   => 9
    ) ;
    // バリデーションルールの配列
    private static $URL_MAPPING_VALIDATE = array(
        'mechanism' => '\w+',
        'hero_name' => '\w+',
        'hero_id'   => '\d{1}'
    ) ;
    /**
     *  コンストラクタ
     *    @param        string    $action_name  アクション名
     *    @param        string    $url_param        パラメータ文字列
     */
    public function __construct($action_name, $url_param)
    {
        $mapper = Net_URL_Mapper::getInstance() ;
        $mapper->connect(
            self::$URL_MAPPING_PARAMS,
            self::$URL_MAPPING_DEFAULT,
            self::$URL_MAPPING_VALIDATE
        ) ;
        $this->param = $mapper->match($action_name . "/" . $url_param) ;
    }
    /**
     *  メインルーチン
     */
    public function swim()
    {
        // do something.
        echo "<pre>" ;
        var_dump($this->param) ;
    }
}
?>

エラー処理は適当にdie()しているが、Net_URL_Mapper#matchにあわせて例外を投げるようにして、まとめてcatchするのがよいだろう。

これで「きれいなURL」で遷移させる準備は整った。だが実はまだ実戦投入していないので使い勝手は未知数だったりする。改良点があったら再度エントリしよう…と心に決めつつ、URLルーティング連載はこれにて完結。