The preg_replace_callback() function in PHP - Using preg_replace_callback() to produce a running sequence (
Page 4 of 5 )
We will now take a look at an even more interesting example – doing a preg_replace using a regular expression with a running sequence.
Suppose you have an ordered list in HTML format:
list 1
<ol>
<li>item A</li>
<li>item B</li>
<li>item C</li>
</ol>
You would like to convert this into a plain text format in running sequence:
list 1
1. item A
2. item B
3. item C
Below is the code using preg_replace_callback():
<?php
$str = "list 1
<ol>
<li>item A</li>
<li>item B</li>
<li>item C</li>
</ol>
";
echo preg_replace_callback('%<ol>(.*)</ol>%si', 'process_list', $str);
function process_list($matches) {
return preg_replace_callback("/<li>(.*)</li>/i", 'process_item', trim($matches[1]));
}
function process_item($matches) {
static $sno=0;
++$sno;
return "$sno. $matches[1]";
}
?>
Note the use of two preg_replace_callback() functions. The first one is used to grab all the items within the tags <ol> and </ol>. The second one is used to process each individual item, in which we append a running serial number in front of each item.
In case you have multiple lists such as:
list 1
<ol>
<li>item A</li>
<li>item B</li>
<li>item C</li>
</ol>
list 2
<ol>
<li>item D</li>
<li>item E</li>
<li>item F</li>
<li>item G</li>
</ol>
list 3
<ol>
<li>item H</li>
<li>item I</li>
<li>item J</li>
<li>item K</li>
<li>item L</li>
</ol>
you have to change the code slightly to accommodate this:
<?php
$str = "
list 1
<ol>
<li>item A</li>
<li>item B</li>
<li>item C</li>
</ol>
list 2
<ol>
<li>item D</li>
<li>item E</li>
<li>item F</li>
<li>item G</li>
</ol>
list 3
<ol>
<li>item H</li>
<li>item I</li>
<li>item J</li>
<li>item K</li>
<li>item L</li>
</ol>
";
echo preg_replace_callback('%<ol>(.*?)</ol>%si', 'process_list', $str);
function process_list($matches) {
global $sno;
$sno = 0;
return preg_replace_callback("/<li>(.*?)</li>/i", 'process_item', trim($matches[1]));
}
function process_item($matches) {
global $sno;
++$sno;
return "$sno. $matches[1]";
}
?>
Note the two key changes here. First, we have used the non-greedy quantifier ‘*?’ so that each list remains as one unit. Second, we need to think of some way to make the serial number restart from one for each new list. Here, for simplicity, I just use a global variable.