<?php
/*
 * framework-spider
 * spider/tags/If.class.php
 * 
 * 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.'TagBase.class.php');
/**
 * HTML用変換タグ: ifタグクラス
 * 
 * requestに登録された属性値に対して、phpコードで値を取り出すことなくページ内でのif分岐利用を
 * 可能にするifタグ機能を実現する変換タグです。
 * ifタグは必ず条件分岐終了地点を分岐終了タグ{/if}で閉じる必要があります。
 * また、現状、{else}タグを記述することによってif(..){...}else{..}文を実現することができます。
 * （現在のバージョンではelse ifは実現できません。）
 * 
 * 記述例）
 * {if:value='a'}
 * 	値はaです
 * {else}
 *  値はaではありません
 * {/if}
 * 
 * また、{if:条件文}の条件文は、and/orで区切って複数の条件を指定することができます。
 * 条件文の中で、アプリケーションが判別可能な関数を記述することも可能です。
 * 
 * 記述例）
 * {if:strlen(value)>0 and value='c'}
 *  値はcで文字が入っています
 * {else}
 *  文字が入っていないかcではありません
 * {/if}
 * 
 * @package spider spiderのコアパッケージ
 * @version 1.2.00
 * @copyright Copyright &copy; 2008, <m_nakashima@users.sourceforge.jp> http://sourceforge.jp/projects/shopformer/
 * @author  m.nakashima <m_nakashima@users.sourceforge.jp>
 * @since PHP 4.3
 */
class spider_tags_If extends spider_tags_TagBase {

	/**
	 * コンストラクタ
	 */
	function spider_tags_If() {
	}
	/**
	 * コンバートメソッド
	 */
	function convert( &$result_strings, &$build_information ){
		// ifの変換
		$vars_tags_aray			= array();
		preg_match_all( '/\\{if\\:[^\\}]*?}/'
			, $result_strings
			, $vars_tags_aray
			, PREG_PATTERN_ORDER );
		$valiable_counter	= 0;
		foreach ( $vars_tags_aray as $vars_tags ) {
			foreach ( $vars_tags as $vars_tag ) {
				$option_string	= preg_replace( '/\\{if\\:/','', $vars_tag );
				$option_string	= preg_replace( '/\\}$/','', $option_string );
				$option_string	= trim( $option_string );
				// 下位互換の為::を含むなら旧ロジック
				$repstr		= '';
				if( preg_match('/\\:\\:/',$option_string) > 0 ) {
					$repstr	= $this->parseCondition( $option_string, $valiable_counter );
				} else {
					$repstr	= '<?php if( '.$this->condition2code( $option_string ).' ) { ?>';
				}
				$result_strings	= str_replace( $vars_tag, $repstr, $result_strings );
				$valiable_counter++;
			}
		}
		// else-ifの変換
		$vars_tags_aray			= array();
		preg_match_all( '/\\{else\\-if\\:[^\\}]*?}/'
			, $result_strings
			, $vars_tags_aray
			, PREG_PATTERN_ORDER );
		$valiable_counter	= 0;
		foreach ( $vars_tags_aray as $vars_tags ) {
			foreach ( $vars_tags as $vars_tag ) {
				$option_string	= preg_replace( '/\\{else\\-if\\:/','', $vars_tag );
				$option_string	= preg_replace( '/\\}$/','', $option_string );
				$option_string	= trim( $option_string );
				$repstr			= '<?php } else if( '.$this->condition2code( $option_string ).' ) { ?>';
				$result_strings	= str_replace( $vars_tag, $repstr, $result_strings );
				$valiable_counter++;
			}
		}
		
		$result_strings = str_replace( '{else}', '<?php } else { ?>', $result_strings );
		$result_strings = str_replace( '{end-if}', '<?php } ?>', $result_strings );
		$result_strings = str_replace( '{/if}', '<?php } ?>', $result_strings );
	}
	/**
	 * ifタグ内の条件式を解析します。
	 */
	function condition2code( $strings ) {
		// 下位互換の文字列変更
		$strings	= $this->oldCondition2NewCondition( $strings );
		// ネイティブコードに変換
		$attribute_name_array	= array();
		return $this->tagCode2NativeCode( $strings, $attribute_name_array );
	}
	/**
	 * if条件文の式を解析する
	 * 定義済み文字列： and / or / = / != / > / < / + / - / * // is null /is not null/in
	 */
	function parseCondition( $strings, $prefix=1 ) {

		// 内部で利用されている変数を全て取り出す
		$var_name_array		= array();
		$and_array			= explode( ' and ', $strings );
		foreach( $and_array as $condition_and ) {
			$or_array		= explode( ' or ', $condition_and );
			foreach( $or_array as $condition_or ) {
				$divider	= '';
				if( preg_match('/\\<=/', $condition_or ) > 0 ) {
					$divider	= '<=';
				} else if( preg_match('/\\</', $condition_or ) > 0 ) {
					$divider	= '<';
				} else if( preg_match('/\\>=/', $condition_or ) > 0 ) {
					$divider	= '>=';
				} else if( preg_match('/\\>/', $condition_or ) > 0 ) {
					$divider	= '>';
				} else if( preg_match('/\\!=/', $condition_or ) > 0 ) {
					$divider	= '!=';
				} else if( preg_match('/\\=/', $condition_or ) > 0 ) {
					$divider	= '=';
				} else if( preg_match('/ is not null/', $condition_or ) > 0 ) {
					$divider	= ' is not null';
				} else if( preg_match('/ is null/', $condition_or ) > 0 ) {
					$divider	= ' is null';
				} else if( preg_match('/ in /', $condition_or ) > 0 ) {
					$divider	= ' in ';
				} else {
					$divider	= '';
				}
				if( $divider == ' in ' ) {
					list( $pre, $post ) = explode( $divider, $condition_or );
					$str	= 'in_array('.$pre.','.$post.')';
					$strings	= str_replace( $condition_or, $str, $strings );
				}
				if( $divider == ' is null' ) {
					list( $pre ) = explode( $divider, $condition_or );
					$str	= 'is_null('.$pre.') || ( is_string('.$pre.') && strlen('.$pre.') == 0 ) ';
					$strings	= str_replace( $condition_or, $str, $strings );
				}
				if( $divider == ' is not null' ) {
					list( $pre ) = explode( $divider, $condition_or );
					$str	= '!is_null('.$pre.')';
					$strings	= str_replace( $condition_or, $str, $strings );
				}
				// =/ !=で前または後が空文字列だった場合の対策
				if( $divider == '=' ) {
					list( $pre, $post ) = explode( $divider, $condition_or );
					if( strlen(trim($pre)) == 0 ) {
						$pre	= "''";
					}
					if( strlen(trim($post)) == 0 ) {
						$post	= "''";
					}
					$str	= trim($pre)." == ".trim($post);
					$strings	= str_replace( $condition_or, $str, $strings );
				}
				if( $divider == '!=' ) {
					list( $pre, $post ) = explode( $divider, $condition_or );
					if( strlen(trim($pre)) == 0 ) {
						$pre	= "''";
					}
					if( strlen(trim($post)) == 0 ) {
						$post	= "''";
					}
					$str	= trim($pre)." != ".trim($post);
					$strings	= str_replace( $condition_or, $str, $strings );
				}
				
				if( strlen($divider) > 0 ) {
					// 比較演算子で区切れるなら計算式の項を前から順に取り出す
					$element_array	= explode($divider,$condition_or);
					foreach( $element_array as $element ) {
						$pos=0;
						for( $i=0; $i<strlen($element); $i++ ) {
							$char	= substr($element,$i,1);
							if( '+' == $char || '-' == $char
								|| '*' == $char || '/' == $char || '%' == $char
								|| '(' == $char || ')' == $char || ',' == $char || '!' == $char ) {
								// 演算子が登場したらひとつ前までを文字列切り出し
								$str	= substr( $element, $pos, $i-$pos );
								$pos	= $i+1;
								if( '(' != $char && strlen( trim($str) ) > 0 ) {
									// 関数名でなければ追加
									$var_name_array[trim($str)]	= '';
								}
							}
						}
						// ループ最後のポジションから最後の要素を切り出し
						$str	= substr( $element, $pos, $i-$pos );
						if( strlen( trim($str) ) > 0 ) {
							$var_name_array[trim($str)]	= '';
						}
					}
				} else {
					// 比較演算子で区切れないなら単一要素
					$pos=0;
					for( $i=0; $i<strlen($condition_or); $i++ ) {
						$char	= substr($condition_or,$i,1);
						if( '+' == $char || '-' == $char
							|| '*' == $char || '/' == $char || '%' == $char
							|| '(' == $char || ')' == $char || ',' == $char || '!' == $char ) {
							// 演算子が登場したらひとつ前までを文字列切り出し
							$str	= substr( $condition_or, $pos, $i-$pos );
							$pos	= $i+1;
							if( '(' != $char && strlen( trim($str) ) > 0 ) {
								// 関数名でなければ追加
								$var_name_array[trim($str)]	= '';
							}
						}
					}
					// ループ最後のポジションから最後の要素を切り出し
					$str	= substr( $condition_or, $pos, $i-$pos );
					if( strlen( trim($str) ) > 0 ) {
						$var_name_array[trim($str)]	= '';
					}
				}
			}
		}
		// 変数一時格納配列
		$temporary_valiable_getter_array	= array();
		
		// 変数の変換を確認
		$vcounter	= 0;
		foreach( $var_name_array as $key => $value ) {
			if( strlen($key) > 0 ) {
				if( is_numeric( $key ) ) {
					// 変数が数字なら変換対象としない
					$var_name_array[$key]	= $key;
				} else if( 'true' == $key || 'false' == $key ) {
					// trueとfalseはそのまま利用する
					$var_name_array[$key]	= $key;
				} else {
					// 変数が文字列の場合
					$tmp_name	= '$____' . sprintf('%03d',$prefix) . sprintf('%03d',$vcounter);
					$var_name_array[$key]	= $tmp_name;
					if( preg_match('/^\\\'(.)*\\\'$/',$key) > 0 ) {
						$str	= $tmp_name. '='.$key.';'."\n";
						array_push( $temporary_valiable_getter_array, $str );
					} else {
						$vname						= $key;
						$vparam						= '';
						if( strpos($key,'::') !== false ) {
							list( $vname, $vparam )		= explode('::',$key);
						}
						// 一時編集取得文字列作成
						$str	= $tmp_name. '=$request_object->getAttribute(\''.$vname.'\');'."\n";
						$str	.= 'if( !$request_object->existAttribute("'.$vname.'") ){'."\n";
						$str	.= "\t".$tmp_name. '=\''.$vname.'\';'."\n";
						$str	.= '} else if(\'array\'==gettype('.$tmp_name.')) {'."\n";
						if( is_numeric($vparam) ) {
							$str	.= "\t".$tmp_name. '='.$tmp_name.'['.$vparam.'];'."\n";
						} else if( strlen(trim($vparam)) > 0 ){
							$str	.= "\t".$tmp_name. '='.$tmp_name.'[\''.$vparam.'\'];'."\n";
						}
						$str	.= '} else if(\'object\'==gettype('.$tmp_name.')) {'."\n";
						if( is_numeric($vparam) ) {
							// オブジェクトの場合、数値は許可できません。(変数名、メソッド名は数値から始められない)
							$str	.= '$GLOBALS[\'request\']->addLocaledError(\'spider.tags.write.objectcanthasnumericmember\',SPIDER_LOG_LEVEL_FATAL);'."\n";
						} else if( strlen(trim($vparam)) > 0 ){
							$str	.= "\t".$tmp_name. '='.$tmp_name.'->'.$vparam.';'."\n";
						}
						$str	.= '}'."\n";
						array_push( $temporary_valiable_getter_array, $str );
					}
					$vcounter++;
				}
			}
		}
		// 変数配列を文字列の長い順にソート
		$order_array	= array();
		foreach( $var_name_array as $key => $value ) {
			if( !isset($order_array[strlen($key)]) || !is_array( $order_array[strlen($key)] ) ) {
				$order_array[strlen($key)]	= array();
			}
			array_push($order_array[strlen($key)],$key);
		}
		krsort($order_array);
		$new_var_name_array		= array();
		foreach( $order_array as $item_array ) {
			foreach( $item_array as $name ) {
				$new_var_name_array[$name]	= $var_name_array[$name];
			}
		}
		
		// 渡された文字列の置換
		foreach( $new_var_name_array as $key => $value ) {
			$strings	= str_replace( $key, $value, $strings );
		}
		// and or の置換
		$strings	= str_replace( 'and', '&&', $strings );
		$strings	= str_replace( 'or', '||', $strings );
		$strings	= 'if( ' . $strings .' ) {';
		// 変数取り出し文字列を冒頭に追加
		$strings	= implode("\n",$temporary_valiable_getter_array).$strings;
		$strings	= '<?php ' . $strings .' ?>';
		
		return $strings;
	}
	/**
	 * 記述の下位互換の為、条件式文字列を変換するメソッド
	 */
	function oldCondition2NewCondition( $strings ) {
		// 演算時の正規表現
		$signiture_regx	= '\\+\\-\\/\\*\\%\\=\\<\\>\\&\\|\\(\\)\\!\\,\\[\\]';
		// 2文字以上連続のスペースは1文字に統一
		while( preg_match('/\\s\\s/',$strings) > 0 ) {
			$strings	= str_replace( '  ',' ', $strings );
		}
		// 下位互換の為、and/orは演算子に置換する
		$strings	= str_replace(' and ', ' && ', $strings );
		$strings	= str_replace(' or ', ' || ', $strings );
		// 下位互換の為、is null / is not nullを置換する
		$strings	= preg_replace('/(|\\s)([^\\s'.$signiture_regx.']*)\\sis\\snull/','$1is_null( $2 )',$strings);
		$strings	= preg_replace('/(|\\s)([^\\s'.$signiture_regx.']*)\\sis\\snot\\snull/','$1!is_null( $2 )',$strings);
		// 下位互換の為inも置換する
		$strings	= preg_replace('/(|\\s)([^\\s]*)\\sin\\s([^\\s\\}'.$signiture_regx.']*)/','$1in_array( $2 )',$strings);
		// 下位互換の為連続していない=は連続=に変換しておく(if文中で代入が利用できなくなる為要検討)
		$strings	= preg_replace('/([^\\+\\-\\/\\*\\%\\=\\<\\>])\\=([^\\=])/','$1==$2',$strings);
		return $strings;
	}
}
?>