preg_replace_callback with Spoilers into Spoilers

喜夏-厌秋 提交于 2019-12-25 06:23:31

问题


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&nbsp;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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!