「EthnaできれいなURLを実現する方法」を速くする

このエントリーをはてなブックマークに追加
はてなブックマーク - 「EthnaできれいなURLを実現する方法」を速くする

平井です。
以前のエントリで紹介したEthnaで「きれいなURL」を実現する方法についてです。
私が構築したアプリケーションにおいて、コードを修正することでパフォーマンスを改善できました。
内容を以下にご紹介します。

前回ご紹介したままの実装を下に再掲します。

< ?php
/**
 *  Hoge_UrlHandler.php
 */

require_once 'Net/Url/Mapper.php';

/**
 *  URLハンドラクラス
 *
 */
class Hoge_UrlHandler extends Ethna_UrlHandler
{
    /** @var array アクションマッピング */
    var $action_map = array(

      // エントリポイントでつける名前
      'index' => array(

        // ログイン
        '/login' => array(
          'action' => 'login',
        ),

        // ページ送り
        '/view/:category/:offset' => array(
          'action' => 'view',
          'category' => '',
          'offset' => '',
        ),
      ),
    );

    /**
     *  Hoge_UrlHandlerクラスのインスタンスを取得する
     */
    function &getInstance($class_name = null)
    {
        $instance =& parent::getInstance(__CLASS__);
        return $instance;
    }

    /**
     * Net_URL_MapperでURLリライティングに対応する
     *
     * @param array $http_vars
     * @link http://labs.cybozu.co.jp/blog/tsuruoka/anubis/blog_show/45
     * @return array
     */
    function requestToAction($http_vars)
    {
        if (isset($http_vars['__url_handler__']) == false
            || isset($this->action_map[$http_vars['__url_handler__']]) == false) {
            return array();
        }
        $url_handler = $http_vars['__url_handler__'];
        $action_map = $this->action_map[$url_handler];
        // parameter fix
        $method = sprintf("_normalizeRequest_%s", ucfirst($url_handler));
        if (method_exists($this, $method)) {
            $http_vars = $this->$method($http_vars);
        }
        // normalize
        if (isset($http_vars['__url_info__'])) {
            $path = $http_vars['__url_info__'];
        } else {
            $path = "";
        }
        list($path, $is_slash) = $this->_normalizePath($path);
        $mapper = Net_URL_Mapper::getInstance($http_vars['__url_handler__']);
        foreach ($this->action_map[$http_vars['__url_handler__']] as $key => $value) {
            $mapper->connect($key, $value);
        }
        $result = $mapper->match($path);
        $http_vars = $this->buildActionParameter($http_vars, $result['action']);
        unset($result['action']);
        $http_vars = array_merge($http_vars, $result);
        return $http_vars;
    }
}
?>

この実装で構築したアプリケーションコードに対してxdebugでプロファイルをかけてみると、上のコード内の71行目付近、

foreach ($this->action_map[$http_vars[’__url_handler__’]] as $key => $value) {
  $mapper->connect($key, $value);
}
$result = $mapper->match($path);

このループ内のconnectメソッドのコストが比較的高いことが判明しました。

この実装では、ループは$action_mapの要素の数だけループします。ループを抜けた後、matchメソッドでURLを評価します。
「マッピングの情報をすべて読み込んでから、URLを評価する」というイメージの実装になってるようですね。

私が構築したアプリケーションでは、以下のようなコードにすることでパフォーマンスを少し改善することができました。

$result = null;
foreach ($this->action_map[$http_vars['__url_handler__']] as $key => $value) {
    $mapper->connect($key, $value);
    $result = $mapper->match($path);
    if(!is_null($result)) break;
    $mapper->reset();
}

この実装では、ループ毎にmatchメソッドでURLを評価しています。
ループ内で$action_mapの要素を順に評価し、該当するアクションがあった時点でループを抜ける実装です。
connectの呼び出しが減って、matchとresetが増えるイメージになります。

パフォーマンスの厳密な評価はしていませんし、ベンチもとってませんし、アプリケーションの構成によっても変わると思います。
よく呼び出されるアクションを、$actioin_mapの上位に定義するとよさそうです。

ご意見、ご指摘をお待ちしております!

参考

EthnaでNet_URL_Mapperを使う

PEAR::Net_URL_MapperでURLルーティングを制御する

カテゴリー: php   パーマリンク

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>