Mapleの森を歩く(1): DIコンテナの生成メカニズム

少しずつMapleを勉強しようと思い、Maple3.00に付属のexample1から攻略していこうと考えました。

これから探検が始まります。大きく脱線していきます...

まずは入り口となるPHPファイルを見ました。
すべての処理のエントリーポイントとなるControllerが気になりました。

$ cd ~/public_html/php/maple/maple-3.0.0/
$ vi htdocs/example1.php
[...]
/**
 * フレームワーク起動
 **/
$controller =& new Controller();
$controller->execute();
?>

grepをしてみると早速その実装が見つかりました。

$ grep -rn "Controller" . | grep function
./maple/core/Controller.class.php:24:   function &Controller() {


Controllerのコンストラクタは空っぽだったので、execute()を見てみることにしました。

$ vi maple/core/Controller.class.php
[...]
    function execute() {
        $log =& LogFactory::getLog();

        //
        // DIContainerを生成する
        //
        $container =& Controller::_createDIContainer();

        if (!is_object($container)) {
            $log->fatal("DIContainerの生成に失敗しました", "Controller#execute");
            return;
        }

_createDIContainer()が気になりました。
ここで1つ良い発見がありました。
それはparse_ini_file()です。
PHPの組み込み関数でiniファイルをパースし, 連想配列に格納してくれるという優れものです。
どんな風に連想配列に格納されているのかを確かめるために、printデバッグの手法で覗いてみました。

[...]
    function &_createDIContainer() {
        $log =& LogFactory::getLog();

        if (!@file_exists(BASE_INI)) {
            $log->fatal("設定ファイルが存在しません", "Controller#_createDIContainer");
            return;
        }

        $config = parse_ini_file(BASE_INI, TRUE);

        // BEGIN ysano2005
        print "<pre>";
        print_r($config);
        print "</pre>";
        // END

ブラウザには以下のように表示されました。

Array
(
    [ConfigUtils] => Array
        (
            [name] => ConfigUtils
            [path] => core/ConfigUtils.class.php
        )

    [Request] => Array
        (
            [name] => Request
            [path] => core/Request.class.php
        )

    [Response] => Array
        (
            [name] => Response
            [path] => core/Response.class.php
        )

    [ActionChain] => Array
        (
            [name] => ActionChain
            [path] => core/ActionChain.class.php
        )

    [FilterChain] => Array
        (
            [name] => FilterChain
            [path] => core/FilterChain.class.php
        )

)

以下の項目を入力してください。
[...]

肝心のiniファイルは以下の通りです。
ブラウザに表示されたprintデバッグの結果とiniファイルを比較すると納得できる形で連想配列に格納されていることがわかりました。
id:kunitさんが、設定ファイルのフォーマットにXMLではなくiniファイルを採用した理由がわかったような気がします。

$ vi webapp/config/base.ini
[ConfigUtils]
name = ConfigUtils
path = core/ConfigUtils.class.php

[Request]
name = Request
path = core/Request.class.php

[Response]
name = Response
path = core/Response.class.php

[ActionChain]
name = ActionChain
path = core/ActionChain.class.php

[FilterChain]
name = FilterChain
path = core/FilterChain.class.php

先ほどの_createDIContainer()に話を戻します。
ここでも面白いコードを見つけました。
foreachのループで設定ファイル(base.ini)に書かれているクラスファイルを動的にロードしているようです。

  1. 動的にクラスファイルをインクルード
  2. 動的にクラスを生成
  3. コンテナに生成したインスタンスを登録

このシンプルでわかりやすいテンポが良いですね。

また

  • 設定ファイルを用いることで、クラスの追加が発生した際の対応を行いやすくしている

も良いですね。

[...]
        foreach ($config as $key => $value) {
            if (isset($config[$key]["name"])) {
                $className = $config[$key]["name"];
            }
            if (isset($config[$key]["path"])) {
                $filename = $config[$key]["path"];
            }
            [...]
            // BEGIN ysano2005
            $log->trace("filename: $filename, classname: $className","Controller#_createDIContainer");
            // END

            include_once($filename);

            $instance =& new $className();
            [...]
            $container->register($instance, $key);
        }

Mapleに用意されている、$log->trace()もついでに試してみました。
きちんとログに残ってます。

$ tail -f webapp/logs/maple.log
[...] 
[2005/07/27 12:24:57] [trace] filename: core/ConfigUtils.class.php, classname: ConfigUtils - Controller#_createDIContainer
[2005/07/27 12:24:57] [trace] filename: core/Request.class.php, classname: Request - Controller#_createDIContainer
[2005/07/27 12:24:57] [trace] filename: core/Response.class.php, classname: Response - Controller#_createDIContainer
[2005/07/27 12:24:57] [trace] filename: core/ActionChain.class.php, classname: ActionChain - Controller#_createDIContainer
[2005/07/27 12:24:57] [trace] filename: core/FilterChain.class.php, classname: FilterChain - Controller#_createDIContainer

ソースコードを読む際、何らかの目的を持って読まないと、迷子になってしまいます。
実は、execute()のまだ始めの方で、これからが肝心な部分なんですよね...

でも、今回は、

  • Mapleの設定ファイルのパースの仕組み
  • 設定ファイルからクラスを動的にロードする仕組み

が発見できたので、Mapleの森探索は良しということで。