CakePHP2 でコンポーネントの遅延ローディング

コントローラの初期化でフィールド変数 $components に定義したコンポーネントを読み込んで初期化していて、ファットコントローラでは大量のコンポーネントを使っていることがある。 更に、PDF出力やエクセル出力など外部ライブラリを使うコンポーネントがあると、それらも毎回読み込むことなる。 非常に単純なアクションでも、そのコントローラで使う可能性のある外部ライブラリを全部読み込むのは無駄なので、コンポーネントの遅延ローディングを試してみた。

方針

コントローラで、フィールド変数$components に定義しているもののうち、遅延ロードすればよいものは、フィールド変数$lazyComponents に移動する。

<?php
class WebApplicationsController extends AppController {
    public $components = array(
        'Session',
    );
    public $lazyComponents = array(
        'Heavy',
    );
 
    public function heavy() {
        $x = $this->Heavy->getHeavy();
    }
}

上のコンポーネントは、コントローラの初期化時は読み込まれないので、初期化されていない。 コンポーネントを使おうとした時、$this->Heavy は未定義のなので、AppController.php の __get で掴まえて初期化するという形にしました。

AppController.php の修正

存在しないフィールド変数を掴まえるマジックメソッド __get をオーバーライドして、$this->lazyComponents で定義したコンポーネントへのアクセスだった場合に、コンポーネントの初期化を行なうカスタマイズを入れてます。

<?php
class AppController extends Controller {
    // コンポーネントの遅延ローディング
    public function __get($name) {
        if (isset($this->lazyComponents)) {
            $components = ComponentCollection::normalizeObjectArray($this->lazyComponents);
            if (in_array($name, array_keys($components))) {
                $this->log('try lazy loading.' . $name, 'debug');
                $this->{$name} = $this->Components->load($name, $components[$name]);
                $this->{$name}->initialize($this);
                $this->{$name}->startup($this);
                $this->log('complete lazy loading.' . $name, 'debug');
                return $this->{$name};
            }
        }
        return parent::__get($name);
    }
}

正確な計測はしてないですが、DebugKitで見たところ単純なアクションではメモリ使用量が60%くらいになって、レスポンスも半分くらいになった。(当社比)

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.