简体   繁体   中英

How can I replace the Nth instance of a string in PHP?

I need to dynamically add an active class to a shortcode in wordpress based on the active number set in it's parent shortcode.

The parent looks like this:

$active = 3;
$output .= "\n\t\t".wpb_js_remove_wpautop($content);

The $content variable can technically consist of anything in between the shortcodes tags, but specifically, should include more child shortcodes (it's a tabs shortcode so the parent is tabs and the children are tab).

What I need to do is parse $content and replace the Nth ( $active variable) instance of [vc_tab with [vc_tab active="true"

I realize there are better ways to design the shortcode to compensate for this but I am limited in what I can do because I am modifying Visual Composer to use Bootstrap tabs instead of jQuery and Bootstrap tabs need the active class on both the <li> and the <div class="tab-pane"> elements but I don't want the users to have to add an active number to the parent shortcode and an active true/false to the child as that will get confusing.

So far from googling I can only find replacing the first occurrence or all occurrences, not the Nth one.

Temporarily I am using jQuery to make the appropriate <div> active but it results in an undesirable FOUC where there is no pane visible on load and then one pops into place when the jQuery runs.

$('.tabs-wrap').each(function(){
  var activeTab = $(this).find('.tabbed li.active a').attr('href');
  $(this).find(activeTab).addClass('active');
});  

I 'm not familiar with the tools you mention so I can't say if there is a better way to achieve the end goal, but you can replace the Nth occurrence of something using preg_replace_callback :

$active = 3;     // target occurrence
$occurrence = 0; // counter
$output = preg_replace_callback(
    '/\[vc_tab/',
    function ($matches) use(&$occurrence, $active) {
        return ++$occurrence != $active
            ? $matches[0]
            : '[vc_tab active="true"';
    },
    $output,
    $active
);

See it in action .

This works by replacing each occurrence with itself unless the occurrence counter is equal to the target number.

Note that I have passed $active as the maximum number of occurrences to replace in order to get free performance increase; this will cause preg_replace_callback to stop after $active occurrences have been replaced (we know that all occurrences after that will be replaced by themselves, so there's no point in going on).

This is an interesting problem, and got me thinking if you really need a regex to do this.

My conclusion is that I don't believe you do. All you need to do is find the occurrence of the Nth $needle , and replace it with your replacement. This can be achieved through some simple string manipulation, as follows:

  1. Define some variables. Your input string, your $active index, the $needle that we are looking for, and the $replace ment that we will replace the Nth match with.

     $string = "[vc_tab [vc_tab [vc_tab [vc_tab [vc_tab [vc_tab"; $active = 3; $needle = "[vc_tab"; $replace = '[vc_tab active="true"'; 
  2. Now, we need to make sure that there are enough $needle s in the $string , otherwise we can't do this replacement.

     if( substr_count( $string, $needle) < $active) { throw new Exception("There aren't enough needles in the string to do this replacement"); } 
  3. Now, we know we can do the replacement. So let's find the $index in the string where the Nth occurrence of $needle ends:

     $count = 0; $index = 0; while( $count++ < $active) { $index = strpos( $string, $needle, $index) + strlen( $needle); } 
  4. Now, $index is pointing here in the string:

     [vc_tab [vc_tab [vc_tab [vc_tab [vc_tab [vc_tab ^ Here 

    We can now do some simple substr() 's to form the final string:

     $result = substr( $string, 0, $index - strlen( $needle)) . $replace . substr( $string, $index); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ Before the Nth match After the Nth match 

    So, we end up concatenating:

    • [vc_tab [vc_tab
    • [vc_tab active="true"
    • [vc_tab [vc_tab [vc_tab

This results in our final output :

[vc_tab [vc_tab [vc_tab active="true" [vc_tab [vc_tab [vc_tab

Try it out in the demo !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM