FuelPHPのAgentクラスが重い問題と暫定的な対処方法

問題の所在

FuelPHPには、ブラウザ種別、バージョン、プラットフォーム、OSなどを取得するAgentクラスがありますが、その処理が重いという問題です。

どうやらAgentクラスが使用するブラウザ情報が記録された「browscap.iniファイル」が大きくなったことに比例して、処理が重くなってしまったようです。

以下のベンチマークはすべてMac OS X、MAMPでの結果です。LinuxではMacほど劇的には重くならないようです。

まず、FuelPHP 1.8/developをインストールしてWelcomeページを表示します。ページの下に処理時間とメモリ消費量が表示されます。

Welcomeページ:

Page rendered in 0.0093s using 0.438mb of memory.

次に、Welcomeコントローラのaction_index()メソッドにecho Agent::browser();を追加します。すると以下のように劇的に重くなりました。

WelcomeページにAgent::browser()を追加:

初回   Page rendered in 23.3956s using 112.952mb of memory.
二度目 Page rendered in 3.6668s using 44.826mb of memory.

Agentクラスの仕様

Agentクラスは、PHPのget_browser()関数が使えない場合、自力でBrowser Capabilities Projectからlite_php_browscap.iniファイル(現在、5,902 KB)をダウンロードしてパースして処理します。

Agentクラスを最初に使った時に、このダウンロードとパースの処理が実行されるため、時間がものすごくかかり、メモリも恐ろしく必要になります。

二度目以降は、パースした結果のキャッシュを使うため改善しますが、それでも非常に遅く、メモリも必要です。

キャッシュファイルは定期的に自動更新されます。

対処方法

1. get_brower()関数を使う

lite_php_browscap.iniファイルを手動でダウンロードし、php.iniのbrowsecapにパスを設定することで、get_browser()関数が使えるようになります。

この場合、browscap.iniファイルはcronなどで別途更新する必要があります。

php.ini

@@ -729,6 +729,7 @@

 [browscap]
 ;browscap = extra/browscap.ini
+browscap = /Users/kenji/work/fuelphp/fuel/app/tmp/browscap.ini

 [Informix]
 ; Default host for ifx_connect() (doesn't apply in safe mode).

browscap.iniファイルをPHPの内部関数でパース処理するため、その処理速度の向上が期待できますが、一方で毎回パースするという欠点もあります。

Page rendered in 3.5592s using 0.459mb of memory.

メモリ使用量が劇的に改善しましたが、処理速度はほとんど変わりませんでした。

2. browscap/browscap-phpを使う

内部関数を使っても速度があまり改善しなかったので、全く別の実装に置き換えるか、現状の処理をチューニングするしかありません。

しかし、完全に別の実装だと現状とのブラウザ判定の仕様の微妙な違いが結構生じるかも知れません。

幸い、Browser Capabilities Projectが公式にサポートしているbrowscap/browscap-phpというのがありました。

browscap/browscap-phpを使う処理に置き換えることで、メモリ消費量を極端には増やさずに、処理速度を向上させることができました。

Page rendered in 0.0513s using 6.456mb of memory.

まず、browscap/browscap-phpをComposerでインストールしてください。

--- a/composer.json
+++ b/composer.json
@@ -137,7 +137,8 @@
         "fuel/parser": "1.7.2",
         "fuelphp/upload": "2.0.2",
         "monolog/monolog": "1.5.*",
-        "michelf/php-markdown": "1.4.0"
+        "michelf/php-markdown": "1.4.0",
+        "browscap/browscap-php": "2.0.*"
     },
     "suggest": {
         "dwoo/dwoo" : "Allow Dwoo templating with the Parser package",

Agentクラスへの暫定的な修正パッチは以下になります。

--- a/classes/agent.php
+++ b/classes/agent.php
@@ -195,8 +195,15 @@ class Agent
            // try the build in get_browser() method
            if (ini_get('browscap') == '' or false === $browser = get_browser(static::$user_agent, true))
            {
+             // if it fails, use browscap/browscap-php
+             $cacheDir = APPPATH.'cache/fuel/agent';
+             $browscap = new \phpbrowscap\Browscap($cacheDir);
+             $browscap->remoteIniUrl = 'http://browscap.org/stream?q=Lite_PHP_BrowsCapINI';
+             $browscap->doAutoUpdate = false;
+             $browser = $browscap->getBrowser(static::$user_agent, true);
+
                // if it fails, emulate get_browser()
-               $browser = static::get_from_browscap();
+             //$browser = static::get_from_browscap();
            }

            if ($browser)
@@ -236,11 +243,11 @@ class Agent
    /**
     * Get the Browser Version
     *
-    * @return  string
+  * @return  float
     */
    public static function version()
    {
-       return static::$properties['version'];
+     return (float) static::$properties['version'];
    }

    // --------------------------------------------------------------------

この場合も、cronなどで以下の処理を実行し、browscap.iniファイルを別途更新する必要があります。

$cacheDir = APPPATH.'cache/fuel/agent';
$browscap = new \phpbrowscap\Browscap($cacheDir);
$browscap->remoteIniUrl = 'http://browscap.org/stream?q=Lite_PHP_BrowsCapINI';
$browscap->updateCache();

関連

Date: 2014/10/31

Tags: fuelphp