Luigi Cavalieri - Authoring Open Source Code

How to Rename a Shortcode Programmatically

When we change mind on the name of a shortcode become almost ubiquitous amongst our Posts, the only reasonable way of renaming all its occurrences is programmatically.

How to Rename a Shortcode Programmatically

It does happen to change mind. But when for some reason we change mind on the name of a shortcode become almost ubiquitous amongst our Posts, Pages and Custom Posts, the only reasonable way of renaming all its occurrences is programmatically — writing scripts all the monotonous work can be delegated to is a habit that, trust me, pays off over time.

The snippet of code that follows is a generalisation of a simpler script I wrote for the migration of data from SiteTree to The Permalinks Cascade, it is basically an implementation of the find-and-replace task iterated over all the posts showing a glimpse of the shortcode we want to rename. Most of its magic is condensed in the regular expressions, used to spot the occurrences of the shortcode which have not been escaped with double square brackets. To accomplish this, the two regex use a negative lookaround, a zero-length assertion you can learn everything about on the Regular-Expressions.info website.

1$old_shortcode_name = 'my-shortcode';
2$new_shortcode_name = 'my-new-shortcode';
3
4// We do a preliminary sorting of the posts by using
5// the search feature built into the WP_Query class, 
6// so as to retrieve from the database only the posts  
7// that might contain the shortcode to rename.
8$arguments = array(
9  'posts_per_page' => -1,
10  'post_type'      => 'any',
11  's'              => "[{$old_shortcode_name}"
12);
13
14$query = new WP_Query( $arguments );
15
16// The negative look-behind and the negative look-ahead in 
17// the regular expressions ensure that shortcodes escaped 
18// with double square brackets are not matched.
19// Also, the capturing group in the first regex is 
20// used to capture the list of attributes that 
21// the shortcode might include. 
22$patterns = array(
23  "#(?<!\[)\[{$old_shortcode_name}( [^\]]+)?\]#",
24  "#\[/{$old_shortcode_name}\](?!\])#"
25);
26
27// The match variable '$1' will be replaced either with 
28// an empty string or a list of shortcode attributes.
29$replacements = array(
30  "[{$new_shortcode_name}\$1]",
31  "[/{$new_shortcode_name}]"
32);
33
34while ( $query->have_posts() ) {
35  $query->the_post();
36
37  $the_content = get_the_content();
38
39  // If $the_content doesn't contain non-escaped 
40  // occurrences of the shortcode we want to rename, 
41  // we just go on to analyse the next post.
42  if (! preg_match( $patterns[0], $the_content ) ) {
43    continue;
44  }
45  
46  // We retrieve the current post 
47  // as an associative array.
48  $the_post = get_post( null, ARRAY_A );
49
50  if (! $the_post ) {
51    continue;
52  }
53
54  $the_post['post_content'] = preg_replace( $patterns,
55                                            $replacements,
56                                            $the_content );
57
58  // We let WordPress automatically update 
59  // the post's modification date.
60  unset( $the_post['post_modified'], $the_post['post_modified_gmt'] );
61
62  // The last parameter hinders WordPress 
63  // from triggering the after-insert hooks.
64  wp_update_post( $the_post, false, false );
65}
66
67// Mandatory when we create 
68// a secondary loop through WP_Query. 
69wp_reset_postdata();

The Edge Case

The presence of nested shortcodes in one or more of your posts is certainly not such an improbability that it should be tagged as edge case, but an escaped shortcode that wraps a very occurrence of the shortcode to rename is without doubt an edge case. So, if somewhere in your website you have written something like that...

1[[my-outer-shortcode]
2  [my-shortcode arg="value"]
3[/my-outer-shortcode]]

...it would be better to preventively exclude from the query the posts containing such pseudo-code. You just have to pass to the constructor of WP_Query a list of IDs as the value of the post__not_in argument.

Running the code: Where and When

Given a generic context, you can simply put the code in the functions.php file of your active theme, and delete it once the renaming is complete. As a precaution however, I would suggest to let WordPress run it when the init hook fires, like so:

1function rename_my_shortcode() {
2  // The snippet goes here.
3}
4add_action( 'init', 'rename_my_shortcode' );

Running the code: A Final Note

Running the snippet only once is more than enough, however, in the event you let the snippet run more than once, you don't have to worry, because the conditional statements it comes with will act as a deterrent to unexpected results.