[wp-trac] [WordPress Trac] #58366: Shortcode Support Regained but Content Filters are messing with Shortcode HTML

WordPress Trac noreply at wordpress.org
Thu May 22 21:06:22 UTC 2025


#58366: Shortcode Support Regained but Content Filters are messing with Shortcode
HTML
-------------------------------------------------+-------------------------
 Reporter:  domainsupport                        |       Owner:  (none)
     Type:  defect (bug)                         |      Status:  new
 Priority:  normal                               |   Milestone:  Future
                                                 |  Release
Component:  Shortcodes                           |     Version:  6.2.2
 Severity:  normal                               |  Resolution:
 Keywords:  has-test-info needs-unit-tests has-  |     Focuses:
  patch dev-feedback                             |
-------------------------------------------------+-------------------------

Comment (by commoncentsus):

 This bug bit me bad late last week.  I thought I'd share my resolution to
 it.

 TLDR:  In the files wp-includes/blocktemplate.php and wp-includes/block
 /template-part.php, move the line that calls do_shortcode($content) down
 to immediately before the line that calls
 wp_filter_content_tags($content,'template').   Diff files are shown after
 the background section of my post.

 **RESEARCH AND BACKGROUND:**

 First, I looked at the processing for the place(s) where shortcodes were
 working and uncorrupted.  I found that I could put my shortcode in the
 content of a post or page and it would be fine, but when I put it in a
 header or footer area of a template, it wouldn't work.

 Looking first at processing for post/page content, I found these lines in
 wp-includes/default-filters.php:
 {{{#!php
 <?php
 add_filter( 'the_content',
 'apply_block_hooks_to_content_from_post_object', 8 ); // BEFORE
 do_blocks().
 add_filter( 'the_content', 'do_blocks', 9 );
 add_filter( 'the_content', 'wptexturize' );
 add_filter( 'the_content', 'convert_smilies', 20 );
 add_filter( 'the_content', 'wpautop' );
 add_filter( 'the_content', 'shortcode_unautop' );
 add_filter( 'the_content', 'prepend_attachment' );
 add_filter( 'the_content', 'wp_replace_insecure_home_url' );
 add_filter( 'the_content', 'do_shortcode', 11 ); // AFTER wpautop().
 add_filter( 'the_content', 'wp_filter_content_tags', 12 ); // Runs after
 do_shortcode().
 }}}
 The priority numbers mean things don't exactly run in the order shown.
 The default priority is #10, so the order is
 apply_block_hooks_to_content_from_post_object first, then do_blocks, then
 wptexturize, wpautop, shortcode_unautop, prepend_attachment,
 wp_replace_insecure_home_url, then do_shortcode, then
 wp_filter_content_tags, and finally convert_smilies.  Note the comment for
 do_shortcode.  It seems someone thought it was important that do_shortcode
 runs AFTER wpautop() and added that comment.

 Loooking elsewhere in the default-filters.php file, I also found filters
 in a similar order with similar comment for widget text content and widget
 block content.  Here's the code for those:

 {{{#!php
 <?php
 add_filter( 'widget_text', 'balanceTags' );
 add_filter( 'widget_text_content', 'capital_P_dangit', 11 );
 add_filter( 'widget_text_content', 'wptexturize' );
 add_filter( 'widget_text_content', 'convert_smilies', 20 );
 add_filter( 'widget_text_content', 'wpautop' );
 add_filter( 'widget_text_content', 'shortcode_unautop' );
 add_filter( 'widget_text_content', 'wp_replace_insecure_home_url' );
 add_filter( 'widget_text_content', 'do_shortcode', 11 ); // Runs after
 wpautop(); note that $post global will be null when shortcodes run.
 add_filter( 'widget_text_content', 'wp_filter_content_tags', 12 ); // Runs
 after do_shortcode().

 add_filter( 'widget_block_content', 'do_blocks', 9 );
 add_filter( 'widget_block_content', 'do_shortcode', 11 );
 add_filter( 'widget_block_content', 'wp_filter_content_tags', 12 ); //
 Runs after do_shortcode().
 }}}

 Again, there's a comment specifically saying that do_shortcode() should
 run after wpautop().

 I also verified that my shortcode runs in a text widget or widget block,
 and isn't corrupted with extraneous </p> and <p> tags like it is if I use
 the shortcode in a header or footer in a template.

 OK, so after looking at that stuff, I opened wp-includes/block-
 template.php and found this:

 {{{#!php
 <?php
         $content = $wp_embed->run_shortcode( $_wp_current_template_content
 );
         $content = $wp_embed->autoembed( $content );
         $content = shortcode_unautop( $content );
         $content = do_shortcode( $content );

         /*
          * Most block themes omit the `core/query` and `core/post-
 template` blocks in their singular content templates.
          * While this technically still works since singular content
 templates are always for only one post, it results in
          * the main query loop never being entered which causes bugs in
 core and the plugin ecosystem.
          *
          * The workaround below ensures that the loop is started even for
 those singular templates. The while loop will by
          * definition only go through a single iteration, i.e.
 `do_blocks()` is only called once. Additional safeguard
          * checks are included to ensure the main query loop has not been
 tampered with and really only encompasses a
          * single post.
          *
          * Even if the block template contained a `core/query` and `core
 /post-template` block referencing the main query
          * loop, it would not cause errors since it would use a cloned
 instance and go through the same loop of a single
          * post, within the actual main query loop.
          *
          * This special logic should be skipped if the current template
 does not come from the current theme, in which case
          * it has been injected by a plugin by hijacking the block
 template loader mechanism. In that case, entirely custom
          * logic may be applied which is unpredictable and therefore safer
 to omit this special handling on.
          */
         if (
                 $_wp_current_template_id &&
                 str_starts_with( $_wp_current_template_id,
 get_stylesheet() . '//' ) &&
                 is_singular() &&
                 1 === $wp_query->post_count &&
                 have_posts()
         ) {
                 while ( have_posts() ) {
                         the_post();
                         $content = do_blocks( $content );
                 }
         } else {
                 $content = do_blocks( $content );
         }

         $content = wptexturize( $content );
         $content = convert_smilies( $content );
         $content = wp_filter_content_tags( $content, 'template' );
         $content = str_replace( ']]>', ']]>', $content );

 }}}
 Right away I notice that do_shortcode() is called much earlier in this.  I
 also looked at wp-includes/blocks/template-part.php. Here's the section
 from that:
 {{{#!php
 <?php
         // Run through the actions that are typically taken on
 the_content.
         $content                       = shortcode_unautop( $content );
         $content                       = do_shortcode( $content );
         $seen_ids[ $template_part_id ] = true;
         $content                       = do_blocks( $content );
         unset( $seen_ids[ $template_part_id ] );
         $content = wptexturize( $content );
         $content = convert_smilies( $content );
         $content = wp_filter_content_tags( $content,
 "template_part_{$area}" );
 }}}

 First, note the opening comment in that.  "Run through the actions that
 are typically taken on the_content".  Actually, I'm not sure that it
 wouldn't be better to just do "apply_filters ('the_content', $content )
 instead.  That's another discussion that probably needs to happen.

 **My solution:**

 So based on the comments in the code I've looked at so far, I decided to
 make changes to includes/block-template.php as follows (this is the diff
 file that can be used to patch the file, I'm not sure how to attach it
 here or what to name it here).

 {{{
 $ cat block-template.php.diff
 --- block-template.php  2025-03-18 22:08:26.000000000 -0400
 +++ new-block-template.php      2025-05-21 12:16:50.440711451 -0400
 @@ -259,7 +259,6 @@
         $content = $wp_embed->run_shortcode( $_wp_current_template_content
 );
         $content = $wp_embed->autoembed( $content );
         $content = shortcode_unautop( $content );
 -       $content = do_shortcode( $content );

         /*
          * Most block themes omit the `core/query` and `core/post-
 template` blocks in their singular content templates.
 @@ -296,6 +295,7 @@

         $content = wptexturize( $content );
         $content = convert_smilies( $content );
 +       $content = do_shortcode( $content ); // MUST BE AFTER wp_autop()
 [?? and wp_texturize() ??]
         $content = wp_filter_content_tags( $content, 'template' );
         $content = str_replace( ']]>', ']]>', $content );

 $
 }}}

 I also made this change to wp-includes/blocks/template-part.php

 {{{
 $ cat template-part.php.diff
 --- template-part.php   2025-03-03 18:08:42.000000000 -0500
 +++ new-template-part.php       2025-05-21 12:06:11.130469904 -0400
 @@ -151,12 +151,12 @@

         // Run through the actions that are typically taken on
 the_content.
         $content                       = shortcode_unautop( $content );
 -       $content                       = do_shortcode( $content );
         $seen_ids[ $template_part_id ] = true;
         $content                       = do_blocks( $content );
         unset( $seen_ids[ $template_part_id ] );
         $content = wptexturize( $content );
         $content = convert_smilies( $content );
 +       $content                       = do_shortcode( $content );      //
 MUST BE AFTER wp_autop() [?? and wp_texturize() ??]
         $content = wp_filter_content_tags( $content,
 "template_part_{$area}" );

         // Handle embeds for block template parts.

 }}}
 So now, the processing is actually more like "the actions that are
 typically taken on the_content," as claimed in the comments in one of
 those files.

 After these two changes, my shortcode works and is uncorrupted in either
 the header or footer area of a page template. No more extraneous </p> and
 <p> tags added to the output of my shortcode.

 YMMV.

 I hope someone finds this useful.

 Actually, I hope someone reviews these two minor changes and includes them
 in the next "bug fix" release if possible.  The whole process in those
 files should be compared to the filter list for "the_content" and there
 might be other changes indicated as well. I'll keep the patches in case
 there's an update that breaks this again, so I can patch and fix with
 this.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/58366#comment:97>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list