[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