<?php
/*
 * framework-spider
 * spider/Builder.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
 * 
 */
$GLOBALS['DEF_SPIDER_CHARSET_REPLACE_ENCODINGS']	= array(
	'SJIS-win'	=> 'Shift_JIS',
	'SJIS'		=> 'Shift_JIS',
	
);
/**
 * 実行ファイルを作成します。
 * 
 * @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_Builder {

	/** 元の本文文字列	*/
	var $org_body_strings;
	/** 出力する本文文字列	*/
	var $out_body_strings;

	/**
	 * コンストラクタ
	 */
	function spider_Builder() {
	}
	/**
	 * HTMLボディを生成する
	 */
	function convertBody( & $build_information_object ) {
		$action_lines				= file( $build_information_object->execute_file_path );
		$this->out_body_strings		= implode( "", $action_lines );
		$this->org_body_strings		= $this->out_body_strings;
		
		// 改行コード統一と連続改行の削除
		$this->out_body_strings = str_replace( "\r\n", "\n", $this->out_body_strings );
		$this->out_body_strings = str_replace( "\r", "\n", $this->out_body_strings );
		$this->out_body_strings = str_replace( "\n\n\n", "\n", $this->out_body_strings );
		$this->out_body_strings = str_replace( "\n\n", "\n", $this->out_body_strings );
		$this->out_body_strings = str_replace( "\n\n", "\n", $this->out_body_strings );

		// cms.phpインクルード行を削除
		preg_match_all( '/include_once\\([^\\)]*?cms\\.php[^\\)]*?\\)\\;/'
			, $this->out_body_strings
			, $output_array
			, PREG_PATTERN_ORDER );
		foreach ( $output_array as $output ) {
			foreach ( $output as $target ) {
				// 空行にならないよう前後が改行も削除
				$this->out_body_strings = str_replace( "\n".$target."\n", "", $this->out_body_strings );
				$this->out_body_strings = str_replace( $target, "", $this->out_body_strings );
			}
		}
		preg_match_all( '/require_once\\([^\\)]*?cms\\.php[^\\)]*?\\)\\;/'
			, $this->out_body_strings
			, $output_array
			, PREG_PATTERN_ORDER );
		foreach ( $output_array as $output ) {
			foreach ( $output as $target ) {
				// 空行にならないよう前後が改行も削除
				$this->out_body_strings = str_replace( "\n".$target."\n", "", $this->out_body_strings );
				$this->out_body_strings = str_replace( $target, "", $this->out_body_strings );
			}
		}
		// spider.inc.phpインクルード行を削除
		preg_match_all( '/include_once\\([^\\)]*?spider\\.inc\\.php[^\\)]*?\\)\\;/'
			, $this->out_body_strings
			, $output_array
			, PREG_PATTERN_ORDER );
		foreach ( $output_array as $output ) {
			foreach ( $output as $target ) {
				// 空行にならないよう前後が改行も削除
				$this->out_body_strings = str_replace( "\n".$target."\n", "", $this->out_body_strings );
				$this->out_body_strings = str_replace( $target, "", $this->out_body_strings );
			}
		}
		preg_match_all( '/require_once\\([^\\)]*?spider\\.inc\\.php[^\\)]*?\\)\\;/'
			, $this->out_body_strings
			, $output_array
			, PREG_PATTERN_ORDER );
		foreach ( $output_array as $output ) {
			foreach ( $output as $target ) {
				// 空行にならないよう前後が改行も削除
				$this->out_body_strings = str_replace( "\n".$target."\n", "", $this->out_body_strings );
				$this->out_body_strings = str_replace( $target, "", $this->out_body_strings );
			}
		}
		
		// 空PHP部分を削除
		$this->out_body_strings	= preg_replace('/<\\?php[\\s]*\\?>\\n/','',$this->out_body_strings);
		$this->out_body_strings	= preg_replace('/<\\?php[\\s]*\\?>/','',$this->out_body_strings);
		
		// 変換タグの読み込みと変換
		$tag_object_array	= array();
		$dir_path_tags	= dirname(__FILE__).'/tags/';
		if ( is_dir($dir_path_tags) ) {
			if ( $dh = opendir( $dir_path_tags ) ) {
				while ( ( $file_name = readdir($dh) ) !== false ) {
					if( $file_name != "." && $file_name != ".."
						&& preg_match('/^[0-9a-zA-Z]+?\\.class\\.php$/',$file_name)) {
						require_once( dirname(__FILE__).'/tags/'.$file_name );
						$tag_class_name	= "spider_tags_".substr($file_name,0,strpos($file_name,"."));
						$tag_class_obj	= new $tag_class_name;
						if( !isset($tag_object_array[$tag_class_obj->priority]) || !is_array($tag_object_array[$tag_class_obj->priority]) ) {
							$tag_object_array[$tag_class_obj->priority]	= array();
						}
						array_push( $tag_object_array[$tag_class_obj->priority], $tag_class_obj );
					}
				}
				closedir($dh);
			} else {
			}
		} else {
		}
		// 外部変換タグの読み込みと変換
		$external_tags_conf_file	= dirname(__FILE__)
			.'/tags/externals.conf';
		if( file_exists( $external_tags_conf_file ) ) {
			$lines	= file( $external_tags_conf_file );
			foreach( $lines as $line ) {
				$line	= trim($line);
				$tag_class_name	= $line;
				$tag_class_file	= DIR_PATH_LIB.'/'.str_replace('_','/',$line).'.class.php';
				if( file_exists($tag_class_file) ) {
					require_once( $tag_class_file );
					$tag_class_obj	= new $tag_class_name;
					if( !isset($tag_object_array[$tag_class_obj->priority]) || !is_array( $tag_object_array[$tag_class_obj->priority]) ) {
						$tag_object_array[$tag_class_obj->priority]	= array();
					}
					array_push( $tag_object_array[$tag_class_obj->priority], $tag_class_obj );
				}
			}
		}
		// 優先順にタグのコンバートメソッドを実行
		ksort($tag_object_array);
		foreach( $tag_object_array as $key => $tag_array ) {
			foreach($tag_array as $tag_obj ) {
				$tag_obj->convert( $this->out_body_strings, $build_information_object );
			}
		}
	}
	/**
	 * 実行ファイル実体を書き出す
	 * @param $bin_file_path 実行ファイルの出力パス
	 */
	function createBinFile( & $build_information_object ) {

		$bin_file_path	= $build_information_object->getAgentPageBuildFilePath();

		// 実行ファイルを解析
		$this->convertBody($build_information_object);

		// 表示前実行部分作成
		$string  = "<?php\n";
		
		// 処理開始前実行コードを記述
		foreach( $build_information_object->processPreModuleHash as $code_array ) {
			foreach( $code_array as $code ) {
				$string	.= $code."\n";
			}
		}

		// モジュール実行のスクリプト追加
		foreach( $build_information_object->module_exec_info_hash as $key => $info_hash ) {
			$module_name		= $info_hash['module_name'];
			$force	= 'false';
			if( $info_hash['force'] ) {
				$force	= 'true';
			}
			$attribute_prefix	= '';
			if( isset($info_hash['attribute_prefix']) ) {
				$attribute_prefix	= $info_hash['attribute_prefix'];
				$attribute_prefix	= str_replace('"','\\"',$attribute_prefix);
			}
			$post_contents		= '';
			if( isset($info_hash['post']) ) {
				$post_contents		= $info_hash['post'];
				$post_contents		= str_replace('"','\\"',$post_contents);
			}
			$get_contents		= '';
			if( isset($info_hash['get']) ) {
				$get_contents		= $info_hash['get'];
				$get_contents		= str_replace('"','\\"',$get_contents);
			}
			$string	.= '$controller->loadModule( "' . $module_name . '", '.$force.',"'.$attribute_prefix
				.'","'.$get_contents.'", "'.$post_contents.'" );'."\n";
			$string	.= 'if( isset($_GET[\'spiderdebugcmd\']) && $_GET[\'spiderdebugcmd\'] == \'viewmoduletime\' '
				.'&& is_array($GLOBALS[\'SPIDER_DEBUG_COMMAND_PERMIT_ADDRESSES\']) '
				.'&& in_array($_SERVER[\'REMOTE_ADDR\'],$GLOBALS[\'SPIDER_DEBUG_COMMAND_PERMIT_ADDRESSES\']) '
				.') { '
				.'$request_object->debugExecTime(\''.$module_name.'\'); '
				.'}'."\n";
		}
		
		// 表示開始前実行コードを記述
		foreach( $build_information_object->processPreViewHash as $code_array ) {
			foreach( $code_array as $code ) {
				$string	.= $code."\n";
			}
		}

		// モジュール実行結果による分岐処理を記述
		$string	.= 'if ( $request_object->isError() ) {'."\n";
			$string	.= '$is_error = true;'."\n";
			$string	.= '$request_object->setAttribute("IS_ERROR", $is_error );'."\n";
			$string	.= '$tmp_array = $request_object->errors;'."\n";
			$string	.= '$request_object->setAttribute("errors", $tmp_array );'."\n";
		$string	.= '} else {'."\n";
			$string	.= '$is_error = false;'."\n";
			$string	.= '$request_object->setAttribute("IS_ERROR", $is_error );'."\n";
		$string	.= '}'."\n";
		
		// リクエスト属性をグローバル変数に格納する
		$string	.= 'foreach( $request_object->attribute_array as $key => $value ) { ';
		$string	.= ' $GLOBALS[$key] = $value; ';
		$string	.= ' }'."\n";

		// ファイルアウトプット機能の追加
		// リクエストオブジェクトに乗せられたヘッダ
		$string	.= 'if( count( $request_object->headers ) > 0 ) {'."\n";
		$string	.= 'foreach( $request_object->headers as $hkey => $hval ) {'."\n";
		$string	.= 'header("$hkey: $hval");'."\n";
		$string	.= '}'."\n";
		$string	.= '}'."\n";
		// SPIDER_ACTION_MODEがデバッグモードの場合に特殊ヘッダを返す
		$string	.= 'if( $request_object->isError() ) {'."\n";
		$string	.= 'if( defined(\'SPIDER_ACTION_MODE\') && SPIDER_ACTION_MODE == SPIDER_LOG_LEVEL_DEBUG ) {'."\n";
		$string	.= '$message = implode(\':\',$request_object->errors);'."\n";
		$string	.= 'header("Framework-Spider-Message: ".$message);'."\n";
		$string	.= '}'."\n";
		$string	.= '}'."\n";
		
		// 優先レスポンスがあるなら処理
		$string	.= 'if( $request_object->hasPreferredResponse() && $request_object->ouptutPreferredResponse() ) {';
		// レスポンスが何も指定されていないなら継続処理
		$string	.= "} else { \n\n?>";
		
		// テキスト出力があるなら出力
		$string	.= $this->out_body_strings;
		
		// 出力を文字列に取得する
		$string .= "<?php\n";
		$string .= 'mb_internal_encoding(\'UTF-8\');'."\n";
		$string .= 'mb_http_output(\'UTF-8\');'."\n";
		$string .= '$outstr = ob_get_clean();'."\n";
		$string .= '$outstr = trim($outstr);'."\n";

		// 表示文字列に対する処理実行コードを記述
		foreach( $build_information_object->convert_view_process_hash as $code_array ) {
			foreach( $code_array as $code ) {
				$string	.= $code."\n";
			}
		}

		// 最終的にできた表示文字列をフラッシュする
		$outputReplaceCode = $build_information_object->output_charset;
		if( isset($GLOBALS['DEF_SPIDER_CHARSET_REPLACE_ENCODINGS'][$outputReplaceCode]) ) {
			// 変換文字が定義されているなら変換文字を使う(SJIS等の場合Shift_JISにするなどの為)
			$outputReplaceCode = $GLOBALS['DEF_SPIDER_CHARSET_REPLACE_ENCODINGS'][$outputReplaceCode];
		}
		$string .= 'if(\'UTF-8\'!=\''.$build_information_object->output_charset.'\'){'."\n";
		$string .= '$needContentType = false;'."\n";
		$string .= '$sentContentType = false;'."\n";
		$string .= 'if( preg_match(\'/^4/\',phpversion()) > 0 || preg_match(\'/^3/\',phpversion()) > 0 ) {'."\n";
		$string .= 'if( headers_sent() ) {'."\n";
		$string .= '$sentContentType = true;'."\n";
		$string .= '}'."\n";
		$string .= '} else {'."\n";
		$string .= '$headers = headers_list();'."\n";
		$string .= 'foreach( $headers as $hk => $hv ) {'."\n";
		$string .= 'if(preg_match(\'/^[cC][oO][nN][tT][eE][nN][tT]\\\\-[tT][yY][pP][eE]/\',$hv) > 0 ) {'."\n";
		$string .= '$sentContentType = true;'."\n";
		$string .= 'if(preg_match(\'/[uU][tT][fF]\\\\-8/\',$hv) > 0 ) {'."\n";
		$string .= '$needContentType = true;'."\n";
		$string .= '}'."\n";
		$string .= '}'."\n";
		$string .= '}'."\n";
		$string .= '}'."\n";
		$string .= 'if(!$sentContentType || $needContentType ){ header(\'Content-Type: text/html;charset='.$outputReplaceCode.';\'); }'."\n";
		$string .= 'echo mb_convert_encoding($outstr,\''.$build_information_object->output_charset.'\',\'UTF-8\');'."\n";
		$string .= '} else {'."\n";
		$string .= 'echo $outstr;'."\n";
		$string .= '}'."\n";
		$string .= "}\n";

		// 表示後実行コードを記述
		foreach( $build_information_object->processPostViewHash as $code_array ) {
			foreach( $code_array as $code ) {
				$string	.= $code."\n";
			}
		}

		// フラッシュ後にモジュール後処理の実行
		$string .= '$controller->post_process_all(); ';
		$string .= "?>\n";
		
		$fp		= @fopen( $bin_file_path, "w" );
		if( $fp ) {
			if (@flock($fp, LOCK_EX)) {
				fwrite( $fp, $string );
				flock($fp, LOCK_UN);
				@fclose( $fp );
				@chmod( $bin_file_path, SPIDER_PERMITTION_DATA_FILE );
			} else {
				@fclose( $fp );
				@chmod( $bin_file_path, SPIDER_PERMITTION_DATA_FILE );
				die('Core Error: Can\'t create execute file!! '.$bin_file_path);
			}
		} else {
			die('Core Error: Can\'t create execute file!! '.$bin_file_path);
		}
		
		// ビルドタイムスタンプファイル作成
		$build_file_path	= $bin_file_path.".build.php";
		$bin_file_path	= str_replace('\\','\\\\',$bin_file_path );
		$string	= "<?php\n";
		$string	.= '$build=false;'."\n";
		$target_file_path	= $build_information_object->execute_file_path;
		$target_file_path	= str_replace('\\','\\\\',$target_file_path );
		$string	.= 'if( file_exists("'.$target_file_path.'") ){if( filemtime("'.$bin_file_path.'") < filemtime("'.$target_file_path.'") ) { $build=true; }}'."\n";
		// 比較ファイルとのタイムスタンプ比較
		foreach( $build_information_object->compare_time_file_array as $compare_file_path ) {
			$compare_file_path	= str_replace('\\','\\\\',$compare_file_path);
			$compare_file_path	= str_replace('"','\\"',$compare_file_path);
			// ファイルが存在する場合、ビルドファイルより更新日が新しいなら再ビルド、ファイルがなくなっていても再ビルド
			$string	.= 'if( file_exists("'.$compare_file_path.'") ){if( filemtime("'.$bin_file_path.'") < filemtime("'.$compare_file_path.'") ) { $build=true; }} else { $build=true; }'."\n";
		}
		// 存在確認ファイル確認
		foreach( $build_information_object->confirm_exists_file_array as $compare_file_path ) {
			if( strlen($compare_file_path) > 0 ) {
				$compare_file_path	= str_replace('\\','\\\\',$compare_file_path);
				$compare_file_path	= str_replace('"','\\"',$compare_file_path);
				// ファイルができていたら再ビルド
				$string	.= 'if( file_exists("'.$compare_file_path.'") ){ $build=true; }'."\n";
			}
		}
		
		$string	.= "?>\n";

		$fp		= @fopen( $build_file_path, "w" );
		if( $fp ) {
			if (@flock($fp, LOCK_EX)) {
				fwrite( $fp, $string );
				flock($fp, LOCK_UN);
				@fclose( $fp );
				@chmod( $build_file_path, SPIDER_PERMITTION_DATA_FILE );
			} else {
				@fclose( $fp );
				@chmod( $build_file_path, SPIDER_PERMITTION_DATA_FILE );
				die('Core Error: Can\'t create build file!!');
			}
		} else {
			die('Core Error: Can\'t create build file!!');
		}
	}
}
?>