问题
i write something like [spoiler=Spoiler Title]text inside the Spoiler[/spoiler]
and use preg_replace_callback("/\[spoiler=(.*)\](.*)\[\/spoiler\]/Usi", 'BBCode_spoiler', $text);
to create a real spoiler, the result with one or more spoiler is:
<script type="text/javascript">
function show_0() {
if(document.getElementById("0").style.display == "inline-block")
document.getElementById("0").style.display = "none";
else document.getElementById("0").style.display = "inline-block"; }
</script>
<a href="javascript:show_0();"><i>Show Spiler:</i> <b>Spoiler Title</a></b><br>
<div id="0" style="display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;">
Text inside the Spoiler
</div>
how do i make it works with a spoiler into a spoiler
like [spoiler=Spoiler Title][spoiler=Second Spoiler]Another Text[/spoiler][/spoiler]
my current function returns by none spoiler into a spoiler
<script type="text/javascript">
function show_0() {
if(document.getElementById("0").style.display == "inline-block")
document.getElementById("0").style.display = "none";
else document.getElementById("0").style.display = "inline-block"; }
</script>
<a href="javascript:show_0();"><i>Show Spiler:</i> <b>Spoiler Title</a></b><br>
<div id="0" style="display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;">
[spoiler=Second Spoiler]Another Text</div>[/spoiler]
my callback function is
<?php
// Spoiler
$counter = 0;
function BBCode_spoiler($hits) {
global $central_lang;
global $counter;
$title = htmlentities(trim($hits[1]));
$text = htmlentities($hits[2]);
$return = "<script type=\"text/javascript\">";
$return .= "function show_".$counter."() {";
$return .= "if(document.getElementById(\"".$counter."\").style.display == \"inline-block\") document.getElementById(\"".$counter."\").style.display = \"none\";";
$return .= "else document.getElementById(\"".$counter."\").style.display = \"inline-block\"; }";
$return .= "</script>";
$return .= "<a href=\"javascript:show_".$counter."();\"><i>".$central_lang['bbcodes']['spoiler']['text'].":</i> <b>".$title."</a></b><br>";
$return .= "<div id=\"".$counter."\" style=\"display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;\">".$text."</div>";
$counter++;
return $return; }
?>
the output i try to make seems like that
<script type="text/javascript">
function show_0() {
if(document.getElementById("0").style.display == "inline-block")
document.getElementById("0").style.display = "none";
else document.getElementById("0").style.display = "inline-block"; }
</script>
<a href="javascript:show_0();"><i>Show Spoiler:</i> <b>Spoiler Title</a></b><br>
<div id="0" style="display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;">
<script type="text/javascript">
function show_1() {
if(document.getElementById("1").style.display == "inline-block")
document.getElementById("1").style.display = "none";
else document.getElementById("1").style.display = "inline-block"; }
</script>
<a href="javascript:show_1();"><i>Show Spoiler:</i> <b>Second Spoiler</a></b><br>
<div id="1" style="display: none; min-width: 100px; border-bottom: 1px solid #000000; border-right: 1px solid #000000; background-color: #FFFFFF; color: #000000;">
Another text
</div>
</div>
i hope it's there is an answer for my question, thank you!
回答1:
This applies to BBCode in general: re-run the replacement code until it stops changing it.
preg_replace_callback
accepts a "count" reference variable, which will be filled with the number of replacements made. So long as this number is not zero, you should re-run the replacement (do..while
is perfect for this)
It doesn't matter that they're crossed over. Let's say we have a BBCode that replaces [div]
with <div>
...
[div]Blah[div]123[/div]Fish[/div]
After one replacement:
<div>Blah[div]123</div>Fish[/div]
After another replacement:
<div>Blah<div>123</div>Fish</div>
So even though they were processed in a crossed-over order, the result is properly nested.
回答2:
You can use the count parameter of preg_replace_callback to process the replacement from innermost to outermost until there is no more tags to replace:
$pattern = '~\[spoiler=([^]]*)]((?>[^[]+|\[(?!/?spoiler\b))*)\[/spoiler]~i';
do {
$result = preg_replace_callback($pattern, 'BBCode_spoiler', $text, -1, $count);
} while ($count>0);
I change a little the pattern to ensure that the match is the innermost tag.
来源:https://stackoverflow.com/questions/22912915/preg-replace-callback-with-spoilers-into-spoilers