<?php
/**
 * -----------------------------------------------------------------------------
 *
 * SyL - Web Application Framework for PHP
 *
 * PHP version 4 (>= 4.3.x) or 5
 *
 * Copyright (C) 2006 k.watanabe
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * -----------------------------------------------------------------------------
 * @package   SyL
 * @author    k.watanabe <k.watanabe@syl.jp>
 * @copyright 2006 k.watanabe
 * @license   http://www.opensource.org/licenses/lgpl-license.php
 * @version   CVS: $Id: SyL_Controller.php,v 1.10 2007/01/03 11:17:37 seasonstream Exp $
 * @link      http://www.syl.jp/
 * -----------------------------------------------------------------------------
 */

/**
 * バッファリング設定
 */
ob_start();
/**
 * magic_quotes_runtime -> off
 */
set_magic_quotes_runtime(0);
/**
 * PHPシステムエラーログの保存
 */
if (SYL_CONFIG_SYSTEM_ERROR_LOG && is_writable(dirname(SYL_CONFIG_SYSTEM_ERROR_LOG_FILE))) {
    ini_set('log_errors', SYL_CONFIG_SYSTEM_ERROR_LOG);
    ini_set('error_log', SYL_CONFIG_SYSTEM_ERROR_LOG_FILE);
}

/**
 * 定数設定ファイルを読み込むクラス
 */
require_once SYL_INCLUDE_DIR . '/framework/SyL_DefinesReader.php';
/**
 * コンテナクラス
 */
require_once SYL_INCLUDE_DIR . '/framework/SyL_Container.php';

/**
 * コンテナ内コントローラ名
 */
define('SYL_CONFIG_CONTROLLER_NAME', 'controller');

/**
 * コントローラクラス
 *
 * 全体の処理の流れを管理する
 * 主な内容は
 *   ・DIコンテナの保持
 *   ・処理の流れに伴う遷移イベント処理
 *
 * @package   SyL
 * @author    k.watanabe <k.watanabe@syl.jp>
 * @copyright 2006 k.watanabe
 * @license   http://www.opensource.org/licenses/lgpl-license.php
 * @version   CVS: $Id: SyL_Controller.php,v 1.10 2007/01/03 11:17:37 seasonstream Exp $
 * @link      http://www.syl.jp/
 */
class SyL_Controller
{
    /**
     * アクションディレクトリルート
     *
     * @access private
     * @var string
     */
    var $action_dir = '';
    /**
     * テンプレートディレクトリルート
     *
     * @access private
     * @var string
     */
    var $template_dir = '';
    /**
     * 定数設定ファイル名
     *
     * @access private
     * @var string
     */
    var $defines_file_name = 'defines.xml';
    /**
     * コンポーネント設定ファイル名
     *
     * @access protected
     * @var string
     */
    var $components_file_name = 'components.xml';
    /**
     * エラーハンドラメソッド名
     * ''の場合はエラーハンドラを設定しない
     *
     * @access private
     * @var string
     */
    var $error_handler_method = 'errorStream';
    /**
     * 処理後破棄メソッド名
     * ''の場合は処理後破棄メソッドを設定しない
     *
     * @access private
     * @var string
     */
    var $destory_handler_method = 'finalStream';
    /**
     * エラー配列
     *
     * @access private
     * @var array
     */
    var $error = array();
    /**
     * コンテナオブジェクト
     *
     * @access private
     * @var object
     */
    var $container = null;

    /**
     * コントローラを取得
     *
     * 実行環境により、WEB or Cmdのコントローラを判定して取得する
     *
     * @access public
     * @param array 環境設定配列
     */
    function &getController($config=array())
    {
        static $singleton;
        if (!is_object($singleton)) {
            if ((PHP_SAPI == 'cli') || ((PHP_SAPI == 'cgi') && !isset($_SERVER['REQUEST_METHOD']))) {
                define('SYL_ENV_TYPE', 'cmd');
            } else {
                define('SYL_ENV_TYPE', 'web');
            }
            $class_name = 'SyL_Controller' . ucfirst(SYL_ENV_TYPE);
            include_once SYL_INCLUDE_DIR . "/framework/Controller/{$class_name}.php";
            // 子クラスのインスタンス生成
            $singleton = new $class_name($config);
        }
        return $singleton;
    }

    /**
     * コンストラクタ
     *
     * @access public
     * @param array 環境設定配列
     */
    function SyL_Controller($config)
    {
        // アプリケーション名定義
        if (!isset($config['app_name']) || !$config['app_name']) {
            trigger_error("[SyL error] Controller parameter app_name not exists", E_USER_ERROR);
        }

        // デバックレベル定義
        define('SYL_DEBUG', (isset($config['debug']) && is_numeric($config['debug'])) ? $config['debug'] : SYL_LOG_NOTICE);
        // WEBアプリケーションディレクトリ定義
        define('SYL_WEBAPP_DIR', (isset($config['webapp_dir']) && $config['webapp_dir']) ? $config['webapp_dir'] : SYL_DIR . '/webapp');
        // キャッシュ定義
        define('SYL_WEBAPP_CACHE_DIR', (isset($config['cache_dir']) && $config['cache_dir']) ? SYL_WEBAPP_DIR . $config['cache_dir'] : false);
        // WEBアプリケーション設定ファイルディレクトリ定義
        define('SYL_WEBAPP_CONFIG_DIR',  SYL_WEBAPP_DIR . '/config');
        // WEBアプリケーションライブラリディレクトリ定義
        define('SYL_WEBAPP_LIB_DIR',  SYL_WEBAPP_DIR . '/lib');
        // アプリケーションディレクトリ定義
        define('SYL_APP_DIR', SYL_WEBAPP_DIR . '/app/' . $config['app_name']);

        // 設定定数読み込み
        SyL_DefinesReader::readConfig($this->defines_file_name);

        // アクションディレクトリ定義
        $this->action_dir = SYL_APP_DIR . '/actions';
        // テンプレートディレクトリ定義
        $this->template_dir = SYL_APP_DIR . '/templates';

        // コンテナ生成
        $this->container =& new SyL_Container();
        $this->container->setComponent(SYL_CONFIG_CONTROLLER_NAME, $this);

        // shutdownハンドラ定義
        if ($this->destory_handler_method != '') {
            register_shutdown_function(array(&$this, $this->destory_handler_method));
        }
        // エラーハンドラ定義
        if ($this->error_handler_method != '') {
            set_error_handler(array(&$this, $this->error_handler_method));
        }
    }

    /**
     * 初期化処理
     *
     * @access protected
     */
    function initStream()
    {
        // イベント実行
        $this->raiseEvent('initStream');
    }

    /**
     * アクション前処理実行
     *
     * @access protected
     */
    function loadStream()
    {
        // イベント実行
        $this->raiseEvent('loadStream');
    }

    /**
     * アクション処理実行
     *
     * @access protected
     */
    function executeStream()
    {
        // イベント実行
        $this->raiseEvent('executeStream');
    }

    /**
     * ビュー表示実行前処理
     *
     * @access protected
     */
    function middleStream()
    {
        // イベント実行
        $this->raiseEvent('middleStream');
    }

    /**
     * ビュー表示処理
     *
     * @access protected
     */
    function renderStream()
    {
        // イベント実行
        $this->raiseEvent('renderStream');
    }

    /**
     * ビュー表示実行後処理
     *
     * @access protected
     */
    function unloadStream()
    {
        // イベント実行
        $this->raiseEvent('unloadStream');
    }

    /**
     * 最終処理
     * ※register_shutdown_function使用
     *
     * @access protected
     */
    function finalStream()
    {
        // イベント実行
        $this->raiseEvent('finalStream');
    }

    /**
     * エラーハンドラ実行処理
     *
     * @access protected
     */
    function errorStream()
    {
        static $num = 1;

        // 現在のエラーレベルからエラー画面を表示するか判定
        if (func_get_arg(0) & error_reporting()) {
            if ($num++ > 1) {
                echo "[SyL error] Error handler trigger 2";
                exit;
            }

            // コンテナが初期化される前にエラーが発生すると何も表示されなくなるため
            // エラー画面表示時は、ob_clean() でクリアされる。
            echo "[SyL_error] Controller init error message: " . func_get_arg(1) . " file: " . func_get_arg(2) . " line: " . func_get_arg(3) . "\r\n";

            for ($i=0; $i<5; $i++) {
                $this->error[$i] = func_get_arg($i);
            }
            $this->error[5] = debug_backtrace();
            // errorStream()メソッドは削除
            array_shift($this->error[5]);

            // イベント実行
            $this->raiseEvent('errorStream');
            exit;
        } else {
            // ロギングとか？
        }
    }

    /**
     * エントリポイント
     *
     * @access public
     */
    function stream()
    {
    }

    /**
     * イベント実行
     *
     * @access protected
     * @param string イベントメソッド名
     * @param string 設定ファイル名
     */
    function raiseEvent($event, $config_type=array())
    {
        $this->container->raiseEvent($event, $config_type);
    }

    /**
     * アクションディレクトリを取得
     *
     * @access public
     * @return string アクションディレクトリ
     */
    function getActionDir()
    {
        return $this->action_dir;
    }

    /**
     * エラーNo取得
     * 
     * @access public
     * @return string エラーNo
     */
    function getErrorNo()
    {
        return isset($this->error[0]) ? $this->error[0] : '';
    }

    /**
     * エラーメッセージ取得
     * 
     * @access public
     * @return string エラーメッセージ
     */
    function getErrorMessage()
    {
        return isset($this->error[1]) ? $this->error[1] : '';
    }

    /**
     * エラーファイル取得
     * 
     * @access public
     * @return string エラーファイル
     */
    function getErrorFile()
    {
        return isset($this->error[2]) ? $this->error[2] : '';
    }

    /**
     * エラー行数取得
     * 
     * @access public
     * @return string エラー行数
     */
    function getErrorLine()
    {
        return isset($this->error[3]) ? $this->error[3] : '';
    }

    /**
     * エラーが発生したスコープ内でのすべての変数の内容を取得
     * 
     * @access public
     * @return array エラーコンテキスト
     */
    function getErrorContext()
    {
        return isset($this->error[4]) ? $this->error[4] : array();
    }

    /**
     * エラーが発生したデバックトレース
     * 
     * @access public
     * @return array デバックトレース
     */
    function getErrorTrace()
    {
        return isset($this->error[5]) ? $this->error[5] : array();
    }
}

?>
