Preg_replace_callback with spoilers in spoilers

I'm writing 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 spoilers:

 <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 can I make it work with a spoiler in a spoiler for example [spoiler=Spoiler Title][spoiler=Second Spoiler]Another Text[/spoiler][/spoiler] my current function is returned by no spoiler to the 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

 <?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 result I'm trying to do is like this:

 <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> 

Hope this answers my question, thanks!

0
source share
2 answers

This applies to BBCode as a whole: re-run the replacement code until it stops changing.

preg_replace_callback accepts the reference variable "count", which will be filled with the number of replacements made. As long as this number is not zero, you should restart the replacement ( do..while perfect for this)

It does not matter that they intersect. 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 the next replacement:

 <div>Blah<div>123</div>Fish</div> 

Therefore, although they were processed in a cross order, the result is correctly nested.

0
source

You can use the count preg_replace_callback parameter to handle the replacement from the innermost to the outer until more tags are replaced:

 $pattern = '~\[spoiler=([^]]*)]((?>[^[]+|\[(?!/?spoiler\b))*)\[/spoiler]~i'; do { $result = preg_replace_callback($pattern, 'BBCode_spoiler', $text, -1, $count); } while ($count>0); 

I am modifying the template a bit to make sure the match is the innermost tag.

0
source

Source: https://habr.com/ru/post/1206323/


All Articles