[wp-trac] [WordPress Trac] #41179: Adding get_the_content-filter
WordPress Trac
noreply at wordpress.org
Sat Aug 2 21:29:42 UTC 2025
#41179: Adding get_the_content-filter
--------------------------------------+------------------------------
Reporter: Ninos Ego | Owner: (none)
Type: enhancement | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Themes | Version: 4.8
Severity: normal | Resolution:
Keywords: needs-codex dev-feedback | Focuses: docs, template
--------------------------------------+------------------------------
Comment (by deyril):
Hello @joyously , I just coming here and a little bit shocked if this idea
has been patched 8 years ago and still not considered if that's useful, I
hope my use case can help you to understand why we need it. At the moment,
I'm working on a plugin that extend a revision system, I asked AI for help
me to explain it, I hope it can explains everything
===========================
= WordPress Trac Ticket Proposal: Add Filter Hook for get_the_content()
Function =
== Summary ==
Request to add a filter hook to the `get_the_content()` function to allow
plugins to modify post content when accessed directly, similar to how
`the_content` filter works for `the_content()` function.
== Problem Description ==
=== Current Limitation ===
The `get_the_content()` function in WordPress core does not provide any
filter hooks, making it impossible for plugins to modify content when it's
accessed directly. This creates an inconsistency with `the_content()`
function which applies the `the_content` filter.
=== Real-World Use Case: Advanced Revision System ===
We are developing an advanced revision system plugin that allows users to
preview specific revisions of posts on the frontend. Our system works by:
1. '''Revision Storage''': Storing complete post revisions with content,
title, and metadata
2. '''Frontend Preview''': Generating secure URLs with nonce verification
to preview revisions
3. '''Content Filtering''': Intercepting content display to show revision
content instead of published content
=== The Problem ===
Our revision system successfully filters content when themes use
`the_content()` via the `the_content` filter. However, many plugins and
themes call `get_the_content()` directly, bypassing our content filtering
entirely.
'''Detailed Example: qi-blocks Plugin Global Styles System'''
The qi-blocks plugin (a Gutenberg blocks framework) has a global styles
system that needs to analyze post content to:
1. '''Generate Dynamic CSS''': Create inline styles based on block
configurations
2. '''Process Reusable Blocks''': Expand reusable block references to get
their actual content
3. '''Font Loading Optimization''': Detect which fonts are actually used
in content
4. '''Style Cleanup''': Remove unused styles from the database when
blocks are deleted
Here's the problematic code from `add_page_inline_style()` method from qi-
blocks plugin https://wordpress.org/plugins/qi-blocks/ at file `class-qi-
blocks.php` :
{{{
public function add_page_inline_style() {
$global_styles = get_option( 'qi_blocks_global_styles' );
if ( ! empty( $global_styles ) ) {
$page_id = apply_filters(
'qi_blocks_filter_page_inline_style_page_id', get_queried_object_id() );
$styles = array();
$fonts = array(
'family' => array(),
'weight' => array(),
'style' => array(),
);
$update = false;
$include_italic_fonts = false;
$templates_selector = 'body ';
// Post blocks.
if ( isset( $global_styles['posts'][ $page_id ] ) && ! empty(
$global_styles['posts'][ $page_id ] ) ) {
$get_page_content = trim( get_the_content( null, false,
$page_id ) );
// Check if the content contains reusable blocks and expand
the page content with the real reusable content.
preg_match_all( '({[\s]?[\'\"]ref[\'\"]:(.*?)[\s]?})',
$get_page_content, $reusable_matches );
if ( ! empty( $reusable_matches ) && isset(
$reusable_matches[1] ) && ! empty( $reusable_matches[1] ) ) {
$usable_content = '';
if ( is_array( $reusable_matches[1] ) ) {
foreach ( $reusable_matches[1] as $reusable_match_item
) {
$usable_content .= get_the_content( null, false,
intval( $reusable_match_item ) );
}
} else {
$usable_content = get_the_content( null, false,
intval( $reusable_matches[1] ) );
}
if ( ! empty( $usable_content ) ) {
$get_page_content .= $usable_content;
}
}
// Check if the content contains italic html selector and
include corresponding font weights and styles for it.
if ( strpos( $get_page_content, '<em>' ) !== false ) {
$include_italic_fonts = true;
}
foreach ( $global_styles['posts'][ $page_id ] as $block_index
=> $block_style ) {
$block_style_fonts = (array) $block_style->fonts;
foreach ( array_keys( $fonts ) as $font_key ) {
if ( array_key_exists( $font_key, $block_style_fonts )
) {
$fonts[ $font_key ] = array_merge( $fonts[
$font_key ], $block_style_fonts[ $font_key ] );
}
}
// Remove unnecessary styles from the database if IDS not
match or values are empty, because Gutenberg can change the block ID.
if ( ! empty( $get_page_content ) && property_exists(
$block_style, 'key' ) ) {
$blocks_removed = false;
if ( empty( $block_style->values ) ) {
$blocks_removed = true;
} elseif ( ! empty( $block_style->key ) && strpos(
$get_page_content, $block_style->key ) === false ) {
$blocks_removed = true;
}
if ( $blocks_removed ) {
unset( $global_styles['posts'][ $page_id ][
$block_index ] );
$update = true;
}
}
foreach ( $block_style->values as $block_element ) {
if ( ! empty( $block_element->styles ) ) {
$styles[] = $block_element->selector . '{' .
$block_element->styles . '}';
}
if ( isset( $block_element->tablet_styles ) && !
empty( $block_element->tablet_styles ) ) {
$styles[] = '@media (max-width: 1024px) { ' .
$block_element->selector . '{' . $block_element->tablet_styles . '} }';
}
if ( isset( $block_element->mobile_styles ) && !
empty( $block_element->mobile_styles ) ) {
$styles[] = '@media (max-width: 680px) { ' .
$block_element->selector . '{' . $block_element->mobile_styles . '} }';
}
if ( isset( $block_element->custom_styles ) && !
empty( $block_element->custom_styles ) ) {
foreach ( $block_element->custom_styles as
$custom_style ) {
if ( isset( $custom_style->value ) && ! empty(
$custom_style->value ) ) {
$styles[] = '@media (max-width: ' .
$custom_style->key . 'px) { ' . $block_element->selector . '{' .
$custom_style->value . '} }';
}
}
}
}
}
// Update global options indexes.
if ( $update ) {
$global_styles['posts'][ $page_id ] = array_values(
$global_styles['posts'][ $page_id ] );
}
}
// Enqueue Google Fonts.
if ( isset( $fonts['family'] ) && ! empty( $fonts['family'] ) ) {
$this->include_google_fonts( $fonts, $include_italic_fonts );
}
// Update global options.
if ( $update ) {
update_option( 'qi_blocks_global_styles', $global_styles );
}
// Load styles.
if ( ! empty( $styles ) ) {
wp_add_inline_style( 'qi-blocks-main', implode( ' ', $styles )
);
}
}
}
}}}
'''Why This Breaks Our Revision System:'''
When a user previews a revision via our system:
1. URL: `example.com/post-name/?revision=123&_wpnonce=abc123`
2. Our `the_content` filter correctly shows revision content in the main
post area
3. But qi-blocks calls `get_the_content()` directly, getting the
'''published''' content instead of '''revision''' content
4. This creates inconsistency: main content shows revision, but styles
are generated from published content
5. Result: Broken layout, missing styles, or incorrect styling for the
revision preview
=== Current Workarounds and Their Limitations ===
We've attempted several workarounds:
1. '''`the_posts` Filter''': Modifying post objects before they're
processed
* '''Issue''': Doesn't reliably intercept `get_the_content()` calls
* '''Timing''': Filter may run too early or late in the process
2. '''Global `$post` Object Modification''': Directly modifying the
global post
* '''Issue''': Risky and can cause conflicts with other plugins
* '''Scope''': May affect unintended parts of the application
3. '''Function Override''': Attempting to override `get_the_content()`
* '''Issue''': Not possible without modifying core files
* '''Maintenance''': Would break on WordPress updates
== Proposed Solution ==
Add a filter hook to the `get_the_content()` function, similar to existing
patterns in WordPress:
=== Suggested Implementation ===
{{{
function get_the_content( $more_link_text = null, $strip_teaser = false,
$post = null ) {
// ... existing code ...
// Before returning content, apply filter
$content = apply_filters( 'get_the_content', $content,
$more_link_text, $strip_teaser, $post );
return $content;
}
}}}
=== Alternative Filter Names ===
* `pre_get_the_content` - Applied before content processing
* `the_content_raw` - Applied to raw content before formatting
* `get_the_content_filter` - Explicit naming
== Benefits ==
=== 1. Plugin Compatibility ===
* Allows content modification plugins to work with all content access
methods
* Ensures consistent behavior between `the_content()` and
`get_the_content()`
* Enables advanced features like revision previews, content translation,
A/B testing
=== 2. Developer Experience ===
* Provides expected filtering capability that developers assume exists
* Maintains consistency with WordPress filter patterns
* Reduces need for complex workarounds
=== 3. Backward Compatibility ===
* Adding a filter hook is non-breaking
* Existing code continues to work unchanged
* Optional for developers who don't need it
== Use Cases Beyond Revision Systems ==
1. '''Content Translation Plugins''': Modify content based on user
language
2. '''A/B Testing Plugins''': Serve different content variations
3. '''Content Security Plugins''': Filter or sanitize content before
display
4. '''SEO Plugins''': Modify content for search engine optimization
5. '''Personalization Plugins''': Customize content based on user
preferences
6. '''Content Caching Plugins''': Implement custom caching strategies
== Technical Considerations ==
=== Performance Impact ===
* Minimal: Filter only runs when `get_the_content()` is called
* Optional: No performance impact if no filters are registered
* Consistent: Same pattern as existing WordPress filters
=== Implementation Location ===
* '''File''': `wp-includes/post-template.php`
* '''Function''': `get_the_content()`
* '''Line''': ~380 (before return statement)
=== Filter Parameters ===
{{{
apply_filters( 'get_the_content', $content, $more_link_text,
$strip_teaser, $post )
}}}
* `$content` (string): The post content
* `$more_link_text` (string): More link text
* `$strip_teaser` (bool): Whether to strip teaser
* `$post` (WP_Post|null): Post object
== Conclusion ==
Adding a filter hook to `get_the_content()` would:
* Solve real-world plugin compatibility issues
* Maintain consistency with WordPress patterns
* Enable advanced content management features
* Require minimal code changes with zero breaking changes
This enhancement would benefit the entire WordPress ecosystem by providing
developers with the filtering capabilities they need for modern content
management requirements.
== Related Functions That Have Filters ==
* `the_content()` - applies `the_content` filter
* `the_title()` - applies `the_title` filter
* `the_excerpt()` - applies `the_excerpt` filter
* `get_the_excerpt()` - applies `get_the_excerpt` filter
The absence of a filter in `get_the_content()` is an inconsistency that
should be addressed.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/41179#comment:9>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list