<?php
/**
* DIコンテナクラス
*
* 詳細
* コンテナの機能を実装するクラス
*
* PHP version 5
*
* @package    core
* @author     stk2k <stk2k@sazysoft.com>
* @copyright  2008 stk2k, sazysoft
*/

class charcoal_DIContainer 
{
	var $_components;
	var $_component_configs;

	/*
	 *    コンストラクタ
	 */
	public function __construct()
	{
		$this->_components = array();
		$this->_component_configs = array();
	}

	/*
	 *    唯一のインスタンス取得
	 */
	private static function &getInstance()
	{
		static $singleton_;
		if ( $singleton_ == null ){
			$singleton_ = new charcoal_DIContainer();
		}
		return $singleton_;
	}

	/**
	 * DIコンテナを作成
	**/
	public static function createContainer( charcoal_IProcedure $procedure )
	{
		log_info( "system",  "DIコンテナの初期化を開始します。", __FILE__, __LINE__ );

		// インスタンスの取得
		$container = & charcoal_DIContainer::getInstance();

		// ==================================================
		// コンポーネント定義ファイルを読み込む
		// ==================================================

		// システムコンポーネント定義ファイルを読み込む
		$file = charcoal_ResourceLocator::getSystemPath( 'config', 'component_defs.ini' );
		$sys_config = is_file($file) ? parse_ini_file($file,TRUE) : NULL;

		// アプリケーション定義のコンポーネント定義ファイルを読み込む
		$file = charcoal_ResourceLocator::getWebAppPath( 'config', 'component_defs.ini' );
		$app_config = is_file($file) ? parse_ini_file($file,TRUE) : NULL;

		// 設定をマージする
		$config = $app_config ? $sys_config + $app_config : $sys_config;

		// 設定を保存
		$container->component_config_files = $config;

		log_info( "system",  "DIコンテナの初期化が完了しました。", __FILE__, __LINE__ );

	}

	/*
	 * DIコンテナを破棄
	 */
	public static function destroy()
	{
		log_info( "system",  "DIコンテナの破棄を開始します。", __FILE__, __LINE__ );

		// インスタンスの取得
		$container = & charcoal_DIContainer::getInstance();

		// コンポーネントの破棄
		$container->destroyComponents();

		log_info( "system",  "DIコンテナの破棄が完了しました。", __FILE__, __LINE__ );
	}

	/*
	 * コンポーネントをロード
	 */
	public function loadComponent( $component_name )
	{
		log_info( "system",  "コンポーネント[$component_name]をロードします。", __FILE__, __LINE__ );

		// コンポーネント設定ファイルの読み込み
		$config = charcoal_ConfigLoader::loadConfig( $component_name, "component" );

		// キャッシュに保存
		$this->_component_configs[ "$component_name" ] = $config;

		// クラス名を取得
		$class_name = $config->getString( "class_name" );

		// コンポーネントスコープを取得
		$scope = $config->getString( 'scope' );

		// コンポーネントスコープによって生成方法を変更
		switch ( $scope ){
		case charcoal_ComponentScope::TRANSIENT:
		case charcoal_ComponentScope::REQUEST:
			{
				// コンポーネントのインスタンス生成
				$component = charcoal_ClassUtil::newInstance( $class_name );
			}
			break;

		case charcoal_ComponentScope::SESSION:
			{
				// コンポーネントのインスタンスをセッションから復元
				$component = unserialize( $_SESSION[ $component_name ] );

				// セッションになければ、インスタンスを生成
				if ( $component == NULL ){
					$component = charcoal_ClassUtil::newInstance( $class_name );
				}

			}
			break;
		}


		if ( !$component ){
			// scopeに指定されたワードが不正
			throw new charcoal_ComponentConfigException( $component_name, "scope", "invalid key word:$scope" );
		}

		// 生成したインスタンスがIComponentインタフェースを実装しているか確認
		if ( !($component instanceof charcoal_IComponent) ){
			// 実装例外
			throw new charcoal_InterfaceImplementException( $class_name, "charcoal_IComponent" );
		}

		log_info( "system",  "コンポーネント[$component_name]を初期化します。", __FILE__, __LINE__ );

		// コンポーネントを初期化
		$component->init( $config );

		// コンポーネントを配列に登録
		$this->_components[ "$component_name" ] = $component;

		log_info( "system",  "コンポーネント[$component_name]をロードしました。", __FILE__, __LINE__ );

		// ロードしたコンポーネントを返却
		return $component;
	}

	/*
	 * 登録済みコンポーネントを取得
	 */
	public static function getComponent( $key )
	{
		// インスタンスの取得
		$container = & charcoal_DIContainer::getInstance();

		// 登録されていなければロードを試みる
		if ( !isset($container->_components[ $key ]) )
		{
			$component = $container->loadComponent( $key );

			if ( $component == NULL ){
				throw new charcoal_ComponentNotRegisteredException( $key );
			}
		}
		else{
			// コンポーネントの取得
			$component = $container->_components[ $key ];
		}

		// コンポーネント設定を取得
		$component_config = $container->_component_configs[ $key ];

		// 登録されていなければ例外
		if ( $component_config == NULL ){
			throw new charcoal_ComponentNotRegisteredException( $key );
		}

		// コンポーネントスコープを取得
		$scope = $component_config->getString( 'scope' );

		// 登録されていなければ例外
		if ( $scope == NULL ){
			throw new charcoal_ComponentConfigException( $key, "scope", "mandatory" );
		}

		// コンポーネントを返却
		switch ( $scope ){
		case charcoal_ComponentScope::TRANSIENT:
			return clone $component;

		case charcoal_ComponentScope::REQUEST:
			return $component;

		case charcoal_ComponentScope::SESSION:
			return $component;
		}

		// scopeに指定されたワードが不正
		throw new charcoal_ComponentConfigException( $key, "scope", "invalid key word:$scope" );

		return NULL;
	}

	/*
	 * 登録済みコンポーネントを破棄する（sessionスコープコンポーネントはセッションに内容を保存）
	 */
	public function destroyComponents()
	{
		log_info( "system",  "DIコンテナ内のコンポーネントを破棄します。", __FILE__, __LINE__ );

		
		// インスタンスの取得
		$container = & charcoal_DIContainer::getInstance();

		// コンポーネントの取得
		$components = $container->_components;

		// すべてのコンポーネントを破棄
		foreach( $components as $component_name => $component ){

			// コンポーネント設定を取得
			$component_config = $container->_component_configs[ $component_name ];

			// コンポーネントスコープを取得
			$scope = $component_config->getString( 'scope' );

			// コンポーネントスコープによって処理を分岐
			switch ( $scope ){
			case charcoal_ComponentScope::TRANSIENT:
			case charcoal_ComponentScope::REQUEST:
				// なにもしない
				break;

			case charcoal_ComponentScope::SESSION:
				{
					// セッションにインスタンスをセット
					$_SESSION[ $component_name ] = serialize( $component );

					log_info( "system",  "コンポーネント[$component_name]はセッションに保存されました。", __FILE__, __LINE__ );
				}
				break;
			}

		}

		log_info( "system",  "DIコンテナ内のコンポーネントを破棄しました。", __FILE__, __LINE__ );

	}

	/*
	 * ダンプ
	 */
	public function dump(){
		return $this->_components[ $key ];
	}
}
