<?php
/*
 * framework-spider
 * spider/Controller.class.php
 * 
 * グローバル関数定義ファイル
 * spiderで利用するグローバル関数を定義するファイルです
 * 
 * CopyRight(C)Framework-Spider Developer Team. 2010. All Right Reserved. 
 * URL         : http://sourceforge.jp/projects/frameworkspider/
 * Mail        : frameworkspider-dev@lists.sourceforge.jp
 * Auther      : Masanori Nakashima
 * Modifier    : Masanori Nakashima
 * Last Updated: 2010.06.23
 * 
 */
require_once( dirname(__FILE__).DIRECTORY_SEPARATOR.'HttpRequest.class.php' );
/**
 * モジュールの実行をコントロールするクラスです。
 * 各モジュールの実行は、本クラスによって行われます。
 * 
 * @package spider spiderのコアパッケージ
 * @version 1.2.00
 * @copyright Copyright &copy; 2008, Framework-Spider Developer Team. <frameworkspider-dev@lists.sourceforge.jp> http://sourceforge.jp/projects/frameworkspider/
 * @author  Masanori Nakashima <m_nakashima@users.sourceforge.jp>
 * @since PHP 4.3
 * 
 */
class spider_Controller {
	/** 実行モジュールのモジュール名+実行パラメータ文字列配列（実行した順） */
	var $executed_module_info_array		= array();
	/** 実行モジュールのオブジェクトハッシュ	*/
	var $executed_module_object_array	= array();
	
	/** リクエストコンテナオブジェクト	*/
	var $request_object					= null;
	/** 出力オブジェクト				*/
	var $http_output_object				= null;
	
	/**
	 * コンストラクタ
	 */
	function spider_Controller( & $request_object, & $http_output_obj ) {
		$this->executed_module_info_array		= array();
		$this->executed_module_object_array		= array();
		$this->post_hash						= $_POST;
		$this->get_hash							= $_GET;
		$this->request_object		= & $request_object;
		$this->http_output_object	= & $http_output_obj;
	}

	/**
	 * 全体実行
	 * @param $request_object 
	 * @param $http_output_obj 
	 */
	function execute( & $request_object, & $http_output_obj ) {
		
		if( is_null($request_object) ) {
			$this->request_object		= & $request_object;
		}
		if( is_null($http_output_obj) ) {
			$this->http_output_object	= & $http_output_obj;
		}
		
		// グローバルエラーが存在したらエラーに追加
		if( isset($_SESSION) ) {
			$global_errors				= unserialize( $_SESSION['spider.global_errors'] );
			if( is_array( $global_errors ) ) {
				$this->request_object->errors	= $global_errors;
			}
			unset( $_SESSION['spider.global_errors'] );
		}
		// リクエスト出力
		$this->http_output_object->output( $this, $this->request_object );
	}
	/**
	 * モジュール実行
	 * @param $module_name 実行するモジュール名
	 */
	function loadModule( $module_name, $force=false, $attribute_prefix=null, $get_contents=null, $post_contents=null ) {
		// forceがtrueでないばあい、requestにエラーがある場合は実行しない
		if( $force || !$this->request_object->isError() ) {
			// 実行済みかどうかを判断する文字列
			$execute_info_name	= $module_name.' '.$attribute_prefix.' '.$get_contents.' '.$post_contents;
			// モジュールクラスファイルの存在確認
			$path = str_replace( ".", '/', $module_name );
			$path = DIR_PATH_LIB.'/'.$path;
			if ( file_exists( $path . ".module.php" ) ) {
				include_once( $path . ".module.php" );
			} else if ( file_exists( $path . ".class.php" ) ) {
				include_once( $path . ".class.php" );
			} else if ( file_exists( $path . ".inc.php" ) ) {
				include_once( $path . ".inc.php" );
			} else if ( file_exists( $path . ".php" ) ) {
				include_once( $path . ".php" );
			} else if ( file_exists( $path . ".php.inc" ) ) {
				include_once( $path . ".php.inc" );
			} else if( file_exists( $path . ".inc" ) ){
				include_once( $path . ".inc" );
			} else {
				header('Content-Type: text/plain;charset=UTF-8');
				die($GLOBALS['spider.messages']['spider.controller.moduleisnotfound'].'[' . $module_name . ']');
			}
			// 実行モジュールクラス名の取り出し
			$module_real_name = str_replace( ".", "_", $module_name );
			$module_obj	= null;
			if( isset($this->executed_module_object_array[$module_name]) ) {
				$module_obj	= $this->executed_module_object_array[$module_name];
			}
			if( is_null( $module_obj ) ) {
				// モジュールがスタックにない場合は新規作成して実行
				$module_obj = new $module_real_name;
				$module_obj->controller_object		= & $this;
				$module_obj->http_output_object		= & $this->http_output_object;
				$module_obj->http_request_object	= & $this->request_object;
				// 要求モジュールが存在して未実行の場合は先に強制実行する
				if( is_array( $module_obj->require_module_array ) ) {
					foreach( $module_obj->require_module_array as $require_module_name ) {
						$this->loadModule( $require_module_name, false, null, null, null );
					}
				}
				// プレフィックス指定があるなら実行前にrequestに設定
				if( !is_null( $attribute_prefix ) && strlen( $attribute_prefix ) > 0 ) {
					$this->request_object->attribute_prefix	= $attribute_prefix;
				}
				// post_contents.get_contentsが渡されているなら上書き
				$org_post	= null;
				$org_get	= null;
				if( strlen($post_contents) > 0 ) {
					$org_post	= $_POST;
					$post_hash	= $this->content2Hash( $post_contents );
					$this->overwriteHash( $post_hash, $_POST );
				}
				if( strlen($get_contents) > 0 ) {
					$org_get	= $_GET;
					$get_hash	= $this->content2Hash( $get_contents );
					$this->overwriteHash( $get_hash, $_GET );
				}
				// 指定モジュールを実行する
				$module_obj->execute( $this->request_object );
				$module_obj	= $this->executed_module_object_array[$module_name]	= $module_obj;
				array_unshift( $this->executed_module_info_array, $execute_info_name );
				// post_contents.get_contentsが渡されているなら元に戻す
				if( strlen($post_contents) > 0 ) {
					$_POST	= $org_post;
				}
				if( strlen($get_contents) > 0 ) {
					$_GET	= $org_get;
				}
				// プレフィックス指定を解除する
				$this->request_object->attribute_prefix	= null;
				// 指定後処理モジュールがあるなら実行する
				if( is_array( $module_obj->post_module_array ) ) {
					foreach( $module_obj->post_module_array as $post_module_name ) {
						$this->loadModule( $post_module_name, false, null, null, null );
					}
				}
			} else if( !in_array( $execute_info_name, $this->executed_module_info_array ) ) {
				// モジュールスタックにオブジェクトがあっても実行済みモジュール情報にないなら実行
				// 要求モジュールが存在して未実行の場合は先に強制実行する
				if( is_array( $module_obj->require_module_array ) ) {
					foreach( $module_obj->require_module_array as $require_module_name ) {
						$this->loadModule( $require_module_name, false, null, null, null );
					}
				}
				// プレフィックス指定があるなら実行前にrequestに設定
				if( !is_null( $attribute_prefix ) && strlen( $attribute_prefix ) > 0 ) {
					$this->request_object->attribute_prefix	= $attribute_prefix;
				}
				// post_contents.get_contentsが渡されているなら上書き
				$org_post	= null;
				$org_get	= null;
				if( strlen($post_contents) > 0 ) {
					$org_post	= $_POST;
					$post_hash	= $this->content2Hash( $post_contents );
					$this->overwriteHash( $post_hash, $_POST );
				}
				if( strlen($get_contents) > 0 ) {
					$org_get	= $_GET;
					$get_hash	= $this->content2Hash( $get_contents );
					$this->overwriteHash( $get_hash, $_GET );
				}
				// 指定モジュールを実行する
				$module_obj->execute( $this->request_object );
				$module_obj	= $this->executed_module_object_array[$module_name]	= $module_obj;
				array_unshift( $this->executed_module_info_array, $execute_info_name );
				// post_contents.get_contentsが渡されているなら元に戻す
				if( strlen($post_contents) > 0 ) {
					$_POST	= $org_post;
				}
				if( strlen($get_contents) > 0 ) {
					$_GET	= $org_get;
				}
				// プレフィックス指定を解除する
				$this->request_object->attribute_prefix	= null;
				// 指定後処理モジュールがあるなら実行する
				if( is_array( $module_obj->post_module_array ) ) {
					foreach( $module_obj->post_module_array as $post_module_name ) {
						$this->loadModule( $post_module_name, false, null, null, null );
					}
				}
			} else {
				// 同条件で実行済みなら何もしない
				return;
			}
		}
	}
	/**
	 * 現在までに実行したモジュールの後処理メソッドを降順で呼び出します。
	 */
	function post_process_all() {
		foreach ( $this->executed_module_info_array as $execute_info_name ) {
			list( $module_name, $attribute_prefix, $get_contents, $post_contents )
				= explode(' ', $execute_info_name );
			$module_obj	= $this->executed_module_object_array[$module_name];
			if ( method_exists ( $module_obj, 'post_process' ) ) {
				$module_obj->post_process( $this->request_object );
			}
		}
	}
	/**
	 * ハッシュのキーと値をsourceからdestinationへ上書きします
	 */
	function overwriteHash( & $source_hash, & $destination_hash ) {
		foreach( $source_hash as $key => $val ) {
			$destination_hash[$key]	= $val;
		}
	}
	/**
	 * コンテンツ文字列を分割してハッシュに格納して返す
	 */
	function content2Hash( $strings ) {
		$reterun_hash	= array();
		$strings		= trim($strings);
		if( strlen($strings) > 0 ) {
			$param_array	= explode('&',$strings);
			foreach( $param_array as $var ) {
				list( $key, $val )	= explode('=',$var);
				$key	= urldecode($key);
				$val	= urldecode($val);
				if( preg_match('/\\[[^\\[\\]]+\\]$/',$key ) > 0 ) {
					$name	= preg_replace('/\\[[^\\[\\]]+\\]$/','',$key);
					if( !is_array($reterun_hash[$name]) ) {
						$reterun_hash[$name]	= array();
					}
					list( $name, $hash_key )	= explode('[',$key);
					$hash_key					= trim(preg_replace('/\\]$/','',$hash_key));
					if( strlen($hash_key) > 0 ) {
						$reterun_hash[$name][$hash_key]	= $val;
					} else {
						array_push( $reterun_hash[$name], $val );
					}
				} else {
					$reterun_hash[$key]	= $val;
				}
			}
		}
		return $reterun_hash;
	}
	/**
	 * このコントローラで動作するのに問題ない環境で設置されているか確認します
	 * @param spider_HttpRequest &$request spider_HttpRequestクラスインスタンス参照
	 * @return boolean 問題ないならtrue
	 */
	function checkEnv( &$request ){
		if( !file_exists(DIR_PATH_LIB) ) {
			$request->addLocaledError('spider.finctions.libdirisnotfound',SPIDER_LOG_LEVEL_FATAL);
		}
		if( !file_exists(DIR_PATH_TEMPLATES) ) {
			$request->addLocaledError('spider.finctions.templatedirisnotfound',SPIDER_LOG_LEVEL_FATAL);
		}
		
		if( !file_exists(DIR_PATH_WORK) ) {
			$request->addLocaledError('spider.finctions.workdirisnotfound',SPIDER_LOG_LEVEL_FATAL);
		} else if( !is_writable(DIR_PATH_WORK) ){
			$request->addLocaledError('spider.finctions.workdirisnotwritable',SPIDER_LOG_LEVEL_FATAL);
		} else {
			if( !file_exists(DIR_PATH_BIN) ) {
				if( @mkdir(DIR_PATH_BIN,SPIDER_PERMITTION_DATA_FOLDER) ) {
					@chmod(DIR_PATH_BIN,SPIDER_PERMITTION_DATA_FOLDER);
				} else {
					$request->addLocaledError('spider.finctions.cantcreatebindir',SPIDER_LOG_LEVEL_FATAL);
				}
			} else if( !is_writable(DIR_PATH_BIN) ){
				$request->addLocaledError('spider.finctions.bindirisnotwritable',SPIDER_LOG_LEVEL_FATAL);
			}
			if( !file_exists(DIR_PATH_LOCK) ) {
				if( @mkdir( DIR_PATH_LOCK, SPIDER_PERMITTION_DATA_FOLDER ) ) {
					@chmod( DIR_PATH_LOCK, SPIDER_PERMITTION_DATA_FOLDER );
				} else {
					$request->addLocaledError('spider.finctions.cantcreatelockdir',SPIDER_LOG_LEVEL_FATAL);
				}
			} else if( !is_writable(DIR_PATH_LOCK) ){
				$request->addLocaledError('spider.finctions.lockdirisnotwritable',SPIDER_LOG_LEVEL_FATAL);
			}
			if( !file_exists(DIR_PATH_TMP) ) {
				if( @mkdir( DIR_PATH_TMP, SPIDER_PERMITTION_DATA_FOLDER ) ) {
					@chmod( DIR_PATH_TMP, SPIDER_PERMITTION_DATA_FOLDER );
				} else {
					$request->addLocaledError('spider.finctions.cantcreatetmpdir',SPIDER_LOG_LEVEL_FATAL);
				}
			} else if( !is_writable(DIR_PATH_TMP) ){
				$request->addLocaledError('spider.finctions.tmpdirisnotwritable',SPIDER_LOG_LEVEL_FATAL);
			}
			if( !file_exists(DIR_PATH_CACHE) ) {
				if( @mkdir( DIR_PATH_CACHE, SPIDER_PERMITTION_DATA_FOLDER ) ) {
					@chmod( DIR_PATH_CACHE, SPIDER_PERMITTION_DATA_FOLDER );
				} else {
					$request->addLocaledError('spider.finctions.cantcreatecachedir',SPIDER_LOG_LEVEL_FATAL);
				}
			} else if( !is_writable(DIR_PATH_CACHE) ){
				$request->addLocaledError('spider.finctions.cachedirisnotwritable',SPIDER_LOG_LEVEL_FATAL);
			}
		}
	
		if( !file_exists(DIR_PATH_DATA) ) {
			$request->addLocaledError('spider.finctions.datadirisnotfound',SPIDER_LOG_LEVEL_FATAL);
		} else if( !is_writable(DIR_PATH_DATA) ){
			$request->addLocaledError('spider.finctions.datadirisnotwritable',SPIDER_LOG_LEVEL_FATAL);
		} else {
			if( !file_exists(DIR_PATH_LOG) ) {
				if( @mkdir( DIR_PATH_LOG, SPIDER_PERMITTION_DATA_FOLDER ) ) {
					@chmod( DIR_PATH_LOG, SPIDER_PERMITTION_DATA_FOLDER );
				} else {
					$request->addLocaledError('spider.finctions.cantcreatelogdir',SPIDER_LOG_LEVEL_FATAL);
				}
			} else if( !is_writable(DIR_PATH_LOG) ){
				$request->addLocaledError('spider.finctions.logdirisnotwritable',SPIDER_LOG_LEVEL_FATAL);
			}
		}
		if( $request->isError() ) {
			return false;
		} else {
			return true;
		}
	}
	/**
	 * クラス名を指定してlibフォルダ内からクラス定義を取得して
	 * オブジェクト生成して返します
	 * @param string $className
	 * @return object
	 */
	function createObject( $className ) {
		if( !class_exists( $className ) ) {
			spider_Controller::loadClassDefinition( $className );
		}
		return new $className;
	}
	/**
	 * クラス名を指定してlibフォルダ内からクラス定義を取得してrequire_onceします
	 * @param string $className
	 * @return object
	 */
	function loadClassDefinition( $className ) {
		if( !class_exists( $className ) ) {
			$classPath = DIR_PATH_LIB
			.DIRECTORY_SEPARATOR
			.str_replace('_',DIRECTORY_SEPARATOR,$className);
			$filePath	= $classPath.'.class.php';
			if( file_exists($filePath) ){
				require_once( $filePath );
			} else {
				$filePath	= $classPath.'.module.php';
				if( file_exists($filePath) ){
					require_once( $filePath );
				} else {
					throw new Exception($className.'が見つかりません。');
				}
			}
		}
		return true;
	}
}
?>