Wrapping shortcodes in another shortcode

Here is the script I came up with. It loops through all the posts in a WordPress instance and splits it up into three sections:

  1. The HTML/content before the row of shortcodes
  2. The row of shortcodes
  3. The HTML/content after the row of shortcodes

I decided to use substr() to split up the content. I then re-assemble everything together towards the end and include the “wrapping” shortcode around the middle piece.

$args = array( 'numberposts' => -1, 'post_status' => 'publish|draft|trash' )
$posts = get_posts( $args );
if( !empty( $posts ) ){
    foreach( $posts as $post ){
        $content = $post->post_content;
        if( !has_shortcode( $content, 'components') ){
            $pattern = '/\[component id=\"[0-9]+\"\]/';
            preg_match_all( $pattern, $content, $matches, PREG_OFFSET_CAPTURE);

            $first_match = $matches[0][0];
            $first_match_start = $first_match[1]; // Start position of the first match

            $last_match = $matches[0][ count($matches[0]) - 1 ];
            $last_match_start = $last_match[1];
            $last_match_end = $last_match_start + strlen($last_match[0]);

            $before_html = substr($content, 0, $first_match_start); // Get all the content before the first match of [component id="XYZ"]
            $component_shortcodes = substr($content, $first_match_start, $last_match_end - $first_match_start ); // Get everything in between the first [component id="XYZ"] and the last
            $after_html = substr($content, $last_match_end ); // Get everything after the last [component id="XYZ"] match
            // Perform the actual wrapping here
            $new_content = $before_html . '[components]' . $component_shortcodes . '[/components]' . $after_html;
            $post->post_content = $new_content;
            wp_update_post( $post );
        }
    }
}