スキン変数の入れ子構造
2007年5月15日
Nucleus のスキン変数・テンプレート変数は、入れ子で使えない。また、タグの始まりは『<%』で終わりは『%>』というルールになっているが、どちらの表記をどちらに用いても、エラーにならない。つまり、
はどれも、エラーなく同じように機能する。
なので、入れ子構造のスキンを例えば次のように表記すると
これは思ったように機能しない。このコードは、次のコードと等価であるからだ。
『blogsetting(id)』はタグの外なのでそのまま表示され、『<%)%>』というタグは存在しなため、『<%)()%>』が表示される。
これは、PARSER::parse() 関数の仕様によるものである。この関数を次のように書き換えれば、入れ子のタグを表示できる。
これを、元の PARSER::parse() 関数と入れ替えればよい。ただ、オリジナルのparse()関数は非常に効率の良いコードで、入れ子構造が必要のないときは、スピードの上で上記のコードはオリジナルのコードにはるか及ばない。なので、コアを変更するよりもプラグインで上記のコードを利用できるようにする、つまり必要なときだけ上記のコードを利用することを考えてみたい。
スキンを入れ子で使えるプラグインはyuさんのNP_IncludeExがあるが、ネストのレベルは一段だけ対応しているようだ。上記のコードは、多段回のネストに対応しているはず。
(スキン変数をネストして用いると、予期せぬセキュリティーホールが発生する可能性もありますので、使用の際は十分注意してください。)
<%blog(default/index,10)%> %>blog(default/index,10)%> <%blog(default/index,10)<% %>blog(default/index,10)<%
はどれも、エラーなく同じように機能する。
なので、入れ子構造のスキンを例えば次のように表記すると
<%blog(default/index,<%blogsetting(id)%>)%>
これは思ったように機能しない。このコードは、次のコードと等価であるからだ。
<%blog(default/index,%>blogsetting(id)<%)%>
『blogsetting(id)』はタグの外なのでそのまま表示され、『<%)%>』というタグは存在しなため、『<%)()%>』が表示される。
これは、PARSER::parse() 関数の仕様によるものである。この関数を次のように書き換えれば、入れ子のタグを表示できる。
function parse($contents) {
$delim=preg_replace('/^\((.*)\)$/','$1',$this->delim);
list($begin,$end)=explode('|',$delim);
if (!$begin || !$end) list($begin,$end)=array('<%','%>');
$stack=array();
while (strlen($contents)) {
if (count($stack)==0) {
// Outside the tag
$i=strpos($contents,$begin /*'<%'*/);
if ($i===false) {
echo $contents;
return;
}
echo substr($contents,0,$i); // Left side
$contents=substr($contents,$i+strlen($begin /*'<%'*/)); // Right side
array_push($stack,'');
}
// Now, we are in the tag.
$i=strpos($contents,$begin /*'<%'*/);
$j=strpos($contents,$end /*'%>'*/);
if ($i===false && $j===false) {
// Both '<%' and '%>' are not found.
$this->doAction($contents);
return;
} else if ($j!==false && ($j<$i || $i===false)) {
// '%>' found. The right characters are outside the tag.
$action=substr($contents,0,$j); // Left side
$contents=substr($contents,$j+strlen($end /*'%>'*/)); // Right side
$actionlc=preg_replace('/^([^\(]+)[\(](.*)$/','$1',strtolower($action));
if (preg_match('/^(if[.]*|else|endif|ifnot|elseif|elseifnot)$/',$actionlc)) {
// if, else, endif etc.
// ob_start() cannot be used here because it's used below.
// In addition, on_start() isn't needed here (if etc. doesn't parse anything).
$this->doAction($action);
} else {
ob_start();
$this->doAction($action);
$contents=ob_get_contents().$contents;
ob_end_clean();
}
if (count($stack)) $contents=array_pop($stack).$contents;
} else {
// '<%' found.
array_push($stack,substr($contents,0,$i)); // Left side
$contents=substr($contents,$i+strlen($begin /*'<%'*/)); // Right side
}
}
}これを、元の PARSER::parse() 関数と入れ替えればよい。ただ、オリジナルのparse()関数は非常に効率の良いコードで、入れ子構造が必要のないときは、スピードの上で上記のコードはオリジナルのコードにはるか及ばない。なので、コアを変更するよりもプラグインで上記のコードを利用できるようにする、つまり必要なときだけ上記のコードを利用することを考えてみたい。
スキンを入れ子で使えるプラグインはyuさんのNP_IncludeExがあるが、ネストのレベルは一段だけ対応しているようだ。上記のコードは、多段回のネストに対応しているはず。
(スキン変数をネストして用いると、予期せぬセキュリティーホールが発生する可能性もありますので、使用の際は十分注意してください。)