<?php
    //========================================================================================================================
    /**
     * システム情報クラス
     *
     * 設定ファイル格納ディレクトリ内のsystem.xml および プログラム個別設定(URL指定されたプログラムID.xml)を
     * 取得・結合しアクセス可能にする
     *
     * LICENSE: This source file is subject to GNU GENERAL PUBLIC LICENSE Version 3
     * http://www.gnu.org/licenses/gpl-3.0.txt.
     *
     * @package    LabbitBox
     * @subpackage core
     * @copyright  2010 LabbitBox Development Team.
     * @license    GPL v3 http://www.gnu.org/licenses/gpl-3.0.txt
     * @author     neobaba <neobaba@labbitbox.org>
     * @version    v0.1 alpha3
     * @since      -
     * @link       http://labbitbox.org/
     * @see        http://labbitbox.org/
     *
     *------------------------------------------------------------------------------------------------------------------------
     * 〔Configファイル群の考え方〕
     * BASE_CONFディレクトリ内に格納されたファイルを下記の順序とルールで読み込む
     *
     * 1.フレームワーク内のデフォルト値格納xmlファイル
     *   (1)default.xml
     *   
     * 2.設定ファイル格納ディレクトリ（通常はBASE_CONF:以下BASE_CONFで説明）直下の拡張子が'xml'のファイル
     *   (2)system.xml
     *   (3)起動ファイル名.xml
     *   (4)アクション名.xml
     *
     *   ※同一IDが存在する場合は後から読み込まれたエントリーを有効とする
     * 
     * ◇使用例
     * $config = new Config(BASE_CONF);
     * 
     * //システム規定エントリー：開発者がタグ名を変更できない
     * //BASE_CONFディレクトリ直下の優先順位の高い xmlファイルのエントリーactionを取得
     * $default_action = $config->get('sys/action');
     * 
     * //アプリケーション固有エントリー：開発者が任意にタグ名を決定する
     * //BASE_CONFディレクトリ直下の優先順位の高い xmlファイルのエントリーactionを取得
     * $default_action = $config->get('sys/action');
     * 
     */
    //========================================================================================================================
    class Config {
        private $_config    = NULL; //基本設定のリスト
        private $_xpath     = NULL; //検索用xpathオブジェクト
        private $_confDir   = '';   //設定ファイル格納パス
                                    
        //-------------------------------------------------------------------------------------------------------------------
        /**
         * コンストラクタ/デストラクタ
         *
         * @param string    $confDir  設定ファイル格納ディレクトリ
         */
        //-------------------------------------------------------------------------------------------------------------------
        public function __construct($confDir, $action = ''){
            $this->_confDir = $confDir;

            //基本設定
            $wPath      = array();
            $wPath[0]   = '';
            $wPath[1]   = '';
            if(defined('LB_BOOT_CONF')){
                // 起動ファイル内で定義された LB_BOOT_CONFパラメータの使用
                // 特にサブディレクトリ下で起動ファイル名が重複した場合に対処に有効
                $wPath[2]   = $confDir.'/'.LB_BOOT_CONF.'.xml';
            }else{
                $wPhpFile   = (basename($_SERVER['SCRIPT_FILENAME'], '.php'));
                $wPath[2]   = $confDir.'/'.$wPhpFile.'.xml';        //起動ファイル名.xml
            }

            $wPath[3]   = $confDir.'/system.xml';               //system.xml
            $wPath[4]   = dirname(__FILE__).'/xml/default.xml'; //デフォルト.xml
            
            foreach ($wPath as $_index => $_path){
                $this->_config[$_index] = new DOMDocument('1.0', 'utf-8');
                if (file_exists($_path)){
                    $this->_config[$_index]->preserveWhiteSpace = false;
                    $this->_config[$_index]->load($_path);
                }
                $this->_xpath[$_index]  = new DOMXPath($this->_config[$_index]);    //検索用xpathオブジェクト生成
            }

            //Action固有設定
            $this->loadActionXml(isset($_REQUEST['action']) ? $_REQUEST['action'] : '');
            $this->_xpath[1]  = new DOMXPath($this->_config[1]);

            // xmlのConfigに$_REQUESTの値をオーバーレイ
            $action = (isset($_REQUEST['action']) ? $_REQUEST['action'] : $this->value('sys.action'));
            $method = (isset($_REQUEST['method']) ? $_REQUEST['method'] : $this->value('sys.method'));
            $type   = (isset($_REQUEST['type'])   ? $_REQUEST['type']   : $this->value('sys.type')  );
            $this->setRequestValue($action, $method, $type, $_REQUEST);
        }
        
        public function loadActionXml($action){
            if (isset($action)){
                $wAction    = $action;
            }
            else{
                $wAction    = $this->value('sys.action');
            }    
            $wAPath     = $this->_confDir.'/'.$wAction.'.xml';
            if (file_exists($wAPath)){
                $this->_config[1]->preserveWhiteSpace = false;
                $this->_config[1]->load($wAPath);
            }
        }
        
        //-------------------------------------------------------------------------------------------------------------------
        /**
         * 設定情報取得
         * @param string    $key    エントリータグ名
         * @return string   エレメント値
         */
        //-------------------------------------------------------------------------------------------------------------------
        public function value($key){
           $wXpath = '/config/'.str_replace('.', '/', $key).'/text()';
           foreach ($this->_config as $_index => $_config){
                $wNode = $this->_xpath[$_index]->query($wXpath)->item(0);
                if ($wNode != NULL){
                    return $wNode->nodeValue;
                }
            }
            ec('No entry "'.$key.'" in config');
            ec('[Degug Trace]');
            var_dump(debug_backtrace());
            debug_print_backtrace();
            die;
        }
        
        //-------------------------------------------------------------------------------------------------------------------
        /**
         * リクエスト→Configオブジェクト[0]セット
         * @param string $action アクションID
         * @param string $method メソッド名
         * @param string $type   起動タイプ
         * @param mixed  $params その他パラメータ連想配列
         */
        //-------------------------------------------------------------------------------------------------------------------
        public function setRequestValue($action, $method, $type, $params = array()){
            $cRequests  = array('action', 'method', 'type');

            $wConfig    = $this->_config[0]->createElement('config');
            $this->_config[0]->appendChild($wConfig);
            $wSys       = $this->_config[0]->createElement('sys');
            $wConfig->appendChild($wSys);

            foreach($cRequests as $curRequest){
                $wNode  = $this->_config[0]->createElement($curRequest, $$curRequest);
                $wSys->appendChild($wNode);
            }

            if (count($params) > 0){
                $wApp   = $this->_config[0]->createElement('sys');
                $wConfig->appendChild($wApp);
                foreach($params as $_key => $_value){
                    $wNode  = $this->_config[0]->createElement(str_replace('*', 'LB_ALL', $_key), $_value);
                    $wApp->appendChild($wNode);
                }
            }
        }

        //-------------------------------------------------------------------------------------------------------------------
        /**
         * DOMエレメント取得
         * @param string    $agName エレメントID
         * @return object   エレメントオブジェクト
         */
        //-------------------------------------------------------------------------------------------------------------------
        public function valueByID($elementID){
            foreach ($this->_config as $_index => $_config){
                $wElement   = $this->getElementById($elementID, $_config);
                if ($wElement != NULL){  
                    if ($wElement->childNodes->item(0)->nodeName == '#text'){
                        return $wElement->childNodes->item(0)->nodeValue;
                    }
                    else{
                        $wElementList   = array();
                        foreach($wElement->childNodes as $_node){
                            $wElementList[$_node->nodeName]     = $_node->nodeValue;
                        }
                        return $wElementList;
                    }
                }
            }
            ec('No entry "'.$elementID.'" in config');
            ec('[Degug Trace]');
            var_dump(debug_backtrace());
            die;
        }
        
        //-------------------------------------------------------------------------------------------------------------------
        /**
         * DOMエレメント取得
         * @param string    $agName エレメントID
         * @param object    $doc          解析対象設定オブジェクト（開発用）
         * @return object   エレメントオブジェクト
         */
        //-------------------------------------------------------------------------------------------------------------------
        private function getElementById($id, $doc = NULL){
            $wkDoc  = ($doc === NULL ? $this->_config : $doc);
            $xpath = new DOMXPath($wkDoc);
            return $xpath->query("//*[@id='$id']")->item(0);
        }
        
        //-------------------------------------------------------------------------------------------------------------------
        /**
         * DOMエレメントリスト取得
         * @param string    $agName エレメントID
         * @return object   エレメントオブジェクト
         */
        //-------------------------------------------------------------------------------------------------------------------
        public function getElementsByTagName($tagName){
            $wElementList   = array();
            for ($i = count($this->_config) - 1; $i >= 0; $i--){
                $wConfig    = $this->_config[$i];
                foreach($wConfig->getElementsByTagName($tagName) as $_node){
                    $wElementList[$_node->getAttribute('id')]   = $_node->nodeValue;
                }
            }
            return $wElementList;
        }
    
    }

?>
