$1", "Some text here... [b][b]Hello, [b]PHP![/b][/b][/b...">

PHP nested templates in preg_replace

preg_replace("/\[b\](.*)\[\/b\]/Usi", "<strong>$1</strong>", "Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... [b]and here[/b]"); 

returns

 Some text here... <strong>[b]Hello, [b]PHP!</strong>[/b][/b] ... <strong>and here</strong> 

But I need to replace all the tags [b] ... [/ b]. Why does this not happen in my case?

+4
source share
4 answers

The reason this doesn't work: you catch the first [b], then move on to the next [/ b] and leave something between unchanged. Those. you change the external [b] tags, but not the ones inside.

Your comment on @meza suggests that you want to replace the pseudo-tags in pairs or leave them untouched. The best way to do this is to use multiple passes, for example

 $markup = "Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... [b]and here[/b]"; $count = 0; do { $markup = preg_replace("/\[b\](.*?)\[\/b\]/usi", "<strong>$1</strong>", $markup, -1, $count ); } while ( $count > 0 ); print $markup; 

I'm not even sure that you can do this in a single line regex, but even if that were possible, it would be rather complicated and therefore it would be difficult to maintain.

+2
source

Yes, a multi-pass approach is required if the elements are nested. This can be achieved in one of two ways; coincidence from inside or outside inside. Here are two tested scenarios with fully commented regular expressions that illustrate each technique:

1. Replace from the inside:

 <?php // test.php Rev:20121016_0900 $re = '% # Match innermost [b]...[/b] structure. \[b\] # Literal start tag. ( # $1: Element contents. # Use Friedls "Unrolling-the-Loop" technique: # Begin: {normal* (special normal*)*} construct. [^[]* # {normal*} Zero or more non-"[". (?: # Begin {(special normal*)*}. \[ # {special} Tag open literal char, (?!/?b\]) # but only if NOT [b] or [/b]. [^[]* # More {normal*}. )* # Finish {(special normal*)*}. ) # $1: Element contents. \[/b\] # Literal end tag. %x'; printf("Replace matching tags from the inside out:\n"); $text = file_get_contents('testdata.txt'); $i=0; // Keep track of iteration number. printf("i[%d]=%s", $i++, $text); while(preg_match($re, $text)){ $text = preg_replace($re, '<strong>$1</strong>', $text); printf("i[%d]=%s", $i++, $text); } ?> 

Output:

 ''' Replace matching tags from the inside out: i[0]=Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... [b]and here[/b] i[1]=Some text here... [b][b]Hello, <strong>PHP!</strong>[/b][/b] ... <strong>and here</strong> i[2]=Some text here... [b]<strong>Hello, <strong>PHP!</strong></strong>[/b] ... <strong>and here</strong> i[3]=Some text here... <strong><strong>Hello, <strong>PHP!</strong></strong></strong> ... <strong>and here</strong> ''' 

2. Replace outside in:

 <?php // test.php Rev:20121016_0901 $re = '% # Match outermost [b]...[/b] structure. \[b\] # Literal start tag. ( # $1: Element contents. (?: # Zero or more contents alternatives. [^[]* # Either non-[b]...[/b] stuff... (?: # Begin {(special normal*)*}. \[ # {special} Tag open literal char, (?!/?b\]) # but only if NOT [b] or [/b]. [^[]* # More {normal*}. )* # Finish {(special normal*)*}. | (?R) # Or a nested [b]...[/b] structure. )* # Zero or more contents alternatives. ) # $1: Element contents. \[/b\] # Literal end tag. %x'; printf("Replace matching tags from the outside in:\n"); $text = file_get_contents('testdata.txt'); $i=0; // Keep track of iteration number. printf("i[%d]=%s", $i++, $text); while(preg_match($re, $text)){ $text = preg_replace($re, '<strong>$1</strong>', $text); printf("i[%d]=%s", $i++, $text); } ?> 

Output:

 ''' Replace matching tags from the outside in: i[0]=Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... [b]and here[/b] i[1]=Some text here... <strong>[b]Hello, [b]PHP![/b][/b]</strong> ... <strong>and here</strong> i[2]=Some text here... <strong><strong>Hello, [b]PHP![/b]</strong></strong> ... <strong>and here</strong> i[3]=Some text here... <strong><strong>Hello, <strong>PHP!</strong></strong></strong> ... <strong>and here</strong> ''' 

Note the recursive expression (?R) used in the second approach.

+3
source

Why use a regex for this particular case? You can leave with a simple line by replacing each [b] with a strong one and each [/ b] with / strong.

0
source

edit your Usi modifiers and replace it with sim .

EDIT:

Take a picture:

 <?php function matchReplaceAll($reg, $replace, $str) { while (preg_match($reg, $str)) { $str = preg_replace($reg, $replace, $str); } return $str; } $str="Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... and here"; $str=matchReplaceAll('/\[b\](.*?)\[\/b\]/sim', '<strong>$1</strong>', $str); echo $str; 
0
source

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


All Articles